LCOV - code coverage report
Current view: top level - gcore - gdalmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 4514 5005 90.2 %
Date: 2024-05-03 15:49:35 Functions: 455 525 86.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * $Id$
       3             :  *
       4             :  * Name:     gdalmultidim.cpp
       5             :  * Project:  GDAL Core
       6             :  * Purpose:  GDAL Core C++/Private implementation for multidimensional support
       7             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
      11             :  *
      12             :  * Permission is hereby granted, free of charge, to any person obtaining a
      13             :  * copy of this software and associated documentation files (the "Software"),
      14             :  * to deal in the Software without restriction, including without limitation
      15             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      16             :  * and/or sell copies of the Software, and to permit persons to whom the
      17             :  * Software is furnished to do so, subject to the following conditions:
      18             :  *
      19             :  * The above copyright notice and this permission notice shall be included
      20             :  * in all copies or substantial portions of the Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      23             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      25             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      26             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      27             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      28             :  * DEALINGS IN THE SOFTWARE.
      29             :  ****************************************************************************/
      30             : 
      31             : #include <assert.h>
      32             : #include <algorithm>
      33             : #include <limits>
      34             : #include <queue>
      35             : #include <set>
      36             : #include <utility>
      37             : #include <time.h>
      38             : 
      39             : #include <ctype.h>  // isalnum
      40             : 
      41             : #include "cpl_error_internal.h"
      42             : #include "gdal_priv.h"
      43             : #include "gdal_pam.h"
      44             : #include "gdal_utils.h"
      45             : #include "cpl_safemaths.hpp"
      46             : #include "memmultidim.h"
      47             : #include "ogrsf_frmts.h"
      48             : #include "gdalmultidim_priv.h"
      49             : 
      50             : #if defined(__clang__) || defined(_MSC_VER)
      51             : #define COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT
      52             : #endif
      53             : 
      54             : /************************************************************************/
      55             : /*                       GDALMDArrayUnscaled                            */
      56             : /************************************************************************/
      57             : 
      58             : class GDALMDArrayUnscaled final : public GDALPamMDArray
      59             : {
      60             :   private:
      61             :     std::shared_ptr<GDALMDArray> m_poParent{};
      62             :     const GDALExtendedDataType m_dt;
      63             :     bool m_bHasNoData;
      64             :     const double m_dfScale;
      65             :     const double m_dfOffset;
      66             :     std::vector<GByte> m_abyRawNoData{};
      67             : 
      68             :   protected:
      69          13 :     explicit GDALMDArrayUnscaled(const std::shared_ptr<GDALMDArray> &poParent,
      70             :                                  double dfScale, double dfOffset,
      71             :                                  double dfOverriddenDstNodata, GDALDataType eDT)
      72          26 :         : GDALAbstractMDArray(std::string(),
      73          26 :                               "Unscaled view of " + poParent->GetFullName()),
      74             :           GDALPamMDArray(
      75          26 :               std::string(), "Unscaled view of " + poParent->GetFullName(),
      76          26 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
      77          13 :           m_poParent(std::move(poParent)),
      78             :           m_dt(GDALExtendedDataType::Create(eDT)),
      79          13 :           m_bHasNoData(m_poParent->GetRawNoDataValue() != nullptr),
      80          78 :           m_dfScale(dfScale), m_dfOffset(dfOffset)
      81             :     {
      82          13 :         m_abyRawNoData.resize(m_dt.GetSize());
      83             :         const auto eNonComplexDT =
      84          13 :             GDALGetNonComplexDataType(m_dt.GetNumericDataType());
      85          26 :         GDALCopyWords(&dfOverriddenDstNodata, GDT_Float64, 0,
      86          13 :                       m_abyRawNoData.data(), eNonComplexDT,
      87             :                       GDALGetDataTypeSizeBytes(eNonComplexDT),
      88          13 :                       GDALDataTypeIsComplex(m_dt.GetNumericDataType()) ? 2 : 1);
      89          13 :     }
      90             : 
      91             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
      92             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      93             :                const GDALExtendedDataType &bufferDataType,
      94             :                void *pDstBuffer) const override;
      95             : 
      96             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
      97             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
      98             :                 const GDALExtendedDataType &bufferDataType,
      99             :                 const void *pSrcBuffer) override;
     100             : 
     101           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
     102             :                      CSLConstList papszOptions) const override
     103             :     {
     104           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
     105             :     }
     106             : 
     107             :   public:
     108             :     static std::shared_ptr<GDALMDArrayUnscaled>
     109          13 :     Create(const std::shared_ptr<GDALMDArray> &poParent, double dfScale,
     110             :            double dfOffset, double dfDstNodata, GDALDataType eDT)
     111             :     {
     112             :         auto newAr(std::shared_ptr<GDALMDArrayUnscaled>(new GDALMDArrayUnscaled(
     113          13 :             poParent, dfScale, dfOffset, dfDstNodata, eDT)));
     114          13 :         newAr->SetSelf(newAr);
     115          13 :         return newAr;
     116             :     }
     117             : 
     118           1 :     bool IsWritable() const override
     119             :     {
     120           1 :         return m_poParent->IsWritable();
     121             :     }
     122             : 
     123          15 :     const std::string &GetFilename() const override
     124             :     {
     125          15 :         return m_poParent->GetFilename();
     126             :     }
     127             : 
     128             :     const std::vector<std::shared_ptr<GDALDimension>> &
     129         220 :     GetDimensions() const override
     130             :     {
     131         220 :         return m_poParent->GetDimensions();
     132             :     }
     133             : 
     134         103 :     const GDALExtendedDataType &GetDataType() const override
     135             :     {
     136         103 :         return m_dt;
     137             :     }
     138             : 
     139           1 :     const std::string &GetUnit() const override
     140             :     {
     141           1 :         return m_poParent->GetUnit();
     142             :     }
     143             : 
     144           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
     145             :     {
     146           1 :         return m_poParent->GetSpatialRef();
     147             :     }
     148             : 
     149           6 :     const void *GetRawNoDataValue() const override
     150             :     {
     151           6 :         return m_bHasNoData ? m_abyRawNoData.data() : nullptr;
     152             :     }
     153             : 
     154           1 :     bool SetRawNoDataValue(const void *pRawNoData) override
     155             :     {
     156           1 :         m_bHasNoData = true;
     157           1 :         memcpy(m_abyRawNoData.data(), pRawNoData, m_dt.GetSize());
     158           1 :         return true;
     159             :     }
     160             : 
     161           4 :     std::vector<GUInt64> GetBlockSize() const override
     162             :     {
     163           4 :         return m_poParent->GetBlockSize();
     164             :     }
     165             : 
     166             :     std::shared_ptr<GDALAttribute>
     167           0 :     GetAttribute(const std::string &osName) const override
     168             :     {
     169           0 :         return m_poParent->GetAttribute(osName);
     170             :     }
     171             : 
     172             :     std::vector<std::shared_ptr<GDALAttribute>>
     173           1 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
     174             :     {
     175           1 :         return m_poParent->GetAttributes(papszOptions);
     176             :     }
     177             : 
     178           0 :     bool SetUnit(const std::string &osUnit) override
     179             :     {
     180           0 :         return m_poParent->SetUnit(osUnit);
     181             :     }
     182             : 
     183           0 :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override
     184             :     {
     185           0 :         return m_poParent->SetSpatialRef(poSRS);
     186             :     }
     187             : 
     188             :     std::shared_ptr<GDALAttribute>
     189           1 :     CreateAttribute(const std::string &osName,
     190             :                     const std::vector<GUInt64> &anDimensions,
     191             :                     const GDALExtendedDataType &oDataType,
     192             :                     CSLConstList papszOptions = nullptr) override
     193             :     {
     194           1 :         return m_poParent->CreateAttribute(osName, anDimensions, oDataType,
     195           1 :                                            papszOptions);
     196             :     }
     197             : };
     198             : 
     199             : /************************************************************************/
     200             : /*                         ~GDALIHasAttribute()                         */
     201             : /************************************************************************/
     202             : 
     203             : GDALIHasAttribute::~GDALIHasAttribute() = default;
     204             : 
     205             : /************************************************************************/
     206             : /*                            GetAttribute()                            */
     207             : /************************************************************************/
     208             : 
     209             : /** Return an attribute by its name.
     210             :  *
     211             :  * If the attribute does not exist, nullptr should be silently returned.
     212             :  *
     213             :  * @note Driver implementation: this method will fallback to
     214             :  * GetAttributeFromAttributes() is not explicitly implemented
     215             :  *
     216             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     217             :  *
     218             :  * This is the same as the C function GDALGroupGetAttribute() or
     219             :  * GDALMDArrayGetAttribute().
     220             :  *
     221             :  * @param osName Attribute name
     222             :  * @return the attribute, or nullptr if it does not exist or an error occurred.
     223             :  */
     224             : std::shared_ptr<GDALAttribute>
     225         712 : GDALIHasAttribute::GetAttribute(const std::string &osName) const
     226             : {
     227         712 :     return GetAttributeFromAttributes(osName);
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                       GetAttributeFromAttributes()                   */
     232             : /************************************************************************/
     233             : 
     234             : /** Possible fallback implementation for GetAttribute() using GetAttributes().
     235             :  */
     236             : std::shared_ptr<GDALAttribute>
     237         712 : GDALIHasAttribute::GetAttributeFromAttributes(const std::string &osName) const
     238             : {
     239        1424 :     auto attrs(GetAttributes());
     240        3921 :     for (const auto &attr : attrs)
     241             :     {
     242        3707 :         if (attr->GetName() == osName)
     243         498 :             return attr;
     244             :     }
     245         214 :     return nullptr;
     246             : }
     247             : 
     248             : /************************************************************************/
     249             : /*                           GetAttributes()                            */
     250             : /************************************************************************/
     251             : 
     252             : /** Return the list of attributes contained in a GDALMDArray or GDALGroup.
     253             :  *
     254             :  * If the attribute does not exist, nullptr should be silently returned.
     255             :  *
     256             :  * @note Driver implementation: optionally implemented. If implemented,
     257             :  * GetAttribute() should also be implemented.
     258             :  *
     259             :  * Drivers known to implement it for groups and arrays: MEM, netCDF.
     260             :  *
     261             :  * This is the same as the C function GDALGroupGetAttributes() or
     262             :  * GDALMDArrayGetAttributes().
     263             : 
     264             :  * @param papszOptions Driver specific options determining how attributes
     265             :  * should be retrieved. Pass nullptr for default behavior.
     266             :  *
     267             :  * @return the attributes.
     268             :  */
     269             : std::vector<std::shared_ptr<GDALAttribute>>
     270          23 : GDALIHasAttribute::GetAttributes(CPL_UNUSED CSLConstList papszOptions) const
     271             : {
     272          23 :     return {};
     273             : }
     274             : 
     275             : /************************************************************************/
     276             : /*                             CreateAttribute()                         */
     277             : /************************************************************************/
     278             : 
     279             : /** Create an attribute within a GDALMDArray or GDALGroup.
     280             :  *
     281             :  * The attribute might not be "physically" created until a value is written
     282             :  * into it.
     283             :  *
     284             :  * Optionally implemented.
     285             :  *
     286             :  * Drivers known to implement it: MEM, netCDF
     287             :  *
     288             :  * This is the same as the C function GDALGroupCreateAttribute() or
     289             :  * GDALMDArrayCreateAttribute()
     290             :  *
     291             :  * @param osName Attribute name.
     292             :  * @param anDimensions List of dimension sizes, ordered from the slowest varying
     293             :  *                     dimension first to the fastest varying dimension last.
     294             :  *                     Empty for a scalar attribute (common case)
     295             :  * @param oDataType  Attribute data type.
     296             :  * @param papszOptions Driver specific options determining how the attribute.
     297             :  * should be created.
     298             :  *
     299             :  * @return the new attribute, or nullptr if case of error
     300             :  */
     301           0 : std::shared_ptr<GDALAttribute> GDALIHasAttribute::CreateAttribute(
     302             :     CPL_UNUSED const std::string &osName,
     303             :     CPL_UNUSED const std::vector<GUInt64> &anDimensions,
     304             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     305             :     CPL_UNUSED CSLConstList papszOptions)
     306             : {
     307           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     308             :              "CreateAttribute() not implemented");
     309           0 :     return nullptr;
     310             : }
     311             : 
     312             : /************************************************************************/
     313             : /*                          DeleteAttribute()                           */
     314             : /************************************************************************/
     315             : 
     316             : /** Delete an attribute from a GDALMDArray or GDALGroup.
     317             :  *
     318             :  * Optionally implemented.
     319             :  *
     320             :  * After this call, if a previously obtained instance of the deleted object
     321             :  * is still alive, no method other than for freeing it should be invoked.
     322             :  *
     323             :  * Drivers known to implement it: MEM, netCDF
     324             :  *
     325             :  * This is the same as the C function GDALGroupDeleteAttribute() or
     326             :  * GDALMDArrayDeleteAttribute()
     327             :  *
     328             :  * @param osName Attribute name.
     329             :  * @param papszOptions Driver specific options determining how the attribute.
     330             :  * should be deleted.
     331             :  *
     332             :  * @return true in case of success
     333             :  * @since GDAL 3.8
     334             :  */
     335           0 : bool GDALIHasAttribute::DeleteAttribute(CPL_UNUSED const std::string &osName,
     336             :                                         CPL_UNUSED CSLConstList papszOptions)
     337             : {
     338           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     339             :              "DeleteAttribute() not implemented");
     340           0 :     return false;
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*                            GDALGroup()                               */
     345             : /************************************************************************/
     346             : 
     347             : //! @cond Doxygen_Suppress
     348        6451 : GDALGroup::GDALGroup(const std::string &osParentName, const std::string &osName,
     349        6451 :                      const std::string &osContext)
     350        6451 :     : m_osName(osParentName.empty() ? "/" : osName),
     351             :       m_osFullName(
     352       12902 :           !osParentName.empty()
     353        9925 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
     354             :               : "/"),
     355       16376 :       m_osContext(osContext)
     356             : {
     357        6451 : }
     358             : 
     359             : //! @endcond
     360             : 
     361             : /************************************************************************/
     362             : /*                            ~GDALGroup()                              */
     363             : /************************************************************************/
     364             : 
     365             : GDALGroup::~GDALGroup() = default;
     366             : 
     367             : /************************************************************************/
     368             : /*                          GetMDArrayNames()                           */
     369             : /************************************************************************/
     370             : 
     371             : /** Return the list of multidimensional array names contained in this group.
     372             :  *
     373             :  * @note Driver implementation: optionally implemented. If implemented,
     374             :  * OpenMDArray() should also be implemented.
     375             :  *
     376             :  * Drivers known to implement it: MEM, netCDF.
     377             :  *
     378             :  * This is the same as the C function GDALGroupGetMDArrayNames().
     379             :  *
     380             :  * @param papszOptions Driver specific options determining how arrays
     381             :  * should be retrieved. Pass nullptr for default behavior.
     382             :  *
     383             :  * @return the array names.
     384             :  */
     385             : std::vector<std::string>
     386           0 : GDALGroup::GetMDArrayNames(CPL_UNUSED CSLConstList papszOptions) const
     387             : {
     388           0 :     return {};
     389             : }
     390             : 
     391             : /************************************************************************/
     392             : /*                            OpenMDArray()                             */
     393             : /************************************************************************/
     394             : 
     395             : /** Open and return a multidimensional array.
     396             :  *
     397             :  * @note Driver implementation: optionally implemented. If implemented,
     398             :  * GetMDArrayNames() should also be implemented.
     399             :  *
     400             :  * Drivers known to implement it: MEM, netCDF.
     401             :  *
     402             :  * This is the same as the C function GDALGroupOpenMDArray().
     403             :  *
     404             :  * @param osName Array name.
     405             :  * @param papszOptions Driver specific options determining how the array should
     406             :  * be opened.  Pass nullptr for default behavior.
     407             :  *
     408             :  * @return the array, or nullptr.
     409             :  */
     410             : std::shared_ptr<GDALMDArray>
     411           0 : GDALGroup::OpenMDArray(CPL_UNUSED const std::string &osName,
     412             :                        CPL_UNUSED CSLConstList papszOptions) const
     413             : {
     414           0 :     return nullptr;
     415             : }
     416             : 
     417             : /************************************************************************/
     418             : /*                           GetGroupNames()                            */
     419             : /************************************************************************/
     420             : 
     421             : /** Return the list of sub-groups contained in this group.
     422             :  *
     423             :  * @note Driver implementation: optionally implemented. If implemented,
     424             :  * OpenGroup() should also be implemented.
     425             :  *
     426             :  * Drivers known to implement it: MEM, netCDF.
     427             :  *
     428             :  * This is the same as the C function GDALGroupGetGroupNames().
     429             :  *
     430             :  * @param papszOptions Driver specific options determining how groups
     431             :  * should be retrieved. Pass nullptr for default behavior.
     432             :  *
     433             :  * @return the group names.
     434             :  */
     435             : std::vector<std::string>
     436           4 : GDALGroup::GetGroupNames(CPL_UNUSED CSLConstList papszOptions) const
     437             : {
     438           4 :     return {};
     439             : }
     440             : 
     441             : /************************************************************************/
     442             : /*                             OpenGroup()                              */
     443             : /************************************************************************/
     444             : 
     445             : /** Open and return a sub-group.
     446             :  *
     447             :  * @note Driver implementation: optionally implemented. If implemented,
     448             :  * GetGroupNames() should also be implemented.
     449             :  *
     450             :  * Drivers known to implement it: MEM, netCDF.
     451             :  *
     452             :  * This is the same as the C function GDALGroupOpenGroup().
     453             :  *
     454             :  * @param osName Sub-group name.
     455             :  * @param papszOptions Driver specific options determining how the sub-group
     456             :  * should be opened.  Pass nullptr for default behavior.
     457             :  *
     458             :  * @return the group, or nullptr.
     459             :  */
     460             : std::shared_ptr<GDALGroup>
     461           4 : GDALGroup::OpenGroup(CPL_UNUSED const std::string &osName,
     462             :                      CPL_UNUSED CSLConstList papszOptions) const
     463             : {
     464           4 :     return nullptr;
     465             : }
     466             : 
     467             : /************************************************************************/
     468             : /*                        GetVectorLayerNames()                         */
     469             : /************************************************************************/
     470             : 
     471             : /** Return the list of layer names contained in this group.
     472             :  *
     473             :  * @note Driver implementation: optionally implemented. If implemented,
     474             :  * OpenVectorLayer() should also be implemented.
     475             :  *
     476             :  * Drivers known to implement it: OpenFileGDB, FileGDB
     477             :  *
     478             :  * Other drivers will return an empty list. GDALDataset::GetLayerCount() and
     479             :  * GDALDataset::GetLayer() should then be used.
     480             :  *
     481             :  * This is the same as the C function GDALGroupGetVectorLayerNames().
     482             :  *
     483             :  * @param papszOptions Driver specific options determining how layers
     484             :  * should be retrieved. Pass nullptr for default behavior.
     485             :  *
     486             :  * @return the vector layer names.
     487             :  * @since GDAL 3.4
     488             :  */
     489             : std::vector<std::string>
     490           1 : GDALGroup::GetVectorLayerNames(CPL_UNUSED CSLConstList papszOptions) const
     491             : {
     492           1 :     return {};
     493             : }
     494             : 
     495             : /************************************************************************/
     496             : /*                           OpenVectorLayer()                          */
     497             : /************************************************************************/
     498             : 
     499             : /** Open and return a vector layer.
     500             :  *
     501             :  * Due to the historical ownership of OGRLayer* by GDALDataset*, the
     502             :  * lifetime of the returned OGRLayer* is linked to the one of the owner
     503             :  * dataset (contrary to the general design of this class where objects can be
     504             :  * used independently of the object that returned them)
     505             :  *
     506             :  * @note Driver implementation: optionally implemented. If implemented,
     507             :  * GetVectorLayerNames() should also be implemented.
     508             :  *
     509             :  * Drivers known to implement it: MEM, netCDF.
     510             :  *
     511             :  * This is the same as the C function GDALGroupOpenVectorLayer().
     512             :  *
     513             :  * @param osName Vector layer name.
     514             :  * @param papszOptions Driver specific options determining how the layer should
     515             :  * be opened.  Pass nullptr for default behavior.
     516             :  *
     517             :  * @return the group, or nullptr.
     518             :  */
     519           2 : OGRLayer *GDALGroup::OpenVectorLayer(CPL_UNUSED const std::string &osName,
     520             :                                      CPL_UNUSED CSLConstList papszOptions) const
     521             : {
     522           2 :     return nullptr;
     523             : }
     524             : 
     525             : /************************************************************************/
     526             : /*                             GetDimensions()                          */
     527             : /************************************************************************/
     528             : 
     529             : /** Return the list of dimensions contained in this group and used by its
     530             :  * arrays.
     531             :  *
     532             :  * This is for dimensions that can potentially be used by several arrays.
     533             :  * Not all drivers might implement this. To retrieve the dimensions used by
     534             :  * a specific array, use GDALMDArray::GetDimensions().
     535             :  *
     536             :  * Drivers known to implement it: MEM, netCDF
     537             :  *
     538             :  * This is the same as the C function GDALGroupGetDimensions().
     539             :  *
     540             :  * @param papszOptions Driver specific options determining how groups
     541             :  * should be retrieved. Pass nullptr for default behavior.
     542             :  *
     543             :  * @return the dimensions.
     544             :  */
     545             : std::vector<std::shared_ptr<GDALDimension>>
     546          11 : GDALGroup::GetDimensions(CPL_UNUSED CSLConstList papszOptions) const
     547             : {
     548          11 :     return {};
     549             : }
     550             : 
     551             : /************************************************************************/
     552             : /*                         GetStructuralInfo()                          */
     553             : /************************************************************************/
     554             : 
     555             : /** Return structural information on the group.
     556             :  *
     557             :  * This may be the compression, etc..
     558             :  *
     559             :  * The return value should not be freed and is valid until GDALGroup is
     560             :  * released or this function called again.
     561             :  *
     562             :  * This is the same as the C function GDALGroupGetStructuralInfo().
     563             :  */
     564          29 : CSLConstList GDALGroup::GetStructuralInfo() const
     565             : {
     566          29 :     return nullptr;
     567             : }
     568             : 
     569             : /************************************************************************/
     570             : /*                              CreateGroup()                           */
     571             : /************************************************************************/
     572             : 
     573             : /** Create a sub-group within a group.
     574             :  *
     575             :  * Optionally implemented by drivers.
     576             :  *
     577             :  * Drivers known to implement it: MEM, netCDF
     578             :  *
     579             :  * This is the same as the C function GDALGroupCreateGroup().
     580             :  *
     581             :  * @param osName Sub-group name.
     582             :  * @param papszOptions Driver specific options determining how the sub-group
     583             :  * should be created.
     584             :  *
     585             :  * @return the new sub-group, or nullptr in case of error.
     586             :  */
     587             : std::shared_ptr<GDALGroup>
     588           0 : GDALGroup::CreateGroup(CPL_UNUSED const std::string &osName,
     589             :                        CPL_UNUSED CSLConstList papszOptions)
     590             : {
     591           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateGroup() not implemented");
     592           0 :     return nullptr;
     593             : }
     594             : 
     595             : /************************************************************************/
     596             : /*                          DeleteGroup()                               */
     597             : /************************************************************************/
     598             : 
     599             : /** Delete a sub-group from a group.
     600             :  *
     601             :  * Optionally implemented.
     602             :  *
     603             :  * After this call, if a previously obtained instance of the deleted object
     604             :  * is still alive, no method other than for freeing it should be invoked.
     605             :  *
     606             :  * Drivers known to implement it: MEM, Zarr
     607             :  *
     608             :  * This is the same as the C function GDALGroupDeleteGroup().
     609             :  *
     610             :  * @param osName Sub-group name.
     611             :  * @param papszOptions Driver specific options determining how the group.
     612             :  * should be deleted.
     613             :  *
     614             :  * @return true in case of success
     615             :  * @since GDAL 3.8
     616             :  */
     617           0 : bool GDALGroup::DeleteGroup(CPL_UNUSED const std::string &osName,
     618             :                             CPL_UNUSED CSLConstList papszOptions)
     619             : {
     620           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteGroup() not implemented");
     621           0 :     return false;
     622             : }
     623             : 
     624             : /************************************************************************/
     625             : /*                            CreateDimension()                         */
     626             : /************************************************************************/
     627             : 
     628             : /** Create a dimension within a group.
     629             :  *
     630             :  * @note Driver implementation: drivers supporting CreateDimension() should
     631             :  * implement this method, but do not have necessarily to implement
     632             :  * GDALGroup::GetDimensions().
     633             :  *
     634             :  * Drivers known to implement it: MEM, netCDF
     635             :  *
     636             :  * This is the same as the C function GDALGroupCreateDimension().
     637             :  *
     638             :  * @param osName Dimension name.
     639             :  * @param osType Dimension type (might be empty, and ignored by drivers)
     640             :  * @param osDirection Dimension direction (might be empty, and ignored by
     641             :  * drivers)
     642             :  * @param nSize  Number of values indexed by this dimension. Should be > 0.
     643             :  * @param papszOptions Driver specific options determining how the dimension
     644             :  * should be created.
     645             :  *
     646             :  * @return the new dimension, or nullptr if case of error
     647             :  */
     648           0 : std::shared_ptr<GDALDimension> GDALGroup::CreateDimension(
     649             :     CPL_UNUSED const std::string &osName, CPL_UNUSED const std::string &osType,
     650             :     CPL_UNUSED const std::string &osDirection, CPL_UNUSED GUInt64 nSize,
     651             :     CPL_UNUSED CSLConstList papszOptions)
     652             : {
     653           0 :     CPLError(CE_Failure, CPLE_NotSupported,
     654             :              "CreateDimension() not implemented");
     655           0 :     return nullptr;
     656             : }
     657             : 
     658             : /************************************************************************/
     659             : /*                             CreateMDArray()                          */
     660             : /************************************************************************/
     661             : 
     662             : /** Create a multidimensional array within a group.
     663             :  *
     664             :  * It is recommended that the GDALDimension objects passed in aoDimensions
     665             :  * belong to this group, either by retrieving them with GetDimensions()
     666             :  * or creating a new one with CreateDimension().
     667             :  *
     668             :  * Optionally implemented.
     669             :  *
     670             :  * Drivers known to implement it: MEM, netCDF
     671             :  *
     672             :  * This is the same as the C function GDALGroupCreateMDArray().
     673             :  *
     674             :  * @note Driver implementation: drivers should take into account the possibility
     675             :  * that GDALDimension object passed in aoDimensions might belong to a different
     676             :  * group / dataset / driver and act accordingly.
     677             :  *
     678             :  * @param osName Array name.
     679             :  * @param aoDimensions List of dimensions, ordered from the slowest varying
     680             :  *                     dimension first to the fastest varying dimension last.
     681             :  *                     Might be empty for a scalar array (if supported by
     682             :  * driver)
     683             :  * @param oDataType  Array data type.
     684             :  * @param papszOptions Driver specific options determining how the array
     685             :  * should be created.
     686             :  *
     687             :  * @return the new array, or nullptr if case of error
     688             :  */
     689           0 : std::shared_ptr<GDALMDArray> GDALGroup::CreateMDArray(
     690             :     CPL_UNUSED const std::string &osName,
     691             :     CPL_UNUSED const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     692             :     CPL_UNUSED const GDALExtendedDataType &oDataType,
     693             :     CPL_UNUSED CSLConstList papszOptions)
     694             : {
     695           0 :     CPLError(CE_Failure, CPLE_NotSupported, "CreateMDArray() not implemented");
     696           0 :     return nullptr;
     697             : }
     698             : 
     699             : /************************************************************************/
     700             : /*                          DeleteMDArray()                             */
     701             : /************************************************************************/
     702             : 
     703             : /** Delete an array from a group.
     704             :  *
     705             :  * Optionally implemented.
     706             :  *
     707             :  * After this call, if a previously obtained instance of the deleted object
     708             :  * is still alive, no method other than for freeing it should be invoked.
     709             :  *
     710             :  * Drivers known to implement it: MEM, Zarr
     711             :  *
     712             :  * This is the same as the C function GDALGroupDeleteMDArray().
     713             :  *
     714             :  * @param osName Arrayname.
     715             :  * @param papszOptions Driver specific options determining how the array.
     716             :  * should be deleted.
     717             :  *
     718             :  * @return true in case of success
     719             :  * @since GDAL 3.8
     720             :  */
     721           0 : bool GDALGroup::DeleteMDArray(CPL_UNUSED const std::string &osName,
     722             :                               CPL_UNUSED CSLConstList papszOptions)
     723             : {
     724           0 :     CPLError(CE_Failure, CPLE_NotSupported, "DeleteMDArray() not implemented");
     725           0 :     return false;
     726             : }
     727             : 
     728             : /************************************************************************/
     729             : /*                           GetTotalCopyCost()                         */
     730             : /************************************************************************/
     731             : 
     732             : /** Return a total "cost" to copy the group.
     733             :  *
     734             :  * Used as a parameter for CopFrom()
     735             :  */
     736          22 : GUInt64 GDALGroup::GetTotalCopyCost() const
     737             : {
     738          22 :     GUInt64 nCost = COPY_COST;
     739          22 :     nCost += GetAttributes().size() * GDALAttribute::COPY_COST;
     740             : 
     741          44 :     auto groupNames = GetGroupNames();
     742          26 :     for (const auto &name : groupNames)
     743             :     {
     744           8 :         auto subGroup = OpenGroup(name);
     745           4 :         if (subGroup)
     746             :         {
     747           4 :             nCost += subGroup->GetTotalCopyCost();
     748             :         }
     749             :     }
     750             : 
     751          22 :     auto arrayNames = GetMDArrayNames();
     752          61 :     for (const auto &name : arrayNames)
     753             :     {
     754          78 :         auto array = OpenMDArray(name);
     755          39 :         if (array)
     756             :         {
     757          39 :             nCost += array->GetTotalCopyCost();
     758             :         }
     759             :     }
     760          44 :     return nCost;
     761             : }
     762             : 
     763             : /************************************************************************/
     764             : /*                               CopyFrom()                             */
     765             : /************************************************************************/
     766             : 
     767             : /** Copy the content of a group into a new (generally empty) group.
     768             :  *
     769             :  * @param poDstRootGroup Destination root group. Must NOT be nullptr.
     770             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
     771             :  *                   of some output drivers this is not recommended)
     772             :  * @param poSrcGroup Source group. Must NOT be nullptr.
     773             :  * @param bStrict Whether to enable stict mode. In strict mode, any error will
     774             :  *                stop the copy. In relaxed mode, the copy will be attempted to
     775             :  *                be pursued.
     776             :  * @param nCurCost  Should be provided as a variable initially set to 0.
     777             :  * @param nTotalCost Total cost from GetTotalCopyCost().
     778             :  * @param pfnProgress Progress callback, or nullptr.
     779             :  * @param pProgressData Progress user data, or nulptr.
     780             :  * @param papszOptions Creation options. Currently, only array creation
     781             :  *                     options are supported. They must be prefixed with
     782             :  * "ARRAY:" . The scope may be further restricted to arrays of a certain
     783             :  *                     dimension by adding "IF(DIM={ndims}):" after "ARRAY:".
     784             :  *                     For example, "ARRAY:IF(DIM=2):BLOCKSIZE=256,256" will
     785             :  *                     restrict BLOCKSIZE=256,256 to arrays of dimension 2.
     786             :  *                     Restriction to arrays of a given name is done with adding
     787             :  *                     "IF(NAME={name}):" after "ARRAY:". {name} can also be
     788             :  *                     a full qualified name.
     789             :  *                     A non-driver specific ARRAY option, "AUTOSCALE=YES" can
     790             :  * be used to ask (non indexing) variables of type Float32 or Float64 to be
     791             :  * scaled to UInt16 with scale and offset values being computed from the minimum
     792             :  * and maximum of the source array. The integer data type used can be set with
     793             :  *                     AUTOSCALE_DATA_TYPE=Byte/UInt16/Int16/UInt32/Int32.
     794             :  *
     795             :  * @return true in case of success (or partial success if bStrict == false).
     796             :  */
     797          22 : bool GDALGroup::CopyFrom(const std::shared_ptr<GDALGroup> &poDstRootGroup,
     798             :                          GDALDataset *poSrcDS,
     799             :                          const std::shared_ptr<GDALGroup> &poSrcGroup,
     800             :                          bool bStrict, GUInt64 &nCurCost,
     801             :                          const GUInt64 nTotalCost, GDALProgressFunc pfnProgress,
     802             :                          void *pProgressData, CSLConstList papszOptions)
     803             : {
     804          22 :     if (pfnProgress == nullptr)
     805           0 :         pfnProgress = GDALDummyProgress;
     806             : 
     807             : #define EXIT_OR_CONTINUE_IF_NULL(x)                                            \
     808             :     if (!(x))                                                                  \
     809             :     {                                                                          \
     810             :         if (bStrict)                                                           \
     811             :             return false;                                                      \
     812             :         continue;                                                              \
     813             :     }                                                                          \
     814             :     (void)0
     815             : 
     816             :     try
     817             :     {
     818          22 :         nCurCost += GDALGroup::COPY_COST;
     819             : 
     820          44 :         const auto srcDims = poSrcGroup->GetDimensions();
     821             :         std::map<std::string, std::shared_ptr<GDALDimension>>
     822          44 :             mapExistingDstDims;
     823          44 :         std::map<std::string, std::string> mapSrcVariableNameToIndexedDimName;
     824          56 :         for (const auto &dim : srcDims)
     825             :         {
     826             :             auto dstDim = CreateDimension(dim->GetName(), dim->GetType(),
     827          34 :                                           dim->GetDirection(), dim->GetSize());
     828          34 :             EXIT_OR_CONTINUE_IF_NULL(dstDim);
     829          34 :             mapExistingDstDims[dim->GetName()] = std::move(dstDim);
     830          68 :             auto poIndexingVarSrc(dim->GetIndexingVariable());
     831          34 :             if (poIndexingVarSrc)
     832             :             {
     833             :                 mapSrcVariableNameToIndexedDimName[poIndexingVarSrc
     834          16 :                                                        ->GetName()] =
     835          32 :                     dim->GetName();
     836             :             }
     837             :         }
     838             : 
     839          44 :         auto attrs = poSrcGroup->GetAttributes();
     840          28 :         for (const auto &attr : attrs)
     841             :         {
     842             :             auto dstAttr =
     843           6 :                 CreateAttribute(attr->GetName(), attr->GetDimensionsSize(),
     844          12 :                                 attr->GetDataType());
     845           6 :             EXIT_OR_CONTINUE_IF_NULL(dstAttr);
     846           6 :             auto raw(attr->ReadAsRaw());
     847           6 :             if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
     848           0 :                 return false;
     849             :         }
     850          22 :         if (!attrs.empty())
     851             :         {
     852           4 :             nCurCost += attrs.size() * GDALAttribute::COPY_COST;
     853           4 :             if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
     854           0 :                 return false;
     855             :         }
     856             : 
     857             :         const auto CopyArray =
     858          39 :             [this, &poSrcDS, &poDstRootGroup, &mapExistingDstDims,
     859             :              &mapSrcVariableNameToIndexedDimName, pfnProgress, pProgressData,
     860             :              papszOptions, bStrict, &nCurCost,
     861         356 :              nTotalCost](const std::shared_ptr<GDALMDArray> &srcArray)
     862             :         {
     863             :             // Map source dimensions to target dimensions
     864          78 :             std::vector<std::shared_ptr<GDALDimension>> dstArrayDims;
     865          39 :             const auto &srcArrayDims(srcArray->GetDimensions());
     866          99 :             for (const auto &dim : srcArrayDims)
     867             :             {
     868             :                 auto dstDim = poDstRootGroup->OpenDimensionFromFullname(
     869          60 :                     dim->GetFullName());
     870          60 :                 if (dstDim && dstDim->GetSize() == dim->GetSize())
     871             :                 {
     872          50 :                     dstArrayDims.emplace_back(dstDim);
     873             :                 }
     874             :                 else
     875             :                 {
     876          10 :                     auto oIter = mapExistingDstDims.find(dim->GetName());
     877          19 :                     if (oIter != mapExistingDstDims.end() &&
     878           9 :                         oIter->second->GetSize() == dim->GetSize())
     879             :                     {
     880           8 :                         dstArrayDims.emplace_back(oIter->second);
     881             :                     }
     882             :                     else
     883             :                     {
     884           2 :                         std::string newDimName;
     885           2 :                         if (oIter == mapExistingDstDims.end())
     886             :                         {
     887           1 :                             newDimName = dim->GetName();
     888             :                         }
     889             :                         else
     890             :                         {
     891           1 :                             std::string newDimNamePrefix(srcArray->GetName() +
     892           3 :                                                          '_' + dim->GetName());
     893           1 :                             newDimName = newDimNamePrefix;
     894           1 :                             int nIterCount = 2;
     895           1 :                             while (mapExistingDstDims.find(newDimName) !=
     896           2 :                                    mapExistingDstDims.end())
     897             :                             {
     898           0 :                                 newDimName = newDimNamePrefix +
     899           0 :                                              CPLSPrintf("_%d", nIterCount);
     900           0 :                                 nIterCount++;
     901             :                             }
     902             :                         }
     903           4 :                         dstDim = CreateDimension(newDimName, dim->GetType(),
     904             :                                                  dim->GetDirection(),
     905           4 :                                                  dim->GetSize());
     906           2 :                         if (!dstDim)
     907           0 :                             return false;
     908           2 :                         mapExistingDstDims[newDimName] = dstDim;
     909           2 :                         dstArrayDims.emplace_back(dstDim);
     910             :                     }
     911             :                 }
     912             :             }
     913             : 
     914          78 :             CPLStringList aosArrayCO;
     915          39 :             bool bAutoScale = false;
     916          39 :             GDALDataType eAutoScaleType = GDT_UInt16;
     917          46 :             for (const char *pszItem : cpl::Iterate(papszOptions))
     918             :             {
     919           7 :                 if (STARTS_WITH_CI(pszItem, "ARRAY:"))
     920             :                 {
     921           7 :                     const char *pszOption = pszItem + strlen("ARRAY:");
     922           7 :                     if (STARTS_WITH_CI(pszOption, "IF(DIM="))
     923             :                     {
     924           1 :                         const char *pszNext = strchr(pszOption, ':');
     925           1 :                         if (pszNext != nullptr)
     926             :                         {
     927           1 :                             int nDim = atoi(pszOption + strlen("IF(DIM="));
     928           1 :                             if (static_cast<size_t>(nDim) ==
     929           1 :                                 dstArrayDims.size())
     930             :                             {
     931           1 :                                 pszOption = pszNext + 1;
     932             :                             }
     933             :                             else
     934             :                             {
     935           0 :                                 pszOption = nullptr;
     936             :                             }
     937             :                         }
     938             :                     }
     939           6 :                     else if (STARTS_WITH_CI(pszOption, "IF(NAME="))
     940             :                     {
     941           2 :                         const char *pszName = pszOption + strlen("IF(NAME=");
     942           2 :                         const char *pszNext = strchr(pszName, ':');
     943           2 :                         if (pszNext != nullptr && pszNext > pszName &&
     944           2 :                             pszNext[-1] == ')')
     945             :                         {
     946           4 :                             CPLString osName;
     947           2 :                             osName.assign(pszName, pszNext - pszName - 1);
     948           3 :                             if (osName == srcArray->GetName() ||
     949           1 :                                 osName == srcArray->GetFullName())
     950             :                             {
     951           2 :                                 pszOption = pszNext + 1;
     952             :                             }
     953             :                             else
     954             :                             {
     955           0 :                                 pszOption = nullptr;
     956             :                             }
     957             :                         }
     958             :                     }
     959           7 :                     if (pszOption)
     960             :                     {
     961           7 :                         if (STARTS_WITH_CI(pszOption, "AUTOSCALE="))
     962             :                         {
     963             :                             bAutoScale =
     964           2 :                                 CPLTestBool(pszOption + strlen("AUTOSCALE="));
     965             :                         }
     966           5 :                         else if (STARTS_WITH_CI(pszOption,
     967             :                                                 "AUTOSCALE_DATA_TYPE="))
     968             :                         {
     969           1 :                             const char *pszDataType =
     970             :                                 pszOption + strlen("AUTOSCALE_DATA_TYPE=");
     971           1 :                             eAutoScaleType = GDALGetDataTypeByName(pszDataType);
     972           2 :                             if (GDALDataTypeIsComplex(eAutoScaleType) ||
     973           1 :                                 GDALDataTypeIsFloating(eAutoScaleType))
     974             :                             {
     975           0 :                                 CPLError(CE_Failure, CPLE_NotSupported,
     976             :                                          "Unsupported value for "
     977             :                                          "AUTOSCALE_DATA_TYPE");
     978           0 :                                 return false;
     979             :                             }
     980             :                         }
     981             :                         else
     982             :                         {
     983           4 :                             aosArrayCO.AddString(pszOption);
     984             :                         }
     985             :                     }
     986             :                 }
     987             :             }
     988             : 
     989             :             auto oIterDimName =
     990          39 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
     991          39 :             const auto &srcArrayType = srcArray->GetDataType();
     992             : 
     993          39 :             std::shared_ptr<GDALMDArray> dstArray;
     994             : 
     995             :             // Only autoscale non-indexing variables
     996          39 :             bool bHasOffset = false;
     997          39 :             bool bHasScale = false;
     998           4 :             if (bAutoScale && srcArrayType.GetClass() == GEDTC_NUMERIC &&
     999           2 :                 (srcArrayType.GetNumericDataType() == GDT_Float32 ||
    1000           0 :                  srcArrayType.GetNumericDataType() == GDT_Float64) &&
    1001           2 :                 srcArray->GetOffset(&bHasOffset) == 0.0 && !bHasOffset &&
    1002          43 :                 srcArray->GetScale(&bHasScale) == 1.0 && !bHasScale &&
    1003          41 :                 oIterDimName == mapSrcVariableNameToIndexedDimName.end())
    1004             :             {
    1005           2 :                 constexpr bool bApproxOK = false;
    1006           2 :                 constexpr bool bForce = true;
    1007           2 :                 double dfMin = 0.0;
    1008           2 :                 double dfMax = 0.0;
    1009           2 :                 if (srcArray->GetStatistics(bApproxOK, bForce, &dfMin, &dfMax,
    1010             :                                             nullptr, nullptr, nullptr, nullptr,
    1011           2 :                                             nullptr) != CE_None)
    1012             :                 {
    1013           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1014             :                              "Could not retrieve statistics for array %s",
    1015           0 :                              srcArray->GetName().c_str());
    1016           0 :                     return false;
    1017             :                 }
    1018           2 :                 double dfDTMin = 0;
    1019           2 :                 double dfDTMax = 0;
    1020             : #define setDTMinMax(ctype)                                                     \
    1021             :     do                                                                         \
    1022             :     {                                                                          \
    1023             :         dfDTMin = static_cast<double>(std::numeric_limits<ctype>::min());      \
    1024             :         dfDTMax = static_cast<double>(std::numeric_limits<ctype>::max());      \
    1025             :     } while (0)
    1026             : 
    1027           2 :                 switch (eAutoScaleType)
    1028             :                 {
    1029           0 :                     case GDT_Byte:
    1030           0 :                         setDTMinMax(GByte);
    1031           0 :                         break;
    1032           0 :                     case GDT_Int8:
    1033           0 :                         setDTMinMax(GInt8);
    1034           0 :                         break;
    1035           1 :                     case GDT_UInt16:
    1036           1 :                         setDTMinMax(GUInt16);
    1037           1 :                         break;
    1038           1 :                     case GDT_Int16:
    1039           1 :                         setDTMinMax(GInt16);
    1040           1 :                         break;
    1041           0 :                     case GDT_UInt32:
    1042           0 :                         setDTMinMax(GUInt32);
    1043           0 :                         break;
    1044           0 :                     case GDT_Int32:
    1045           0 :                         setDTMinMax(GInt32);
    1046           0 :                         break;
    1047           0 :                     case GDT_UInt64:
    1048           0 :                         setDTMinMax(std::uint64_t);
    1049           0 :                         break;
    1050           0 :                     case GDT_Int64:
    1051           0 :                         setDTMinMax(std::int64_t);
    1052           0 :                         break;
    1053           0 :                     case GDT_Float32:
    1054             :                     case GDT_Float64:
    1055             :                     case GDT_Unknown:
    1056             :                     case GDT_CInt16:
    1057             :                     case GDT_CInt32:
    1058             :                     case GDT_CFloat32:
    1059             :                     case GDT_CFloat64:
    1060             :                     case GDT_TypeCount:
    1061           0 :                         CPLAssert(false);
    1062             :                 }
    1063             : 
    1064             :                 dstArray =
    1065           4 :                     CreateMDArray(srcArray->GetName(), dstArrayDims,
    1066           4 :                                   GDALExtendedDataType::Create(eAutoScaleType),
    1067           4 :                                   aosArrayCO.List());
    1068           2 :                 if (!dstArray)
    1069           0 :                     return !bStrict;
    1070             : 
    1071           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1072             :                 {
    1073             :                     // If there's a nodata value in the source array, reserve
    1074             :                     // DTMax for that purpose in the target scaled array
    1075           1 :                     if (!dstArray->SetNoDataValue(dfDTMax))
    1076             :                     {
    1077           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1078             :                                  "Cannot set nodata value");
    1079           0 :                         return false;
    1080             :                     }
    1081           1 :                     dfDTMax--;
    1082             :                 }
    1083           2 :                 const double dfScale =
    1084           2 :                     dfMax > dfMin ? (dfMax - dfMin) / (dfDTMax - dfDTMin) : 1.0;
    1085           2 :                 const double dfOffset = dfMin - dfDTMin * dfScale;
    1086             : 
    1087           4 :                 if (!dstArray->SetOffset(dfOffset) ||
    1088           2 :                     !dstArray->SetScale(dfScale))
    1089             :                 {
    1090           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1091             :                              "Cannot set scale/offset");
    1092           0 :                     return false;
    1093             :                 }
    1094             : 
    1095           2 :                 auto poUnscaled = dstArray->GetUnscaled();
    1096           2 :                 if (srcArray->GetRawNoDataValue() != nullptr)
    1097             :                 {
    1098           1 :                     poUnscaled->SetNoDataValue(
    1099             :                         srcArray->GetNoDataValueAsDouble());
    1100             :                 }
    1101             : 
    1102             :                 // Copy source array into unscaled array
    1103           4 :                 if (!poUnscaled->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1104             :                                           nCurCost, nTotalCost, pfnProgress,
    1105           2 :                                           pProgressData))
    1106             :                 {
    1107           0 :                     return false;
    1108             :                 }
    1109             :             }
    1110             :             else
    1111             :             {
    1112          74 :                 dstArray = CreateMDArray(srcArray->GetName(), dstArrayDims,
    1113          74 :                                          srcArrayType, aosArrayCO.List());
    1114          37 :                 if (!dstArray)
    1115           0 :                     return !bStrict;
    1116             : 
    1117          74 :                 if (!dstArray->CopyFrom(poSrcDS, srcArray.get(), bStrict,
    1118             :                                         nCurCost, nTotalCost, pfnProgress,
    1119          37 :                                         pProgressData))
    1120             :                 {
    1121           0 :                     return false;
    1122             :                 }
    1123             :             }
    1124             : 
    1125             :             // If this array is the indexing variable of a dimension, link them
    1126             :             // together.
    1127          39 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1128             :             {
    1129             :                 auto oCorrespondingDimIter =
    1130          16 :                     mapExistingDstDims.find(oIterDimName->second);
    1131          16 :                 if (oCorrespondingDimIter != mapExistingDstDims.end())
    1132             :                 {
    1133             :                     CPLErrorStateBackuper oErrorStateBackuper(
    1134          16 :                         CPLQuietErrorHandler);
    1135          32 :                     oCorrespondingDimIter->second->SetIndexingVariable(
    1136          16 :                         std::move(dstArray));
    1137             :                 }
    1138             :             }
    1139             : 
    1140          39 :             return true;
    1141          22 :         };
    1142             : 
    1143          44 :         const auto arrayNames = poSrcGroup->GetMDArrayNames();
    1144             : 
    1145             :         // Start by copying arrays that are indexing variables of dimensions
    1146          61 :         for (const auto &name : arrayNames)
    1147             :         {
    1148          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1149          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1150             : 
    1151             :             const auto oIterDimName =
    1152          39 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
    1153          39 :             if (oIterDimName != mapSrcVariableNameToIndexedDimName.end())
    1154             :             {
    1155          16 :                 if (!CopyArray(srcArray))
    1156           0 :                     return false;
    1157             :             }
    1158             :         }
    1159             : 
    1160             :         // Then copy regular arrays
    1161          61 :         for (const auto &name : arrayNames)
    1162             :         {
    1163          39 :             auto srcArray = poSrcGroup->OpenMDArray(name);
    1164          39 :             EXIT_OR_CONTINUE_IF_NULL(srcArray);
    1165             : 
    1166             :             const auto oIterDimName =
    1167          39 :                 mapSrcVariableNameToIndexedDimName.find(srcArray->GetName());
    1168          39 :             if (oIterDimName == mapSrcVariableNameToIndexedDimName.end())
    1169             :             {
    1170          23 :                 if (!CopyArray(srcArray))
    1171           0 :                     return false;
    1172             :             }
    1173             :         }
    1174             : 
    1175          44 :         const auto groupNames = poSrcGroup->GetGroupNames();
    1176          26 :         for (const auto &name : groupNames)
    1177             :         {
    1178           4 :             auto srcSubGroup = poSrcGroup->OpenGroup(name);
    1179           4 :             EXIT_OR_CONTINUE_IF_NULL(srcSubGroup);
    1180           4 :             auto dstSubGroup = CreateGroup(name);
    1181           4 :             EXIT_OR_CONTINUE_IF_NULL(dstSubGroup);
    1182           8 :             if (!dstSubGroup->CopyFrom(
    1183             :                     poDstRootGroup, poSrcDS, srcSubGroup, bStrict, nCurCost,
    1184           4 :                     nTotalCost, pfnProgress, pProgressData, papszOptions))
    1185           0 :                 return false;
    1186             :         }
    1187             : 
    1188          22 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    1189           0 :             return false;
    1190             : 
    1191          22 :         return true;
    1192             :     }
    1193           0 :     catch (const std::exception &e)
    1194             :     {
    1195           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1196           0 :         return false;
    1197             :     }
    1198             : }
    1199             : 
    1200             : /************************************************************************/
    1201             : /*                         GetInnerMostGroup()                          */
    1202             : /************************************************************************/
    1203             : 
    1204             : //! @cond Doxygen_Suppress
    1205             : const GDALGroup *
    1206         989 : GDALGroup::GetInnerMostGroup(const std::string &osPathOrArrayOrDim,
    1207             :                              std::shared_ptr<GDALGroup> &curGroupHolder,
    1208             :                              std::string &osLastPart) const
    1209             : {
    1210         989 :     if (osPathOrArrayOrDim.empty() || osPathOrArrayOrDim[0] != '/')
    1211           6 :         return nullptr;
    1212         983 :     const GDALGroup *poCurGroup = this;
    1213             :     CPLStringList aosTokens(
    1214        1966 :         CSLTokenizeString2(osPathOrArrayOrDim.c_str(), "/", 0));
    1215         983 :     if (aosTokens.size() == 0)
    1216             :     {
    1217           0 :         return nullptr;
    1218             :     }
    1219             : 
    1220        1297 :     for (int i = 0; i < aosTokens.size() - 1; i++)
    1221             :     {
    1222         317 :         curGroupHolder = poCurGroup->OpenGroup(aosTokens[i], nullptr);
    1223         317 :         if (!curGroupHolder)
    1224             :         {
    1225           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
    1226             :                      aosTokens[i]);
    1227           3 :             return nullptr;
    1228             :         }
    1229         314 :         poCurGroup = curGroupHolder.get();
    1230             :     }
    1231         980 :     osLastPart = aosTokens[aosTokens.size() - 1];
    1232         980 :     return poCurGroup;
    1233             : }
    1234             : 
    1235             : //! @endcond
    1236             : 
    1237             : /************************************************************************/
    1238             : /*                      OpenMDArrayFromFullname()                       */
    1239             : /************************************************************************/
    1240             : 
    1241             : /** Get an array from its fully qualified name */
    1242             : std::shared_ptr<GDALMDArray>
    1243         336 : GDALGroup::OpenMDArrayFromFullname(const std::string &osFullName,
    1244             :                                    CSLConstList papszOptions) const
    1245             : {
    1246         672 :     std::string osName;
    1247         336 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1248         336 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1249         336 :     if (poGroup == nullptr)
    1250           5 :         return nullptr;
    1251         331 :     return poGroup->OpenMDArray(osName, papszOptions);
    1252             : }
    1253             : 
    1254             : /************************************************************************/
    1255             : /*                          ResolveMDArray()                            */
    1256             : /************************************************************************/
    1257             : 
    1258             : /** Locate an array in a group and its subgroups by name.
    1259             :  *
    1260             :  * If osName is a fully qualified name, then OpenMDArrayFromFullname() is first
    1261             :  * used
    1262             :  * Otherwise the search will start from the group identified by osStartingPath,
    1263             :  * and an array whose name is osName will be looked for in this group (if
    1264             :  * osStartingPath is empty or "/", then the current group is used). If there
    1265             :  * is no match, then a recursive descendent search will be made in its
    1266             :  * subgroups. If there is no match in the subgroups, then the parent (if
    1267             :  * existing) of the group pointed by osStartingPath will be used as the new
    1268             :  * starting point for the search.
    1269             :  *
    1270             :  * @param osName name, qualified or not
    1271             :  * @param osStartingPath fully qualified name of the (sub-)group from which
    1272             :  *                       the search should be started. If this is a non-empty
    1273             :  *                       string, the group on which this method is called should
    1274             :  *                       nominally be the root group (otherwise the path will
    1275             :  *                       be interpreted as from the current group)
    1276             :  * @param papszOptions options to pass to OpenMDArray()
    1277             :  * @since GDAL 3.2
    1278             :  */
    1279             : std::shared_ptr<GDALMDArray>
    1280          19 : GDALGroup::ResolveMDArray(const std::string &osName,
    1281             :                           const std::string &osStartingPath,
    1282             :                           CSLConstList papszOptions) const
    1283             : {
    1284          19 :     if (!osName.empty() && osName[0] == '/')
    1285             :     {
    1286           1 :         auto poArray = OpenMDArrayFromFullname(osName, papszOptions);
    1287           1 :         if (poArray)
    1288           1 :             return poArray;
    1289             :     }
    1290          36 :     std::string osPath(osStartingPath);
    1291          36 :     std::set<std::string> oSetAlreadyVisited;
    1292             : 
    1293             :     while (true)
    1294             :     {
    1295           0 :         std::shared_ptr<GDALGroup> curGroupHolder;
    1296           0 :         std::shared_ptr<GDALGroup> poGroup;
    1297             : 
    1298          22 :         std::queue<std::shared_ptr<GDALGroup>> oQueue;
    1299          22 :         bool goOn = false;
    1300          22 :         if (osPath.empty() || osPath == "/")
    1301             :         {
    1302          11 :             goOn = true;
    1303             :         }
    1304             :         else
    1305             :         {
    1306          22 :             std::string osLastPart;
    1307             :             const GDALGroup *poGroupPtr =
    1308          11 :                 GetInnerMostGroup(osPath, curGroupHolder, osLastPart);
    1309          11 :             if (poGroupPtr)
    1310          11 :                 poGroup = poGroupPtr->OpenGroup(osLastPart);
    1311          22 :             if (poGroup && oSetAlreadyVisited.find(poGroup->GetFullName()) ==
    1312          33 :                                oSetAlreadyVisited.end())
    1313             :             {
    1314          11 :                 oQueue.push(poGroup);
    1315          11 :                 goOn = true;
    1316             :             }
    1317             :         }
    1318             : 
    1319          22 :         if (goOn)
    1320             :         {
    1321          17 :             do
    1322             :             {
    1323             :                 const GDALGroup *groupPtr;
    1324          39 :                 if (!oQueue.empty())
    1325             :                 {
    1326          28 :                     poGroup = oQueue.front();
    1327          28 :                     oQueue.pop();
    1328          28 :                     groupPtr = poGroup.get();
    1329             :                 }
    1330             :                 else
    1331             :                 {
    1332          11 :                     groupPtr = this;
    1333             :                 }
    1334             : 
    1335          39 :                 auto poArray = groupPtr->OpenMDArray(osName, papszOptions);
    1336          39 :                 if (poArray)
    1337          16 :                     return poArray;
    1338             : 
    1339          46 :                 const auto aosGroupNames = groupPtr->GetGroupNames();
    1340          47 :                 for (const auto &osGroupName : aosGroupNames)
    1341             :                 {
    1342          48 :                     auto poSubGroup = groupPtr->OpenGroup(osGroupName);
    1343          48 :                     if (poSubGroup &&
    1344          24 :                         oSetAlreadyVisited.find(poSubGroup->GetFullName()) ==
    1345          72 :                             oSetAlreadyVisited.end())
    1346             :                     {
    1347          24 :                         oQueue.push(poSubGroup);
    1348          24 :                         oSetAlreadyVisited.insert(poSubGroup->GetFullName());
    1349             :                     }
    1350             :                 }
    1351          23 :             } while (!oQueue.empty());
    1352             :         }
    1353             : 
    1354           6 :         if (osPath.empty() || osPath == "/")
    1355           2 :             break;
    1356             : 
    1357           4 :         const auto nPos = osPath.rfind('/');
    1358           4 :         if (nPos == 0)
    1359           1 :             osPath = "/";
    1360             :         else
    1361             :         {
    1362           3 :             if (nPos == std::string::npos)
    1363           0 :                 break;
    1364           3 :             osPath.resize(nPos);
    1365             :         }
    1366           4 :     }
    1367           2 :     return nullptr;
    1368             : }
    1369             : 
    1370             : /************************************************************************/
    1371             : /*                       OpenGroupFromFullname()                        */
    1372             : /************************************************************************/
    1373             : 
    1374             : /** Get a group from its fully qualified name.
    1375             :  * @since GDAL 3.2
    1376             :  */
    1377             : std::shared_ptr<GDALGroup>
    1378         517 : GDALGroup::OpenGroupFromFullname(const std::string &osFullName,
    1379             :                                  CSLConstList papszOptions) const
    1380             : {
    1381        1034 :     std::string osName;
    1382         517 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1383         517 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1384         517 :     if (poGroup == nullptr)
    1385           2 :         return nullptr;
    1386         515 :     return poGroup->OpenGroup(osName, papszOptions);
    1387             : }
    1388             : 
    1389             : /************************************************************************/
    1390             : /*                      OpenDimensionFromFullname()                     */
    1391             : /************************************************************************/
    1392             : 
    1393             : /** Get a dimension from its fully qualified name */
    1394             : std::shared_ptr<GDALDimension>
    1395         125 : GDALGroup::OpenDimensionFromFullname(const std::string &osFullName) const
    1396             : {
    1397         250 :     std::string osName;
    1398         125 :     std::shared_ptr<GDALGroup> curGroupHolder;
    1399         125 :     auto poGroup(GetInnerMostGroup(osFullName, curGroupHolder, osName));
    1400         125 :     if (poGroup == nullptr)
    1401           2 :         return nullptr;
    1402         246 :     auto dims(poGroup->GetDimensions());
    1403         203 :     for (auto &dim : dims)
    1404             :     {
    1405         164 :         if (dim->GetName() == osName)
    1406          84 :             return dim;
    1407             :     }
    1408          39 :     return nullptr;
    1409             : }
    1410             : 
    1411             : /************************************************************************/
    1412             : /*                           ClearStatistics()                          */
    1413             : /************************************************************************/
    1414             : 
    1415             : /**
    1416             :  * \brief Clear statistics.
    1417             :  *
    1418             :  * @since GDAL 3.4
    1419             :  */
    1420           0 : void GDALGroup::ClearStatistics()
    1421             : {
    1422           0 :     auto groupNames = GetGroupNames();
    1423           0 :     for (const auto &name : groupNames)
    1424             :     {
    1425           0 :         auto subGroup = OpenGroup(name);
    1426           0 :         if (subGroup)
    1427             :         {
    1428           0 :             subGroup->ClearStatistics();
    1429             :         }
    1430             :     }
    1431             : 
    1432           0 :     auto arrayNames = GetMDArrayNames();
    1433           0 :     for (const auto &name : arrayNames)
    1434             :     {
    1435           0 :         auto array = OpenMDArray(name);
    1436           0 :         if (array)
    1437             :         {
    1438           0 :             array->ClearStatistics();
    1439             :         }
    1440             :     }
    1441           0 : }
    1442             : 
    1443             : /************************************************************************/
    1444             : /*                            Rename()                                  */
    1445             : /************************************************************************/
    1446             : 
    1447             : /** Rename the group.
    1448             :  *
    1449             :  * This is not implemented by all drivers.
    1450             :  *
    1451             :  * Drivers known to implement it: MEM, netCDF, ZARR.
    1452             :  *
    1453             :  * This is the same as the C function GDALGroupRename().
    1454             :  *
    1455             :  * @param osNewName New name.
    1456             :  *
    1457             :  * @return true in case of success
    1458             :  * @since GDAL 3.8
    1459             :  */
    1460           0 : bool GDALGroup::Rename(CPL_UNUSED const std::string &osNewName)
    1461             : {
    1462           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1463           0 :     return false;
    1464             : }
    1465             : 
    1466             : /************************************************************************/
    1467             : /*                         BaseRename()                                 */
    1468             : /************************************************************************/
    1469             : 
    1470             : //! @cond Doxygen_Suppress
    1471           8 : void GDALGroup::BaseRename(const std::string &osNewName)
    1472             : {
    1473           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    1474           8 :     m_osFullName += osNewName;
    1475           8 :     m_osName = osNewName;
    1476             : 
    1477           8 :     NotifyChildrenOfRenaming();
    1478           8 : }
    1479             : 
    1480             : //! @endcond
    1481             : 
    1482             : /************************************************************************/
    1483             : /*                        ParentRenamed()                               */
    1484             : /************************************************************************/
    1485             : 
    1486             : //! @cond Doxygen_Suppress
    1487           7 : void GDALGroup::ParentRenamed(const std::string &osNewParentFullName)
    1488             : {
    1489           7 :     m_osFullName = osNewParentFullName;
    1490           7 :     m_osFullName += "/";
    1491           7 :     m_osFullName += m_osName;
    1492             : 
    1493           7 :     NotifyChildrenOfRenaming();
    1494           7 : }
    1495             : 
    1496             : //! @endcond
    1497             : 
    1498             : /************************************************************************/
    1499             : /*                             Deleted()                                */
    1500             : /************************************************************************/
    1501             : 
    1502             : //! @cond Doxygen_Suppress
    1503          22 : void GDALGroup::Deleted()
    1504             : {
    1505          22 :     m_bValid = false;
    1506             : 
    1507          22 :     NotifyChildrenOfDeletion();
    1508          22 : }
    1509             : 
    1510             : //! @endcond
    1511             : 
    1512             : /************************************************************************/
    1513             : /*                        ParentDeleted()                               */
    1514             : /************************************************************************/
    1515             : 
    1516             : //! @cond Doxygen_Suppress
    1517           3 : void GDALGroup::ParentDeleted()
    1518             : {
    1519           3 :     Deleted();
    1520           3 : }
    1521             : 
    1522             : //! @endcond
    1523             : 
    1524             : /************************************************************************/
    1525             : /*                     CheckValidAndErrorOutIfNot()                     */
    1526             : /************************************************************************/
    1527             : 
    1528             : //! @cond Doxygen_Suppress
    1529       11467 : bool GDALGroup::CheckValidAndErrorOutIfNot() const
    1530             : {
    1531       11467 :     if (!m_bValid)
    1532             :     {
    1533          14 :         CPLError(CE_Failure, CPLE_AppDefined,
    1534             :                  "This object has been deleted. No action on it is possible");
    1535             :     }
    1536       11467 :     return m_bValid;
    1537             : }
    1538             : 
    1539             : //! @endcond
    1540             : 
    1541             : /************************************************************************/
    1542             : /*                       ~GDALAbstractMDArray()                         */
    1543             : /************************************************************************/
    1544             : 
    1545             : GDALAbstractMDArray::~GDALAbstractMDArray() = default;
    1546             : 
    1547             : /************************************************************************/
    1548             : /*                        GDALAbstractMDArray()                         */
    1549             : /************************************************************************/
    1550             : 
    1551             : //! @cond Doxygen_Suppress
    1552       17871 : GDALAbstractMDArray::GDALAbstractMDArray(const std::string &osParentName,
    1553       17871 :                                          const std::string &osName)
    1554             :     : m_osName(osName),
    1555             :       m_osFullName(
    1556       17871 :           !osParentName.empty()
    1557       34193 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
    1558       69935 :               : osName)
    1559             : {
    1560       17871 : }
    1561             : 
    1562             : //! @endcond
    1563             : 
    1564             : /************************************************************************/
    1565             : /*                           GetDimensions()                            */
    1566             : /************************************************************************/
    1567             : 
    1568             : /** \fn GDALAbstractMDArray::GetDimensions() const
    1569             :  * \brief Return the dimensions of an attribute/array.
    1570             :  *
    1571             :  * This is the same as the C functions GDALMDArrayGetDimensions() and
    1572             :  * similar to GDALAttributeGetDimensionsSize().
    1573             :  */
    1574             : 
    1575             : /************************************************************************/
    1576             : /*                           GetDataType()                              */
    1577             : /************************************************************************/
    1578             : 
    1579             : /** \fn GDALAbstractMDArray::GetDataType() const
    1580             :  * \brief Return the data type of an attribute/array.
    1581             :  *
    1582             :  * This is the same as the C functions GDALMDArrayGetDataType() and
    1583             :  * GDALAttributeGetDataType()
    1584             :  */
    1585             : 
    1586             : /************************************************************************/
    1587             : /*                        GetDimensionCount()                           */
    1588             : /************************************************************************/
    1589             : 
    1590             : /** Return the number of dimensions.
    1591             :  *
    1592             :  * Default implementation is GetDimensions().size(), and may be overridden by
    1593             :  * drivers if they have a faster / less expensive implementations.
    1594             :  *
    1595             :  * This is the same as the C function GDALMDArrayGetDimensionCount() or
    1596             :  * GDALAttributeGetDimensionCount().
    1597             :  *
    1598             :  */
    1599       21450 : size_t GDALAbstractMDArray::GetDimensionCount() const
    1600             : {
    1601       21450 :     return GetDimensions().size();
    1602             : }
    1603             : 
    1604             : /************************************************************************/
    1605             : /*                            Rename()                                  */
    1606             : /************************************************************************/
    1607             : 
    1608             : /** Rename the attribute/array.
    1609             :  *
    1610             :  * This is not implemented by all drivers.
    1611             :  *
    1612             :  * Drivers known to implement it: MEM, netCDF, Zarr.
    1613             :  *
    1614             :  * This is the same as the C functions GDALMDArrayRename() or
    1615             :  * GDALAttributeRename().
    1616             :  *
    1617             :  * @param osNewName New name.
    1618             :  *
    1619             :  * @return true in case of success
    1620             :  * @since GDAL 3.8
    1621             :  */
    1622           0 : bool GDALAbstractMDArray::Rename(CPL_UNUSED const std::string &osNewName)
    1623             : {
    1624           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
    1625           0 :     return false;
    1626             : }
    1627             : 
    1628             : /************************************************************************/
    1629             : /*                             CopyValue()                              */
    1630             : /************************************************************************/
    1631             : 
    1632             : /** Convert a value from a source type to a destination type.
    1633             :  *
    1634             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1635             :  * that must be freed with CPLFree().
    1636             :  */
    1637       77583 : bool GDALExtendedDataType::CopyValue(const void *pSrc,
    1638             :                                      const GDALExtendedDataType &srcType,
    1639             :                                      void *pDst,
    1640             :                                      const GDALExtendedDataType &dstType)
    1641             : {
    1642      152162 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1643       74579 :         dstType.GetClass() == GEDTC_NUMERIC)
    1644             :     {
    1645       74389 :         GDALCopyWords(pSrc, srcType.GetNumericDataType(), 0, pDst,
    1646             :                       dstType.GetNumericDataType(), 0, 1);
    1647       74389 :         return true;
    1648             :     }
    1649        6058 :     if (srcType.GetClass() == GEDTC_STRING &&
    1650        2864 :         dstType.GetClass() == GEDTC_STRING)
    1651             :     {
    1652             :         const char *srcStrPtr;
    1653        2480 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1654        2480 :         char *pszDup = srcStrPtr ? CPLStrdup(srcStrPtr) : nullptr;
    1655        2480 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1656        2480 :         return true;
    1657             :     }
    1658         904 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1659         190 :         dstType.GetClass() == GEDTC_STRING)
    1660             :     {
    1661         190 :         const char *str = nullptr;
    1662         190 :         switch (srcType.GetNumericDataType())
    1663             :         {
    1664           0 :             case GDT_Unknown:
    1665           0 :                 break;
    1666           0 :             case GDT_Byte:
    1667           0 :                 str = CPLSPrintf("%d", *static_cast<const GByte *>(pSrc));
    1668           0 :                 break;
    1669           3 :             case GDT_Int8:
    1670           3 :                 str = CPLSPrintf("%d", *static_cast<const GInt8 *>(pSrc));
    1671           3 :                 break;
    1672          48 :             case GDT_UInt16:
    1673          48 :                 str = CPLSPrintf("%d", *static_cast<const GUInt16 *>(pSrc));
    1674          48 :                 break;
    1675           0 :             case GDT_Int16:
    1676           0 :                 str = CPLSPrintf("%d", *static_cast<const GInt16 *>(pSrc));
    1677           0 :                 break;
    1678           8 :             case GDT_UInt32:
    1679           8 :                 str = CPLSPrintf("%u", *static_cast<const GUInt32 *>(pSrc));
    1680           8 :                 break;
    1681          36 :             case GDT_Int32:
    1682          36 :                 str = CPLSPrintf("%d", *static_cast<const GInt32 *>(pSrc));
    1683          36 :                 break;
    1684           0 :             case GDT_UInt64:
    1685             :                 str =
    1686           0 :                     CPLSPrintf(CPL_FRMT_GUIB,
    1687             :                                static_cast<GUIntBig>(
    1688             :                                    *static_cast<const std::uint64_t *>(pSrc)));
    1689           0 :                 break;
    1690           0 :             case GDT_Int64:
    1691           0 :                 str = CPLSPrintf(CPL_FRMT_GIB,
    1692             :                                  static_cast<GIntBig>(
    1693             :                                      *static_cast<const std::int64_t *>(pSrc)));
    1694           0 :                 break;
    1695          17 :             case GDT_Float32:
    1696          17 :                 str = CPLSPrintf("%.9g", *static_cast<const float *>(pSrc));
    1697          17 :                 break;
    1698          76 :             case GDT_Float64:
    1699          76 :                 str = CPLSPrintf("%.18g", *static_cast<const double *>(pSrc));
    1700          76 :                 break;
    1701           2 :             case GDT_CInt16:
    1702             :             {
    1703           2 :                 const GInt16 *src = static_cast<const GInt16 *>(pSrc);
    1704           2 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1705           2 :                 break;
    1706             :             }
    1707           0 :             case GDT_CInt32:
    1708             :             {
    1709           0 :                 const GInt32 *src = static_cast<const GInt32 *>(pSrc);
    1710           0 :                 str = CPLSPrintf("%d+%dj", src[0], src[1]);
    1711           0 :                 break;
    1712             :             }
    1713           0 :             case GDT_CFloat32:
    1714             :             {
    1715           0 :                 const float *src = static_cast<const float *>(pSrc);
    1716           0 :                 str = CPLSPrintf("%.9g+%.9gj", src[0], src[1]);
    1717           0 :                 break;
    1718             :             }
    1719           0 :             case GDT_CFloat64:
    1720             :             {
    1721           0 :                 const double *src = static_cast<const double *>(pSrc);
    1722           0 :                 str = CPLSPrintf("%.18g+%.18gj", src[0], src[1]);
    1723           0 :                 break;
    1724             :             }
    1725           0 :             case GDT_TypeCount:
    1726           0 :                 CPLAssert(false);
    1727             :                 break;
    1728             :         }
    1729         190 :         char *pszDup = str ? CPLStrdup(str) : nullptr;
    1730         190 :         *reinterpret_cast<void **>(pDst) = pszDup;
    1731         190 :         return true;
    1732             :     }
    1733         908 :     if (srcType.GetClass() == GEDTC_STRING &&
    1734         384 :         dstType.GetClass() == GEDTC_NUMERIC)
    1735             :     {
    1736             :         const char *srcStrPtr;
    1737         384 :         memcpy(&srcStrPtr, pSrc, sizeof(const char *));
    1738         384 :         if (dstType.GetNumericDataType() == GDT_Int64)
    1739             :         {
    1740           2 :             *(static_cast<int64_t *>(pDst)) =
    1741           2 :                 srcStrPtr == nullptr ? 0
    1742           1 :                                      : static_cast<int64_t>(atoll(srcStrPtr));
    1743             :         }
    1744         382 :         else if (dstType.GetNumericDataType() == GDT_UInt64)
    1745             :         {
    1746           2 :             *(static_cast<uint64_t *>(pDst)) =
    1747           2 :                 srcStrPtr == nullptr
    1748           2 :                     ? 0
    1749           1 :                     : static_cast<uint64_t>(strtoull(srcStrPtr, nullptr, 10));
    1750             :         }
    1751             :         else
    1752             :         {
    1753             :             // FIXME GDT_UInt64
    1754         380 :             const double dfVal = srcStrPtr == nullptr ? 0 : CPLAtof(srcStrPtr);
    1755         380 :             GDALCopyWords(&dfVal, GDT_Float64, 0, pDst,
    1756             :                           dstType.GetNumericDataType(), 0, 1);
    1757             :         }
    1758         384 :         return true;
    1759             :     }
    1760         280 :     if (srcType.GetClass() == GEDTC_COMPOUND &&
    1761         140 :         dstType.GetClass() == GEDTC_COMPOUND)
    1762             :     {
    1763         140 :         const auto &srcComponents = srcType.GetComponents();
    1764         140 :         const auto &dstComponents = dstType.GetComponents();
    1765         140 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1766         140 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1767             : 
    1768             :         std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
    1769         280 :             srcComponentMap;
    1770         562 :         for (const auto &srcComp : srcComponents)
    1771             :         {
    1772         422 :             srcComponentMap[srcComp->GetName()] = &srcComp;
    1773             :         }
    1774         414 :         for (const auto &dstComp : dstComponents)
    1775             :         {
    1776         274 :             auto oIter = srcComponentMap.find(dstComp->GetName());
    1777         274 :             if (oIter == srcComponentMap.end())
    1778           0 :                 return false;
    1779         274 :             const auto &srcComp = *(oIter->second);
    1780         822 :             if (!GDALExtendedDataType::CopyValue(
    1781         274 :                     pabySrc + srcComp->GetOffset(), srcComp->GetType(),
    1782         274 :                     pabyDst + dstComp->GetOffset(), dstComp->GetType()))
    1783             :             {
    1784           0 :                 return false;
    1785             :             };
    1786             :         }
    1787         140 :         return true;
    1788             :     }
    1789             : 
    1790           0 :     return false;
    1791             : }
    1792             : 
    1793             : /************************************************************************/
    1794             : /*                             CopyValues()                             */
    1795             : /************************************************************************/
    1796             : 
    1797             : /** Convert severals value from a source type to a destination type.
    1798             :  *
    1799             :  * If dstType is GEDTC_STRING, the written value will be a pointer to a char*,
    1800             :  * that must be freed with CPLFree().
    1801             :  */
    1802         316 : bool GDALExtendedDataType::CopyValues(const void *pSrc,
    1803             :                                       const GDALExtendedDataType &srcType,
    1804             :                                       GPtrDiff_t nSrcStrideInElts, void *pDst,
    1805             :                                       const GDALExtendedDataType &dstType,
    1806             :                                       GPtrDiff_t nDstStrideInElts,
    1807             :                                       size_t nValues)
    1808             : {
    1809             :     const auto nSrcStrideInBytes =
    1810         316 :         nSrcStrideInElts * static_cast<GPtrDiff_t>(srcType.GetSize());
    1811             :     const auto nDstStrideInBytes =
    1812         316 :         nDstStrideInElts * static_cast<GPtrDiff_t>(dstType.GetSize());
    1813         582 :     if (srcType.GetClass() == GEDTC_NUMERIC &&
    1814         266 :         dstType.GetClass() == GEDTC_NUMERIC &&
    1815         266 :         nSrcStrideInBytes >= std::numeric_limits<int>::min() &&
    1816         266 :         nSrcStrideInBytes <= std::numeric_limits<int>::max() &&
    1817         848 :         nDstStrideInBytes >= std::numeric_limits<int>::min() &&
    1818         266 :         nDstStrideInBytes <= std::numeric_limits<int>::max())
    1819             :     {
    1820         266 :         GDALCopyWords64(pSrc, srcType.GetNumericDataType(),
    1821             :                         static_cast<int>(nSrcStrideInBytes), pDst,
    1822             :                         dstType.GetNumericDataType(),
    1823             :                         static_cast<int>(nDstStrideInBytes), nValues);
    1824             :     }
    1825             :     else
    1826             :     {
    1827          50 :         const GByte *pabySrc = static_cast<const GByte *>(pSrc);
    1828          50 :         GByte *pabyDst = static_cast<GByte *>(pDst);
    1829         100 :         for (size_t i = 0; i < nValues; ++i)
    1830             :         {
    1831          50 :             if (!CopyValue(pabySrc, srcType, pabyDst, dstType))
    1832           0 :                 return false;
    1833          50 :             pabySrc += nSrcStrideInBytes;
    1834          50 :             pabyDst += nDstStrideInBytes;
    1835             :         }
    1836             :     }
    1837         316 :     return true;
    1838             : }
    1839             : 
    1840             : /************************************************************************/
    1841             : /*                       CheckReadWriteParams()                         */
    1842             : /************************************************************************/
    1843             : //! @cond Doxygen_Suppress
    1844        7299 : bool GDALAbstractMDArray::CheckReadWriteParams(
    1845             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *&arrayStep,
    1846             :     const GPtrDiff_t *&bufferStride, const GDALExtendedDataType &bufferDataType,
    1847             :     const void *buffer, const void *buffer_alloc_start,
    1848             :     size_t buffer_alloc_size, std::vector<GInt64> &tmp_arrayStep,
    1849             :     std::vector<GPtrDiff_t> &tmp_bufferStride) const
    1850             : {
    1851           0 :     const auto lamda_error = []()
    1852             :     {
    1853           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1854             :                  "Not all elements pointed by buffer will fit in "
    1855             :                  "[buffer_alloc_start, "
    1856             :                  "buffer_alloc_start + buffer_alloc_size[");
    1857           0 :     };
    1858             : 
    1859        7299 :     const auto &dims = GetDimensions();
    1860        7299 :     if (dims.empty())
    1861             :     {
    1862        2694 :         if (buffer_alloc_start)
    1863             :         {
    1864        2353 :             const size_t elementSize = bufferDataType.GetSize();
    1865        2353 :             const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    1866        2353 :             const GByte *paby_buffer_alloc_start =
    1867             :                 static_cast<const GByte *>(buffer_alloc_start);
    1868        2353 :             const GByte *paby_buffer_alloc_end =
    1869             :                 paby_buffer_alloc_start + buffer_alloc_size;
    1870             : 
    1871        2353 :             if (paby_buffer < paby_buffer_alloc_start ||
    1872        2353 :                 paby_buffer + elementSize > paby_buffer_alloc_end)
    1873             :             {
    1874           0 :                 lamda_error();
    1875           0 :                 return false;
    1876             :             }
    1877             :         }
    1878        2694 :         return true;
    1879             :     }
    1880             : 
    1881        4605 :     if (arrayStep == nullptr)
    1882             :     {
    1883        1191 :         tmp_arrayStep.resize(dims.size(), 1);
    1884        1191 :         arrayStep = tmp_arrayStep.data();
    1885             :     }
    1886       13147 :     for (size_t i = 0; i < dims.size(); i++)
    1887             :     {
    1888        8542 :         if (count[i] == 0)
    1889             :         {
    1890           0 :             CPLError(CE_Failure, CPLE_AppDefined, "count[%u] = 0 is invalid",
    1891             :                      static_cast<unsigned>(i));
    1892           0 :             return false;
    1893             :         }
    1894             :     }
    1895        4605 :     bool bufferStride_all_positive = true;
    1896        4605 :     if (bufferStride == nullptr)
    1897             :     {
    1898         902 :         GPtrDiff_t stride = 1;
    1899             :         // To compute strides we must proceed from the fastest varying dimension
    1900             :         // (the last one), and then reverse the result
    1901        2061 :         for (size_t i = dims.size(); i != 0;)
    1902             :         {
    1903        1159 :             --i;
    1904        1159 :             tmp_bufferStride.push_back(stride);
    1905        1159 :             GUInt64 newStride = 0;
    1906             :             bool bOK;
    1907             :             try
    1908             :             {
    1909        1159 :                 newStride = (CPLSM(static_cast<GUInt64>(stride)) *
    1910        2318 :                              CPLSM(static_cast<GUInt64>(count[i])))
    1911        1159 :                                 .v();
    1912        1159 :                 bOK = static_cast<size_t>(newStride) == newStride &&
    1913        1159 :                       newStride < std::numeric_limits<size_t>::max() / 2;
    1914             :             }
    1915           0 :             catch (...)
    1916             :             {
    1917           0 :                 bOK = false;
    1918             :             }
    1919        1159 :             if (!bOK)
    1920             :             {
    1921           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Too big count values");
    1922           0 :                 return false;
    1923             :             }
    1924        1159 :             stride = static_cast<GPtrDiff_t>(newStride);
    1925             :         }
    1926         902 :         std::reverse(tmp_bufferStride.begin(), tmp_bufferStride.end());
    1927         902 :         bufferStride = tmp_bufferStride.data();
    1928             :     }
    1929             :     else
    1930             :     {
    1931       11084 :         for (size_t i = 0; i < dims.size(); i++)
    1932             :         {
    1933        7382 :             if (bufferStride[i] < 0)
    1934             :             {
    1935           1 :                 bufferStride_all_positive = false;
    1936           1 :                 break;
    1937             :             }
    1938             :         }
    1939             :     }
    1940       13118 :     for (size_t i = 0; i < dims.size(); i++)
    1941             :     {
    1942        8523 :         if (arrayStartIdx[i] >= dims[i]->GetSize())
    1943             :         {
    1944           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    1945             :                      "arrayStartIdx[%u] = " CPL_FRMT_GUIB " >= " CPL_FRMT_GUIB,
    1946             :                      static_cast<unsigned>(i),
    1947           2 :                      static_cast<GUInt64>(arrayStartIdx[i]),
    1948           2 :                      static_cast<GUInt64>(dims[i]->GetSize()));
    1949           2 :             return false;
    1950             :         }
    1951             :         bool bOverflow;
    1952        8521 :         if (arrayStep[i] >= 0)
    1953             :         {
    1954             :             try
    1955             :             {
    1956        7929 :                 bOverflow = (CPLSM(static_cast<GUInt64>(arrayStartIdx[i])) +
    1957        7931 :                              CPLSM(static_cast<GUInt64>(count[i] - 1)) *
    1958       31719 :                                  CPLSM(static_cast<GUInt64>(arrayStep[i])))
    1959        7929 :                                 .v() >= dims[i]->GetSize();
    1960             :             }
    1961           1 :             catch (...)
    1962             :             {
    1963           1 :                 bOverflow = true;
    1964             :             }
    1965        7930 :             if (bOverflow)
    1966             :             {
    1967           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1968             :                          "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] "
    1969             :                          ">= " CPL_FRMT_GUIB,
    1970             :                          static_cast<unsigned>(i), static_cast<unsigned>(i),
    1971             :                          static_cast<unsigned>(i),
    1972           5 :                          static_cast<GUInt64>(dims[i]->GetSize()));
    1973           5 :                 return false;
    1974             :             }
    1975             :         }
    1976             :         else
    1977             :         {
    1978             :             try
    1979             :             {
    1980         591 :                 bOverflow =
    1981         591 :                     arrayStartIdx[i] <
    1982         591 :                     (CPLSM(static_cast<GUInt64>(count[i] - 1)) *
    1983        1182 :                      CPLSM(arrayStep[i] == std::numeric_limits<GInt64>::min()
    1984             :                                ? (static_cast<GUInt64>(1) << 63)
    1985        1182 :                                : static_cast<GUInt64>(-arrayStep[i])))
    1986         591 :                         .v();
    1987             :             }
    1988           0 :             catch (...)
    1989             :             {
    1990           0 :                 bOverflow = true;
    1991             :             }
    1992         591 :             if (bOverflow)
    1993             :             {
    1994           3 :                 CPLError(
    1995             :                     CE_Failure, CPLE_AppDefined,
    1996             :                     "arrayStartIdx[%u] + (count[%u]-1) * arrayStep[%u] < 0",
    1997             :                     static_cast<unsigned>(i), static_cast<unsigned>(i),
    1998             :                     static_cast<unsigned>(i));
    1999           3 :                 return false;
    2000             :             }
    2001             :         }
    2002             :     }
    2003             : 
    2004        4595 :     if (buffer_alloc_start)
    2005             :     {
    2006        2445 :         const size_t elementSize = bufferDataType.GetSize();
    2007        2445 :         const GByte *paby_buffer = static_cast<const GByte *>(buffer);
    2008        2445 :         const GByte *paby_buffer_alloc_start =
    2009             :             static_cast<const GByte *>(buffer_alloc_start);
    2010        2445 :         const GByte *paby_buffer_alloc_end =
    2011             :             paby_buffer_alloc_start + buffer_alloc_size;
    2012        2445 :         if (bufferStride_all_positive)
    2013             :         {
    2014        2445 :             if (paby_buffer < paby_buffer_alloc_start)
    2015             :             {
    2016           0 :                 lamda_error();
    2017           0 :                 return false;
    2018             :             }
    2019        2445 :             GUInt64 nOffset = elementSize;
    2020        7102 :             for (size_t i = 0; i < dims.size(); i++)
    2021             :             {
    2022             :                 try
    2023             :                 {
    2024        4657 :                     nOffset = (CPLSM(static_cast<GUInt64>(nOffset)) +
    2025        4657 :                                CPLSM(static_cast<GUInt64>(bufferStride[i])) *
    2026        9314 :                                    CPLSM(static_cast<GUInt64>(count[i] - 1)) *
    2027       18628 :                                    CPLSM(static_cast<GUInt64>(elementSize)))
    2028        4657 :                                   .v();
    2029             :                 }
    2030           0 :                 catch (...)
    2031             :                 {
    2032           0 :                     lamda_error();
    2033           0 :                     return false;
    2034             :                 }
    2035             :             }
    2036             : #if SIZEOF_VOIDP == 4
    2037             :             if (static_cast<size_t>(nOffset) != nOffset)
    2038             :             {
    2039             :                 lamda_error();
    2040             :                 return false;
    2041             :             }
    2042             : #endif
    2043        2445 :             if (paby_buffer + nOffset > paby_buffer_alloc_end)
    2044             :             {
    2045           0 :                 lamda_error();
    2046           0 :                 return false;
    2047             :             }
    2048             :         }
    2049           0 :         else if (dims.size() < 31)
    2050             :         {
    2051             :             // Check all corners of the hypercube
    2052           0 :             const unsigned nLoops = 1U << static_cast<unsigned>(dims.size());
    2053           0 :             for (unsigned iCornerCode = 0; iCornerCode < nLoops; iCornerCode++)
    2054             :             {
    2055           0 :                 const GByte *paby = paby_buffer;
    2056           0 :                 for (unsigned i = 0; i < static_cast<unsigned>(dims.size());
    2057             :                      i++)
    2058             :                 {
    2059           0 :                     if (iCornerCode & (1U << i))
    2060             :                     {
    2061             :                         // We should check for integer overflows
    2062           0 :                         paby += bufferStride[i] * (count[i] - 1) * elementSize;
    2063             :                     }
    2064             :                 }
    2065           0 :                 if (paby < paby_buffer_alloc_start ||
    2066           0 :                     paby + elementSize > paby_buffer_alloc_end)
    2067             :                 {
    2068           0 :                     lamda_error();
    2069           0 :                     return false;
    2070             :                 }
    2071             :             }
    2072             :         }
    2073             :     }
    2074             : 
    2075        4595 :     return true;
    2076             : }
    2077             : 
    2078             : //! @endcond
    2079             : 
    2080             : /************************************************************************/
    2081             : /*                               Read()                                 */
    2082             : /************************************************************************/
    2083             : 
    2084             : /** Read part or totality of a multidimensional array or attribute.
    2085             :  *
    2086             :  * This will extract the content of a hyper-rectangle from the array into
    2087             :  * a user supplied buffer.
    2088             :  *
    2089             :  * If bufferDataType is of type string, the values written in pDstBuffer
    2090             :  * will be char* pointers and the strings should be freed with CPLFree().
    2091             :  *
    2092             :  * This is the same as the C function GDALMDArrayRead().
    2093             :  *
    2094             :  * @param arrayStartIdx Values representing the starting index to read
    2095             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2096             :  *                      Array of GetDimensionCount() values. Must not be
    2097             :  *                      nullptr, unless for a zero-dimensional array.
    2098             :  *
    2099             :  * @param count         Values representing the number of values to extract in
    2100             :  *                      each dimension.
    2101             :  *                      Array of GetDimensionCount() values. Must not be
    2102             :  *                      nullptr, unless for a zero-dimensional array.
    2103             :  *
    2104             :  * @param arrayStep     Spacing between values to extract in each dimension.
    2105             :  *                      The spacing is in number of array elements, not bytes.
    2106             :  *                      If provided, must contain GetDimensionCount() values.
    2107             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2108             :  * default to indicate consecutive elements.
    2109             :  *
    2110             :  * @param bufferStride  Spacing between values to store in pDstBuffer.
    2111             :  *                      The spacing is in number of array elements, not bytes.
    2112             :  *                      If provided, must contain GetDimensionCount() values.
    2113             :  *                      Negative values are possible (for example to reorder
    2114             :  *                      from bottom-to-top to top-to-bottom).
    2115             :  *                      If set to nullptr, will be set so that pDstBuffer is
    2116             :  *                      written in a compact way, with elements of the last /
    2117             :  *                      fastest varying dimension being consecutive.
    2118             :  *
    2119             :  * @param bufferDataType Data type of values in pDstBuffer.
    2120             :  *
    2121             :  * @param pDstBuffer    User buffer to store the values read. Should be big
    2122             :  *                      enough to store the number of values indicated by
    2123             :  * count[] and with the spacing of bufferStride[].
    2124             :  *
    2125             :  * @param pDstBufferAllocStart Optional pointer that can be used to validate the
    2126             :  *                             validty of pDstBuffer. pDstBufferAllocStart
    2127             :  * should be the pointer returned by the malloc() or equivalent call used to
    2128             :  * allocate the buffer. It will generally be equal to pDstBuffer (when
    2129             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2130             :  * nDstBufferAllocSize should be also set to the appropriate value. If no
    2131             :  * validation is needed, nullptr can be passed.
    2132             :  *
    2133             :  * @param nDstBufferAllocSize  Optional buffer size, that can be used to
    2134             :  * validate the validty of pDstBuffer. This is the size of the buffer starting
    2135             :  * at pDstBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2136             :  *                             set to the appropriate value.
    2137             :  *                             If no validation is needed, 0 can be passed.
    2138             :  *
    2139             :  * @return true in case of success.
    2140             :  */
    2141        1992 : bool GDALAbstractMDArray::Read(
    2142             :     const GUInt64 *arrayStartIdx, const size_t *count,
    2143             :     const GInt64 *arrayStep,         // step in elements
    2144             :     const GPtrDiff_t *bufferStride,  // stride in elements
    2145             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    2146             :     const void *pDstBufferAllocStart, size_t nDstBufferAllocSize) const
    2147             : {
    2148        1992 :     if (!GetDataType().CanConvertTo(bufferDataType))
    2149             :     {
    2150           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2151             :                  "Array data type is not convertible to buffer data type");
    2152           0 :         return false;
    2153             :     }
    2154             : 
    2155        3984 :     std::vector<GInt64> tmp_arrayStep;
    2156        3984 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2157        1992 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2158             :                               bufferDataType, pDstBuffer, pDstBufferAllocStart,
    2159             :                               nDstBufferAllocSize, tmp_arrayStep,
    2160             :                               tmp_bufferStride))
    2161             :     {
    2162           0 :         return false;
    2163             :     }
    2164             : 
    2165        1992 :     return IRead(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2166        1992 :                  pDstBuffer);
    2167             : }
    2168             : 
    2169             : /************************************************************************/
    2170             : /*                                IWrite()                              */
    2171             : /************************************************************************/
    2172             : 
    2173             : //! @cond Doxygen_Suppress
    2174           1 : bool GDALAbstractMDArray::IWrite(const GUInt64 *, const size_t *,
    2175             :                                  const GInt64 *, const GPtrDiff_t *,
    2176             :                                  const GDALExtendedDataType &, const void *)
    2177             : {
    2178           1 :     CPLError(CE_Failure, CPLE_AppDefined, "IWrite() not implemented");
    2179           1 :     return false;
    2180             : }
    2181             : 
    2182             : //! @endcond
    2183             : 
    2184             : /************************************************************************/
    2185             : /*                               Write()                                 */
    2186             : /************************************************************************/
    2187             : 
    2188             : /** Write part or totality of a multidimensional array or attribute.
    2189             :  *
    2190             :  * This will set the content of a hyper-rectangle into the array from
    2191             :  * a user supplied buffer.
    2192             :  *
    2193             :  * If bufferDataType is of type string, the values read from pSrcBuffer
    2194             :  * will be char* pointers.
    2195             :  *
    2196             :  * This is the same as the C function GDALMDArrayWrite().
    2197             :  *
    2198             :  * @param arrayStartIdx Values representing the starting index to write
    2199             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2200             :  *                      Array of GetDimensionCount() values. Must not be
    2201             :  *                      nullptr, unless for a zero-dimensional array.
    2202             :  *
    2203             :  * @param count         Values representing the number of values to write in
    2204             :  *                      each dimension.
    2205             :  *                      Array of GetDimensionCount() values. Must not be
    2206             :  *                      nullptr, unless for a zero-dimensional array.
    2207             :  *
    2208             :  * @param arrayStep     Spacing between values to write in each dimension.
    2209             :  *                      The spacing is in number of array elements, not bytes.
    2210             :  *                      If provided, must contain GetDimensionCount() values.
    2211             :  *                      If set to nullptr, [1, 1, ... 1] will be used as a
    2212             :  * default to indicate consecutive elements.
    2213             :  *
    2214             :  * @param bufferStride  Spacing between values to read from pSrcBuffer.
    2215             :  *                      The spacing is in number of array elements, not bytes.
    2216             :  *                      If provided, must contain GetDimensionCount() values.
    2217             :  *                      Negative values are possible (for example to reorder
    2218             :  *                      from bottom-to-top to top-to-bottom).
    2219             :  *                      If set to nullptr, will be set so that pSrcBuffer is
    2220             :  *                      written in a compact way, with elements of the last /
    2221             :  *                      fastest varying dimension being consecutive.
    2222             :  *
    2223             :  * @param bufferDataType Data type of values in pSrcBuffer.
    2224             :  *
    2225             :  * @param pSrcBuffer    User buffer to read the values from. Should be big
    2226             :  *                      enough to store the number of values indicated by
    2227             :  * count[] and with the spacing of bufferStride[].
    2228             :  *
    2229             :  * @param pSrcBufferAllocStart Optional pointer that can be used to validate the
    2230             :  *                             validty of pSrcBuffer. pSrcBufferAllocStart
    2231             :  * should be the pointer returned by the malloc() or equivalent call used to
    2232             :  * allocate the buffer. It will generally be equal to pSrcBuffer (when
    2233             :  * bufferStride[] values are all positive), but not necessarily. If specified,
    2234             :  * nSrcBufferAllocSize should be also set to the appropriate value. If no
    2235             :  * validation is needed, nullptr can be passed.
    2236             :  *
    2237             :  * @param nSrcBufferAllocSize  Optional buffer size, that can be used to
    2238             :  * validate the validty of pSrcBuffer. This is the size of the buffer starting
    2239             :  * at pSrcBufferAllocStart. If specified, pDstBufferAllocStart should be also
    2240             :  *                             set to the appropriate value.
    2241             :  *                             If no validation is needed, 0 can be passed.
    2242             :  *
    2243             :  * @return true in case of success.
    2244             :  */
    2245        1670 : bool GDALAbstractMDArray::Write(const GUInt64 *arrayStartIdx,
    2246             :                                 const size_t *count, const GInt64 *arrayStep,
    2247             :                                 const GPtrDiff_t *bufferStride,
    2248             :                                 const GDALExtendedDataType &bufferDataType,
    2249             :                                 const void *pSrcBuffer,
    2250             :                                 const void *pSrcBufferAllocStart,
    2251             :                                 size_t nSrcBufferAllocSize)
    2252             : {
    2253        1670 :     if (!bufferDataType.CanConvertTo(GetDataType()))
    2254             :     {
    2255           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2256             :                  "Buffer data type is not convertible to array data type");
    2257           0 :         return false;
    2258             :     }
    2259             : 
    2260        3340 :     std::vector<GInt64> tmp_arrayStep;
    2261        3340 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    2262        1670 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    2263             :                               bufferDataType, pSrcBuffer, pSrcBufferAllocStart,
    2264             :                               nSrcBufferAllocSize, tmp_arrayStep,
    2265             :                               tmp_bufferStride))
    2266             :     {
    2267           0 :         return false;
    2268             :     }
    2269             : 
    2270        1670 :     return IWrite(arrayStartIdx, count, arrayStep, bufferStride, bufferDataType,
    2271        1670 :                   pSrcBuffer);
    2272             : }
    2273             : 
    2274             : /************************************************************************/
    2275             : /*                          GetTotalElementsCount()                     */
    2276             : /************************************************************************/
    2277             : 
    2278             : /** Return the total number of values in the array.
    2279             :  *
    2280             :  * This is the same as the C functions GDALMDArrayGetTotalElementsCount()
    2281             :  * and GDALAttributeGetTotalElementsCount().
    2282             :  *
    2283             :  */
    2284         949 : GUInt64 GDALAbstractMDArray::GetTotalElementsCount() const
    2285             : {
    2286         949 :     const auto &dims = GetDimensions();
    2287         949 :     if (dims.empty())
    2288         499 :         return 1;
    2289         450 :     GUInt64 nElts = 1;
    2290        1010 :     for (const auto &dim : dims)
    2291             :     {
    2292             :         try
    2293             :         {
    2294         560 :             nElts = (CPLSM(static_cast<GUInt64>(nElts)) *
    2295        1680 :                      CPLSM(static_cast<GUInt64>(dim->GetSize())))
    2296         560 :                         .v();
    2297             :         }
    2298           0 :         catch (...)
    2299             :         {
    2300           0 :             return 0;
    2301             :         }
    2302             :     }
    2303         450 :     return nElts;
    2304             : }
    2305             : 
    2306             : /************************************************************************/
    2307             : /*                           GetBlockSize()                             */
    2308             : /************************************************************************/
    2309             : 
    2310             : /** Return the "natural" block size of the array along all dimensions.
    2311             :  *
    2312             :  * Some drivers might organize the array in tiles/blocks and reading/writing
    2313             :  * aligned on those tile/block boundaries will be more efficient.
    2314             :  *
    2315             :  * The returned number of elements in the vector is the same as
    2316             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
    2317             :  * the natural block size along the considered dimension.
    2318             :  * "Flat" arrays will typically return a vector of values set to 0.
    2319             :  *
    2320             :  * The default implementation will return a vector of values set to 0.
    2321             :  *
    2322             :  * This method is used by GetProcessingChunkSize().
    2323             :  *
    2324             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
    2325             :  * theoretical case of a 32-bit platform, this might exceed its size_t
    2326             :  * allocation capabilities.
    2327             :  *
    2328             :  * This is the same as the C function GDALMDArrayGetBlockSize().
    2329             :  *
    2330             :  * @return the block size, in number of elements along each dimension.
    2331             :  */
    2332         215 : std::vector<GUInt64> GDALAbstractMDArray::GetBlockSize() const
    2333             : {
    2334         215 :     return std::vector<GUInt64>(GetDimensionCount());
    2335             : }
    2336             : 
    2337             : /************************************************************************/
    2338             : /*                       GetProcessingChunkSize()                       */
    2339             : /************************************************************************/
    2340             : 
    2341             : /** \brief Return an optimal chunk size for read/write operations, given the
    2342             :  * natural block size and memory constraints specified.
    2343             :  *
    2344             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
    2345             :  * multiple of those returned by GetBlockSize() (unless the block define by
    2346             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
    2347             :  * returned by this method).
    2348             :  *
    2349             :  * This is the same as the C function GDALMDArrayGetProcessingChunkSize().
    2350             :  *
    2351             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
    2352             :  * chunk.
    2353             :  *
    2354             :  * @return the chunk size, in number of elements along each dimension.
    2355             :  */
    2356             : std::vector<size_t>
    2357          60 : GDALAbstractMDArray::GetProcessingChunkSize(size_t nMaxChunkMemory) const
    2358             : {
    2359          60 :     const auto &dims = GetDimensions();
    2360          60 :     const auto &nDTSize = GetDataType().GetSize();
    2361          60 :     std::vector<size_t> anChunkSize;
    2362         120 :     auto blockSize = GetBlockSize();
    2363          60 :     CPLAssert(blockSize.size() == dims.size());
    2364          60 :     size_t nChunkSize = nDTSize;
    2365          60 :     bool bOverflow = false;
    2366          60 :     constexpr auto kSIZE_T_MAX = std::numeric_limits<size_t>::max();
    2367             :     // Initialize anChunkSize[i] with blockSize[i] by properly clamping in
    2368             :     // [1, min(sizet_max, dim_size[i])]
    2369             :     // Also make sure that the product of all anChunkSize[i]) fits on size_t
    2370         173 :     for (size_t i = 0; i < dims.size(); i++)
    2371             :     {
    2372             :         const auto sizeDimI =
    2373         226 :             std::max(static_cast<size_t>(1),
    2374         226 :                      static_cast<size_t>(
    2375         226 :                          std::min(static_cast<GUInt64>(kSIZE_T_MAX),
    2376         113 :                                   std::min(blockSize[i], dims[i]->GetSize()))));
    2377         113 :         anChunkSize.push_back(sizeDimI);
    2378         113 :         if (nChunkSize > kSIZE_T_MAX / sizeDimI)
    2379             :         {
    2380           4 :             bOverflow = true;
    2381             :         }
    2382             :         else
    2383             :         {
    2384         109 :             nChunkSize *= sizeDimI;
    2385             :         }
    2386             :     }
    2387          60 :     if (nChunkSize == 0)
    2388           0 :         return anChunkSize;
    2389             : 
    2390             :     // If the product of all anChunkSize[i] does not fit on size_t, then
    2391             :     // set lowest anChunkSize[i] to 1.
    2392          60 :     if (bOverflow)
    2393             :     {
    2394           2 :         nChunkSize = nDTSize;
    2395           2 :         bOverflow = false;
    2396           8 :         for (size_t i = dims.size(); i > 0;)
    2397             :         {
    2398           6 :             --i;
    2399           6 :             if (bOverflow || nChunkSize > kSIZE_T_MAX / anChunkSize[i])
    2400             :             {
    2401           4 :                 bOverflow = true;
    2402           4 :                 anChunkSize[i] = 1;
    2403             :             }
    2404             :             else
    2405             :             {
    2406           2 :                 nChunkSize *= anChunkSize[i];
    2407             :             }
    2408             :         }
    2409             :     }
    2410             : 
    2411          60 :     nChunkSize = nDTSize;
    2412         120 :     std::vector<size_t> anAccBlockSizeFromStart;
    2413         173 :     for (size_t i = 0; i < dims.size(); i++)
    2414             :     {
    2415         113 :         nChunkSize *= anChunkSize[i];
    2416         113 :         anAccBlockSizeFromStart.push_back(nChunkSize);
    2417             :     }
    2418          60 :     if (nChunkSize <= nMaxChunkMemory / 2)
    2419             :     {
    2420          56 :         size_t nVoxelsFromEnd = 1;
    2421         161 :         for (size_t i = dims.size(); i > 0;)
    2422             :         {
    2423         105 :             --i;
    2424             :             const auto nCurBlockSize =
    2425         105 :                 anAccBlockSizeFromStart[i] * nVoxelsFromEnd;
    2426         105 :             const auto nMul = nMaxChunkMemory / nCurBlockSize;
    2427         105 :             if (nMul >= 2)
    2428             :             {
    2429          97 :                 const auto nSizeThisDim(dims[i]->GetSize());
    2430             :                 const auto nBlocksThisDim =
    2431          97 :                     DIV_ROUND_UP(nSizeThisDim, anChunkSize[i]);
    2432          97 :                 anChunkSize[i] = static_cast<size_t>(std::min(
    2433          97 :                     anChunkSize[i] *
    2434         194 :                         std::min(static_cast<GUInt64>(nMul), nBlocksThisDim),
    2435          97 :                     nSizeThisDim));
    2436             :             }
    2437         105 :             nVoxelsFromEnd *= anChunkSize[i];
    2438             :         }
    2439             :     }
    2440          60 :     return anChunkSize;
    2441             : }
    2442             : 
    2443             : /************************************************************************/
    2444             : /*                         BaseRename()                                 */
    2445             : /************************************************************************/
    2446             : 
    2447             : //! @cond Doxygen_Suppress
    2448          18 : void GDALAbstractMDArray::BaseRename(const std::string &osNewName)
    2449             : {
    2450          18 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
    2451          18 :     m_osFullName += osNewName;
    2452          18 :     m_osName = osNewName;
    2453             : 
    2454          18 :     NotifyChildrenOfRenaming();
    2455          18 : }
    2456             : 
    2457             : //! @endcond
    2458             : 
    2459             : //! @cond Doxygen_Suppress
    2460             : /************************************************************************/
    2461             : /*                          ParentRenamed()                             */
    2462             : /************************************************************************/
    2463             : 
    2464          50 : void GDALAbstractMDArray::ParentRenamed(const std::string &osNewParentFullName)
    2465             : {
    2466          50 :     m_osFullName = osNewParentFullName;
    2467          50 :     m_osFullName += "/";
    2468          50 :     m_osFullName += m_osName;
    2469             : 
    2470          50 :     NotifyChildrenOfRenaming();
    2471          50 : }
    2472             : 
    2473             : //! @endcond
    2474             : 
    2475             : /************************************************************************/
    2476             : /*                             Deleted()                                */
    2477             : /************************************************************************/
    2478             : 
    2479             : //! @cond Doxygen_Suppress
    2480          52 : void GDALAbstractMDArray::Deleted()
    2481             : {
    2482          52 :     m_bValid = false;
    2483             : 
    2484          52 :     NotifyChildrenOfDeletion();
    2485          52 : }
    2486             : 
    2487             : //! @endcond
    2488             : 
    2489             : /************************************************************************/
    2490             : /*                        ParentDeleted()                               */
    2491             : /************************************************************************/
    2492             : 
    2493             : //! @cond Doxygen_Suppress
    2494          28 : void GDALAbstractMDArray::ParentDeleted()
    2495             : {
    2496          28 :     Deleted();
    2497          28 : }
    2498             : 
    2499             : //! @endcond
    2500             : 
    2501             : /************************************************************************/
    2502             : /*                     CheckValidAndErrorOutIfNot()                     */
    2503             : /************************************************************************/
    2504             : 
    2505             : //! @cond Doxygen_Suppress
    2506        5379 : bool GDALAbstractMDArray::CheckValidAndErrorOutIfNot() const
    2507             : {
    2508        5379 :     if (!m_bValid)
    2509             :     {
    2510          26 :         CPLError(CE_Failure, CPLE_AppDefined,
    2511             :                  "This object has been deleted. No action on it is possible");
    2512             :     }
    2513        5379 :     return m_bValid;
    2514             : }
    2515             : 
    2516             : //! @endcond
    2517             : 
    2518             : /************************************************************************/
    2519             : /*                             SetUnit()                                */
    2520             : /************************************************************************/
    2521             : 
    2522             : /** Set the variable unit.
    2523             :  *
    2524             :  * Values should conform as much as possible with those allowed by
    2525             :  * the NetCDF CF conventions:
    2526             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2527             :  * but others might be returned.
    2528             :  *
    2529             :  * Few examples are "meter", "degrees", "second", ...
    2530             :  * Empty value means unknown.
    2531             :  *
    2532             :  * This is the same as the C function GDALMDArraySetUnit()
    2533             :  *
    2534             :  * @note Driver implementation: optionally implemented.
    2535             :  *
    2536             :  * @param osUnit unit name.
    2537             :  * @return true in case of success.
    2538             :  */
    2539           0 : bool GDALMDArray::SetUnit(CPL_UNUSED const std::string &osUnit)
    2540             : {
    2541           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetUnit() not implemented");
    2542           0 :     return false;
    2543             : }
    2544             : 
    2545             : /************************************************************************/
    2546             : /*                             GetUnit()                                */
    2547             : /************************************************************************/
    2548             : 
    2549             : /** Return the array unit.
    2550             :  *
    2551             :  * Values should conform as much as possible with those allowed by
    2552             :  * the NetCDF CF conventions:
    2553             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
    2554             :  * but others might be returned.
    2555             :  *
    2556             :  * Few examples are "meter", "degrees", "second", ...
    2557             :  * Empty value means unknown.
    2558             :  *
    2559             :  * This is the same as the C function GDALMDArrayGetUnit()
    2560             :  */
    2561           5 : const std::string &GDALMDArray::GetUnit() const
    2562             : {
    2563           5 :     static const std::string emptyString;
    2564           5 :     return emptyString;
    2565             : }
    2566             : 
    2567             : /************************************************************************/
    2568             : /*                          SetSpatialRef()                             */
    2569             : /************************************************************************/
    2570             : 
    2571             : /** Assign a spatial reference system object to the array.
    2572             :  *
    2573             :  * This is the same as the C function GDALMDArraySetSpatialRef().
    2574             :  */
    2575           0 : bool GDALMDArray::SetSpatialRef(CPL_UNUSED const OGRSpatialReference *poSRS)
    2576             : {
    2577           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetSpatialRef() not implemented");
    2578           0 :     return false;
    2579             : }
    2580             : 
    2581             : /************************************************************************/
    2582             : /*                          GetSpatialRef()                             */
    2583             : /************************************************************************/
    2584             : 
    2585             : /** Return the spatial reference system object associated with the array.
    2586             :  *
    2587             :  * This is the same as the C function GDALMDArrayGetSpatialRef().
    2588             :  */
    2589           4 : std::shared_ptr<OGRSpatialReference> GDALMDArray::GetSpatialRef() const
    2590             : {
    2591           4 :     return nullptr;
    2592             : }
    2593             : 
    2594             : /************************************************************************/
    2595             : /*                        GetRawNoDataValue()                           */
    2596             : /************************************************************************/
    2597             : 
    2598             : /** Return the nodata value as a "raw" value.
    2599             :  *
    2600             :  * The value returned might be nullptr in case of no nodata value. When
    2601             :  * a nodata value is registered, a non-nullptr will be returned whose size in
    2602             :  * bytes is GetDataType().GetSize().
    2603             :  *
    2604             :  * The returned value should not be modified or freed. It is valid until
    2605             :  * the array is destroyed, or the next call to GetRawNoDataValue() or
    2606             :  * SetRawNoDataValue(), or any similar methods.
    2607             :  *
    2608             :  * @note Driver implementation: this method shall be implemented if nodata
    2609             :  * is supported.
    2610             :  *
    2611             :  * This is the same as the C function GDALMDArrayGetRawNoDataValue().
    2612             :  *
    2613             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
    2614             :  */
    2615           5 : const void *GDALMDArray::GetRawNoDataValue() const
    2616             : {
    2617           5 :     return nullptr;
    2618             : }
    2619             : 
    2620             : /************************************************************************/
    2621             : /*                        GetNoDataValueAsDouble()                      */
    2622             : /************************************************************************/
    2623             : 
    2624             : /** Return the nodata value as a double.
    2625             :  *
    2626             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsDouble().
    2627             :  *
    2628             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2629             :  * a nodata value exists and can be converted to double. Might be nullptr.
    2630             :  *
    2631             :  * @return the nodata value as a double. A 0.0 value might also indicate the
    2632             :  * absence of a nodata value or an error in the conversion (*pbHasNoData will be
    2633             :  * set to false then).
    2634             :  */
    2635       22389 : double GDALMDArray::GetNoDataValueAsDouble(bool *pbHasNoData) const
    2636             : {
    2637       22389 :     const void *pNoData = GetRawNoDataValue();
    2638       22389 :     double dfNoData = 0.0;
    2639       22389 :     const auto &eDT = GetDataType();
    2640       22389 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2641       22389 :     if (ok)
    2642             :     {
    2643       22152 :         GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &dfNoData,
    2644             :                       GDT_Float64, 0, 1);
    2645             :     }
    2646       22389 :     if (pbHasNoData)
    2647         372 :         *pbHasNoData = ok;
    2648       22389 :     return dfNoData;
    2649             : }
    2650             : 
    2651             : /************************************************************************/
    2652             : /*                        GetNoDataValueAsInt64()                       */
    2653             : /************************************************************************/
    2654             : 
    2655             : /** Return the nodata value as a Int64.
    2656             :  *
    2657             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2658             :  * a nodata value exists and can be converted to Int64. Might be nullptr.
    2659             :  *
    2660             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsInt64().
    2661             :  *
    2662             :  * @return the nodata value as a Int64
    2663             :  *
    2664             :  * @since GDAL 3.5
    2665             :  */
    2666          12 : int64_t GDALMDArray::GetNoDataValueAsInt64(bool *pbHasNoData) const
    2667             : {
    2668          12 :     const void *pNoData = GetRawNoDataValue();
    2669          12 :     int64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_INT64;
    2670          12 :     const auto &eDT = GetDataType();
    2671          12 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2672          12 :     if (ok)
    2673             :     {
    2674           8 :         GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData, GDT_Int64,
    2675             :                       0, 1);
    2676             :     }
    2677          12 :     if (pbHasNoData)
    2678          12 :         *pbHasNoData = ok;
    2679          12 :     return nNoData;
    2680             : }
    2681             : 
    2682             : /************************************************************************/
    2683             : /*                       GetNoDataValueAsUInt64()                       */
    2684             : /************************************************************************/
    2685             : 
    2686             : /** Return the nodata value as a UInt64.
    2687             :  *
    2688             :  * This is the same as the C function GDALMDArrayGetNoDataValueAsUInt64().
    2689             : 
    2690             :  * @param pbHasNoData Pointer to a output boolean that will be set to true if
    2691             :  * a nodata value exists and can be converted to UInt64. Might be nullptr.
    2692             :  *
    2693             :  * @return the nodata value as a UInt64
    2694             :  *
    2695             :  * @since GDAL 3.5
    2696             :  */
    2697           8 : uint64_t GDALMDArray::GetNoDataValueAsUInt64(bool *pbHasNoData) const
    2698             : {
    2699           8 :     const void *pNoData = GetRawNoDataValue();
    2700           8 :     uint64_t nNoData = GDAL_PAM_DEFAULT_NODATA_VALUE_UINT64;
    2701           8 :     const auto &eDT = GetDataType();
    2702           8 :     const bool ok = pNoData != nullptr && eDT.GetClass() == GEDTC_NUMERIC;
    2703           8 :     if (ok)
    2704             :     {
    2705           6 :         GDALCopyWords(pNoData, eDT.GetNumericDataType(), 0, &nNoData,
    2706             :                       GDT_UInt64, 0, 1);
    2707             :     }
    2708           8 :     if (pbHasNoData)
    2709           8 :         *pbHasNoData = ok;
    2710           8 :     return nNoData;
    2711             : }
    2712             : 
    2713             : /************************************************************************/
    2714             : /*                        SetRawNoDataValue()                           */
    2715             : /************************************************************************/
    2716             : 
    2717             : /** Set the nodata value as a "raw" value.
    2718             :  *
    2719             :  * The value passed might be nullptr in case of no nodata value. When
    2720             :  * a nodata value is registered, a non-nullptr whose size in
    2721             :  * bytes is GetDataType().GetSize() must be passed.
    2722             :  *
    2723             :  * This is the same as the C function GDALMDArraySetRawNoDataValue().
    2724             :  *
    2725             :  * @note Driver implementation: this method shall be implemented if setting
    2726             :  nodata
    2727             :  * is supported.
    2728             : 
    2729             :  * @return true in case of success.
    2730             :  */
    2731           0 : bool GDALMDArray::SetRawNoDataValue(CPL_UNUSED const void *pRawNoData)
    2732             : {
    2733           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2734             :              "SetRawNoDataValue() not implemented");
    2735           0 :     return false;
    2736             : }
    2737             : 
    2738             : /************************************************************************/
    2739             : /*                           SetNoDataValue()                           */
    2740             : /************************************************************************/
    2741             : 
    2742             : /** Set the nodata value as a double.
    2743             :  *
    2744             :  * If the natural data type of the attribute/array is not double, type
    2745             :  * conversion will occur to the type returned by GetDataType().
    2746             :  *
    2747             :  * This is the same as the C function GDALMDArraySetNoDataValueAsDouble().
    2748             :  *
    2749             :  * @return true in case of success.
    2750             :  */
    2751          56 : bool GDALMDArray::SetNoDataValue(double dfNoData)
    2752             : {
    2753          56 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2754          56 :     bool bRet = false;
    2755          56 :     if (GDALExtendedDataType::CopyValue(
    2756         112 :             &dfNoData, GDALExtendedDataType::Create(GDT_Float64), pRawNoData,
    2757          56 :             GetDataType()))
    2758             :     {
    2759          56 :         bRet = SetRawNoDataValue(pRawNoData);
    2760             :     }
    2761          56 :     CPLFree(pRawNoData);
    2762          56 :     return bRet;
    2763             : }
    2764             : 
    2765             : /************************************************************************/
    2766             : /*                           SetNoDataValue()                           */
    2767             : /************************************************************************/
    2768             : 
    2769             : /** Set the nodata value as a Int64.
    2770             :  *
    2771             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2772             :  * will occur to the type returned by GetDataType().
    2773             :  *
    2774             :  * This is the same as the C function GDALMDArraySetNoDataValueAsInt64().
    2775             :  *
    2776             :  * @return true in case of success.
    2777             :  *
    2778             :  * @since GDAL 3.5
    2779             :  */
    2780           3 : bool GDALMDArray::SetNoDataValue(int64_t nNoData)
    2781             : {
    2782           3 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2783           3 :     bool bRet = false;
    2784           3 :     if (GDALExtendedDataType::CopyValue(&nNoData,
    2785           6 :                                         GDALExtendedDataType::Create(GDT_Int64),
    2786           3 :                                         pRawNoData, GetDataType()))
    2787             :     {
    2788           3 :         bRet = SetRawNoDataValue(pRawNoData);
    2789             :     }
    2790           3 :     CPLFree(pRawNoData);
    2791           3 :     return bRet;
    2792             : }
    2793             : 
    2794             : /************************************************************************/
    2795             : /*                           SetNoDataValue()                           */
    2796             : /************************************************************************/
    2797             : 
    2798             : /** Set the nodata value as a Int64.
    2799             :  *
    2800             :  * If the natural data type of the attribute/array is not Int64, type conversion
    2801             :  * will occur to the type returned by GetDataType().
    2802             :  *
    2803             :  * This is the same as the C function GDALMDArraySetNoDataValueAsUInt64().
    2804             :  *
    2805             :  * @return true in case of success.
    2806             :  *
    2807             :  * @since GDAL 3.5
    2808             :  */
    2809           1 : bool GDALMDArray::SetNoDataValue(uint64_t nNoData)
    2810             : {
    2811           1 :     void *pRawNoData = CPLMalloc(GetDataType().GetSize());
    2812           1 :     bool bRet = false;
    2813           1 :     if (GDALExtendedDataType::CopyValue(
    2814           2 :             &nNoData, GDALExtendedDataType::Create(GDT_UInt64), pRawNoData,
    2815           1 :             GetDataType()))
    2816             :     {
    2817           1 :         bRet = SetRawNoDataValue(pRawNoData);
    2818             :     }
    2819           1 :     CPLFree(pRawNoData);
    2820           1 :     return bRet;
    2821             : }
    2822             : 
    2823             : /************************************************************************/
    2824             : /*                            Resize()                                  */
    2825             : /************************************************************************/
    2826             : 
    2827             : /** Resize an array to new dimensions.
    2828             :  *
    2829             :  * Not all drivers may allow this operation, and with restrictions (e.g.
    2830             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
    2831             :  *
    2832             :  * Resizing a dimension used in other arrays will cause those other arrays
    2833             :  * to be resized.
    2834             :  *
    2835             :  * This is the same as the C function GDALMDArrayResize().
    2836             :  *
    2837             :  * @param anNewDimSizes Array of GetDimensionCount() values containing the
    2838             :  *                      new size of each indexing dimension.
    2839             :  * @param papszOptions Options. (Driver specific)
    2840             :  * @return true in case of success.
    2841             :  * @since GDAL 3.7
    2842             :  */
    2843           0 : bool GDALMDArray::Resize(CPL_UNUSED const std::vector<GUInt64> &anNewDimSizes,
    2844             :                          CPL_UNUSED CSLConstList papszOptions)
    2845             : {
    2846           0 :     CPLError(CE_Failure, CPLE_NotSupported,
    2847             :              "Resize() is not supported for this array");
    2848           0 :     return false;
    2849             : }
    2850             : 
    2851             : /************************************************************************/
    2852             : /*                               SetScale()                             */
    2853             : /************************************************************************/
    2854             : 
    2855             : /** Set the scale value to apply to raw values.
    2856             :  *
    2857             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2858             :  *
    2859             :  * This is the same as the C function GDALMDArraySetScale() /
    2860             :  * GDALMDArraySetScaleEx().
    2861             :  *
    2862             :  * @note Driver implementation: this method shall be implemented if setting
    2863             :  * scale is supported.
    2864             :  *
    2865             :  * @param dfScale scale
    2866             :  * @param eStorageType Data type to which create the potential attribute that
    2867             :  * will store the scale. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2868             :  * implementation will decide automatically the data type. Note that changing
    2869             :  * the data type after initial setting might not be supported.
    2870             :  * @return true in case of success.
    2871             :  */
    2872           0 : bool GDALMDArray::SetScale(CPL_UNUSED double dfScale,
    2873             :                            CPL_UNUSED GDALDataType eStorageType)
    2874             : {
    2875           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetScale() not implemented");
    2876           0 :     return false;
    2877             : }
    2878             : 
    2879             : /************************************************************************/
    2880             : /*                               SetOffset)                             */
    2881             : /************************************************************************/
    2882             : 
    2883             : /** Set the offset value to apply to raw values.
    2884             :  *
    2885             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2886             :  *
    2887             :  * This is the same as the C function GDALMDArraySetOffset() /
    2888             :  * GDALMDArraySetOffsetEx().
    2889             :  *
    2890             :  * @note Driver implementation: this method shall be implemented if setting
    2891             :  * offset is supported.
    2892             :  *
    2893             :  * @param dfOffset Offset
    2894             :  * @param eStorageType Data type to which create the potential attribute that
    2895             :  * will store the offset. Added in GDAL 3.3 If let to its GDT_Unknown value, the
    2896             :  * implementation will decide automatically the data type. Note that changing
    2897             :  * the data type after initial setting might not be supported.
    2898             :  * @return true in case of success.
    2899             :  */
    2900           0 : bool GDALMDArray::SetOffset(CPL_UNUSED double dfOffset,
    2901             :                             CPL_UNUSED GDALDataType eStorageType)
    2902             : {
    2903           0 :     CPLError(CE_Failure, CPLE_NotSupported, "SetOffset() not implemented");
    2904           0 :     return false;
    2905             : }
    2906             : 
    2907             : /************************************************************************/
    2908             : /*                               GetScale()                             */
    2909             : /************************************************************************/
    2910             : 
    2911             : /** Get the scale value to apply to raw values.
    2912             :  *
    2913             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2914             :  *
    2915             :  * This is the same as the C function GDALMDArrayGetScale().
    2916             :  *
    2917             :  * @note Driver implementation: this method shall be implemented if gettings
    2918             :  * scale is supported.
    2919             :  *
    2920             :  * @param pbHasScale Pointer to a output boolean that will be set to true if
    2921             :  * a scale value exists. Might be nullptr.
    2922             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2923             :  * the storage type of the scale value, when known/relevant. Otherwise will be
    2924             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2925             :  *
    2926             :  * @return the scale value. A 1.0 value might also indicate the
    2927             :  * absence of a scale value.
    2928             :  */
    2929          13 : double GDALMDArray::GetScale(CPL_UNUSED bool *pbHasScale,
    2930             :                              CPL_UNUSED GDALDataType *peStorageType) const
    2931             : {
    2932          13 :     if (pbHasScale)
    2933          13 :         *pbHasScale = false;
    2934          13 :     return 1.0;
    2935             : }
    2936             : 
    2937             : /************************************************************************/
    2938             : /*                               GetOffset()                            */
    2939             : /************************************************************************/
    2940             : 
    2941             : /** Get the offset value to apply to raw values.
    2942             :  *
    2943             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    2944             :  *
    2945             :  * This is the same as the C function GDALMDArrayGetOffset().
    2946             :  *
    2947             :  * @note Driver implementation: this method shall be implemented if gettings
    2948             :  * offset is supported.
    2949             :  *
    2950             :  * @param pbHasOffset Pointer to a output boolean that will be set to true if
    2951             :  * a offset value exists. Might be nullptr.
    2952             :  * @param peStorageType Pointer to a output GDALDataType that will be set to
    2953             :  * the storage type of the offset value, when known/relevant. Otherwise will be
    2954             :  * set to GDT_Unknown. Might be nullptr. Since GDAL 3.3
    2955             :  *
    2956             :  * @return the offset value. A 0.0 value might also indicate the
    2957             :  * absence of a offset value.
    2958             :  */
    2959          13 : double GDALMDArray::GetOffset(CPL_UNUSED bool *pbHasOffset,
    2960             :                               CPL_UNUSED GDALDataType *peStorageType) const
    2961             : {
    2962          13 :     if (pbHasOffset)
    2963          13 :         *pbHasOffset = false;
    2964          13 :     return 0.0;
    2965             : }
    2966             : 
    2967             : /************************************************************************/
    2968             : /*                         ProcessPerChunk()                            */
    2969             : /************************************************************************/
    2970             : 
    2971             : namespace
    2972             : {
    2973             : enum class Caller
    2974             : {
    2975             :     CALLER_END_OF_LOOP,
    2976             :     CALLER_IN_LOOP,
    2977             : };
    2978             : }
    2979             : 
    2980             : /** \brief Call a user-provided function to operate on an array chunk by chunk.
    2981             :  *
    2982             :  * This method is to be used when doing operations on an array, or a subset of
    2983             :  * it, in a chunk by chunk way.
    2984             :  *
    2985             :  * @param arrayStartIdx Values representing the starting index to use
    2986             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    2987             :  *                      Array of GetDimensionCount() values. Must not be
    2988             :  *                      nullptr, unless for a zero-dimensional array.
    2989             :  *
    2990             :  * @param count         Values representing the number of values to use in
    2991             :  *                      each dimension.
    2992             :  *                      Array of GetDimensionCount() values. Must not be
    2993             :  *                      nullptr, unless for a zero-dimensional array.
    2994             :  *
    2995             :  * @param chunkSize     Values representing the chunk size in each dimension.
    2996             :  *                      Might typically the output of GetProcessingChunkSize().
    2997             :  *                      Array of GetDimensionCount() values. Must not be
    2998             :  *                      nullptr, unless for a zero-dimensional array.
    2999             :  *
    3000             :  * @param pfnFunc       User-provided function of type FuncProcessPerChunkType.
    3001             :  *                      Must NOT be nullptr.
    3002             :  *
    3003             :  * @param pUserData     Pointer to pass as the value of the pUserData argument
    3004             :  * of FuncProcessPerChunkType. Might be nullptr (depends on pfnFunc.
    3005             :  *
    3006             :  * @return true in case of success.
    3007             :  */
    3008          58 : bool GDALAbstractMDArray::ProcessPerChunk(const GUInt64 *arrayStartIdx,
    3009             :                                           const GUInt64 *count,
    3010             :                                           const size_t *chunkSize,
    3011             :                                           FuncProcessPerChunkType pfnFunc,
    3012             :                                           void *pUserData)
    3013             : {
    3014          58 :     const auto &dims = GetDimensions();
    3015          58 :     if (dims.empty())
    3016             :     {
    3017           2 :         return pfnFunc(this, nullptr, nullptr, 1, 1, pUserData);
    3018             :     }
    3019             : 
    3020             :     // Sanity check
    3021          56 :     size_t nTotalChunkSize = 1;
    3022         146 :     for (size_t i = 0; i < dims.size(); i++)
    3023             :     {
    3024          97 :         const auto nSizeThisDim(dims[i]->GetSize());
    3025          97 :         if (count[i] == 0 || count[i] > nSizeThisDim ||
    3026          95 :             arrayStartIdx[i] > nSizeThisDim - count[i])
    3027             :         {
    3028           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    3029             :                      "Inconsistent arrayStartIdx[] / count[] values "
    3030             :                      "regarding array size");
    3031           4 :             return false;
    3032             :         }
    3033         184 :         if (chunkSize[i] == 0 || chunkSize[i] > nSizeThisDim ||
    3034          91 :             chunkSize[i] > std::numeric_limits<size_t>::max() / nTotalChunkSize)
    3035             :         {
    3036           3 :             CPLError(CE_Failure, CPLE_AppDefined,
    3037             :                      "Inconsistent chunkSize[] values");
    3038           3 :             return false;
    3039             :         }
    3040          90 :         nTotalChunkSize *= chunkSize[i];
    3041             :     }
    3042             : 
    3043          49 :     size_t dimIdx = 0;
    3044          98 :     std::vector<GUInt64> chunkArrayStartIdx(dims.size());
    3045          98 :     std::vector<size_t> chunkCount(dims.size());
    3046             : 
    3047             :     struct Stack
    3048             :     {
    3049             :         GUInt64 nBlockCounter = 0;
    3050             :         GUInt64 nBlocksMinusOne = 0;
    3051             :         size_t first_count = 0;  // only used if nBlocks > 1
    3052             :         Caller return_point = Caller::CALLER_END_OF_LOOP;
    3053             :     };
    3054             : 
    3055          98 :     std::vector<Stack> stack(dims.size());
    3056          49 :     GUInt64 iCurChunk = 0;
    3057          49 :     GUInt64 nChunkCount = 1;
    3058         138 :     for (size_t i = 0; i < dims.size(); i++)
    3059             :     {
    3060          89 :         const auto nStartBlock = arrayStartIdx[i] / chunkSize[i];
    3061          89 :         const auto nEndBlock = (arrayStartIdx[i] + count[i] - 1) / chunkSize[i];
    3062          89 :         stack[i].nBlocksMinusOne = nEndBlock - nStartBlock;
    3063          89 :         nChunkCount *= 1 + stack[i].nBlocksMinusOne;
    3064          89 :         if (stack[i].nBlocksMinusOne == 0)
    3065             :         {
    3066          84 :             chunkArrayStartIdx[i] = arrayStartIdx[i];
    3067          84 :             chunkCount[i] = static_cast<size_t>(count[i]);
    3068             :         }
    3069             :         else
    3070             :         {
    3071           5 :             stack[i].first_count = static_cast<size_t>(
    3072           5 :                 (nStartBlock + 1) * chunkSize[i] - arrayStartIdx[i]);
    3073             :         }
    3074             :     }
    3075             : 
    3076          49 : lbl_next_depth:
    3077         248 :     if (dimIdx == dims.size())
    3078             :     {
    3079          82 :         ++iCurChunk;
    3080          82 :         if (!pfnFunc(this, chunkArrayStartIdx.data(), chunkCount.data(),
    3081             :                      iCurChunk, nChunkCount, pUserData))
    3082             :         {
    3083           0 :             return false;
    3084             :         }
    3085             :     }
    3086             :     else
    3087             :     {
    3088         166 :         if (stack[dimIdx].nBlocksMinusOne != 0)
    3089             :         {
    3090          11 :             stack[dimIdx].nBlockCounter = stack[dimIdx].nBlocksMinusOne;
    3091          11 :             chunkArrayStartIdx[dimIdx] = arrayStartIdx[dimIdx];
    3092          11 :             chunkCount[dimIdx] = stack[dimIdx].first_count;
    3093          11 :             stack[dimIdx].return_point = Caller::CALLER_IN_LOOP;
    3094             :             while (true)
    3095             :             {
    3096          33 :                 dimIdx++;
    3097          33 :                 goto lbl_next_depth;
    3098          33 :             lbl_return_to_caller_in_loop:
    3099          33 :                 --stack[dimIdx].nBlockCounter;
    3100          33 :                 if (stack[dimIdx].nBlockCounter == 0)
    3101          11 :                     break;
    3102          22 :                 chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3103          22 :                 chunkCount[dimIdx] = chunkSize[dimIdx];
    3104             :             }
    3105             : 
    3106          11 :             chunkArrayStartIdx[dimIdx] += chunkCount[dimIdx];
    3107          22 :             chunkCount[dimIdx] =
    3108          11 :                 static_cast<size_t>(arrayStartIdx[dimIdx] + count[dimIdx] -
    3109          11 :                                     chunkArrayStartIdx[dimIdx]);
    3110          11 :             stack[dimIdx].return_point = Caller::CALLER_END_OF_LOOP;
    3111             :         }
    3112         166 :         dimIdx++;
    3113         166 :         goto lbl_next_depth;
    3114         166 :     lbl_return_to_caller_end_of_loop:
    3115         166 :         if (dimIdx == 0)
    3116          49 :             goto end;
    3117             :     }
    3118             : 
    3119         199 :     dimIdx--;
    3120             :     // cppcheck-suppress negativeContainerIndex
    3121         199 :     switch (stack[dimIdx].return_point)
    3122             :     {
    3123         166 :         case Caller::CALLER_END_OF_LOOP:
    3124         166 :             goto lbl_return_to_caller_end_of_loop;
    3125          33 :         case Caller::CALLER_IN_LOOP:
    3126          33 :             goto lbl_return_to_caller_in_loop;
    3127             :     }
    3128          49 : end:
    3129          49 :     return true;
    3130             : }
    3131             : 
    3132             : /************************************************************************/
    3133             : /*                          GDALAttribute()                             */
    3134             : /************************************************************************/
    3135             : 
    3136             : //! @cond Doxygen_Suppress
    3137       11863 : GDALAttribute::GDALAttribute(CPL_UNUSED const std::string &osParentName,
    3138           0 :                              CPL_UNUSED const std::string &osName)
    3139             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3140       11863 :     : GDALAbstractMDArray(osParentName, osName)
    3141             : #endif
    3142             : {
    3143       11863 : }
    3144             : 
    3145             : //! @endcond
    3146             : 
    3147             : /************************************************************************/
    3148             : /*                        GetDimensionSize()                            */
    3149             : /************************************************************************/
    3150             : 
    3151             : /** Return the size of the dimensions of the attribute.
    3152             :  *
    3153             :  * This will be an empty array for a scalar (single value) attribute.
    3154             :  *
    3155             :  * This is the same as the C function GDALAttributeGetDimensionsSize().
    3156             :  */
    3157         321 : std::vector<GUInt64> GDALAttribute::GetDimensionsSize() const
    3158             : {
    3159         321 :     const auto &dims = GetDimensions();
    3160         321 :     std::vector<GUInt64> ret;
    3161         321 :     ret.reserve(dims.size());
    3162         390 :     for (const auto &dim : dims)
    3163          69 :         ret.push_back(dim->GetSize());
    3164         321 :     return ret;
    3165             : }
    3166             : 
    3167             : /************************************************************************/
    3168             : /*                            GDALRawResult()                           */
    3169             : /************************************************************************/
    3170             : 
    3171             : //! @cond Doxygen_Suppress
    3172         148 : GDALRawResult::GDALRawResult(GByte *raw, const GDALExtendedDataType &dt,
    3173         148 :                              size_t nEltCount)
    3174         296 :     : m_dt(dt), m_nEltCount(nEltCount), m_nSize(nEltCount * dt.GetSize()),
    3175         148 :       m_raw(raw)
    3176             : {
    3177         148 : }
    3178             : 
    3179             : //! @endcond
    3180             : 
    3181             : /************************************************************************/
    3182             : /*                            GDALRawResult()                           */
    3183             : /************************************************************************/
    3184             : 
    3185             : /** Move constructor. */
    3186           0 : GDALRawResult::GDALRawResult(GDALRawResult &&other)
    3187           0 :     : m_dt(std::move(other.m_dt)), m_nEltCount(other.m_nEltCount),
    3188           0 :       m_nSize(other.m_nSize), m_raw(other.m_raw)
    3189             : {
    3190           0 :     other.m_nEltCount = 0;
    3191           0 :     other.m_nSize = 0;
    3192           0 :     other.m_raw = nullptr;
    3193           0 : }
    3194             : 
    3195             : /************************************************************************/
    3196             : /*                               FreeMe()                               */
    3197             : /************************************************************************/
    3198             : 
    3199         148 : void GDALRawResult::FreeMe()
    3200             : {
    3201         148 :     if (m_raw && m_dt.NeedsFreeDynamicMemory())
    3202             :     {
    3203          47 :         GByte *pabyPtr = m_raw;
    3204          47 :         const auto nDTSize(m_dt.GetSize());
    3205          94 :         for (size_t i = 0; i < m_nEltCount; ++i)
    3206             :         {
    3207          47 :             m_dt.FreeDynamicMemory(pabyPtr);
    3208          47 :             pabyPtr += nDTSize;
    3209             :         }
    3210             :     }
    3211         148 :     VSIFree(m_raw);
    3212         148 : }
    3213             : 
    3214             : /************************************************************************/
    3215             : /*                             operator=()                              */
    3216             : /************************************************************************/
    3217             : 
    3218             : /** Move assignment. */
    3219           0 : GDALRawResult &GDALRawResult::operator=(GDALRawResult &&other)
    3220             : {
    3221           0 :     FreeMe();
    3222           0 :     m_dt = std::move(other.m_dt);
    3223           0 :     m_nEltCount = other.m_nEltCount;
    3224           0 :     m_nSize = other.m_nSize;
    3225           0 :     m_raw = other.m_raw;
    3226           0 :     other.m_nEltCount = 0;
    3227           0 :     other.m_nSize = 0;
    3228           0 :     other.m_raw = nullptr;
    3229           0 :     return *this;
    3230             : }
    3231             : 
    3232             : /************************************************************************/
    3233             : /*                         ~GDALRawResult()                             */
    3234             : /************************************************************************/
    3235             : 
    3236             : /** Destructor. */
    3237         148 : GDALRawResult::~GDALRawResult()
    3238             : {
    3239         148 :     FreeMe();
    3240         148 : }
    3241             : 
    3242             : /************************************************************************/
    3243             : /*                            StealData()                               */
    3244             : /************************************************************************/
    3245             : 
    3246             : //! @cond Doxygen_Suppress
    3247             : /** Return buffer to caller which becomes owner of it.
    3248             :  * Only to be used by GDALAttributeReadAsRaw().
    3249             :  */
    3250           6 : GByte *GDALRawResult::StealData()
    3251             : {
    3252           6 :     GByte *ret = m_raw;
    3253           6 :     m_raw = nullptr;
    3254           6 :     m_nEltCount = 0;
    3255           6 :     m_nSize = 0;
    3256           6 :     return ret;
    3257             : }
    3258             : 
    3259             : //! @endcond
    3260             : 
    3261             : /************************************************************************/
    3262             : /*                             ReadAsRaw()                              */
    3263             : /************************************************************************/
    3264             : 
    3265             : /** Return the raw value of an attribute.
    3266             :  *
    3267             :  *
    3268             :  * This is the same as the C function GDALAttributeReadAsRaw().
    3269             :  */
    3270         148 : GDALRawResult GDALAttribute::ReadAsRaw() const
    3271             : {
    3272         148 :     const auto nEltCount(GetTotalElementsCount());
    3273         148 :     const auto &dt(GetDataType());
    3274         148 :     const auto nDTSize(dt.GetSize());
    3275             :     GByte *res = static_cast<GByte *>(
    3276         148 :         VSI_MALLOC2_VERBOSE(static_cast<size_t>(nEltCount), nDTSize));
    3277         148 :     if (!res)
    3278           0 :         return GDALRawResult(nullptr, dt, 0);
    3279         148 :     const auto &dims = GetDimensions();
    3280         148 :     const auto nDims = GetDimensionCount();
    3281         296 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3282         296 :     std::vector<size_t> count(1 + nDims);
    3283         167 :     for (size_t i = 0; i < nDims; i++)
    3284             :     {
    3285          19 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3286             :     }
    3287         148 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr, dt, &res[0],
    3288         148 :               &res[0], static_cast<size_t>(nEltCount * nDTSize)))
    3289             :     {
    3290           0 :         VSIFree(res);
    3291           0 :         return GDALRawResult(nullptr, dt, 0);
    3292             :     }
    3293         148 :     return GDALRawResult(res, dt, static_cast<size_t>(nEltCount));
    3294             : }
    3295             : 
    3296             : /************************************************************************/
    3297             : /*                            ReadAsString()                            */
    3298             : /************************************************************************/
    3299             : 
    3300             : /** Return the value of an attribute as a string.
    3301             :  *
    3302             :  * The returned string should not be freed, and its lifetime does not
    3303             :  * excess a next call to ReadAsString() on the same object, or the deletion
    3304             :  * of the object itself.
    3305             :  *
    3306             :  * This function will only return the first element if there are several.
    3307             :  *
    3308             :  * This is the same as the C function GDALAttributeReadAsString()
    3309             :  *
    3310             :  * @return a string, or nullptr.
    3311             :  */
    3312        1195 : const char *GDALAttribute::ReadAsString() const
    3313             : {
    3314        1195 :     const auto nDims = GetDimensionCount();
    3315        2390 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3316        2390 :     std::vector<size_t> count(1 + nDims, 1);
    3317        1195 :     char *szRet = nullptr;
    3318        1195 :     if (!Read(startIdx.data(), count.data(), nullptr, nullptr,
    3319        1195 :               GDALExtendedDataType::CreateString(), &szRet, &szRet,
    3320        3584 :               sizeof(szRet)) ||
    3321        1194 :         szRet == nullptr)
    3322             :     {
    3323           4 :         return nullptr;
    3324             :     }
    3325        1191 :     m_osCachedVal = szRet;
    3326        1191 :     CPLFree(szRet);
    3327        1191 :     return m_osCachedVal.c_str();
    3328             : }
    3329             : 
    3330             : /************************************************************************/
    3331             : /*                            ReadAsInt()                               */
    3332             : /************************************************************************/
    3333             : 
    3334             : /** Return the value of an attribute as a integer.
    3335             :  *
    3336             :  * This function will only return the first element if there are several.
    3337             :  *
    3338             :  * It can fail if its value can be converted to integer.
    3339             :  *
    3340             :  * This is the same as the C function GDALAttributeReadAsInt()
    3341             :  *
    3342             :  * @return a integer, or INT_MIN in case of error.
    3343             :  */
    3344         161 : int GDALAttribute::ReadAsInt() const
    3345             : {
    3346         161 :     const auto nDims = GetDimensionCount();
    3347         322 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3348         161 :     std::vector<size_t> count(1 + nDims, 1);
    3349         161 :     int nRet = INT_MIN;
    3350         161 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3351         322 :          GDALExtendedDataType::Create(GDT_Int32), &nRet, &nRet, sizeof(nRet));
    3352         322 :     return nRet;
    3353             : }
    3354             : 
    3355             : /************************************************************************/
    3356             : /*                            ReadAsDouble()                            */
    3357             : /************************************************************************/
    3358             : 
    3359             : /** Return the value of an attribute as a double.
    3360             :  *
    3361             :  * This function will only return the first element if there are several.
    3362             :  *
    3363             :  * It can fail if its value can be converted to double.
    3364             :  *
    3365             :  * This is the same as the C function GDALAttributeReadAsInt()
    3366             :  *
    3367             :  * @return a double value.
    3368             :  */
    3369         259 : double GDALAttribute::ReadAsDouble() const
    3370             : {
    3371         259 :     const auto nDims = GetDimensionCount();
    3372         518 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3373         259 :     std::vector<size_t> count(1 + nDims, 1);
    3374         259 :     double dfRet = 0;
    3375         259 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3376         259 :          GDALExtendedDataType::Create(GDT_Float64), &dfRet, &dfRet,
    3377         259 :          sizeof(dfRet));
    3378         518 :     return dfRet;
    3379             : }
    3380             : 
    3381             : /************************************************************************/
    3382             : /*                          ReadAsStringArray()                         */
    3383             : /************************************************************************/
    3384             : 
    3385             : /** Return the value of an attribute as an array of strings.
    3386             :  *
    3387             :  * This is the same as the C function GDALAttributeReadAsStringArray()
    3388             :  */
    3389          97 : CPLStringList GDALAttribute::ReadAsStringArray() const
    3390             : {
    3391          97 :     const auto nElts = GetTotalElementsCount();
    3392          97 :     if (nElts > static_cast<unsigned>(std::numeric_limits<int>::max() - 1))
    3393           0 :         return CPLStringList();
    3394             :     char **papszList = static_cast<char **>(
    3395          97 :         VSI_CALLOC_VERBOSE(static_cast<int>(nElts) + 1, sizeof(char *)));
    3396          97 :     const auto &dims = GetDimensions();
    3397          97 :     const auto nDims = GetDimensionCount();
    3398         194 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3399         194 :     std::vector<size_t> count(1 + nDims);
    3400         149 :     for (size_t i = 0; i < nDims; i++)
    3401             :     {
    3402          52 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3403             :     }
    3404          97 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3405          97 :          GDALExtendedDataType::CreateString(), papszList, papszList,
    3406          97 :          sizeof(char *) * static_cast<int>(nElts));
    3407         253 :     for (int i = 0; i < static_cast<int>(nElts); i++)
    3408             :     {
    3409         156 :         if (papszList[i] == nullptr)
    3410          13 :             papszList[i] = CPLStrdup("");
    3411             :     }
    3412          97 :     return CPLStringList(papszList);
    3413             : }
    3414             : 
    3415             : /************************************************************************/
    3416             : /*                          ReadAsIntArray()                            */
    3417             : /************************************************************************/
    3418             : 
    3419             : /** Return the value of an attribute as an array of integers.
    3420             :  *
    3421             :  * This is the same as the C function GDALAttributeReadAsIntArray().
    3422             :  */
    3423          15 : std::vector<int> GDALAttribute::ReadAsIntArray() const
    3424             : {
    3425          15 :     const auto nElts = GetTotalElementsCount();
    3426             : #if SIZEOF_VOIDP == 4
    3427             :     if (nElts > static_cast<size_t>(nElts))
    3428             :         return {};
    3429             : #endif
    3430          15 :     std::vector<int> res(static_cast<size_t>(nElts));
    3431          15 :     const auto &dims = GetDimensions();
    3432          15 :     const auto nDims = GetDimensionCount();
    3433          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3434          30 :     std::vector<size_t> count(1 + nDims);
    3435          32 :     for (size_t i = 0; i < nDims; i++)
    3436             :     {
    3437          17 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3438             :     }
    3439          15 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3440          30 :          GDALExtendedDataType::Create(GDT_Int32), &res[0], res.data(),
    3441          15 :          res.size() * sizeof(res[0]));
    3442          30 :     return res;
    3443             : }
    3444             : 
    3445             : /************************************************************************/
    3446             : /*                         ReadAsDoubleArray()                          */
    3447             : /************************************************************************/
    3448             : 
    3449             : /** Return the value of an attribute as an array of double.
    3450             :  *
    3451             :  * This is the same as the C function GDALAttributeReadAsDoubleArray().
    3452             :  */
    3453          79 : std::vector<double> GDALAttribute::ReadAsDoubleArray() const
    3454             : {
    3455          79 :     const auto nElts = GetTotalElementsCount();
    3456             : #if SIZEOF_VOIDP == 4
    3457             :     if (nElts > static_cast<size_t>(nElts))
    3458             :         return {};
    3459             : #endif
    3460          79 :     std::vector<double> res(static_cast<size_t>(nElts));
    3461          79 :     const auto &dims = GetDimensions();
    3462          79 :     const auto nDims = GetDimensionCount();
    3463         158 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3464         158 :     std::vector<size_t> count(1 + nDims);
    3465         142 :     for (size_t i = 0; i < nDims; i++)
    3466             :     {
    3467          63 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3468             :     }
    3469          79 :     Read(startIdx.data(), count.data(), nullptr, nullptr,
    3470         158 :          GDALExtendedDataType::Create(GDT_Float64), &res[0], res.data(),
    3471          79 :          res.size() * sizeof(res[0]));
    3472         158 :     return res;
    3473             : }
    3474             : 
    3475             : /************************************************************************/
    3476             : /*                               Write()                                */
    3477             : /************************************************************************/
    3478             : 
    3479             : /** Write an attribute from raw values expressed in GetDataType()
    3480             :  *
    3481             :  * The values should be provided in the type of GetDataType() and there should
    3482             :  * be exactly GetTotalElementsCount() of them.
    3483             :  * If GetDataType() is a string, each value should be a char* pointer.
    3484             :  *
    3485             :  * This is the same as the C function GDALAttributeWriteRaw().
    3486             :  *
    3487             :  * @param pabyValue Buffer of nLen bytes.
    3488             :  * @param nLen Size of pabyValue in bytes. Should be equal to
    3489             :  *             GetTotalElementsCount() * GetDataType().GetSize()
    3490             :  * @return true in case of success.
    3491             :  */
    3492          91 : bool GDALAttribute::Write(const void *pabyValue, size_t nLen)
    3493             : {
    3494          91 :     if (nLen != GetTotalElementsCount() * GetDataType().GetSize())
    3495             :     {
    3496           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    3497             :                  "Length is not of expected value");
    3498           0 :         return false;
    3499             :     }
    3500          91 :     const auto &dims = GetDimensions();
    3501          91 :     const auto nDims = GetDimensionCount();
    3502         182 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3503         182 :     std::vector<size_t> count(1 + nDims);
    3504         114 :     for (size_t i = 0; i < nDims; i++)
    3505             :     {
    3506          23 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3507             :     }
    3508          91 :     return Write(startIdx.data(), count.data(), nullptr, nullptr, GetDataType(),
    3509          91 :                  pabyValue, pabyValue, nLen);
    3510             : }
    3511             : 
    3512             : /************************************************************************/
    3513             : /*                               Write()                                */
    3514             : /************************************************************************/
    3515             : 
    3516             : /** Write an attribute from a string value.
    3517             :  *
    3518             :  * Type conversion will be performed if needed. If the attribute contains
    3519             :  * multiple values, only the first one will be updated.
    3520             :  *
    3521             :  * This is the same as the C function GDALAttributeWriteString().
    3522             :  *
    3523             :  * @param pszValue Pointer to a string.
    3524             :  * @return true in case of success.
    3525             :  */
    3526         303 : bool GDALAttribute::Write(const char *pszValue)
    3527             : {
    3528         303 :     const auto nDims = GetDimensionCount();
    3529         606 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3530         303 :     std::vector<size_t> count(1 + nDims, 1);
    3531         303 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3532         606 :                  GDALExtendedDataType::CreateString(), &pszValue, &pszValue,
    3533         606 :                  sizeof(pszValue));
    3534             : }
    3535             : 
    3536             : /************************************************************************/
    3537             : /*                              WriteInt()                              */
    3538             : /************************************************************************/
    3539             : 
    3540             : /** Write an attribute from a integer value.
    3541             :  *
    3542             :  * Type conversion will be performed if needed. If the attribute contains
    3543             :  * multiple values, only the first one will be updated.
    3544             :  *
    3545             :  * This is the same as the C function GDALAttributeWriteInt().
    3546             :  *
    3547             :  * @param nVal Value.
    3548             :  * @return true in case of success.
    3549             :  */
    3550          22 : bool GDALAttribute::WriteInt(int nVal)
    3551             : {
    3552          22 :     const auto nDims = GetDimensionCount();
    3553          44 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3554          22 :     std::vector<size_t> count(1 + nDims, 1);
    3555          22 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3556          44 :                  GDALExtendedDataType::Create(GDT_Int32), &nVal, &nVal,
    3557          44 :                  sizeof(nVal));
    3558             : }
    3559             : 
    3560             : /************************************************************************/
    3561             : /*                                Write()                               */
    3562             : /************************************************************************/
    3563             : 
    3564             : /** Write an attribute from a double value.
    3565             :  *
    3566             :  * Type conversion will be performed if needed. If the attribute contains
    3567             :  * multiple values, only the first one will be updated.
    3568             :  *
    3569             :  * This is the same as the C function GDALAttributeWriteDouble().
    3570             :  *
    3571             :  * @param dfVal Value.
    3572             :  * @return true in case of success.
    3573             :  */
    3574          41 : bool GDALAttribute::Write(double dfVal)
    3575             : {
    3576          41 :     const auto nDims = GetDimensionCount();
    3577          82 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3578          41 :     std::vector<size_t> count(1 + nDims, 1);
    3579          41 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3580          82 :                  GDALExtendedDataType::Create(GDT_Float64), &dfVal, &dfVal,
    3581          82 :                  sizeof(dfVal));
    3582             : }
    3583             : 
    3584             : /************************************************************************/
    3585             : /*                                Write()                               */
    3586             : /************************************************************************/
    3587             : 
    3588             : /** Write an attribute from an array of strings.
    3589             :  *
    3590             :  * Type conversion will be performed if needed.
    3591             :  *
    3592             :  * Exactly GetTotalElementsCount() strings must be provided
    3593             :  *
    3594             :  * This is the same as the C function GDALAttributeWriteStringArray().
    3595             :  *
    3596             :  * @param vals Array of strings.
    3597             :  * @return true in case of success.
    3598             :  */
    3599           8 : bool GDALAttribute::Write(CSLConstList vals)
    3600             : {
    3601           8 :     if (static_cast<size_t>(CSLCount(vals)) != GetTotalElementsCount())
    3602             :     {
    3603           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3604           1 :         return false;
    3605             :     }
    3606           7 :     const auto nDims = GetDimensionCount();
    3607          14 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3608           7 :     std::vector<size_t> count(1 + nDims);
    3609           7 :     const auto &dims = GetDimensions();
    3610          15 :     for (size_t i = 0; i < nDims; i++)
    3611           8 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3612           7 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3613           7 :                  GDALExtendedDataType::CreateString(), vals, vals,
    3614          14 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(char *));
    3615             : }
    3616             : 
    3617             : /************************************************************************/
    3618             : /*                                Write()                               */
    3619             : /************************************************************************/
    3620             : 
    3621             : /** Write an attribute from an array of double.
    3622             :  *
    3623             :  * Type conversion will be performed if needed.
    3624             :  *
    3625             :  * Exactly GetTotalElementsCount() strings must be provided
    3626             :  *
    3627             :  * This is the same as the C function GDALAttributeWriteDoubleArray()
    3628             :  *
    3629             :  * @param vals Array of double.
    3630             :  * @param nVals Should be equal to GetTotalElementsCount().
    3631             :  * @return true in case of success.
    3632             :  */
    3633          17 : bool GDALAttribute::Write(const double *vals, size_t nVals)
    3634             : {
    3635          17 :     if (nVals != GetTotalElementsCount())
    3636             :     {
    3637           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid number of input values");
    3638           2 :         return false;
    3639             :     }
    3640          15 :     const auto nDims = GetDimensionCount();
    3641          30 :     std::vector<GUInt64> startIdx(1 + nDims, 0);
    3642          15 :     std::vector<size_t> count(1 + nDims);
    3643          15 :     const auto &dims = GetDimensions();
    3644          31 :     for (size_t i = 0; i < nDims; i++)
    3645          16 :         count[i] = static_cast<size_t>(dims[i]->GetSize());
    3646          15 :     return Write(startIdx.data(), count.data(), nullptr, nullptr,
    3647          15 :                  GDALExtendedDataType::Create(GDT_Float64), vals, vals,
    3648          30 :                  static_cast<size_t>(GetTotalElementsCount()) * sizeof(double));
    3649             : }
    3650             : 
    3651             : /************************************************************************/
    3652             : /*                           GDALMDArray()                              */
    3653             : /************************************************************************/
    3654             : 
    3655             : //! @cond Doxygen_Suppress
    3656        6008 : GDALMDArray::GDALMDArray(CPL_UNUSED const std::string &osParentName,
    3657             :                          CPL_UNUSED const std::string &osName,
    3658           0 :                          const std::string &osContext)
    3659             :     :
    3660             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
    3661             :       GDALAbstractMDArray(osParentName, osName),
    3662             : #endif
    3663        6008 :       m_osContext(osContext)
    3664             : {
    3665        6008 : }
    3666             : 
    3667             : //! @endcond
    3668             : 
    3669             : /************************************************************************/
    3670             : /*                           GetTotalCopyCost()                         */
    3671             : /************************************************************************/
    3672             : 
    3673             : /** Return a total "cost" to copy the array.
    3674             :  *
    3675             :  * Used as a parameter for CopyFrom()
    3676             :  */
    3677          43 : GUInt64 GDALMDArray::GetTotalCopyCost() const
    3678             : {
    3679          86 :     return COPY_COST + GetAttributes().size() * GDALAttribute::COPY_COST +
    3680          86 :            GetTotalElementsCount() * GetDataType().GetSize();
    3681             : }
    3682             : 
    3683             : /************************************************************************/
    3684             : /*                       CopyFromAllExceptValues()                      */
    3685             : /************************************************************************/
    3686             : 
    3687             : //! @cond Doxygen_Suppress
    3688             : 
    3689         144 : bool GDALMDArray::CopyFromAllExceptValues(const GDALMDArray *poSrcArray,
    3690             :                                           bool bStrict, GUInt64 &nCurCost,
    3691             :                                           const GUInt64 nTotalCost,
    3692             :                                           GDALProgressFunc pfnProgress,
    3693             :                                           void *pProgressData)
    3694             : {
    3695             :     // Nodata setting must be one of the first things done for TileDB
    3696         144 :     const void *pNoData = poSrcArray->GetRawNoDataValue();
    3697         144 :     if (pNoData && poSrcArray->GetDataType() == GetDataType())
    3698             :     {
    3699          13 :         SetRawNoDataValue(pNoData);
    3700             :     }
    3701             : 
    3702         144 :     const bool bThisIsUnscaledArray =
    3703         144 :         dynamic_cast<GDALMDArrayUnscaled *>(this) != nullptr;
    3704         288 :     auto attrs = poSrcArray->GetAttributes();
    3705         191 :     for (const auto &attr : attrs)
    3706             :     {
    3707          47 :         const auto &osAttrName = attr->GetName();
    3708          47 :         if (bThisIsUnscaledArray)
    3709             :         {
    3710           6 :             if (osAttrName == "missing_value" || osAttrName == "_FillValue" ||
    3711           7 :                 osAttrName == "valid_min" || osAttrName == "valid_max" ||
    3712           1 :                 osAttrName == "valid_range")
    3713             :             {
    3714           1 :                 continue;
    3715             :             }
    3716             :         }
    3717             : 
    3718          46 :         auto dstAttr = CreateAttribute(osAttrName, attr->GetDimensionsSize(),
    3719          92 :                                        attr->GetDataType());
    3720          46 :         if (!dstAttr)
    3721             :         {
    3722           0 :             if (bStrict)
    3723           0 :                 return false;
    3724           0 :             continue;
    3725             :         }
    3726          46 :         auto raw = attr->ReadAsRaw();
    3727          46 :         if (!dstAttr->Write(raw.data(), raw.size()) && bStrict)
    3728           0 :             return false;
    3729             :     }
    3730         144 :     if (!attrs.empty())
    3731             :     {
    3732          26 :         nCurCost += attrs.size() * GDALAttribute::COPY_COST;
    3733          46 :         if (pfnProgress &&
    3734          20 :             !pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3735           0 :             return false;
    3736             :     }
    3737             : 
    3738         144 :     auto srcSRS = poSrcArray->GetSpatialRef();
    3739         144 :     if (srcSRS)
    3740             :     {
    3741          11 :         SetSpatialRef(srcSRS.get());
    3742             :     }
    3743             : 
    3744         144 :     const std::string &osUnit(poSrcArray->GetUnit());
    3745         144 :     if (!osUnit.empty())
    3746             :     {
    3747          18 :         SetUnit(osUnit);
    3748             :     }
    3749             : 
    3750         144 :     bool bGotValue = false;
    3751         144 :     GDALDataType eOffsetStorageType = GDT_Unknown;
    3752             :     const double dfOffset =
    3753         144 :         poSrcArray->GetOffset(&bGotValue, &eOffsetStorageType);
    3754         144 :     if (bGotValue)
    3755             :     {
    3756           3 :         SetOffset(dfOffset, eOffsetStorageType);
    3757             :     }
    3758             : 
    3759         144 :     bGotValue = false;
    3760         144 :     GDALDataType eScaleStorageType = GDT_Unknown;
    3761         144 :     const double dfScale = poSrcArray->GetScale(&bGotValue, &eScaleStorageType);
    3762         144 :     if (bGotValue)
    3763             :     {
    3764           3 :         SetScale(dfScale, eScaleStorageType);
    3765             :     }
    3766             : 
    3767         144 :     return true;
    3768             : }
    3769             : 
    3770             : //! @endcond
    3771             : 
    3772             : /************************************************************************/
    3773             : /*                               CopyFrom()                             */
    3774             : /************************************************************************/
    3775             : 
    3776             : /** Copy the content of an array into a new (generally empty) array.
    3777             :  *
    3778             :  * @param poSrcDS    Source dataset. Might be nullptr (but for correct behavior
    3779             :  *                   of some output drivers this is not recommended)
    3780             :  * @param poSrcArray Source array. Should NOT be nullptr.
    3781             :  * @param bStrict Whether to enable stict mode. In strict mode, any error will
    3782             :  *                stop the copy. In relaxed mode, the copy will be attempted to
    3783             :  *                be pursued.
    3784             :  * @param nCurCost  Should be provided as a variable initially set to 0.
    3785             :  * @param nTotalCost Total cost from GetTotalCopyCost().
    3786             :  * @param pfnProgress Progress callback, or nullptr.
    3787             :  * @param pProgressData Progress user data, or nulptr.
    3788             :  *
    3789             :  * @return true in case of success (or partial success if bStrict == false).
    3790             :  */
    3791          41 : bool GDALMDArray::CopyFrom(CPL_UNUSED GDALDataset *poSrcDS,
    3792             :                            const GDALMDArray *poSrcArray, bool bStrict,
    3793             :                            GUInt64 &nCurCost, const GUInt64 nTotalCost,
    3794             :                            GDALProgressFunc pfnProgress, void *pProgressData)
    3795             : {
    3796          41 :     if (pfnProgress == nullptr)
    3797           4 :         pfnProgress = GDALDummyProgress;
    3798             : 
    3799          41 :     nCurCost += GDALMDArray::COPY_COST;
    3800             : 
    3801          41 :     if (!CopyFromAllExceptValues(poSrcArray, bStrict, nCurCost, nTotalCost,
    3802             :                                  pfnProgress, pProgressData))
    3803             :     {
    3804           0 :         return false;
    3805             :     }
    3806             : 
    3807          41 :     const auto &dims = poSrcArray->GetDimensions();
    3808          41 :     const auto nDTSize = poSrcArray->GetDataType().GetSize();
    3809          41 :     if (dims.empty())
    3810             :     {
    3811           2 :         std::vector<GByte> abyTmp(nDTSize);
    3812           2 :         if (!(poSrcArray->Read(nullptr, nullptr, nullptr, nullptr,
    3813           2 :                                GetDataType(), &abyTmp[0]) &&
    3814           2 :               Write(nullptr, nullptr, nullptr, nullptr, GetDataType(),
    3815           4 :                     &abyTmp[0])) &&
    3816             :             bStrict)
    3817             :         {
    3818           0 :             return false;
    3819             :         }
    3820           2 :         nCurCost += GetTotalElementsCount() * GetDataType().GetSize();
    3821           2 :         if (!pfnProgress(double(nCurCost) / nTotalCost, "", pProgressData))
    3822           0 :             return false;
    3823             :     }
    3824             :     else
    3825             :     {
    3826          39 :         std::vector<GUInt64> arrayStartIdx(dims.size());
    3827          39 :         std::vector<GUInt64> count(dims.size());
    3828         106 :         for (size_t i = 0; i < dims.size(); i++)
    3829             :         {
    3830          67 :             count[i] = static_cast<size_t>(dims[i]->GetSize());
    3831             :         }
    3832             : 
    3833             :         struct CopyFunc
    3834             :         {
    3835             :             GDALMDArray *poDstArray = nullptr;
    3836             :             std::vector<GByte> abyTmp{};
    3837             :             GDALProgressFunc pfnProgress = nullptr;
    3838             :             void *pProgressData = nullptr;
    3839             :             GUInt64 nCurCost = 0;
    3840             :             GUInt64 nTotalCost = 0;
    3841             :             GUInt64 nTotalBytesThisArray = 0;
    3842             :             bool bStop = false;
    3843             : 
    3844          57 :             static bool f(GDALAbstractMDArray *l_poSrcArray,
    3845             :                           const GUInt64 *chunkArrayStartIdx,
    3846             :                           const size_t *chunkCount, GUInt64 iCurChunk,
    3847             :                           GUInt64 nChunkCount, void *pUserData)
    3848             :             {
    3849          57 :                 const auto &dt(l_poSrcArray->GetDataType());
    3850          57 :                 auto data = static_cast<CopyFunc *>(pUserData);
    3851          57 :                 auto poDstArray = data->poDstArray;
    3852          57 :                 if (!l_poSrcArray->Read(chunkArrayStartIdx, chunkCount, nullptr,
    3853          57 :                                         nullptr, dt, &data->abyTmp[0]))
    3854             :                 {
    3855           0 :                     return false;
    3856             :                 }
    3857             :                 bool bRet =
    3858          57 :                     poDstArray->Write(chunkArrayStartIdx, chunkCount, nullptr,
    3859          57 :                                       nullptr, dt, &data->abyTmp[0]);
    3860          57 :                 if (dt.NeedsFreeDynamicMemory())
    3861             :                 {
    3862           2 :                     const auto l_nDTSize = dt.GetSize();
    3863           2 :                     GByte *ptr = &data->abyTmp[0];
    3864           2 :                     const size_t l_nDims(l_poSrcArray->GetDimensionCount());
    3865           2 :                     size_t nEltCount = 1;
    3866           4 :                     for (size_t i = 0; i < l_nDims; ++i)
    3867             :                     {
    3868           2 :                         nEltCount *= chunkCount[i];
    3869             :                     }
    3870          10 :                     for (size_t i = 0; i < nEltCount; i++)
    3871             :                     {
    3872           8 :                         dt.FreeDynamicMemory(ptr);
    3873           8 :                         ptr += l_nDTSize;
    3874             :                     }
    3875             :                 }
    3876          57 :                 if (!bRet)
    3877             :                 {
    3878           0 :                     return false;
    3879             :                 }
    3880             : 
    3881          57 :                 double dfCurCost =
    3882          57 :                     double(data->nCurCost) + double(iCurChunk) / nChunkCount *
    3883          57 :                                                  data->nTotalBytesThisArray;
    3884          57 :                 if (!data->pfnProgress(dfCurCost / data->nTotalCost, "",
    3885             :                                        data->pProgressData))
    3886             :                 {
    3887           0 :                     data->bStop = true;
    3888           0 :                     return false;
    3889             :                 }
    3890             : 
    3891          57 :                 return true;
    3892             :             }
    3893             :         };
    3894             : 
    3895          39 :         CopyFunc copyFunc;
    3896          39 :         copyFunc.poDstArray = this;
    3897          39 :         copyFunc.nCurCost = nCurCost;
    3898          39 :         copyFunc.nTotalCost = nTotalCost;
    3899          39 :         copyFunc.nTotalBytesThisArray = GetTotalElementsCount() * nDTSize;
    3900          39 :         copyFunc.pfnProgress = pfnProgress;
    3901          39 :         copyFunc.pProgressData = pProgressData;
    3902             :         const char *pszSwathSize =
    3903          39 :             CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    3904             :         const size_t nMaxChunkSize =
    3905             :             pszSwathSize
    3906          39 :                 ? static_cast<size_t>(
    3907           1 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    3908           1 :                                CPLAtoGIntBig(pszSwathSize)))
    3909             :                 : static_cast<size_t>(
    3910          38 :                       std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    3911          38 :                                GDALGetCacheMax64() / 4));
    3912          39 :         const auto anChunkSizes(GetProcessingChunkSize(nMaxChunkSize));
    3913          39 :         size_t nRealChunkSize = nDTSize;
    3914         106 :         for (const auto &nChunkSize : anChunkSizes)
    3915             :         {
    3916          67 :             nRealChunkSize *= nChunkSize;
    3917             :         }
    3918             :         try
    3919             :         {
    3920          39 :             copyFunc.abyTmp.resize(nRealChunkSize);
    3921             :         }
    3922           0 :         catch (const std::exception &)
    3923             :         {
    3924           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    3925             :                      "Cannot allocate temporary buffer");
    3926           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    3927           0 :             return false;
    3928             :         }
    3929         116 :         if (copyFunc.nTotalBytesThisArray != 0 &&
    3930          38 :             !const_cast<GDALMDArray *>(poSrcArray)
    3931          38 :                  ->ProcessPerChunk(arrayStartIdx.data(), count.data(),
    3932             :                                    anChunkSizes.data(), CopyFunc::f,
    3933          77 :                                    &copyFunc) &&
    3934           0 :             (bStrict || copyFunc.bStop))
    3935             :         {
    3936           0 :             nCurCost += copyFunc.nTotalBytesThisArray;
    3937           0 :             return false;
    3938             :         }
    3939          39 :         nCurCost += copyFunc.nTotalBytesThisArray;
    3940             :     }
    3941             : 
    3942          41 :     return true;
    3943             : }
    3944             : 
    3945             : /************************************************************************/
    3946             : /*                         GetStructuralInfo()                          */
    3947             : /************************************************************************/
    3948             : 
    3949             : /** Return structural information on the array.
    3950             :  *
    3951             :  * This may be the compression, etc..
    3952             :  *
    3953             :  * The return value should not be freed and is valid until GDALMDArray is
    3954             :  * released or this function called again.
    3955             :  *
    3956             :  * This is the same as the C function GDALMDArrayGetStructuralInfo().
    3957             :  */
    3958          55 : CSLConstList GDALMDArray::GetStructuralInfo() const
    3959             : {
    3960          55 :     return nullptr;
    3961             : }
    3962             : 
    3963             : /************************************************************************/
    3964             : /*                          AdviseRead()                                */
    3965             : /************************************************************************/
    3966             : 
    3967             : /** Advise driver of upcoming read requests.
    3968             :  *
    3969             :  * Some GDAL drivers operate more efficiently if they know in advance what
    3970             :  * set of upcoming read requests will be made.  The AdviseRead() method allows
    3971             :  * an application to notify the driver of the region of interest.
    3972             :  *
    3973             :  * Many drivers just ignore the AdviseRead() call, but it can dramatically
    3974             :  * accelerate access via some drivers. One such case is when reading through
    3975             :  * a DAP dataset with the netCDF driver (a in-memory cache array is then created
    3976             :  * with the region of interest defined by AdviseRead())
    3977             :  *
    3978             :  * This is the same as the C function GDALMDArrayAdviseRead().
    3979             :  *
    3980             :  * @param arrayStartIdx Values representing the starting index to read
    3981             :  *                      in each dimension (in [0, aoDims[i].GetSize()-1] range).
    3982             :  *                      Array of GetDimensionCount() values.
    3983             :  *                      Can be nullptr as a synonymous for [0 for i in
    3984             :  * range(GetDimensionCount() ]
    3985             :  *
    3986             :  * @param count         Values representing the number of values to extract in
    3987             :  *                      each dimension.
    3988             :  *                      Array of GetDimensionCount() values.
    3989             :  *                      Can be nullptr as a synonymous for
    3990             :  *                      [ aoDims[i].GetSize() - arrayStartIdx[i] for i in
    3991             :  * range(GetDimensionCount() ]
    3992             :  *
    3993             :  * @param papszOptions Driver specific options, or nullptr. Consult driver
    3994             :  * documentation.
    3995             :  *
    3996             :  * @return true in case of success (ignoring the advice is a success)
    3997             :  *
    3998             :  * @since GDAL 3.2
    3999             :  */
    4000          25 : bool GDALMDArray::AdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4001             :                              CSLConstList papszOptions) const
    4002             : {
    4003          25 :     const auto nDimCount = GetDimensionCount();
    4004          25 :     if (nDimCount == 0)
    4005           2 :         return true;
    4006             : 
    4007          46 :     std::vector<GUInt64> tmp_arrayStartIdx;
    4008          23 :     if (arrayStartIdx == nullptr)
    4009             :     {
    4010           0 :         tmp_arrayStartIdx.resize(nDimCount);
    4011           0 :         arrayStartIdx = tmp_arrayStartIdx.data();
    4012             :     }
    4013             : 
    4014          46 :     std::vector<size_t> tmp_count;
    4015          23 :     if (count == nullptr)
    4016             :     {
    4017           0 :         tmp_count.resize(nDimCount);
    4018           0 :         const auto &dims = GetDimensions();
    4019           0 :         for (size_t i = 0; i < nDimCount; i++)
    4020             :         {
    4021           0 :             const GUInt64 nSize = dims[i]->GetSize() - arrayStartIdx[i];
    4022             : #if SIZEOF_VOIDP < 8
    4023             :             if (nSize != static_cast<size_t>(nSize))
    4024             :             {
    4025             :                 CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    4026             :                 return false;
    4027             :             }
    4028             : #endif
    4029           0 :             tmp_count[i] = static_cast<size_t>(nSize);
    4030             :         }
    4031           0 :         count = tmp_count.data();
    4032             :     }
    4033             : 
    4034          46 :     std::vector<GInt64> tmp_arrayStep;
    4035          46 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4036          23 :     const GInt64 *arrayStep = nullptr;
    4037          23 :     const GPtrDiff_t *bufferStride = nullptr;
    4038          23 :     if (!CheckReadWriteParams(arrayStartIdx, count, arrayStep, bufferStride,
    4039          46 :                               GDALExtendedDataType::Create(GDT_Unknown),
    4040             :                               nullptr, nullptr, 0, tmp_arrayStep,
    4041             :                               tmp_bufferStride))
    4042             :     {
    4043           1 :         return false;
    4044             :     }
    4045             : 
    4046          22 :     return IAdviseRead(arrayStartIdx, count, papszOptions);
    4047             : }
    4048             : 
    4049             : /************************************************************************/
    4050             : /*                             IAdviseRead()                            */
    4051             : /************************************************************************/
    4052             : 
    4053             : //! @cond Doxygen_Suppress
    4054           3 : bool GDALMDArray::IAdviseRead(const GUInt64 *, const size_t *,
    4055             :                               CSLConstList /* papszOptions*/) const
    4056             : {
    4057           3 :     return true;
    4058             : }
    4059             : 
    4060             : //! @endcond
    4061             : 
    4062             : /************************************************************************/
    4063             : /*                            MassageName()                             */
    4064             : /************************************************************************/
    4065             : 
    4066             : //! @cond Doxygen_Suppress
    4067          32 : /*static*/ std::string GDALMDArray::MassageName(const std::string &inputName)
    4068             : {
    4069          32 :     std::string ret;
    4070         604 :     for (const char ch : inputName)
    4071             :     {
    4072         572 :         if (!isalnum(static_cast<unsigned char>(ch)))
    4073         138 :             ret += '_';
    4074             :         else
    4075         434 :             ret += ch;
    4076             :     }
    4077          32 :     return ret;
    4078             : }
    4079             : 
    4080             : //! @endcond
    4081             : 
    4082             : /************************************************************************/
    4083             : /*                         GetCacheRootGroup()                          */
    4084             : /************************************************************************/
    4085             : 
    4086             : //! @cond Doxygen_Suppress
    4087             : std::shared_ptr<GDALGroup>
    4088        1300 : GDALMDArray::GetCacheRootGroup(bool bCanCreate,
    4089             :                                std::string &osCacheFilenameOut) const
    4090             : {
    4091        1300 :     const auto &osFilename = GetFilename();
    4092        1300 :     if (osFilename.empty())
    4093             :     {
    4094           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4095             :                  "Cannot cache an array with an empty filename");
    4096           0 :         return nullptr;
    4097             :     }
    4098             : 
    4099        1300 :     osCacheFilenameOut = osFilename + ".gmac";
    4100        1300 :     const char *pszProxy = PamGetProxy(osCacheFilenameOut.c_str());
    4101        1300 :     if (pszProxy != nullptr)
    4102           7 :         osCacheFilenameOut = pszProxy;
    4103             : 
    4104        1300 :     std::unique_ptr<GDALDataset> poDS;
    4105             :     VSIStatBufL sStat;
    4106        1300 :     if (VSIStatL(osCacheFilenameOut.c_str(), &sStat) == 0)
    4107             :     {
    4108          28 :         poDS.reset(GDALDataset::Open(osCacheFilenameOut.c_str(),
    4109             :                                      GDAL_OF_MULTIDIM_RASTER | GDAL_OF_UPDATE,
    4110             :                                      nullptr, nullptr, nullptr));
    4111             :     }
    4112        1300 :     if (poDS)
    4113             :     {
    4114          28 :         CPLDebug("GDAL", "Opening cache %s", osCacheFilenameOut.c_str());
    4115          28 :         return poDS->GetRootGroup();
    4116             :     }
    4117             : 
    4118        1272 :     if (bCanCreate)
    4119             :     {
    4120           4 :         const char *pszDrvName = "netCDF";
    4121           4 :         GDALDriver *poDrv = GetGDALDriverManager()->GetDriverByName(pszDrvName);
    4122           4 :         if (poDrv == nullptr)
    4123             :         {
    4124           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot get driver %s",
    4125             :                      pszDrvName);
    4126           0 :             return nullptr;
    4127             :         }
    4128             :         {
    4129           8 :             CPLErrorHandlerPusher oHandlerPusher(CPLQuietErrorHandler);
    4130           8 :             CPLErrorStateBackuper oErrorStateBackuper;
    4131           4 :             poDS.reset(poDrv->CreateMultiDimensional(osCacheFilenameOut.c_str(),
    4132             :                                                      nullptr, nullptr));
    4133             :         }
    4134           4 :         if (!poDS)
    4135             :         {
    4136           1 :             pszProxy = PamAllocateProxy(osCacheFilenameOut.c_str());
    4137           1 :             if (pszProxy)
    4138             :             {
    4139           1 :                 osCacheFilenameOut = pszProxy;
    4140           1 :                 poDS.reset(poDrv->CreateMultiDimensional(
    4141             :                     osCacheFilenameOut.c_str(), nullptr, nullptr));
    4142             :             }
    4143             :         }
    4144           4 :         if (poDS)
    4145             :         {
    4146           4 :             CPLDebug("GDAL", "Creating cache %s", osCacheFilenameOut.c_str());
    4147           4 :             return poDS->GetRootGroup();
    4148             :         }
    4149             :         else
    4150             :         {
    4151           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4152             :                      "Cannot create %s. Set the GDAL_PAM_PROXY_DIR "
    4153             :                      "configuration option to write the cache in "
    4154             :                      "another directory",
    4155             :                      osCacheFilenameOut.c_str());
    4156             :         }
    4157             :     }
    4158             : 
    4159        1268 :     return nullptr;
    4160             : }
    4161             : 
    4162             : //! @endcond
    4163             : 
    4164             : /************************************************************************/
    4165             : /*                              Cache()                                 */
    4166             : /************************************************************************/
    4167             : 
    4168             : /** Cache the content of the array into an auxiliary filename.
    4169             :  *
    4170             :  * The main purpose of this method is to be able to cache views that are
    4171             :  * expensive to compute, such as transposed arrays.
    4172             :  *
    4173             :  * The array will be stored in a file whose name is the one of
    4174             :  * GetFilename(), with an extra .gmac extension (stands for GDAL
    4175             :  * Multidimensional Array Cache). The cache is a netCDF dataset.
    4176             :  *
    4177             :  * If the .gmac file cannot be written next to the dataset, the
    4178             :  * GDAL_PAM_PROXY_DIR will be used, if set, to write the cache file into that
    4179             :  * directory.
    4180             :  *
    4181             :  * The GDALMDArray::Read() method will automatically use the cache when it
    4182             :  * exists. There is no timestamp checks between the source array and the cached
    4183             :  * array. If the source arrays changes, the cache must be manually deleted.
    4184             :  *
    4185             :  * This is the same as the C function GDALMDArrayCache()
    4186             :  *
    4187             :  * @note Driver implementation: optionally implemented.
    4188             :  *
    4189             :  * @param papszOptions List of options, null terminated, or NULL. Currently
    4190             :  *                     the only option supported is BLOCKSIZE=bs0,bs1,...,bsN
    4191             :  *                     to specify the block size of the cached array.
    4192             :  * @return true in case of success.
    4193             :  */
    4194           6 : bool GDALMDArray::Cache(CSLConstList papszOptions) const
    4195             : {
    4196          12 :     std::string osCacheFilename;
    4197          12 :     auto poRG = GetCacheRootGroup(true, osCacheFilename);
    4198           6 :     if (!poRG)
    4199           0 :         return false;
    4200             : 
    4201          12 :     const std::string osCachedArrayName(MassageName(GetFullName()));
    4202           6 :     if (poRG->OpenMDArray(osCachedArrayName))
    4203             :     {
    4204           2 :         CPLError(CE_Failure, CPLE_NotSupported,
    4205             :                  "An array with same name %s already exists in %s",
    4206             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4207           2 :         return false;
    4208             :     }
    4209             : 
    4210           8 :     CPLStringList aosOptions;
    4211           4 :     aosOptions.SetNameValue("COMPRESS", "DEFLATE");
    4212           4 :     const auto &aoDims = GetDimensions();
    4213           8 :     std::vector<std::shared_ptr<GDALDimension>> aoNewDims;
    4214           4 :     if (!aoDims.empty())
    4215             :     {
    4216             :         std::string osBlockSize(
    4217           4 :             CSLFetchNameValueDef(papszOptions, "BLOCKSIZE", ""));
    4218           4 :         if (osBlockSize.empty())
    4219             :         {
    4220           6 :             const auto anBlockSize = GetBlockSize();
    4221           3 :             int idxDim = 0;
    4222          10 :             for (auto nBlockSize : anBlockSize)
    4223             :             {
    4224           7 :                 if (idxDim > 0)
    4225           4 :                     osBlockSize += ',';
    4226           7 :                 if (nBlockSize == 0)
    4227           7 :                     nBlockSize = 256;
    4228           7 :                 nBlockSize = std::min(nBlockSize, aoDims[idxDim]->GetSize());
    4229             :                 osBlockSize +=
    4230           7 :                     std::to_string(static_cast<uint64_t>(nBlockSize));
    4231           7 :                 idxDim++;
    4232             :             }
    4233             :         }
    4234           4 :         aosOptions.SetNameValue("BLOCKSIZE", osBlockSize.c_str());
    4235             : 
    4236           4 :         int idxDim = 0;
    4237          13 :         for (const auto &poDim : aoDims)
    4238             :         {
    4239           9 :             auto poNewDim = poRG->CreateDimension(
    4240          18 :                 osCachedArrayName + '_' + std::to_string(idxDim),
    4241          18 :                 poDim->GetType(), poDim->GetDirection(), poDim->GetSize());
    4242           9 :             if (!poNewDim)
    4243           0 :                 return false;
    4244           9 :             aoNewDims.emplace_back(poNewDim);
    4245           9 :             idxDim++;
    4246             :         }
    4247             :     }
    4248             : 
    4249           4 :     auto poCachedArray = poRG->CreateMDArray(osCachedArrayName, aoNewDims,
    4250           8 :                                              GetDataType(), aosOptions.List());
    4251           4 :     if (!poCachedArray)
    4252             :     {
    4253           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot create %s in %s",
    4254             :                  osCachedArrayName.c_str(), osCacheFilename.c_str());
    4255           0 :         return false;
    4256             :     }
    4257             : 
    4258           4 :     GUInt64 nCost = 0;
    4259           8 :     return poCachedArray->CopyFrom(nullptr, this,
    4260             :                                    false,  // strict
    4261           4 :                                    nCost, GetTotalCopyCost(), nullptr, nullptr);
    4262             : }
    4263             : 
    4264             : /************************************************************************/
    4265             : /*                               Read()                                 */
    4266             : /************************************************************************/
    4267             : 
    4268        3614 : bool GDALMDArray::Read(const GUInt64 *arrayStartIdx, const size_t *count,
    4269             :                        const GInt64 *arrayStep,         // step in elements
    4270             :                        const GPtrDiff_t *bufferStride,  // stride in elements
    4271             :                        const GDALExtendedDataType &bufferDataType,
    4272             :                        void *pDstBuffer, const void *pDstBufferAllocStart,
    4273             :                        size_t nDstBufferAllocSize) const
    4274             : {
    4275        3614 :     if (!m_bHasTriedCachedArray)
    4276             :     {
    4277        1578 :         m_bHasTriedCachedArray = true;
    4278        1578 :         if (IsCacheable())
    4279             :         {
    4280        1578 :             const auto &osFilename = GetFilename();
    4281        2678 :             if (!osFilename.empty() &&
    4282        1100 :                 !EQUAL(CPLGetExtension(osFilename.c_str()), "gmac"))
    4283             :             {
    4284        2180 :                 std::string osCacheFilename;
    4285        2180 :                 auto poRG = GetCacheRootGroup(false, osCacheFilename);
    4286        1090 :                 if (poRG)
    4287             :                 {
    4288             :                     const std::string osCachedArrayName(
    4289          32 :                         MassageName(GetFullName()));
    4290          16 :                     m_poCachedArray = poRG->OpenMDArray(osCachedArrayName);
    4291          16 :                     if (m_poCachedArray)
    4292             :                     {
    4293           6 :                         const auto &dims = GetDimensions();
    4294             :                         const auto &cachedDims =
    4295           6 :                             m_poCachedArray->GetDimensions();
    4296           6 :                         const size_t nDims = dims.size();
    4297             :                         bool ok =
    4298          12 :                             m_poCachedArray->GetDataType() == GetDataType() &&
    4299           6 :                             cachedDims.size() == nDims;
    4300          19 :                         for (size_t i = 0; ok && i < nDims; ++i)
    4301             :                         {
    4302          13 :                             ok = dims[i]->GetSize() == cachedDims[i]->GetSize();
    4303             :                         }
    4304           6 :                         if (ok)
    4305             :                         {
    4306           6 :                             CPLDebug("GDAL", "Cached array for %s found in %s",
    4307             :                                      osCachedArrayName.c_str(),
    4308             :                                      osCacheFilename.c_str());
    4309             :                         }
    4310             :                         else
    4311             :                         {
    4312           0 :                             CPLError(CE_Warning, CPLE_AppDefined,
    4313             :                                      "Cached array %s in %s has incompatible "
    4314             :                                      "characteristics with current array.",
    4315             :                                      osCachedArrayName.c_str(),
    4316             :                                      osCacheFilename.c_str());
    4317           0 :                             m_poCachedArray.reset();
    4318             :                         }
    4319             :                     }
    4320             :                 }
    4321             :             }
    4322             :         }
    4323             :     }
    4324             : 
    4325        3614 :     const auto array = m_poCachedArray ? m_poCachedArray.get() : this;
    4326        3614 :     if (!array->GetDataType().CanConvertTo(bufferDataType))
    4327             :     {
    4328           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    4329             :                  "Array data type is not convertible to buffer data type");
    4330           0 :         return false;
    4331             :     }
    4332             : 
    4333        7228 :     std::vector<GInt64> tmp_arrayStep;
    4334        7228 :     std::vector<GPtrDiff_t> tmp_bufferStride;
    4335        3614 :     if (!array->CheckReadWriteParams(arrayStartIdx, count, arrayStep,
    4336             :                                      bufferStride, bufferDataType, pDstBuffer,
    4337             :                                      pDstBufferAllocStart, nDstBufferAllocSize,
    4338             :                                      tmp_arrayStep, tmp_bufferStride))
    4339             :     {
    4340           9 :         return false;
    4341             :     }
    4342             : 
    4343        3605 :     return array->IRead(arrayStartIdx, count, arrayStep, bufferStride,
    4344        3605 :                         bufferDataType, pDstBuffer);
    4345             : }
    4346             : 
    4347             : /************************************************************************/
    4348             : /*                          GetRootGroup()                              */
    4349             : /************************************************************************/
    4350             : 
    4351             : /** Return the root group to which this arrays belongs too.
    4352             :  *
    4353             :  * Note that arrays may be free standing and some drivers may not implement
    4354             :  * this method, hence nullptr may be returned.
    4355             :  *
    4356             :  * It is used internally by the GetResampled() method to detect if GLT
    4357             :  * orthorectification is available.
    4358             :  *
    4359             :  * @return the root group, or nullptr.
    4360             :  * @since GDAL 3.8
    4361             :  */
    4362           0 : std::shared_ptr<GDALGroup> GDALMDArray::GetRootGroup() const
    4363             : {
    4364           0 :     return nullptr;
    4365             : }
    4366             : 
    4367             : //! @cond Doxygen_Suppress
    4368             : 
    4369             : /************************************************************************/
    4370             : /*                       IsTransposedRequest()                          */
    4371             : /************************************************************************/
    4372             : 
    4373         640 : bool GDALMDArray::IsTransposedRequest(
    4374             :     const size_t *count,
    4375             :     const GPtrDiff_t *bufferStride) const  // stride in elements
    4376             : {
    4377             :     /*
    4378             :     For example:
    4379             :     count = [2,3,4]
    4380             :     strides = [12, 4, 1]            (2-1)*12+(3-1)*4+(4-1)*1=23   ==> row major
    4381             :     stride [12, 1, 3]            (2-1)*12+(3-1)*1+(4-1)*3=23   ==>
    4382             :     (axis[0],axis[2],axis[1]) transposition [1, 8, 2]             (2-1)*1+
    4383             :     (3-1)*8+(4-1)*2=23   ==> (axis[2],axis[1],axis[0]) transposition
    4384             :     */
    4385         640 :     const size_t nDims(GetDimensionCount());
    4386         640 :     size_t nCurStrideForRowMajorStrides = 1;
    4387         640 :     bool bRowMajorStrides = true;
    4388         640 :     size_t nElts = 1;
    4389         640 :     size_t nLastIdx = 0;
    4390        1837 :     for (size_t i = nDims; i > 0;)
    4391             :     {
    4392        1197 :         --i;
    4393        1197 :         if (bufferStride[i] < 0)
    4394           0 :             return false;
    4395        1197 :         if (static_cast<size_t>(bufferStride[i]) !=
    4396             :             nCurStrideForRowMajorStrides)
    4397             :         {
    4398         209 :             bRowMajorStrides = false;
    4399             :         }
    4400             :         // Integer overflows have already been checked in CheckReadWriteParams()
    4401        1197 :         nCurStrideForRowMajorStrides *= count[i];
    4402        1197 :         nElts *= count[i];
    4403        1197 :         nLastIdx += static_cast<size_t>(bufferStride[i]) * (count[i] - 1);
    4404             :     }
    4405         640 :     if (bRowMajorStrides)
    4406         503 :         return false;
    4407         137 :     return nLastIdx == nElts - 1;
    4408             : }
    4409             : 
    4410             : /************************************************************************/
    4411             : /*                   CopyToFinalBufferSameDataType()                    */
    4412             : /************************************************************************/
    4413             : 
    4414             : template <size_t N>
    4415          60 : void CopyToFinalBufferSameDataType(const void *pSrcBuffer, void *pDstBuffer,
    4416             :                                    size_t nDims, const size_t *count,
    4417             :                                    const GPtrDiff_t *bufferStride)
    4418             : {
    4419         120 :     std::vector<size_t> anStackCount(nDims);
    4420         120 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4421          60 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4422             : #if defined(__GNUC__)
    4423             : #pragma GCC diagnostic push
    4424             : #pragma GCC diagnostic ignored "-Wnull-dereference"
    4425             : #endif
    4426          60 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4427             : #if defined(__GNUC__)
    4428             : #pragma GCC diagnostic pop
    4429             : #endif
    4430          60 :     size_t iDim = 0;
    4431             : 
    4432         749 : lbl_next_depth:
    4433         749 :     if (iDim == nDims - 1)
    4434             :     {
    4435         661 :         size_t n = count[iDim];
    4436         661 :         GByte *pabyDstBuffer = pabyDstBufferStack[iDim];
    4437         661 :         const auto bufferStrideLastDim = bufferStride[iDim] * N;
    4438       29186 :         while (n > 0)
    4439             :         {
    4440       28525 :             --n;
    4441       28525 :             memcpy(pabyDstBuffer, pabySrcBuffer, N);
    4442       28525 :             pabyDstBuffer += bufferStrideLastDim;
    4443       28525 :             pabySrcBuffer += N;
    4444             :         }
    4445             :     }
    4446             :     else
    4447             :     {
    4448          88 :         anStackCount[iDim] = count[iDim];
    4449             :         while (true)
    4450             :         {
    4451         689 :             ++iDim;
    4452         689 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4453         689 :             goto lbl_next_depth;
    4454         689 :         lbl_return_to_caller_in_loop:
    4455         689 :             --iDim;
    4456         689 :             --anStackCount[iDim];
    4457         689 :             if (anStackCount[iDim] == 0)
    4458          88 :                 break;
    4459         601 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * N;
    4460             :         }
    4461             :     }
    4462         749 :     if (iDim > 0)
    4463         689 :         goto lbl_return_to_caller_in_loop;
    4464          60 : }
    4465             : 
    4466             : /************************************************************************/
    4467             : /*                        CopyToFinalBuffer()                           */
    4468             : /************************************************************************/
    4469             : 
    4470         117 : static void CopyToFinalBuffer(const void *pSrcBuffer,
    4471             :                               const GDALExtendedDataType &eSrcDataType,
    4472             :                               void *pDstBuffer,
    4473             :                               const GDALExtendedDataType &eDstDataType,
    4474             :                               size_t nDims, const size_t *count,
    4475             :                               const GPtrDiff_t *bufferStride)
    4476             : {
    4477         117 :     const size_t nSrcDataTypeSize(eSrcDataType.GetSize());
    4478             :     // Use specialized implementation for well-known data types when no
    4479             :     // type conversion is needed
    4480         117 :     if (eSrcDataType == eDstDataType)
    4481             :     {
    4482         100 :         if (nSrcDataTypeSize == 1)
    4483             :         {
    4484          41 :             CopyToFinalBufferSameDataType<1>(pSrcBuffer, pDstBuffer, nDims,
    4485             :                                              count, bufferStride);
    4486          60 :             return;
    4487             :         }
    4488          59 :         else if (nSrcDataTypeSize == 2)
    4489             :         {
    4490           1 :             CopyToFinalBufferSameDataType<2>(pSrcBuffer, pDstBuffer, nDims,
    4491             :                                              count, bufferStride);
    4492           1 :             return;
    4493             :         }
    4494          58 :         else if (nSrcDataTypeSize == 4)
    4495             :         {
    4496          14 :             CopyToFinalBufferSameDataType<4>(pSrcBuffer, pDstBuffer, nDims,
    4497             :                                              count, bufferStride);
    4498          14 :             return;
    4499             :         }
    4500          44 :         else if (nSrcDataTypeSize == 8)
    4501             :         {
    4502           4 :             CopyToFinalBufferSameDataType<8>(pSrcBuffer, pDstBuffer, nDims,
    4503             :                                              count, bufferStride);
    4504           4 :             return;
    4505             :         }
    4506             :     }
    4507             : 
    4508          57 :     const size_t nDstDataTypeSize(eDstDataType.GetSize());
    4509         114 :     std::vector<size_t> anStackCount(nDims);
    4510         114 :     std::vector<GByte *> pabyDstBufferStack(nDims + 1);
    4511          57 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4512          57 :     pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
    4513          57 :     size_t iDim = 0;
    4514             : 
    4515         326 : lbl_next_depth:
    4516         326 :     if (iDim == nDims - 1)
    4517             :     {
    4518         316 :         GDALExtendedDataType::CopyValues(pabySrcBuffer, eSrcDataType, 1,
    4519         316 :                                          pabyDstBufferStack[iDim], eDstDataType,
    4520         316 :                                          bufferStride[iDim], count[iDim]);
    4521         316 :         pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
    4522             :     }
    4523             :     else
    4524             :     {
    4525          10 :         anStackCount[iDim] = count[iDim];
    4526             :         while (true)
    4527             :         {
    4528         269 :             ++iDim;
    4529         269 :             pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
    4530         269 :             goto lbl_next_depth;
    4531         269 :         lbl_return_to_caller_in_loop:
    4532         269 :             --iDim;
    4533         269 :             --anStackCount[iDim];
    4534         269 :             if (anStackCount[iDim] == 0)
    4535          10 :                 break;
    4536         259 :             pabyDstBufferStack[iDim] += bufferStride[iDim] * nDstDataTypeSize;
    4537             :         }
    4538             :     }
    4539         326 :     if (iDim > 0)
    4540         269 :         goto lbl_return_to_caller_in_loop;
    4541             : }
    4542             : 
    4543             : /************************************************************************/
    4544             : /*                          Transpose2D()                               */
    4545             : /************************************************************************/
    4546             : 
    4547             : template <class T>
    4548          39 : static void Transpose2D(T *dst, const T *src, size_t src_height,
    4549             :                         size_t src_width)
    4550             : {
    4551          39 :     constexpr size_t blocksize = 32;
    4552         142 :     for (size_t i = 0; i < src_height; i += blocksize)
    4553             :     {
    4554        1264 :         for (size_t j = 0; j < src_width; j += blocksize)
    4555             :         {
    4556             :             // transpose the block beginning at [i,j]
    4557        1161 :             const size_t max_k = std::min(i + blocksize, src_height);
    4558       36699 :             for (size_t k = i; k < max_k; ++k)
    4559             :             {
    4560       35538 :                 const size_t max_l = std::min(j + blocksize, src_width);
    4561     1135486 :                 for (size_t l = j; l < max_l; ++l)
    4562             :                 {
    4563     1099948 :                     dst[k + l * src_height] = src[l + k * src_width];
    4564             :                 }
    4565             :             }
    4566             :         }
    4567             :     }
    4568          39 : }
    4569             : 
    4570             : /************************************************************************/
    4571             : /*                      TransposeLast2Dims()                            */
    4572             : /************************************************************************/
    4573             : 
    4574          19 : static bool TransposeLast2Dims(void *pDstBuffer,
    4575             :                                const GDALExtendedDataType &eDT,
    4576             :                                const size_t nDims, const size_t *count,
    4577             :                                const size_t nEltsNonLast2Dims)
    4578             : {
    4579          19 :     const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4580          19 :     const auto nDTSize = eDT.GetSize();
    4581             :     void *pTempBufferForLast2DimsTranspose =
    4582          19 :         VSI_MALLOC2_VERBOSE(nEltsLast2Dims, nDTSize);
    4583          19 :     if (pTempBufferForLast2DimsTranspose == nullptr)
    4584           0 :         return false;
    4585             : 
    4586          19 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4587          58 :     for (size_t i = 0; i < nEltsNonLast2Dims; ++i)
    4588             :     {
    4589          39 :         if (nDTSize == 1)
    4590             :         {
    4591          14 :             Transpose2D(
    4592             :                 static_cast<uint8_t *>(pTempBufferForLast2DimsTranspose),
    4593             :                 reinterpret_cast<const uint8_t *>(pabyDstBuffer),
    4594          14 :                 count[nDims - 2], count[nDims - 1]);
    4595             :         }
    4596          25 :         else if (nDTSize == 2)
    4597             :         {
    4598           8 :             Transpose2D(
    4599             :                 static_cast<uint16_t *>(pTempBufferForLast2DimsTranspose),
    4600             :                 reinterpret_cast<const uint16_t *>(pabyDstBuffer),
    4601           8 :                 count[nDims - 2], count[nDims - 1]);
    4602             :         }
    4603          17 :         else if (nDTSize == 4)
    4604             :         {
    4605           9 :             Transpose2D(
    4606             :                 static_cast<uint32_t *>(pTempBufferForLast2DimsTranspose),
    4607             :                 reinterpret_cast<const uint32_t *>(pabyDstBuffer),
    4608           9 :                 count[nDims - 2], count[nDims - 1]);
    4609             :         }
    4610           8 :         else if (nDTSize == 8)
    4611             :         {
    4612           8 :             Transpose2D(
    4613             :                 static_cast<uint64_t *>(pTempBufferForLast2DimsTranspose),
    4614             :                 reinterpret_cast<const uint64_t *>(pabyDstBuffer),
    4615           8 :                 count[nDims - 2], count[nDims - 1]);
    4616             :         }
    4617             :         else
    4618             :         {
    4619           0 :             CPLAssert(false);
    4620             :         }
    4621          39 :         memcpy(pabyDstBuffer, pTempBufferForLast2DimsTranspose,
    4622             :                nDTSize * nEltsLast2Dims);
    4623          39 :         pabyDstBuffer += nDTSize * nEltsLast2Dims;
    4624             :     }
    4625             : 
    4626          19 :     VSIFree(pTempBufferForLast2DimsTranspose);
    4627             : 
    4628          19 :     return true;
    4629             : }
    4630             : 
    4631             : /************************************************************************/
    4632             : /*                      ReadForTransposedRequest()                      */
    4633             : /************************************************************************/
    4634             : 
    4635             : // Using the netCDF/HDF5 APIs to read a slice with strides that express a
    4636             : // transposed view yield to extremely poor/unusable performance. This fixes
    4637             : // this by using temporary memory to read in a contiguous buffer in a
    4638             : // row-major order, and then do the transposition to the final buffer.
    4639             : 
    4640         136 : bool GDALMDArray::ReadForTransposedRequest(
    4641             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4642             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4643             :     void *pDstBuffer) const
    4644             : {
    4645         136 :     const size_t nDims(GetDimensionCount());
    4646         136 :     if (nDims == 0)
    4647             :     {
    4648           0 :         CPLAssert(false);
    4649             :         return false;  // shouldn't happen
    4650             :     }
    4651         136 :     size_t nElts = 1;
    4652         394 :     for (size_t i = 0; i < nDims; ++i)
    4653         258 :         nElts *= count[i];
    4654             : 
    4655         272 :     std::vector<GPtrDiff_t> tmpBufferStrides(nDims);
    4656         136 :     tmpBufferStrides.back() = 1;
    4657         258 :     for (size_t i = nDims - 1; i > 0;)
    4658             :     {
    4659         122 :         --i;
    4660         122 :         tmpBufferStrides[i] = tmpBufferStrides[i + 1] * count[i + 1];
    4661             :     }
    4662             : 
    4663         136 :     const auto &eDT = GetDataType();
    4664         136 :     const auto nDTSize = eDT.GetSize();
    4665         255 :     if (bufferDataType == eDT && nDims >= 2 && bufferStride[nDims - 2] == 1 &&
    4666         271 :         static_cast<size_t>(bufferStride[nDims - 1]) == count[nDims - 2] &&
    4667          16 :         (nDTSize == 1 || nDTSize == 2 || nDTSize == 4 || nDTSize == 8))
    4668             :     {
    4669             :         // Optimization of the optimization if only the last 2 dims are
    4670             :         // transposed that saves on temporary buffer allocation
    4671          23 :         const size_t nEltsLast2Dims = count[nDims - 2] * count[nDims - 1];
    4672          23 :         size_t nCurStrideForRowMajorStrides = nEltsLast2Dims;
    4673          23 :         bool bRowMajorStridesForNonLast2Dims = true;
    4674          23 :         size_t nEltsNonLast2Dims = 1;
    4675          40 :         for (size_t i = nDims - 2; i > 0;)
    4676             :         {
    4677          17 :             --i;
    4678          17 :             if (static_cast<size_t>(bufferStride[i]) !=
    4679             :                 nCurStrideForRowMajorStrides)
    4680             :             {
    4681           4 :                 bRowMajorStridesForNonLast2Dims = false;
    4682             :             }
    4683             :             // Integer overflows have already been checked in
    4684             :             // CheckReadWriteParams()
    4685          17 :             nCurStrideForRowMajorStrides *= count[i];
    4686          17 :             nEltsNonLast2Dims *= count[i];
    4687             :         }
    4688          23 :         if (bRowMajorStridesForNonLast2Dims)
    4689             :         {
    4690             :             // We read in the final buffer!
    4691          19 :             if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(),
    4692          19 :                        eDT, pDstBuffer))
    4693             :             {
    4694           0 :                 return false;
    4695             :             }
    4696             : 
    4697          19 :             return TransposeLast2Dims(pDstBuffer, eDT, nDims, count,
    4698          19 :                                       nEltsNonLast2Dims);
    4699             :         }
    4700             :     }
    4701             : 
    4702         117 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    4703         117 :     if (pTempBuffer == nullptr)
    4704           0 :         return false;
    4705             : 
    4706         117 :     if (!IRead(arrayStartIdx, count, arrayStep, tmpBufferStrides.data(), eDT,
    4707         117 :                pTempBuffer))
    4708             :     {
    4709           0 :         VSIFree(pTempBuffer);
    4710           0 :         return false;
    4711             :     }
    4712         117 :     CopyToFinalBuffer(pTempBuffer, eDT, pDstBuffer, bufferDataType, nDims,
    4713             :                       count, bufferStride);
    4714             : 
    4715         117 :     if (eDT.NeedsFreeDynamicMemory())
    4716             :     {
    4717          46 :         GByte *pabyPtr = static_cast<GByte *>(pTempBuffer);
    4718          92 :         for (size_t i = 0; i < nElts; ++i)
    4719             :         {
    4720          46 :             eDT.FreeDynamicMemory(pabyPtr);
    4721          46 :             pabyPtr += nDTSize;
    4722             :         }
    4723             :     }
    4724             : 
    4725         117 :     VSIFree(pTempBuffer);
    4726         117 :     return true;
    4727             : }
    4728             : 
    4729             : /************************************************************************/
    4730             : /*               IsStepOneContiguousRowMajorOrderedSameDataType()       */
    4731             : /************************************************************************/
    4732             : 
    4733             : // Returns true if at all following conditions are met:
    4734             : // arrayStep[] == 1, bufferDataType == GetDataType() and bufferStride[]
    4735             : // defines a row-major ordered contiguous buffer.
    4736          78 : bool GDALMDArray::IsStepOneContiguousRowMajorOrderedSameDataType(
    4737             :     const size_t *count, const GInt64 *arrayStep,
    4738             :     const GPtrDiff_t *bufferStride,
    4739             :     const GDALExtendedDataType &bufferDataType) const
    4740             : {
    4741          78 :     if (bufferDataType != GetDataType())
    4742           5 :         return false;
    4743          73 :     size_t nExpectedStride = 1;
    4744         166 :     for (size_t i = GetDimensionCount(); i > 0;)
    4745             :     {
    4746          96 :         --i;
    4747          96 :         if (arrayStep[i] != 1 || bufferStride[i] < 0 ||
    4748          94 :             static_cast<size_t>(bufferStride[i]) != nExpectedStride)
    4749             :         {
    4750           3 :             return false;
    4751             :         }
    4752          93 :         nExpectedStride *= count[i];
    4753             :     }
    4754          70 :     return true;
    4755             : }
    4756             : 
    4757             : /************************************************************************/
    4758             : /*                      ReadUsingContiguousIRead()                      */
    4759             : /************************************************************************/
    4760             : 
    4761             : // Used for example by the TileDB driver when requesting it with
    4762             : // arrayStep[] != 1, bufferDataType != GetDataType() or bufferStride[]
    4763             : // not defining a row-major ordered contiguous buffer.
    4764             : // Should only be called when at least one of the above conditions are true,
    4765             : // which can be tested with IsStepOneContiguousRowMajorOrderedSameDataType()
    4766             : // returning none.
    4767             : // This method will call IRead() again with arrayStep[] == 1,
    4768             : // bufferDataType == GetDataType() and bufferStride[] defining a row-major
    4769             : // ordered contiguous buffer, on a temporary buffer. And it will rearrange the
    4770             : // content of that temporary buffer onto pDstBuffer.
    4771           7 : bool GDALMDArray::ReadUsingContiguousIRead(
    4772             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    4773             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    4774             :     void *pDstBuffer) const
    4775             : {
    4776           7 :     const size_t nDims(GetDimensionCount());
    4777          14 :     std::vector<GUInt64> anTmpStartIdx(nDims);
    4778          14 :     std::vector<size_t> anTmpCount(nDims);
    4779           7 :     const auto &oType = GetDataType();
    4780           7 :     size_t nMemArraySize = oType.GetSize();
    4781          14 :     std::vector<GPtrDiff_t> anTmpStride(nDims);
    4782           7 :     GPtrDiff_t nStride = 1;
    4783          18 :     for (size_t i = nDims; i > 0;)
    4784             :     {
    4785          11 :         --i;
    4786          11 :         if (arrayStep[i] > 0)
    4787           9 :             anTmpStartIdx[i] = arrayStartIdx[i];
    4788             :         else
    4789           2 :             anTmpStartIdx[i] =
    4790           2 :                 arrayStartIdx[i] - (count[i] - 1) * (-arrayStep[i]);
    4791             :         const uint64_t nCount =
    4792          11 :             (count[i] - 1) * static_cast<uint64_t>(std::abs(arrayStep[i])) + 1;
    4793          11 :         if (nCount > std::numeric_limits<size_t>::max() / nMemArraySize)
    4794             :         {
    4795           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    4796             :                      "Read() failed due to too large memory requirement");
    4797           0 :             return false;
    4798             :         }
    4799          11 :         anTmpCount[i] = static_cast<size_t>(nCount);
    4800          11 :         nMemArraySize *= anTmpCount[i];
    4801          11 :         anTmpStride[i] = nStride;
    4802          11 :         nStride *= anTmpCount[i];
    4803             :     }
    4804             :     std::unique_ptr<void, decltype(&VSIFree)> pTmpBuffer(
    4805          14 :         VSI_MALLOC_VERBOSE(nMemArraySize), VSIFree);
    4806           7 :     if (!pTmpBuffer)
    4807           0 :         return false;
    4808           7 :     if (!IRead(anTmpStartIdx.data(), anTmpCount.data(),
    4809          14 :                std::vector<GInt64>(nDims, 1).data(),  // steps
    4810           7 :                anTmpStride.data(), oType, pTmpBuffer.get()))
    4811             :     {
    4812           0 :         return false;
    4813             :     }
    4814          14 :     std::vector<std::shared_ptr<GDALDimension>> apoTmpDims(nDims);
    4815          18 :     for (size_t i = 0; i < nDims; ++i)
    4816             :     {
    4817          11 :         if (arrayStep[i] > 0)
    4818           9 :             anTmpStartIdx[i] = 0;
    4819             :         else
    4820           2 :             anTmpStartIdx[i] = anTmpCount[i] - 1;
    4821          22 :         apoTmpDims[i] = std::make_shared<GDALDimension>(
    4822          22 :             std::string(), std::string(), std::string(), std::string(),
    4823          22 :             anTmpCount[i]);
    4824             :     }
    4825             :     auto poMEMArray =
    4826          14 :         MEMMDArray::Create(std::string(), std::string(), apoTmpDims, oType);
    4827          21 :     return poMEMArray->Init(static_cast<GByte *>(pTmpBuffer.get())) &&
    4828           7 :            poMEMArray->Read(anTmpStartIdx.data(), count, arrayStep,
    4829           7 :                             bufferStride, bufferDataType, pDstBuffer);
    4830             : }
    4831             : 
    4832             : //! @endcond
    4833             : 
    4834             : /************************************************************************/
    4835             : /*                       GDALSlicedMDArray                              */
    4836             : /************************************************************************/
    4837             : 
    4838             : class GDALSlicedMDArray final : public GDALPamMDArray
    4839             : {
    4840             :   private:
    4841             :     std::shared_ptr<GDALMDArray> m_poParent{};
    4842             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    4843             :     std::vector<size_t> m_mapDimIdxToParentDimIdx{};  // of size m_dims.size()
    4844             :     std::vector<Range>
    4845             :         m_parentRanges{};  // of size m_poParent->GetDimensionCount()
    4846             : 
    4847             :     mutable std::vector<GUInt64> m_parentStart;
    4848             :     mutable std::vector<size_t> m_parentCount;
    4849             :     mutable std::vector<GInt64> m_parentStep;
    4850             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    4851             : 
    4852             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    4853             :                              const GInt64 *arrayStep,
    4854             :                              const GPtrDiff_t *bufferStride) const;
    4855             : 
    4856             :   protected:
    4857         562 :     explicit GDALSlicedMDArray(
    4858             :         const std::shared_ptr<GDALMDArray> &poParent,
    4859             :         const std::string &viewExpr,
    4860             :         std::vector<std::shared_ptr<GDALDimension>> &&dims,
    4861             :         std::vector<size_t> &&mapDimIdxToParentDimIdx,
    4862             :         std::vector<Range> &&parentRanges)
    4863        1686 :         : GDALAbstractMDArray(std::string(), "Sliced view of " +
    4864        1686 :                                                  poParent->GetFullName() +
    4865        1124 :                                                  " (" + viewExpr + ")"),
    4866        1124 :           GDALPamMDArray(std::string(),
    4867        1124 :                          "Sliced view of " + poParent->GetFullName() + " (" +
    4868        1124 :                              viewExpr + ")",
    4869        1124 :                          GDALPamMultiDim::GetPAM(poParent),
    4870             :                          poParent->GetContext()),
    4871        1124 :           m_poParent(std::move(poParent)), m_dims(std::move(dims)),
    4872         562 :           m_mapDimIdxToParentDimIdx(std::move(mapDimIdxToParentDimIdx)),
    4873         562 :           m_parentRanges(std::move(parentRanges)),
    4874         562 :           m_parentStart(m_poParent->GetDimensionCount()),
    4875         562 :           m_parentCount(m_poParent->GetDimensionCount(), 1),
    4876         562 :           m_parentStep(m_poParent->GetDimensionCount()),
    4877        4496 :           m_parentStride(m_poParent->GetDimensionCount())
    4878             :     {
    4879         562 :     }
    4880             : 
    4881             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4882             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    4883             :                const GDALExtendedDataType &bufferDataType,
    4884             :                void *pDstBuffer) const override;
    4885             : 
    4886             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    4887             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    4888             :                 const GDALExtendedDataType &bufferDataType,
    4889             :                 const void *pSrcBuffer) override;
    4890             : 
    4891             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4892             :                      CSLConstList papszOptions) const override;
    4893             : 
    4894             :   public:
    4895             :     static std::shared_ptr<GDALSlicedMDArray>
    4896         562 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    4897             :            const std::string &viewExpr,
    4898             :            std::vector<std::shared_ptr<GDALDimension>> &&dims,
    4899             :            std::vector<size_t> &&mapDimIdxToParentDimIdx,
    4900             :            std::vector<Range> &&parentRanges)
    4901             :     {
    4902         562 :         CPLAssert(dims.size() == mapDimIdxToParentDimIdx.size());
    4903         562 :         CPLAssert(parentRanges.size() == poParent->GetDimensionCount());
    4904             : 
    4905             :         auto newAr(std::shared_ptr<GDALSlicedMDArray>(new GDALSlicedMDArray(
    4906         562 :             poParent, viewExpr, std::move(dims),
    4907         562 :             std::move(mapDimIdxToParentDimIdx), std::move(parentRanges))));
    4908         562 :         newAr->SetSelf(newAr);
    4909         562 :         return newAr;
    4910             :     }
    4911             : 
    4912          38 :     bool IsWritable() const override
    4913             :     {
    4914          38 :         return m_poParent->IsWritable();
    4915             :     }
    4916             : 
    4917         922 :     const std::string &GetFilename() const override
    4918             :     {
    4919         922 :         return m_poParent->GetFilename();
    4920             :     }
    4921             : 
    4922             :     const std::vector<std::shared_ptr<GDALDimension>> &
    4923        3535 :     GetDimensions() const override
    4924             :     {
    4925        3535 :         return m_dims;
    4926             :     }
    4927             : 
    4928        1298 :     const GDALExtendedDataType &GetDataType() const override
    4929             :     {
    4930        1298 :         return m_poParent->GetDataType();
    4931             :     }
    4932             : 
    4933           2 :     const std::string &GetUnit() const override
    4934             :     {
    4935           2 :         return m_poParent->GetUnit();
    4936             :     }
    4937             : 
    4938             :     // bool SetUnit(const std::string& osUnit) override  { return
    4939             :     // m_poParent->SetUnit(osUnit); }
    4940             : 
    4941           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    4942             :     {
    4943           4 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    4944           2 :         if (!poSrcSRS)
    4945           1 :             return nullptr;
    4946           2 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    4947           2 :         std::vector<int> dstMapping;
    4948           3 :         for (int srcAxis : srcMapping)
    4949             :         {
    4950           2 :             bool bFound = false;
    4951           3 :             for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); i++)
    4952             :             {
    4953           3 :                 if (static_cast<int>(m_mapDimIdxToParentDimIdx[i]) ==
    4954           3 :                     srcAxis - 1)
    4955             :                 {
    4956           2 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    4957           2 :                     bFound = true;
    4958           2 :                     break;
    4959             :                 }
    4960             :             }
    4961           2 :             if (!bFound)
    4962             :             {
    4963           0 :                 dstMapping.push_back(0);
    4964             :             }
    4965             :         }
    4966           2 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    4967           1 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    4968           1 :         return poClone;
    4969             :     }
    4970             : 
    4971          32 :     const void *GetRawNoDataValue() const override
    4972             :     {
    4973          32 :         return m_poParent->GetRawNoDataValue();
    4974             :     }
    4975             : 
    4976             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    4977             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    4978             : 
    4979           2 :     double GetOffset(bool *pbHasOffset,
    4980             :                      GDALDataType *peStorageType) const override
    4981             :     {
    4982           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    4983             :     }
    4984             : 
    4985           2 :     double GetScale(bool *pbHasScale,
    4986             :                     GDALDataType *peStorageType) const override
    4987             :     {
    4988           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    4989             :     }
    4990             : 
    4991             :     // bool SetOffset(double dfOffset) override { return
    4992             :     // m_poParent->SetOffset(dfOffset); }
    4993             : 
    4994             :     // bool SetScale(double dfScale) override { return
    4995             :     // m_poParent->SetScale(dfScale); }
    4996             : 
    4997         174 :     std::vector<GUInt64> GetBlockSize() const override
    4998             :     {
    4999         174 :         std::vector<GUInt64> ret(GetDimensionCount());
    5000         348 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5001         526 :         for (size_t i = 0; i < m_mapDimIdxToParentDimIdx.size(); ++i)
    5002             :         {
    5003         352 :             const auto iOldAxis = m_mapDimIdxToParentDimIdx[i];
    5004         352 :             if (iOldAxis != static_cast<size_t>(-1))
    5005             :             {
    5006         352 :                 ret[i] = parentBlockSize[iOldAxis];
    5007             :             }
    5008             :         }
    5009         348 :         return ret;
    5010             :     }
    5011             : 
    5012             :     std::shared_ptr<GDALAttribute>
    5013           6 :     GetAttribute(const std::string &osName) const override
    5014             :     {
    5015           6 :         return m_poParent->GetAttribute(osName);
    5016             :     }
    5017             : 
    5018             :     std::vector<std::shared_ptr<GDALAttribute>>
    5019          23 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5020             :     {
    5021          23 :         return m_poParent->GetAttributes(papszOptions);
    5022             :     }
    5023             : };
    5024             : 
    5025             : /************************************************************************/
    5026             : /*                        PrepareParentArrays()                         */
    5027             : /************************************************************************/
    5028             : 
    5029         467 : void GDALSlicedMDArray::PrepareParentArrays(
    5030             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5031             :     const GPtrDiff_t *bufferStride) const
    5032             : {
    5033         467 :     const size_t nParentDimCount = m_parentRanges.size();
    5034        1454 :     for (size_t i = 0; i < nParentDimCount; i++)
    5035             :     {
    5036             :         // For dimensions in parent that have no existence in sliced array
    5037         987 :         m_parentStart[i] = m_parentRanges[i].m_nStartIdx;
    5038             :     }
    5039             : 
    5040        1226 :     for (size_t i = 0; i < m_dims.size(); i++)
    5041             :     {
    5042         759 :         const auto iParent = m_mapDimIdxToParentDimIdx[i];
    5043         759 :         if (iParent != static_cast<size_t>(-1))
    5044             :         {
    5045         757 :             m_parentStart[iParent] =
    5046         757 :                 m_parentRanges[iParent].m_nIncr >= 0
    5047         757 :                     ? m_parentRanges[iParent].m_nStartIdx +
    5048         733 :                           arrayStartIdx[i] * m_parentRanges[iParent].m_nIncr
    5049          24 :                     : m_parentRanges[iParent].m_nStartIdx -
    5050          48 :                           arrayStartIdx[i] *
    5051          24 :                               static_cast<GUInt64>(
    5052          24 :                                   -m_parentRanges[iParent].m_nIncr);
    5053         757 :             m_parentCount[iParent] = count[i];
    5054         757 :             if (arrayStep)
    5055             :             {
    5056         756 :                 m_parentStep[iParent] =
    5057         756 :                     count[i] == 1 ? 1 :
    5058             :                                   // other checks should have ensured this does
    5059             :                         // not overflow
    5060         574 :                         arrayStep[i] * m_parentRanges[iParent].m_nIncr;
    5061             :             }
    5062         757 :             if (bufferStride)
    5063             :             {
    5064         756 :                 m_parentStride[iParent] = bufferStride[i];
    5065             :             }
    5066             :         }
    5067             :     }
    5068         467 : }
    5069             : 
    5070             : /************************************************************************/
    5071             : /*                             IRead()                                  */
    5072             : /************************************************************************/
    5073             : 
    5074         437 : bool GDALSlicedMDArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5075             :                               const GInt64 *arrayStep,
    5076             :                               const GPtrDiff_t *bufferStride,
    5077             :                               const GDALExtendedDataType &bufferDataType,
    5078             :                               void *pDstBuffer) const
    5079             : {
    5080         437 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5081         874 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5082         437 :                             m_parentStep.data(), m_parentStride.data(),
    5083         437 :                             bufferDataType, pDstBuffer);
    5084             : }
    5085             : 
    5086             : /************************************************************************/
    5087             : /*                             IWrite()                                  */
    5088             : /************************************************************************/
    5089             : 
    5090          29 : bool GDALSlicedMDArray::IWrite(const GUInt64 *arrayStartIdx,
    5091             :                                const size_t *count, const GInt64 *arrayStep,
    5092             :                                const GPtrDiff_t *bufferStride,
    5093             :                                const GDALExtendedDataType &bufferDataType,
    5094             :                                const void *pSrcBuffer)
    5095             : {
    5096          29 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5097          58 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5098          29 :                              m_parentStep.data(), m_parentStride.data(),
    5099          29 :                              bufferDataType, pSrcBuffer);
    5100             : }
    5101             : 
    5102             : /************************************************************************/
    5103             : /*                             IAdviseRead()                            */
    5104             : /************************************************************************/
    5105             : 
    5106           1 : bool GDALSlicedMDArray::IAdviseRead(const GUInt64 *arrayStartIdx,
    5107             :                                     const size_t *count,
    5108             :                                     CSLConstList papszOptions) const
    5109             : {
    5110           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5111           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5112           1 :                                   papszOptions);
    5113             : }
    5114             : 
    5115             : /************************************************************************/
    5116             : /*                        CreateSlicedArray()                           */
    5117             : /************************************************************************/
    5118             : 
    5119             : static std::shared_ptr<GDALMDArray>
    5120         580 : CreateSlicedArray(const std::shared_ptr<GDALMDArray> &self,
    5121             :                   const std::string &viewExpr, const std::string &activeSlice,
    5122             :                   bool bRenameDimensions,
    5123             :                   std::vector<GDALMDArray::ViewSpec> &viewSpecs)
    5124             : {
    5125         580 :     const auto &srcDims(self->GetDimensions());
    5126         580 :     if (srcDims.empty())
    5127             :     {
    5128           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot slice a 0-d array");
    5129           2 :         return nullptr;
    5130             :     }
    5131             : 
    5132        1156 :     CPLStringList aosTokens(CSLTokenizeString2(activeSlice.c_str(), ",", 0));
    5133         578 :     const auto nTokens = static_cast<size_t>(aosTokens.size());
    5134             : 
    5135        1156 :     std::vector<std::shared_ptr<GDALDimension>> newDims;
    5136        1156 :     std::vector<size_t> mapDimIdxToParentDimIdx;
    5137        1156 :     std::vector<GDALSlicedMDArray::Range> parentRanges;
    5138         578 :     newDims.reserve(nTokens);
    5139         578 :     mapDimIdxToParentDimIdx.reserve(nTokens);
    5140         578 :     parentRanges.reserve(nTokens);
    5141             : 
    5142         578 :     bool bGotEllipsis = false;
    5143         578 :     size_t nCurSrcDim = 0;
    5144        1705 :     for (size_t i = 0; i < nTokens; i++)
    5145             :     {
    5146        1143 :         const char *pszIdxSpec = aosTokens[i];
    5147        1143 :         if (EQUAL(pszIdxSpec, "..."))
    5148             :         {
    5149          28 :             if (bGotEllipsis)
    5150             :             {
    5151           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5152             :                          "Only one single ellipsis is supported");
    5153           2 :                 return nullptr;
    5154             :             }
    5155          26 :             bGotEllipsis = true;
    5156          26 :             const auto nSubstitutionCount = srcDims.size() - (nTokens - 1);
    5157          59 :             for (size_t j = 0; j < nSubstitutionCount; j++, nCurSrcDim++)
    5158             :             {
    5159          33 :                 parentRanges.emplace_back(0, 1);
    5160          33 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5161          33 :                 mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5162             :             }
    5163          26 :             continue;
    5164             :         }
    5165        1115 :         else if (EQUAL(pszIdxSpec, "newaxis") ||
    5166        1112 :                  EQUAL(pszIdxSpec, "np.newaxis"))
    5167             :         {
    5168           3 :             newDims.push_back(std::make_shared<GDALDimension>(
    5169           6 :                 std::string(), "newaxis", std::string(), std::string(), 1));
    5170           3 :             mapDimIdxToParentDimIdx.push_back(static_cast<size_t>(-1));
    5171           3 :             continue;
    5172             :         }
    5173        1112 :         else if (CPLGetValueType(pszIdxSpec) == CPL_VALUE_INTEGER)
    5174             :         {
    5175         320 :             if (nCurSrcDim >= srcDims.size())
    5176             :             {
    5177           2 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5178             :                          activeSlice.c_str());
    5179           7 :                 return nullptr;
    5180             :             }
    5181             : 
    5182         318 :             auto nVal = CPLAtoGIntBig(pszIdxSpec);
    5183         318 :             GUInt64 nDimSize = srcDims[nCurSrcDim]->GetSize();
    5184         318 :             if ((nVal >= 0 && static_cast<GUInt64>(nVal) >= nDimSize) ||
    5185         314 :                 (nVal < 0 && nDimSize < static_cast<GUInt64>(-nVal)))
    5186             :             {
    5187           5 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5188             :                          "Index " CPL_FRMT_GIB " is out of bounds", nVal);
    5189           5 :                 return nullptr;
    5190             :             }
    5191         313 :             if (nVal < 0)
    5192           0 :                 nVal += nDimSize;
    5193         313 :             parentRanges.emplace_back(nVal, 0);
    5194             :         }
    5195             :         else
    5196             :         {
    5197         792 :             if (nCurSrcDim >= srcDims.size())
    5198             :             {
    5199           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many values in %s",
    5200             :                          activeSlice.c_str());
    5201           7 :                 return nullptr;
    5202             :             }
    5203             : 
    5204             :             CPLStringList aosRangeTokens(
    5205         791 :                 CSLTokenizeString2(pszIdxSpec, ":", CSLT_ALLOWEMPTYTOKENS));
    5206         791 :             int nRangeTokens = aosRangeTokens.size();
    5207         791 :             if (nRangeTokens > 3)
    5208             :             {
    5209           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too many : in %s",
    5210             :                          pszIdxSpec);
    5211           1 :                 return nullptr;
    5212             :             }
    5213         790 :             if (nRangeTokens <= 1)
    5214             :             {
    5215           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid value %s",
    5216             :                          pszIdxSpec);
    5217           1 :                 return nullptr;
    5218             :             }
    5219         789 :             const char *pszStart = aosRangeTokens[0];
    5220         789 :             const char *pszEnd = aosRangeTokens[1];
    5221         789 :             const char *pszInc = (nRangeTokens == 3) ? aosRangeTokens[2] : "";
    5222         789 :             GDALSlicedMDArray::Range range;
    5223         789 :             const GUInt64 nDimSize(srcDims[nCurSrcDim]->GetSize());
    5224         789 :             range.m_nIncr = EQUAL(pszInc, "") ? 1 : CPLAtoGIntBig(pszInc);
    5225        1577 :             if (range.m_nIncr == 0 ||
    5226         788 :                 range.m_nIncr == std::numeric_limits<GInt64>::min())
    5227             :             {
    5228           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Invalid increment");
    5229           1 :                 return nullptr;
    5230             :             }
    5231         788 :             auto startIdx(CPLAtoGIntBig(pszStart));
    5232         788 :             if (startIdx < 0)
    5233             :             {
    5234           0 :                 if (nDimSize < static_cast<GUInt64>(-startIdx))
    5235           0 :                     startIdx = 0;
    5236             :                 else
    5237           0 :                     startIdx = nDimSize + startIdx;
    5238             :             }
    5239         788 :             const bool bPosIncr = range.m_nIncr > 0;
    5240         788 :             range.m_nStartIdx = startIdx;
    5241        1576 :             range.m_nStartIdx = EQUAL(pszStart, "")
    5242         788 :                                     ? (bPosIncr ? 0 : nDimSize - 1)
    5243             :                                     : range.m_nStartIdx;
    5244         788 :             if (range.m_nStartIdx >= nDimSize - 1)
    5245         176 :                 range.m_nStartIdx = nDimSize - 1;
    5246         788 :             auto endIdx(CPLAtoGIntBig(pszEnd));
    5247         788 :             if (endIdx < 0)
    5248             :             {
    5249           1 :                 const auto positiveEndIdx = static_cast<GUInt64>(-endIdx);
    5250           1 :                 if (nDimSize < positiveEndIdx)
    5251           0 :                     endIdx = 0;
    5252             :                 else
    5253           1 :                     endIdx = nDimSize - positiveEndIdx;
    5254             :             }
    5255         788 :             GUInt64 nEndIdx = endIdx;
    5256         788 :             nEndIdx = EQUAL(pszEnd, "") ? (!bPosIncr ? 0 : nDimSize) : nEndIdx;
    5257         788 :             if ((bPosIncr && range.m_nStartIdx >= nEndIdx) ||
    5258         786 :                 (!bPosIncr && range.m_nStartIdx <= nEndIdx))
    5259             :             {
    5260           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5261             :                          "Output dimension of size 0 is not allowed");
    5262           3 :                 return nullptr;
    5263             :             }
    5264         785 :             int inc = (EQUAL(pszEnd, "") && !bPosIncr) ? 1 : 0;
    5265         785 :             const auto nAbsIncr = std::abs(range.m_nIncr);
    5266         785 :             const GUInt64 newSize =
    5267             :                 bPosIncr
    5268         809 :                     ? DIV_ROUND_UP(nEndIdx - range.m_nStartIdx, nAbsIncr)
    5269          24 :                     : DIV_ROUND_UP(inc + range.m_nStartIdx - nEndIdx, nAbsIncr);
    5270        1301 :             if (range.m_nStartIdx == 0 && range.m_nIncr == 1 &&
    5271         516 :                 newSize == srcDims[nCurSrcDim]->GetSize())
    5272             :             {
    5273         147 :                 newDims.push_back(srcDims[nCurSrcDim]);
    5274             :             }
    5275             :             else
    5276             :             {
    5277         638 :                 std::string osNewDimName(srcDims[nCurSrcDim]->GetName());
    5278         638 :                 if (bRenameDimensions)
    5279             :                 {
    5280             :                     osNewDimName =
    5281        1192 :                         "subset_" + srcDims[nCurSrcDim]->GetName() +
    5282             :                         CPLSPrintf("_" CPL_FRMT_GUIB "_" CPL_FRMT_GIB
    5283             :                                    "_" CPL_FRMT_GUIB,
    5284         596 :                                    static_cast<GUIntBig>(range.m_nStartIdx),
    5285         596 :                                    static_cast<GIntBig>(range.m_nIncr),
    5286         596 :                                    static_cast<GUIntBig>(newSize));
    5287             :                 }
    5288         638 :                 newDims.push_back(std::make_shared<GDALDimension>(
    5289        1276 :                     std::string(), osNewDimName, srcDims[nCurSrcDim]->GetType(),
    5290        1276 :                     range.m_nIncr > 0 ? srcDims[nCurSrcDim]->GetDirection()
    5291             :                                       : std::string(),
    5292             :                     newSize));
    5293             :             }
    5294         785 :             mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5295         785 :             parentRanges.emplace_back(range);
    5296             :         }
    5297             : 
    5298        1098 :         nCurSrcDim++;
    5299             :     }
    5300         635 :     for (; nCurSrcDim < srcDims.size(); nCurSrcDim++)
    5301             :     {
    5302          73 :         parentRanges.emplace_back(0, 1);
    5303          73 :         newDims.push_back(srcDims[nCurSrcDim]);
    5304          73 :         mapDimIdxToParentDimIdx.push_back(nCurSrcDim);
    5305             :     }
    5306             : 
    5307         562 :     GDALMDArray::ViewSpec viewSpec;
    5308         562 :     viewSpec.m_mapDimIdxToParentDimIdx = mapDimIdxToParentDimIdx;
    5309         562 :     viewSpec.m_parentRanges = parentRanges;
    5310         562 :     viewSpecs.emplace_back(std::move(viewSpec));
    5311             : 
    5312        1124 :     return GDALSlicedMDArray::Create(self, viewExpr, std::move(newDims),
    5313         562 :                                      std::move(mapDimIdxToParentDimIdx),
    5314        1124 :                                      std::move(parentRanges));
    5315             : }
    5316             : 
    5317             : /************************************************************************/
    5318             : /*                       GDALExtractFieldMDArray                        */
    5319             : /************************************************************************/
    5320             : 
    5321             : class GDALExtractFieldMDArray final : public GDALPamMDArray
    5322             : {
    5323             :   private:
    5324             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5325             :     GDALExtendedDataType m_dt;
    5326             :     std::string m_srcCompName;
    5327             :     mutable std::vector<GByte> m_pabyNoData{};
    5328             : 
    5329             :   protected:
    5330          42 :     GDALExtractFieldMDArray(const std::shared_ptr<GDALMDArray> &poParent,
    5331             :                             const std::string &fieldName,
    5332             :                             const std::unique_ptr<GDALEDTComponent> &srcComp)
    5333         168 :         : GDALAbstractMDArray(std::string(), "Extract field " + fieldName +
    5334          84 :                                                  " of " +
    5335          42 :                                                  poParent->GetFullName()),
    5336             :           GDALPamMDArray(
    5337          84 :               std::string(),
    5338          84 :               "Extract field " + fieldName + " of " + poParent->GetFullName(),
    5339          84 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    5340             :           m_poParent(poParent), m_dt(srcComp->GetType()),
    5341         210 :           m_srcCompName(srcComp->GetName())
    5342             :     {
    5343          42 :         m_pabyNoData.resize(m_dt.GetSize());
    5344          42 :     }
    5345             : 
    5346             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5347             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5348             :                const GDALExtendedDataType &bufferDataType,
    5349             :                void *pDstBuffer) const override;
    5350             : 
    5351           1 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5352             :                      CSLConstList papszOptions) const override
    5353             :     {
    5354           1 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    5355             :     }
    5356             : 
    5357             :   public:
    5358             :     static std::shared_ptr<GDALExtractFieldMDArray>
    5359          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5360             :            const std::string &fieldName,
    5361             :            const std::unique_ptr<GDALEDTComponent> &srcComp)
    5362             :     {
    5363             :         auto newAr(std::shared_ptr<GDALExtractFieldMDArray>(
    5364          42 :             new GDALExtractFieldMDArray(poParent, fieldName, srcComp)));
    5365          42 :         newAr->SetSelf(newAr);
    5366          42 :         return newAr;
    5367             :     }
    5368             : 
    5369          84 :     ~GDALExtractFieldMDArray()
    5370          42 :     {
    5371          42 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5372          84 :     }
    5373             : 
    5374          22 :     bool IsWritable() const override
    5375             :     {
    5376          22 :         return m_poParent->IsWritable();
    5377             :     }
    5378             : 
    5379         136 :     const std::string &GetFilename() const override
    5380             :     {
    5381         136 :         return m_poParent->GetFilename();
    5382             :     }
    5383             : 
    5384             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5385         211 :     GetDimensions() const override
    5386             :     {
    5387         211 :         return m_poParent->GetDimensions();
    5388             :     }
    5389             : 
    5390         159 :     const GDALExtendedDataType &GetDataType() const override
    5391             :     {
    5392         159 :         return m_dt;
    5393             :     }
    5394             : 
    5395           2 :     const std::string &GetUnit() const override
    5396             :     {
    5397           2 :         return m_poParent->GetUnit();
    5398             :     }
    5399             : 
    5400           2 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5401             :     {
    5402           2 :         return m_poParent->GetSpatialRef();
    5403             :     }
    5404             : 
    5405          33 :     const void *GetRawNoDataValue() const override
    5406             :     {
    5407          33 :         const void *parentNoData = m_poParent->GetRawNoDataValue();
    5408          33 :         if (parentNoData == nullptr)
    5409           1 :             return nullptr;
    5410             : 
    5411          32 :         m_dt.FreeDynamicMemory(&m_pabyNoData[0]);
    5412          32 :         memset(&m_pabyNoData[0], 0, m_dt.GetSize());
    5413             : 
    5414          64 :         std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5415          64 :         comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5416          64 :             new GDALEDTComponent(m_srcCompName, 0, m_dt)));
    5417          32 :         auto tmpDT(GDALExtendedDataType::Create(std::string(), m_dt.GetSize(),
    5418          96 :                                                 std::move(comps)));
    5419             : 
    5420          32 :         GDALExtendedDataType::CopyValue(parentNoData, m_poParent->GetDataType(),
    5421          32 :                                         &m_pabyNoData[0], tmpDT);
    5422             : 
    5423          32 :         return &m_pabyNoData[0];
    5424             :     }
    5425             : 
    5426           2 :     double GetOffset(bool *pbHasOffset,
    5427             :                      GDALDataType *peStorageType) const override
    5428             :     {
    5429           2 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5430             :     }
    5431             : 
    5432           2 :     double GetScale(bool *pbHasScale,
    5433             :                     GDALDataType *peStorageType) const override
    5434             :     {
    5435           2 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5436             :     }
    5437             : 
    5438          23 :     std::vector<GUInt64> GetBlockSize() const override
    5439             :     {
    5440          23 :         return m_poParent->GetBlockSize();
    5441             :     }
    5442             : };
    5443             : 
    5444             : /************************************************************************/
    5445             : /*                             IRead()                                  */
    5446             : /************************************************************************/
    5447             : 
    5448          38 : bool GDALExtractFieldMDArray::IRead(const GUInt64 *arrayStartIdx,
    5449             :                                     const size_t *count,
    5450             :                                     const GInt64 *arrayStep,
    5451             :                                     const GPtrDiff_t *bufferStride,
    5452             :                                     const GDALExtendedDataType &bufferDataType,
    5453             :                                     void *pDstBuffer) const
    5454             : {
    5455          76 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    5456          76 :     comps.emplace_back(std::unique_ptr<GDALEDTComponent>(
    5457          76 :         new GDALEDTComponent(m_srcCompName, 0, bufferDataType)));
    5458             :     auto tmpDT(GDALExtendedDataType::Create(
    5459          76 :         std::string(), bufferDataType.GetSize(), std::move(comps)));
    5460             : 
    5461          38 :     return m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    5462          76 :                             tmpDT, pDstBuffer);
    5463             : }
    5464             : 
    5465             : /************************************************************************/
    5466             : /*                      CreateFieldNameExtractArray()                   */
    5467             : /************************************************************************/
    5468             : 
    5469             : static std::shared_ptr<GDALMDArray>
    5470          43 : CreateFieldNameExtractArray(const std::shared_ptr<GDALMDArray> &self,
    5471             :                             const std::string &fieldName)
    5472             : {
    5473          43 :     CPLAssert(self->GetDataType().GetClass() == GEDTC_COMPOUND);
    5474          43 :     const std::unique_ptr<GDALEDTComponent> *srcComp = nullptr;
    5475          91 :     for (const auto &comp : self->GetDataType().GetComponents())
    5476             :     {
    5477          90 :         if (comp->GetName() == fieldName)
    5478             :         {
    5479          42 :             srcComp = &comp;
    5480          42 :             break;
    5481             :         }
    5482             :     }
    5483          43 :     if (srcComp == nullptr)
    5484             :     {
    5485           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find field %s",
    5486             :                  fieldName.c_str());
    5487           1 :         return nullptr;
    5488             :     }
    5489          42 :     return GDALExtractFieldMDArray::Create(self, fieldName, *srcComp);
    5490             : }
    5491             : 
    5492             : /************************************************************************/
    5493             : /*                             GetView()                                */
    5494             : /************************************************************************/
    5495             : 
    5496             : // clang-format off
    5497             : /** Return a view of the array using slicing or field access.
    5498             :  *
    5499             :  * The slice expression uses the same syntax as NumPy basic slicing and
    5500             :  * indexing. See
    5501             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#basic-slicing-and-indexing
    5502             :  * Or it can use field access by name. See
    5503             :  * https://www.numpy.org/devdocs/reference/arrays.indexing.html#field-access
    5504             :  *
    5505             :  * Multiple [] bracket elements can be concatenated, with a slice expression
    5506             :  * or field name inside each.
    5507             :  *
    5508             :  * For basic slicing and indexing, inside each [] bracket element, a list of
    5509             :  * indexes that apply to successive source dimensions, can be specified, using
    5510             :  * integer indexing (e.g. 1), range indexing (start:stop:step), ellipsis (...)
    5511             :  * or newaxis, using a comma separator.
    5512             :  *
    5513             :  * Examples with a 2-dimensional array whose content is [[0,1,2,3],[4,5,6,7]].
    5514             :  * <ul>
    5515             :  * <li>GetView("[1][2]"): returns a 0-dimensional/scalar array with the value
    5516             :  *     at index 1 in the first dimension, and index 2 in the second dimension
    5517             :  *     from the source array. That is 5</li>
    5518             :  * <li>GetView("[1]")->GetView("[2]"): same as above. Above is actually
    5519             :  * implemented internally doing this intermediate slicing approach.</li>
    5520             :  * <li>GetView("[1,2]"): same as above, but a bit more performant.</li>
    5521             :  * <li>GetView("[1]"): returns a 1-dimensional array, sliced at index 1 in the
    5522             :  *     first dimension. That is [4,5,6,7].</li>
    5523             :  * <li>GetView("[:,2]"): returns a 1-dimensional array, sliced at index 2 in the
    5524             :  *     second dimension. That is [2,6].</li>
    5525             :  * <li>GetView("[:,2:3:]"): returns a 2-dimensional array, sliced at index 2 in
    5526             :  * the second dimension. That is [[2],[6]].</li>
    5527             :  * <li>GetView("[::,2]"): Same as
    5528             :  * above.</li> <li>GetView("[...,2]"): same as above, in that case, since the
    5529             :  * ellipsis only expands to one dimension here.</li>
    5530             :  * <li>GetView("[:,::2]"):
    5531             :  * returns a 2-dimensional array, with even-indexed elements of the second
    5532             :  * dimension. That is [[0,2],[4,6]].</li>
    5533             :  * <li>GetView("[:,1::2]"): returns a
    5534             :  * 2-dimensional array, with odd-indexed elements of the second dimension. That
    5535             :  * is [[1,3],[5,7]].</li>
    5536             :  * <li>GetView("[:,1:3:]"): returns a 2-dimensional
    5537             :  * array, with elements of the second dimension with index in the range [1,3[.
    5538             :  * That is [[1,2],[5,6]].</li>
    5539             :  * <li>GetView("[::-1,:]"): returns a 2-dimensional
    5540             :  * array, with the values in first dimension reversed. That is
    5541             :  * [[4,5,6,7],[0,1,2,3]].</li>
    5542             :  * <li>GetView("[newaxis,...]"): returns a
    5543             :  * 3-dimensional array, with an addditional dimension of size 1 put at the
    5544             :  * beginning. That is [[[0,1,2,3],[4,5,6,7]]].</li>
    5545             :  * </ul>
    5546             :  *
    5547             :  * One difference with NumPy behavior is that ranges that would result in
    5548             :  * zero elements are not allowed (dimensions of size 0 not being allowed in the
    5549             :  * GDAL multidimensional model).
    5550             :  *
    5551             :  * For field access, the syntax to use is ["field_name"] or ['field_name'].
    5552             :  * Multiple field specification is not supported currently.
    5553             :  *
    5554             :  * Both type of access can be combined, e.g. GetView("[1]['field_name']")
    5555             :  *
    5556             :  * \note When using the GDAL Python bindings, natural Python syntax can be
    5557             :  * used. That is ar[0,::,1]["foo"] will be internally translated to
    5558             :  * ar.GetView("[0,::,1]['foo']")
    5559             :  * \note When using the C++ API and integer indexing only, you may use the
    5560             :  * at(idx0, idx1, ...) method.
    5561             :  *
    5562             :  * The returned array holds a reference to the original one, and thus is
    5563             :  * a view of it (not a copy). If the content of the original array changes,
    5564             :  * the content of the view array too. When using basic slicing and indexing,
    5565             :  * the view can be written if the underlying array is writable.
    5566             :  *
    5567             :  * This is the same as the C function GDALMDArrayGetView()
    5568             :  *
    5569             :  * @param viewExpr Expression expressing basic slicing and indexing, or field
    5570             :  * access.
    5571             :  * @return a new array, that holds a reference to the original one, and thus is
    5572             :  * a view of it (not a copy), or nullptr in case of error.
    5573             :  */
    5574             : // clang-format on
    5575             : std::shared_ptr<GDALMDArray>
    5576         566 : GDALMDArray::GetView(const std::string &viewExpr) const
    5577             : {
    5578        1132 :     std::vector<ViewSpec> viewSpecs;
    5579        1132 :     return GetView(viewExpr, true, viewSpecs);
    5580             : }
    5581             : 
    5582             : //! @cond Doxygen_Suppress
    5583             : std::shared_ptr<GDALMDArray>
    5584         629 : GDALMDArray::GetView(const std::string &viewExpr, bool bRenameDimensions,
    5585             :                      std::vector<ViewSpec> &viewSpecs) const
    5586             : {
    5587        1258 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    5588         629 :     if (!self)
    5589             :     {
    5590           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    5591             :                  "Driver implementation issue: m_pSelf not set !");
    5592           1 :         return nullptr;
    5593             :     }
    5594         628 :     std::string curExpr(viewExpr);
    5595             :     while (true)
    5596             :     {
    5597         631 :         if (curExpr.empty() || curExpr[0] != '[')
    5598             :         {
    5599           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    5600             :                      "Slice string should start with ['");
    5601         628 :             return nullptr;
    5602             :         }
    5603             : 
    5604         629 :         std::string fieldName;
    5605             :         size_t endExpr;
    5606         629 :         if (curExpr.size() > 2 && (curExpr[1] == '"' || curExpr[1] == '\''))
    5607             :         {
    5608          47 :             if (self->GetDataType().GetClass() != GEDTC_COMPOUND)
    5609             :             {
    5610           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5611             :                          "Field access not allowed on non-compound data type");
    5612           2 :                 return nullptr;
    5613             :             }
    5614          45 :             size_t idx = 2;
    5615         402 :             for (; idx < curExpr.size(); idx++)
    5616             :             {
    5617         401 :                 const char ch = curExpr[idx];
    5618         401 :                 if (ch == curExpr[1])
    5619          44 :                     break;
    5620         357 :                 if (ch == '\\' && idx + 1 < curExpr.size())
    5621             :                 {
    5622           1 :                     fieldName += curExpr[idx + 1];
    5623           1 :                     idx++;
    5624             :                 }
    5625             :                 else
    5626             :                 {
    5627         356 :                     fieldName += ch;
    5628             :                 }
    5629             :             }
    5630          45 :             if (idx + 1 >= curExpr.size() || curExpr[idx + 1] != ']')
    5631             :             {
    5632           2 :                 CPLError(CE_Failure, CPLE_AppDefined,
    5633             :                          "Invalid field access specification");
    5634           2 :                 return nullptr;
    5635             :             }
    5636          43 :             endExpr = idx + 1;
    5637             :         }
    5638             :         else
    5639             :         {
    5640         582 :             endExpr = curExpr.find(']');
    5641             :         }
    5642         625 :         if (endExpr == std::string::npos)
    5643             :         {
    5644           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Missing ]'");
    5645           1 :             return nullptr;
    5646             :         }
    5647         624 :         if (endExpr == 1)
    5648             :         {
    5649           1 :             CPLError(CE_Failure, CPLE_AppDefined, "[] not allowed");
    5650           1 :             return nullptr;
    5651             :         }
    5652         623 :         std::string activeSlice(curExpr.substr(1, endExpr - 1));
    5653             : 
    5654         623 :         if (!fieldName.empty())
    5655             :         {
    5656          86 :             ViewSpec viewSpec;
    5657          43 :             viewSpec.m_osFieldName = fieldName;
    5658          43 :             viewSpecs.emplace_back(std::move(viewSpec));
    5659             :         }
    5660             : 
    5661         623 :         auto newArray = !fieldName.empty()
    5662             :                             ? CreateFieldNameExtractArray(self, fieldName)
    5663             :                             : CreateSlicedArray(self, viewExpr, activeSlice,
    5664         623 :                                                 bRenameDimensions, viewSpecs);
    5665             : 
    5666         623 :         if (endExpr == curExpr.size() - 1)
    5667             :         {
    5668         620 :             return newArray;
    5669             :         }
    5670           3 :         self = std::move(newArray);
    5671           3 :         curExpr = curExpr.substr(endExpr + 1);
    5672           3 :     }
    5673             : }
    5674             : 
    5675             : //! @endcond
    5676             : 
    5677             : std::shared_ptr<GDALMDArray>
    5678          19 : GDALMDArray::GetView(const std::vector<GUInt64> &indices) const
    5679             : {
    5680          19 :     std::string osExpr("[");
    5681          19 :     bool bFirst = true;
    5682          45 :     for (const auto &idx : indices)
    5683             :     {
    5684          26 :         if (!bFirst)
    5685           7 :             osExpr += ',';
    5686          26 :         bFirst = false;
    5687          26 :         osExpr += CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(idx));
    5688             :     }
    5689          57 :     return GetView(osExpr + ']');
    5690             : }
    5691             : 
    5692             : /************************************************************************/
    5693             : /*                            operator[]                                */
    5694             : /************************************************************************/
    5695             : 
    5696             : /** Return a view of the array using field access
    5697             :  *
    5698             :  * Equivalent of GetView("['fieldName']")
    5699             :  *
    5700             :  * \note When operationg on a shared_ptr, use (*array)["fieldName"] syntax.
    5701             :  */
    5702             : std::shared_ptr<GDALMDArray>
    5703           2 : GDALMDArray::operator[](const std::string &fieldName) const
    5704             : {
    5705           2 :     return GetView(CPLSPrintf("['%s']", CPLString(fieldName)
    5706           4 :                                             .replaceAll('\\', "\\\\")
    5707           4 :                                             .replaceAll('\'', "\\\'")
    5708           6 :                                             .c_str()));
    5709             : }
    5710             : 
    5711             : /************************************************************************/
    5712             : /*                      GDALMDArrayTransposed                           */
    5713             : /************************************************************************/
    5714             : 
    5715             : class GDALMDArrayTransposed final : public GDALPamMDArray
    5716             : {
    5717             :   private:
    5718             :     std::shared_ptr<GDALMDArray> m_poParent{};
    5719             :     std::vector<int> m_anMapNewAxisToOldAxis{};
    5720             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    5721             : 
    5722             :     mutable std::vector<GUInt64> m_parentStart;
    5723             :     mutable std::vector<size_t> m_parentCount;
    5724             :     mutable std::vector<GInt64> m_parentStep;
    5725             :     mutable std::vector<GPtrDiff_t> m_parentStride;
    5726             : 
    5727             :     void PrepareParentArrays(const GUInt64 *arrayStartIdx, const size_t *count,
    5728             :                              const GInt64 *arrayStep,
    5729             :                              const GPtrDiff_t *bufferStride) const;
    5730             : 
    5731             :     static std::string
    5732          84 :     MappingToStr(const std::vector<int> &anMapNewAxisToOldAxis)
    5733             :     {
    5734          84 :         std::string ret;
    5735          84 :         ret += '[';
    5736         312 :         for (size_t i = 0; i < anMapNewAxisToOldAxis.size(); ++i)
    5737             :         {
    5738         228 :             if (i > 0)
    5739         144 :                 ret += ',';
    5740         228 :             ret += CPLSPrintf("%d", anMapNewAxisToOldAxis[i]);
    5741             :         }
    5742          84 :         ret += ']';
    5743          84 :         return ret;
    5744             :     }
    5745             : 
    5746             :   protected:
    5747          42 :     GDALMDArrayTransposed(const std::shared_ptr<GDALMDArray> &poParent,
    5748             :                           const std::vector<int> &anMapNewAxisToOldAxis,
    5749             :                           std::vector<std::shared_ptr<GDALDimension>> &&dims)
    5750          84 :         : GDALAbstractMDArray(std::string(),
    5751          84 :                               "Transposed view of " + poParent->GetFullName() +
    5752          84 :                                   " along " +
    5753          42 :                                   MappingToStr(anMapNewAxisToOldAxis)),
    5754          84 :           GDALPamMDArray(std::string(),
    5755          84 :                          "Transposed view of " + poParent->GetFullName() +
    5756         168 :                              " along " + MappingToStr(anMapNewAxisToOldAxis),
    5757          84 :                          GDALPamMultiDim::GetPAM(poParent),
    5758             :                          poParent->GetContext()),
    5759          42 :           m_poParent(std::move(poParent)),
    5760             :           m_anMapNewAxisToOldAxis(anMapNewAxisToOldAxis),
    5761          42 :           m_dims(std::move(dims)),
    5762          42 :           m_parentStart(m_poParent->GetDimensionCount()),
    5763          42 :           m_parentCount(m_poParent->GetDimensionCount()),
    5764          42 :           m_parentStep(m_poParent->GetDimensionCount()),
    5765         336 :           m_parentStride(m_poParent->GetDimensionCount())
    5766             :     {
    5767          42 :     }
    5768             : 
    5769             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5770             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5771             :                const GDALExtendedDataType &bufferDataType,
    5772             :                void *pDstBuffer) const override;
    5773             : 
    5774             :     bool IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    5775             :                 const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    5776             :                 const GDALExtendedDataType &bufferDataType,
    5777             :                 const void *pSrcBuffer) override;
    5778             : 
    5779             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    5780             :                      CSLConstList papszOptions) const override;
    5781             : 
    5782             :   public:
    5783             :     static std::shared_ptr<GDALMDArrayTransposed>
    5784          42 :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    5785             :            const std::vector<int> &anMapNewAxisToOldAxis)
    5786             :     {
    5787          42 :         const auto &parentDims(poParent->GetDimensions());
    5788          84 :         std::vector<std::shared_ptr<GDALDimension>> dims;
    5789         156 :         for (const auto iOldAxis : anMapNewAxisToOldAxis)
    5790             :         {
    5791         114 :             if (iOldAxis < 0)
    5792             :             {
    5793           1 :                 dims.push_back(std::make_shared<GDALDimension>(
    5794           2 :                     std::string(), "newaxis", std::string(), std::string(), 1));
    5795             :             }
    5796             :             else
    5797             :             {
    5798         113 :                 dims.emplace_back(parentDims[iOldAxis]);
    5799             :             }
    5800             :         }
    5801             : 
    5802             :         auto newAr(
    5803             :             std::shared_ptr<GDALMDArrayTransposed>(new GDALMDArrayTransposed(
    5804          42 :                 poParent, anMapNewAxisToOldAxis, std::move(dims))));
    5805          42 :         newAr->SetSelf(newAr);
    5806          84 :         return newAr;
    5807             :     }
    5808             : 
    5809           1 :     bool IsWritable() const override
    5810             :     {
    5811           1 :         return m_poParent->IsWritable();
    5812             :     }
    5813             : 
    5814          84 :     const std::string &GetFilename() const override
    5815             :     {
    5816          84 :         return m_poParent->GetFilename();
    5817             :     }
    5818             : 
    5819             :     const std::vector<std::shared_ptr<GDALDimension>> &
    5820         358 :     GetDimensions() const override
    5821             :     {
    5822         358 :         return m_dims;
    5823             :     }
    5824             : 
    5825         141 :     const GDALExtendedDataType &GetDataType() const override
    5826             :     {
    5827         141 :         return m_poParent->GetDataType();
    5828             :     }
    5829             : 
    5830           4 :     const std::string &GetUnit() const override
    5831             :     {
    5832           4 :         return m_poParent->GetUnit();
    5833             :     }
    5834             : 
    5835           5 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    5836             :     {
    5837          10 :         auto poSrcSRS = m_poParent->GetSpatialRef();
    5838           5 :         if (!poSrcSRS)
    5839           2 :             return nullptr;
    5840           6 :         auto srcMapping = poSrcSRS->GetDataAxisToSRSAxisMapping();
    5841           6 :         std::vector<int> dstMapping;
    5842           9 :         for (int srcAxis : srcMapping)
    5843             :         {
    5844           6 :             bool bFound = false;
    5845          14 :             for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); i++)
    5846             :             {
    5847          14 :                 if (m_anMapNewAxisToOldAxis[i] == srcAxis - 1)
    5848             :                 {
    5849           6 :                     dstMapping.push_back(static_cast<int>(i) + 1);
    5850           6 :                     bFound = true;
    5851           6 :                     break;
    5852             :                 }
    5853             :             }
    5854           6 :             if (!bFound)
    5855             :             {
    5856           0 :                 dstMapping.push_back(0);
    5857             :             }
    5858             :         }
    5859           6 :         auto poClone(std::shared_ptr<OGRSpatialReference>(poSrcSRS->Clone()));
    5860           3 :         poClone->SetDataAxisToSRSAxisMapping(dstMapping);
    5861           3 :         return poClone;
    5862             :     }
    5863             : 
    5864           4 :     const void *GetRawNoDataValue() const override
    5865             :     {
    5866           4 :         return m_poParent->GetRawNoDataValue();
    5867             :     }
    5868             : 
    5869             :     // bool SetRawNoDataValue(const void* pRawNoData) override { return
    5870             :     // m_poParent->SetRawNoDataValue(pRawNoData); }
    5871             : 
    5872           4 :     double GetOffset(bool *pbHasOffset,
    5873             :                      GDALDataType *peStorageType) const override
    5874             :     {
    5875           4 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    5876             :     }
    5877             : 
    5878           4 :     double GetScale(bool *pbHasScale,
    5879             :                     GDALDataType *peStorageType) const override
    5880             :     {
    5881           4 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    5882             :     }
    5883             : 
    5884             :     // bool SetOffset(double dfOffset) override { return
    5885             :     // m_poParent->SetOffset(dfOffset); }
    5886             : 
    5887             :     // bool SetScale(double dfScale) override { return
    5888             :     // m_poParent->SetScale(dfScale); }
    5889             : 
    5890           3 :     std::vector<GUInt64> GetBlockSize() const override
    5891             :     {
    5892           3 :         std::vector<GUInt64> ret(GetDimensionCount());
    5893           6 :         const auto parentBlockSize(m_poParent->GetBlockSize());
    5894          11 :         for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    5895             :         {
    5896           8 :             const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    5897           8 :             if (iOldAxis >= 0)
    5898             :             {
    5899           7 :                 ret[i] = parentBlockSize[iOldAxis];
    5900             :             }
    5901             :         }
    5902           6 :         return ret;
    5903             :     }
    5904             : 
    5905             :     std::shared_ptr<GDALAttribute>
    5906           1 :     GetAttribute(const std::string &osName) const override
    5907             :     {
    5908           1 :         return m_poParent->GetAttribute(osName);
    5909             :     }
    5910             : 
    5911             :     std::vector<std::shared_ptr<GDALAttribute>>
    5912           6 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    5913             :     {
    5914           6 :         return m_poParent->GetAttributes(papszOptions);
    5915             :     }
    5916             : };
    5917             : 
    5918             : /************************************************************************/
    5919             : /*                         PrepareParentArrays()                        */
    5920             : /************************************************************************/
    5921             : 
    5922          47 : void GDALMDArrayTransposed::PrepareParentArrays(
    5923             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    5924             :     const GPtrDiff_t *bufferStride) const
    5925             : {
    5926         176 :     for (size_t i = 0; i < m_anMapNewAxisToOldAxis.size(); ++i)
    5927             :     {
    5928         129 :         const auto iOldAxis = m_anMapNewAxisToOldAxis[i];
    5929         129 :         if (iOldAxis >= 0)
    5930             :         {
    5931         128 :             m_parentStart[iOldAxis] = arrayStartIdx[i];
    5932         128 :             m_parentCount[iOldAxis] = count[i];
    5933         128 :             if (arrayStep)  // only null when called from IAdviseRead()
    5934             :             {
    5935         126 :                 m_parentStep[iOldAxis] = arrayStep[i];
    5936             :             }
    5937         128 :             if (bufferStride)  // only null when called from IAdviseRead()
    5938             :             {
    5939         126 :                 m_parentStride[iOldAxis] = bufferStride[i];
    5940             :             }
    5941             :         }
    5942             :     }
    5943          47 : }
    5944             : 
    5945             : /************************************************************************/
    5946             : /*                             IRead()                                  */
    5947             : /************************************************************************/
    5948             : 
    5949          44 : bool GDALMDArrayTransposed::IRead(const GUInt64 *arrayStartIdx,
    5950             :                                   const size_t *count, const GInt64 *arrayStep,
    5951             :                                   const GPtrDiff_t *bufferStride,
    5952             :                                   const GDALExtendedDataType &bufferDataType,
    5953             :                                   void *pDstBuffer) const
    5954             : {
    5955          44 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5956          88 :     return m_poParent->Read(m_parentStart.data(), m_parentCount.data(),
    5957          44 :                             m_parentStep.data(), m_parentStride.data(),
    5958          44 :                             bufferDataType, pDstBuffer);
    5959             : }
    5960             : 
    5961             : /************************************************************************/
    5962             : /*                            IWrite()                                  */
    5963             : /************************************************************************/
    5964             : 
    5965           2 : bool GDALMDArrayTransposed::IWrite(const GUInt64 *arrayStartIdx,
    5966             :                                    const size_t *count, const GInt64 *arrayStep,
    5967             :                                    const GPtrDiff_t *bufferStride,
    5968             :                                    const GDALExtendedDataType &bufferDataType,
    5969             :                                    const void *pSrcBuffer)
    5970             : {
    5971           2 :     PrepareParentArrays(arrayStartIdx, count, arrayStep, bufferStride);
    5972           4 :     return m_poParent->Write(m_parentStart.data(), m_parentCount.data(),
    5973           2 :                              m_parentStep.data(), m_parentStride.data(),
    5974           2 :                              bufferDataType, pSrcBuffer);
    5975             : }
    5976             : 
    5977             : /************************************************************************/
    5978             : /*                             IAdviseRead()                            */
    5979             : /************************************************************************/
    5980             : 
    5981           1 : bool GDALMDArrayTransposed::IAdviseRead(const GUInt64 *arrayStartIdx,
    5982             :                                         const size_t *count,
    5983             :                                         CSLConstList papszOptions) const
    5984             : {
    5985           1 :     PrepareParentArrays(arrayStartIdx, count, nullptr, nullptr);
    5986           1 :     return m_poParent->AdviseRead(m_parentStart.data(), m_parentCount.data(),
    5987           1 :                                   papszOptions);
    5988             : }
    5989             : 
    5990             : /************************************************************************/
    5991             : /*                           Transpose()                                */
    5992             : /************************************************************************/
    5993             : 
    5994             : /** Return a view of the array whose axis have been reordered.
    5995             :  *
    5996             :  * The anMapNewAxisToOldAxis parameter should contain all the values between 0
    5997             :  * and GetDimensionCount() - 1, and each only once.
    5998             :  * -1 can be used as a special index value to ask for the insertion of a new
    5999             :  * axis of size 1.
    6000             :  * The new array will have anMapNewAxisToOldAxis.size() axis, and if i is the
    6001             :  * index of one of its dimension, it corresponds to the axis of index
    6002             :  * anMapNewAxisToOldAxis[i] from the current array.
    6003             :  *
    6004             :  * This is similar to the numpy.transpose() method
    6005             :  *
    6006             :  * The returned array holds a reference to the original one, and thus is
    6007             :  * a view of it (not a copy). If the content of the original array changes,
    6008             :  * the content of the view array too. The view can be written if the underlying
    6009             :  * array is writable.
    6010             :  *
    6011             :  * Note that I/O performance in such a transposed view might be poor.
    6012             :  *
    6013             :  * This is the same as the C function GDALMDArrayTranspose().
    6014             :  *
    6015             :  * @return a new array, that holds a reference to the original one, and thus is
    6016             :  * a view of it (not a copy), or nullptr in case of error.
    6017             :  */
    6018             : std::shared_ptr<GDALMDArray>
    6019          50 : GDALMDArray::Transpose(const std::vector<int> &anMapNewAxisToOldAxis) const
    6020             : {
    6021         100 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6022          50 :     if (!self)
    6023             :     {
    6024           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6025             :                  "Driver implementation issue: m_pSelf not set !");
    6026           0 :         return nullptr;
    6027             :     }
    6028          50 :     const int nDims = static_cast<int>(GetDimensionCount());
    6029         100 :     std::vector<bool> alreadyUsedOldAxis(nDims, false);
    6030          50 :     int nCountOldAxis = 0;
    6031         179 :     for (const auto iOldAxis : anMapNewAxisToOldAxis)
    6032             :     {
    6033         133 :         if (iOldAxis < -1 || iOldAxis >= nDims)
    6034             :         {
    6035           3 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid axis number");
    6036           4 :             return nullptr;
    6037             :         }
    6038         130 :         if (iOldAxis >= 0)
    6039             :         {
    6040         128 :             if (alreadyUsedOldAxis[iOldAxis])
    6041             :             {
    6042           1 :                 CPLError(CE_Failure, CPLE_AppDefined, "Axis %d is repeated",
    6043             :                          iOldAxis);
    6044           1 :                 return nullptr;
    6045             :             }
    6046         127 :             alreadyUsedOldAxis[iOldAxis] = true;
    6047         127 :             nCountOldAxis++;
    6048             :         }
    6049             :     }
    6050          46 :     if (nCountOldAxis != nDims)
    6051             :     {
    6052           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    6053             :                  "One or several original axis missing");
    6054           4 :         return nullptr;
    6055             :     }
    6056          42 :     return GDALMDArrayTransposed::Create(self, anMapNewAxisToOldAxis);
    6057             : }
    6058             : 
    6059             : /************************************************************************/
    6060             : /*                             IRead()                                  */
    6061             : /************************************************************************/
    6062             : 
    6063          16 : bool GDALMDArrayUnscaled::IRead(const GUInt64 *arrayStartIdx,
    6064             :                                 const size_t *count, const GInt64 *arrayStep,
    6065             :                                 const GPtrDiff_t *bufferStride,
    6066             :                                 const GDALExtendedDataType &bufferDataType,
    6067             :                                 void *pDstBuffer) const
    6068             : {
    6069          16 :     const double dfScale = m_dfScale;
    6070          16 :     const double dfOffset = m_dfOffset;
    6071          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6072             :     const auto dtDouble =
    6073          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6074          16 :     const size_t nDTSize = dtDouble.GetSize();
    6075          16 :     const bool bTempBufferNeeded = (dtDouble != bufferDataType);
    6076             : 
    6077          16 :     double adfSrcNoData[2] = {0, 0};
    6078          16 :     if (m_bHasNoData)
    6079             :     {
    6080           9 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6081           9 :                                         m_poParent->GetDataType(),
    6082             :                                         &adfSrcNoData[0], dtDouble);
    6083             :     }
    6084             : 
    6085          16 :     const auto nDims = GetDimensions().size();
    6086          16 :     if (nDims == 0)
    6087             :     {
    6088             :         double adfVal[2];
    6089           9 :         if (!m_poParent->Read(arrayStartIdx, count, arrayStep, bufferStride,
    6090             :                               dtDouble, &adfVal[0]))
    6091             :         {
    6092           0 :             return false;
    6093             :         }
    6094           9 :         if (!m_bHasNoData || adfVal[0] != adfSrcNoData[0])
    6095             :         {
    6096           6 :             adfVal[0] = adfVal[0] * dfScale + dfOffset;
    6097           6 :             if (bDTIsComplex)
    6098             :             {
    6099           2 :                 adfVal[1] = adfVal[1] * dfScale + dfOffset;
    6100             :             }
    6101           6 :             GDALExtendedDataType::CopyValue(&adfVal[0], dtDouble, pDstBuffer,
    6102             :                                             bufferDataType);
    6103             :         }
    6104             :         else
    6105             :         {
    6106           3 :             GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt,
    6107             :                                             pDstBuffer, bufferDataType);
    6108             :         }
    6109           9 :         return true;
    6110             :     }
    6111             : 
    6112          14 :     std::vector<GPtrDiff_t> actualBufferStrideVector;
    6113           7 :     const GPtrDiff_t *actualBufferStridePtr = bufferStride;
    6114           7 :     void *pTempBuffer = pDstBuffer;
    6115           7 :     if (bTempBufferNeeded)
    6116             :     {
    6117           2 :         size_t nElts = 1;
    6118           2 :         actualBufferStrideVector.resize(nDims);
    6119           7 :         for (size_t i = 0; i < nDims; i++)
    6120           5 :             nElts *= count[i];
    6121           2 :         actualBufferStrideVector.back() = 1;
    6122           5 :         for (size_t i = nDims - 1; i > 0;)
    6123             :         {
    6124           3 :             --i;
    6125           3 :             actualBufferStrideVector[i] =
    6126           3 :                 actualBufferStrideVector[i + 1] * count[i + 1];
    6127             :         }
    6128           2 :         actualBufferStridePtr = actualBufferStrideVector.data();
    6129           2 :         pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6130           2 :         if (!pTempBuffer)
    6131           0 :             return false;
    6132             :     }
    6133           7 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6134             :                           actualBufferStridePtr, dtDouble, pTempBuffer))
    6135             :     {
    6136           0 :         if (bTempBufferNeeded)
    6137           0 :             VSIFree(pTempBuffer);
    6138           0 :         return false;
    6139             :     }
    6140             : 
    6141             :     struct Stack
    6142             :     {
    6143             :         size_t nIters = 0;
    6144             :         double *src_ptr = nullptr;
    6145             :         GByte *dst_ptr = nullptr;
    6146             :         GPtrDiff_t src_inc_offset = 0;
    6147             :         GPtrDiff_t dst_inc_offset = 0;
    6148             :     };
    6149             : 
    6150           7 :     std::vector<Stack> stack(nDims);
    6151           7 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6152          23 :     for (size_t i = 0; i < nDims; i++)
    6153             :     {
    6154          32 :         stack[i].src_inc_offset =
    6155          16 :             actualBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6156          16 :         stack[i].dst_inc_offset =
    6157          16 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6158             :     }
    6159           7 :     stack[0].src_ptr = static_cast<double *>(pTempBuffer);
    6160           7 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6161             : 
    6162           7 :     size_t dimIdx = 0;
    6163           7 :     const size_t nDimsMinus1 = nDims - 1;
    6164             :     GByte abyDstNoData[16];
    6165           7 :     CPLAssert(nBufferDTSize <= sizeof(abyDstNoData));
    6166           7 :     GDALExtendedDataType::CopyValue(m_abyRawNoData.data(), m_dt, abyDstNoData,
    6167             :                                     bufferDataType);
    6168             : 
    6169          37 : lbl_next_depth:
    6170          37 :     if (dimIdx == nDimsMinus1)
    6171             :     {
    6172          25 :         auto nIters = count[dimIdx];
    6173          25 :         double *padfVal = stack[dimIdx].src_ptr;
    6174          25 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6175             :         while (true)
    6176             :         {
    6177          92 :             if (!m_bHasNoData || padfVal[0] != adfSrcNoData[0])
    6178             :             {
    6179          88 :                 padfVal[0] = padfVal[0] * dfScale + dfOffset;
    6180          88 :                 if (bDTIsComplex)
    6181             :                 {
    6182           1 :                     padfVal[1] = padfVal[1] * dfScale + dfOffset;
    6183             :                 }
    6184          88 :                 if (bTempBufferNeeded)
    6185             :                 {
    6186          29 :                     GDALExtendedDataType::CopyValue(&padfVal[0], dtDouble,
    6187             :                                                     dst_ptr, bufferDataType);
    6188             :                 }
    6189             :             }
    6190             :             else
    6191             :             {
    6192           4 :                 memcpy(dst_ptr, abyDstNoData, nBufferDTSize);
    6193             :             }
    6194             : 
    6195          92 :             if ((--nIters) == 0)
    6196          25 :                 break;
    6197          67 :             padfVal += stack[dimIdx].src_inc_offset;
    6198          67 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6199             :         }
    6200             :     }
    6201             :     else
    6202             :     {
    6203          12 :         stack[dimIdx].nIters = count[dimIdx];
    6204             :         while (true)
    6205             :         {
    6206          30 :             dimIdx++;
    6207          30 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6208          30 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6209          30 :             goto lbl_next_depth;
    6210          30 :         lbl_return_to_caller:
    6211          30 :             dimIdx--;
    6212          30 :             if ((--stack[dimIdx].nIters) == 0)
    6213          12 :                 break;
    6214          18 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6215          18 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6216             :         }
    6217             :     }
    6218          37 :     if (dimIdx > 0)
    6219          30 :         goto lbl_return_to_caller;
    6220             : 
    6221           7 :     if (bTempBufferNeeded)
    6222           2 :         VSIFree(pTempBuffer);
    6223           7 :     return true;
    6224             : }
    6225             : 
    6226             : /************************************************************************/
    6227             : /*                             IWrite()                                 */
    6228             : /************************************************************************/
    6229             : 
    6230          16 : bool GDALMDArrayUnscaled::IWrite(const GUInt64 *arrayStartIdx,
    6231             :                                  const size_t *count, const GInt64 *arrayStep,
    6232             :                                  const GPtrDiff_t *bufferStride,
    6233             :                                  const GDALExtendedDataType &bufferDataType,
    6234             :                                  const void *pSrcBuffer)
    6235             : {
    6236          16 :     const double dfScale = m_dfScale;
    6237          16 :     const double dfOffset = m_dfOffset;
    6238          16 :     const bool bDTIsComplex = GDALDataTypeIsComplex(m_dt.GetNumericDataType());
    6239             :     const auto dtDouble =
    6240          32 :         GDALExtendedDataType::Create(bDTIsComplex ? GDT_CFloat64 : GDT_Float64);
    6241          16 :     const size_t nDTSize = dtDouble.GetSize();
    6242          16 :     const bool bIsBufferDataTypeNativeDataType = (dtDouble == bufferDataType);
    6243             :     const bool bSelfAndParentHaveNoData =
    6244          16 :         m_bHasNoData && m_poParent->GetRawNoDataValue() != nullptr;
    6245          16 :     double dfNoData = 0;
    6246          16 :     if (m_bHasNoData)
    6247             :     {
    6248           7 :         GDALCopyWords(m_abyRawNoData.data(), m_dt.GetNumericDataType(), 0,
    6249             :                       &dfNoData, GDT_Float64, 0, 1);
    6250             :     }
    6251             : 
    6252          16 :     double adfSrcNoData[2] = {0, 0};
    6253          16 :     if (bSelfAndParentHaveNoData)
    6254             :     {
    6255           7 :         GDALExtendedDataType::CopyValue(m_poParent->GetRawNoDataValue(),
    6256           7 :                                         m_poParent->GetDataType(),
    6257             :                                         &adfSrcNoData[0], dtDouble);
    6258             :     }
    6259             : 
    6260          16 :     const auto nDims = GetDimensions().size();
    6261          16 :     if (nDims == 0)
    6262             :     {
    6263             :         double adfVal[2];
    6264          10 :         GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &adfVal[0],
    6265             :                                         dtDouble);
    6266          16 :         if (bSelfAndParentHaveNoData &&
    6267           6 :             (std::isnan(adfVal[0]) || adfVal[0] == dfNoData))
    6268             :         {
    6269           4 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6270           2 :                                      bufferStride, m_poParent->GetDataType(),
    6271           4 :                                      m_poParent->GetRawNoDataValue());
    6272             :         }
    6273             :         else
    6274             :         {
    6275           8 :             adfVal[0] = (adfVal[0] - dfOffset) / dfScale;
    6276           8 :             if (bDTIsComplex)
    6277             :             {
    6278           2 :                 adfVal[1] = (adfVal[1] - dfOffset) / dfScale;
    6279             :             }
    6280           8 :             return m_poParent->Write(arrayStartIdx, count, arrayStep,
    6281           8 :                                      bufferStride, dtDouble, &adfVal[0]);
    6282             :         }
    6283             :     }
    6284             : 
    6285          12 :     std::vector<GPtrDiff_t> tmpBufferStrideVector;
    6286           6 :     size_t nElts = 1;
    6287           6 :     tmpBufferStrideVector.resize(nDims);
    6288          20 :     for (size_t i = 0; i < nDims; i++)
    6289          14 :         nElts *= count[i];
    6290           6 :     tmpBufferStrideVector.back() = 1;
    6291          14 :     for (size_t i = nDims - 1; i > 0;)
    6292             :     {
    6293           8 :         --i;
    6294           8 :         tmpBufferStrideVector[i] = tmpBufferStrideVector[i + 1] * count[i + 1];
    6295             :     }
    6296           6 :     const GPtrDiff_t *tmpBufferStridePtr = tmpBufferStrideVector.data();
    6297           6 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nDTSize, nElts);
    6298           6 :     if (!pTempBuffer)
    6299           0 :         return false;
    6300             : 
    6301             :     struct Stack
    6302             :     {
    6303             :         size_t nIters = 0;
    6304             :         double *dst_ptr = nullptr;
    6305             :         const GByte *src_ptr = nullptr;
    6306             :         GPtrDiff_t src_inc_offset = 0;
    6307             :         GPtrDiff_t dst_inc_offset = 0;
    6308             :     };
    6309             : 
    6310           6 :     std::vector<Stack> stack(nDims);
    6311           6 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    6312          20 :     for (size_t i = 0; i < nDims; i++)
    6313             :     {
    6314          28 :         stack[i].dst_inc_offset =
    6315          14 :             tmpBufferStridePtr[i] * (bDTIsComplex ? 2 : 1);
    6316          14 :         stack[i].src_inc_offset =
    6317          14 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6318             :     }
    6319           6 :     stack[0].dst_ptr = static_cast<double *>(pTempBuffer);
    6320           6 :     stack[0].src_ptr = static_cast<const GByte *>(pSrcBuffer);
    6321             : 
    6322           6 :     size_t dimIdx = 0;
    6323           6 :     const size_t nDimsMinus1 = nDims - 1;
    6324             : 
    6325          34 : lbl_next_depth:
    6326          34 :     if (dimIdx == nDimsMinus1)
    6327             :     {
    6328          23 :         auto nIters = count[dimIdx];
    6329          23 :         double *dst_ptr = stack[dimIdx].dst_ptr;
    6330          23 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    6331             :         while (true)
    6332             :         {
    6333             :             double adfVal[2];
    6334             :             const double *padfSrcVal;
    6335          86 :             if (bIsBufferDataTypeNativeDataType)
    6336             :             {
    6337          50 :                 padfSrcVal = reinterpret_cast<const double *>(src_ptr);
    6338             :             }
    6339             :             else
    6340             :             {
    6341          36 :                 GDALExtendedDataType::CopyValue(src_ptr, bufferDataType,
    6342             :                                                 &adfVal[0], dtDouble);
    6343          36 :                 padfSrcVal = adfVal;
    6344             :             }
    6345             : 
    6346         148 :             if (bSelfAndParentHaveNoData &&
    6347          62 :                 (std::isnan(padfSrcVal[0]) || padfSrcVal[0] == dfNoData))
    6348             :             {
    6349           3 :                 dst_ptr[0] = adfSrcNoData[0];
    6350           3 :                 if (bDTIsComplex)
    6351             :                 {
    6352           1 :                     dst_ptr[1] = adfSrcNoData[1];
    6353             :                 }
    6354             :             }
    6355             :             else
    6356             :             {
    6357          83 :                 dst_ptr[0] = (padfSrcVal[0] - dfOffset) / dfScale;
    6358          83 :                 if (bDTIsComplex)
    6359             :                 {
    6360           1 :                     dst_ptr[1] = (padfSrcVal[1] - dfOffset) / dfScale;
    6361             :                 }
    6362             :             }
    6363             : 
    6364          86 :             if ((--nIters) == 0)
    6365          23 :                 break;
    6366          63 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    6367          63 :             src_ptr += stack[dimIdx].src_inc_offset;
    6368          63 :         }
    6369             :     }
    6370             :     else
    6371             :     {
    6372          11 :         stack[dimIdx].nIters = count[dimIdx];
    6373             :         while (true)
    6374             :         {
    6375          28 :             dimIdx++;
    6376          28 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    6377          28 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6378          28 :             goto lbl_next_depth;
    6379          28 :         lbl_return_to_caller:
    6380          28 :             dimIdx--;
    6381          28 :             if ((--stack[dimIdx].nIters) == 0)
    6382          11 :                 break;
    6383          17 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    6384          17 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6385             :         }
    6386             :     }
    6387          34 :     if (dimIdx > 0)
    6388          28 :         goto lbl_return_to_caller;
    6389             : 
    6390             :     // If the parent array is not double/complex-double, then convert the
    6391             :     // values to it, before calling Write(), as some implementations can be
    6392             :     // very slow when doing the type conversion.
    6393           6 :     const auto &eParentDT = m_poParent->GetDataType();
    6394           6 :     const size_t nParentDTSize = eParentDT.GetSize();
    6395           6 :     if (nParentDTSize <= nDTSize / 2)
    6396             :     {
    6397             :         // Copy in-place by making sure that source and target do not overlap
    6398           6 :         const auto eNumericDT = dtDouble.GetNumericDataType();
    6399           6 :         const auto eParentNumericDT = eParentDT.GetNumericDataType();
    6400             : 
    6401             :         // Copy first element
    6402             :         {
    6403           6 :             std::vector<GByte> abyTemp(nParentDTSize);
    6404           6 :             GDALCopyWords64(static_cast<GByte *>(pTempBuffer), eNumericDT,
    6405           6 :                             static_cast<int>(nDTSize), &abyTemp[0],
    6406             :                             eParentNumericDT, static_cast<int>(nParentDTSize),
    6407             :                             1);
    6408           6 :             memcpy(pTempBuffer, abyTemp.data(), abyTemp.size());
    6409             :         }
    6410             :         // Remaining elements
    6411          86 :         for (size_t i = 1; i < nElts; ++i)
    6412             :         {
    6413          80 :             GDALCopyWords(static_cast<GByte *>(pTempBuffer) + i * nDTSize,
    6414             :                           eNumericDT, 0,
    6415          80 :                           static_cast<GByte *>(pTempBuffer) + i * nParentDTSize,
    6416             :                           eParentNumericDT, 0, 1);
    6417             :         }
    6418             :     }
    6419             : 
    6420             :     const bool ret =
    6421           6 :         m_poParent->Write(arrayStartIdx, count, arrayStep, tmpBufferStridePtr,
    6422             :                           eParentDT, pTempBuffer);
    6423             : 
    6424           6 :     VSIFree(pTempBuffer);
    6425           6 :     return ret;
    6426             : }
    6427             : 
    6428             : /************************************************************************/
    6429             : /*                           GetUnscaled()                              */
    6430             : /************************************************************************/
    6431             : 
    6432             : /** Return an array that is the unscaled version of the current one.
    6433             :  *
    6434             :  * That is each value of the unscaled array will be
    6435             :  * unscaled_value = raw_value * GetScale() + GetOffset()
    6436             :  *
    6437             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
    6438             :  * from unscaled values to raw values.
    6439             :  *
    6440             :  * This is the same as the C function GDALMDArrayGetUnscaled().
    6441             :  *
    6442             :  * @param dfOverriddenScale Custom scale value instead of GetScale()
    6443             :  * @param dfOverriddenOffset Custom offset value instead of GetOffset()
    6444             :  * @param dfOverriddenDstNodata Custom target nodata value instead of NaN
    6445             :  * @return a new array, that holds a reference to the original one, and thus is
    6446             :  * a view of it (not a copy), or nullptr in case of error.
    6447             :  */
    6448             : std::shared_ptr<GDALMDArray>
    6449          17 : GDALMDArray::GetUnscaled(double dfOverriddenScale, double dfOverriddenOffset,
    6450             :                          double dfOverriddenDstNodata) const
    6451             : {
    6452          34 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    6453          17 :     if (!self)
    6454             :     {
    6455           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6456             :                  "Driver implementation issue: m_pSelf not set !");
    6457           0 :         return nullptr;
    6458             :     }
    6459          17 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    6460             :     {
    6461           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    6462             :                  "GetUnscaled() only supports numeric data type");
    6463           0 :         return nullptr;
    6464             :     }
    6465             :     const double dfScale =
    6466          17 :         std::isnan(dfOverriddenScale) ? GetScale() : dfOverriddenScale;
    6467             :     const double dfOffset =
    6468          17 :         std::isnan(dfOverriddenOffset) ? GetOffset() : dfOverriddenOffset;
    6469          17 :     if (dfScale == 1.0 && dfOffset == 0.0)
    6470           4 :         return self;
    6471             : 
    6472          13 :     GDALDataType eDT = GDALDataTypeIsComplex(GetDataType().GetNumericDataType())
    6473          13 :                            ? GDT_CFloat64
    6474          13 :                            : GDT_Float64;
    6475          14 :     if (dfOverriddenScale == -1 && dfOverriddenOffset == 0 &&
    6476           1 :         GetDataType().GetNumericDataType() == GDT_Float32)
    6477           1 :         eDT = GDT_Float32;
    6478             : 
    6479          26 :     return GDALMDArrayUnscaled::Create(self, dfScale, dfOffset,
    6480          13 :                                        dfOverriddenDstNodata, eDT);
    6481             : }
    6482             : 
    6483             : /************************************************************************/
    6484             : /*                         GDALMDArrayMask                              */
    6485             : /************************************************************************/
    6486             : 
    6487             : class GDALMDArrayMask final : public GDALPamMDArray
    6488             : {
    6489             :   private:
    6490             :     std::shared_ptr<GDALMDArray> m_poParent{};
    6491             :     GDALExtendedDataType m_dt{GDALExtendedDataType::Create(GDT_Byte)};
    6492             :     double m_dfMissingValue = 0.0;
    6493             :     bool m_bHasMissingValue = false;
    6494             :     double m_dfFillValue = 0.0;
    6495             :     bool m_bHasFillValue = false;
    6496             :     double m_dfValidMin = 0.0;
    6497             :     bool m_bHasValidMin = false;
    6498             :     double m_dfValidMax = 0.0;
    6499             :     bool m_bHasValidMax = false;
    6500             :     std::vector<uint32_t> m_anValidFlagMasks{};
    6501             :     std::vector<uint32_t> m_anValidFlagValues{};
    6502             : 
    6503             :     bool Init(CSLConstList papszOptions);
    6504             : 
    6505             :     template <typename Type>
    6506             :     void
    6507             :     ReadInternal(const size_t *count, const GPtrDiff_t *bufferStride,
    6508             :                  const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    6509             :                  const void *pTempBuffer,
    6510             :                  const GDALExtendedDataType &oTmpBufferDT,
    6511             :                  const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const;
    6512             : 
    6513             :   protected:
    6514          45 :     explicit GDALMDArrayMask(const std::shared_ptr<GDALMDArray> &poParent)
    6515          90 :         : GDALAbstractMDArray(std::string(),
    6516          90 :                               "Mask of " + poParent->GetFullName()),
    6517          90 :           GDALPamMDArray(std::string(), "Mask of " + poParent->GetFullName(),
    6518          90 :                          GDALPamMultiDim::GetPAM(poParent),
    6519             :                          poParent->GetContext()),
    6520         225 :           m_poParent(std::move(poParent))
    6521             :     {
    6522          45 :     }
    6523             : 
    6524             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6525             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    6526             :                const GDALExtendedDataType &bufferDataType,
    6527             :                void *pDstBuffer) const override;
    6528             : 
    6529           0 :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6530             :                      CSLConstList papszOptions) const override
    6531             :     {
    6532           0 :         return m_poParent->AdviseRead(arrayStartIdx, count, papszOptions);
    6533             :     }
    6534             : 
    6535             :   public:
    6536             :     static std::shared_ptr<GDALMDArrayMask>
    6537             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    6538             :            CSLConstList papszOptions);
    6539             : 
    6540           1 :     bool IsWritable() const override
    6541             :     {
    6542           1 :         return false;
    6543             :     }
    6544             : 
    6545          48 :     const std::string &GetFilename() const override
    6546             :     {
    6547          48 :         return m_poParent->GetFilename();
    6548             :     }
    6549             : 
    6550             :     const std::vector<std::shared_ptr<GDALDimension>> &
    6551         373 :     GetDimensions() const override
    6552             :     {
    6553         373 :         return m_poParent->GetDimensions();
    6554             :     }
    6555             : 
    6556         132 :     const GDALExtendedDataType &GetDataType() const override
    6557             :     {
    6558         132 :         return m_dt;
    6559             :     }
    6560             : 
    6561           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    6562             :     {
    6563           1 :         return m_poParent->GetSpatialRef();
    6564             :     }
    6565             : 
    6566           2 :     std::vector<GUInt64> GetBlockSize() const override
    6567             :     {
    6568           2 :         return m_poParent->GetBlockSize();
    6569             :     }
    6570             : };
    6571             : 
    6572             : /************************************************************************/
    6573             : /*                    GDALMDArrayMask::Create()                         */
    6574             : /************************************************************************/
    6575             : 
    6576             : /* static */ std::shared_ptr<GDALMDArrayMask>
    6577          45 : GDALMDArrayMask::Create(const std::shared_ptr<GDALMDArray> &poParent,
    6578             :                         CSLConstList papszOptions)
    6579             : {
    6580          90 :     auto newAr(std::shared_ptr<GDALMDArrayMask>(new GDALMDArrayMask(poParent)));
    6581          45 :     newAr->SetSelf(newAr);
    6582          45 :     if (!newAr->Init(papszOptions))
    6583           6 :         return nullptr;
    6584          39 :     return newAr;
    6585             : }
    6586             : 
    6587             : /************************************************************************/
    6588             : /*                    GDALMDArrayMask::Init()                           */
    6589             : /************************************************************************/
    6590             : 
    6591          45 : bool GDALMDArrayMask::Init(CSLConstList papszOptions)
    6592             : {
    6593             :     const auto GetSingleValNumericAttr =
    6594         180 :         [this](const char *pszAttrName, bool &bHasVal, double &dfVal)
    6595             :     {
    6596         540 :         auto poAttr = m_poParent->GetAttribute(pszAttrName);
    6597         180 :         if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    6598             :         {
    6599          22 :             const auto anDimSizes = poAttr->GetDimensionsSize();
    6600          21 :             if (anDimSizes.empty() ||
    6601          10 :                 (anDimSizes.size() == 1 && anDimSizes[0] == 1))
    6602             :             {
    6603          11 :                 bHasVal = true;
    6604          11 :                 dfVal = poAttr->ReadAsDouble();
    6605             :             }
    6606             :         }
    6607         180 :     };
    6608             : 
    6609          45 :     GetSingleValNumericAttr("missing_value", m_bHasMissingValue,
    6610          45 :                             m_dfMissingValue);
    6611          45 :     GetSingleValNumericAttr("_FillValue", m_bHasFillValue, m_dfFillValue);
    6612          45 :     GetSingleValNumericAttr("valid_min", m_bHasValidMin, m_dfValidMin);
    6613          45 :     GetSingleValNumericAttr("valid_max", m_bHasValidMax, m_dfValidMax);
    6614             : 
    6615             :     {
    6616         135 :         auto poValidRange = m_poParent->GetAttribute("valid_range");
    6617          50 :         if (poValidRange && poValidRange->GetDimensionsSize().size() == 1 &&
    6618          55 :             poValidRange->GetDimensionsSize()[0] == 2 &&
    6619           5 :             poValidRange->GetDataType().GetClass() == GEDTC_NUMERIC)
    6620             :         {
    6621           5 :             m_bHasValidMin = true;
    6622           5 :             m_bHasValidMax = true;
    6623           5 :             auto vals = poValidRange->ReadAsDoubleArray();
    6624           5 :             CPLAssert(vals.size() == 2);
    6625           5 :             m_dfValidMin = vals[0];
    6626           5 :             m_dfValidMax = vals[1];
    6627             :         }
    6628             :     }
    6629             : 
    6630             :     // Take into account
    6631             :     // https://cfconventions.org/cf-conventions/cf-conventions.html#flags
    6632             :     // Cf GDALMDArray::GetMask() for semantics of UNMASK_FLAGS
    6633             :     const char *pszUnmaskFlags =
    6634          45 :         CSLFetchNameValue(papszOptions, "UNMASK_FLAGS");
    6635          45 :     if (pszUnmaskFlags)
    6636             :     {
    6637             :         const auto IsScalarStringAttr =
    6638          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6639             :         {
    6640          26 :             return poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    6641          26 :                    (poAttr->GetDimensionsSize().empty() ||
    6642          13 :                     (poAttr->GetDimensionsSize().size() == 1 &&
    6643          26 :                      poAttr->GetDimensionsSize()[0] == 1));
    6644             :         };
    6645             : 
    6646          28 :         auto poFlagMeanings = m_poParent->GetAttribute("flag_meanings");
    6647          14 :         if (!(poFlagMeanings && IsScalarStringAttr(poFlagMeanings)))
    6648             :         {
    6649           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6650             :                      "UNMASK_FLAGS option specified but array has no "
    6651             :                      "flag_meanings attribute");
    6652           1 :             return false;
    6653             :         }
    6654          13 :         const char *pszFlagMeanings = poFlagMeanings->ReadAsString();
    6655          13 :         if (!pszFlagMeanings)
    6656             :         {
    6657           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6658             :                      "Cannot read flag_meanings attribute");
    6659           1 :             return false;
    6660             :         }
    6661             : 
    6662             :         const auto IsSingleDimNumericAttr =
    6663          13 :             [](const std::shared_ptr<GDALAttribute> &poAttr)
    6664             :         {
    6665          26 :             return poAttr->GetDataType().GetClass() == GEDTC_NUMERIC &&
    6666          26 :                    poAttr->GetDimensionsSize().size() == 1;
    6667             :         };
    6668             : 
    6669          24 :         auto poFlagValues = m_poParent->GetAttribute("flag_values");
    6670             :         const bool bHasFlagValues =
    6671          12 :             poFlagValues && IsSingleDimNumericAttr(poFlagValues);
    6672             : 
    6673          24 :         auto poFlagMasks = m_poParent->GetAttribute("flag_masks");
    6674             :         const bool bHasFlagMasks =
    6675          12 :             poFlagMasks && IsSingleDimNumericAttr(poFlagMasks);
    6676             : 
    6677          12 :         if (!bHasFlagValues && !bHasFlagMasks)
    6678             :         {
    6679           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6680             :                      "Cannot find flag_values and/or flag_masks attribute");
    6681           1 :             return false;
    6682             :         }
    6683             : 
    6684             :         const CPLStringList aosUnmaskFlags(
    6685          11 :             CSLTokenizeString2(pszUnmaskFlags, ",", 0));
    6686             :         const CPLStringList aosFlagMeanings(
    6687          11 :             CSLTokenizeString2(pszFlagMeanings, " ", 0));
    6688             : 
    6689          11 :         if (bHasFlagValues)
    6690             :         {
    6691           7 :             const auto eType = poFlagValues->GetDataType().GetNumericDataType();
    6692             :             // We could support Int64 or UInt64, but more work...
    6693           7 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6694           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6695             :             {
    6696           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6697             :                          "Unsupported data type for flag_values attribute: %s",
    6698             :                          GDALGetDataTypeName(eType));
    6699           0 :                 return false;
    6700             :             }
    6701             :         }
    6702             : 
    6703          11 :         if (bHasFlagMasks)
    6704             :         {
    6705           6 :             const auto eType = poFlagMasks->GetDataType().GetNumericDataType();
    6706             :             // We could support Int64 or UInt64, but more work...
    6707           6 :             if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
    6708           0 :                 eType != GDT_Int16 && eType != GDT_UInt32 && eType != GDT_Int32)
    6709             :             {
    6710           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    6711             :                          "Unsupported data type for flag_masks attribute: %s",
    6712             :                          GDALGetDataTypeName(eType));
    6713           0 :                 return false;
    6714             :             }
    6715             :         }
    6716             : 
    6717             :         const std::vector<double> adfValues(
    6718             :             bHasFlagValues ? poFlagValues->ReadAsDoubleArray()
    6719          11 :                            : std::vector<double>());
    6720             :         const std::vector<double> adfMasks(
    6721             :             bHasFlagMasks ? poFlagMasks->ReadAsDoubleArray()
    6722          11 :                           : std::vector<double>());
    6723             : 
    6724          18 :         if (bHasFlagValues &&
    6725           7 :             adfValues.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6726             :         {
    6727           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6728             :                      "Number of values in flag_values attribute is different "
    6729             :                      "from the one in flag_meanings");
    6730           1 :             return false;
    6731             :         }
    6732             : 
    6733          16 :         if (bHasFlagMasks &&
    6734           6 :             adfMasks.size() != static_cast<size_t>(aosFlagMeanings.size()))
    6735             :         {
    6736           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    6737             :                      "Number of values in flag_masks attribute is different "
    6738             :                      "from the one in flag_meanings");
    6739           1 :             return false;
    6740             :         }
    6741             : 
    6742          19 :         for (int i = 0; i < aosUnmaskFlags.size(); ++i)
    6743             :         {
    6744          11 :             const int nIdxFlag = aosFlagMeanings.FindString(aosUnmaskFlags[i]);
    6745          11 :             if (nIdxFlag < 0)
    6746             :             {
    6747           1 :                 CPLError(
    6748             :                     CE_Failure, CPLE_AppDefined,
    6749             :                     "Cannot fing flag %s in flag_meanings = '%s' attribute",
    6750             :                     aosUnmaskFlags[i], pszFlagMeanings);
    6751           1 :                 return false;
    6752             :             }
    6753             : 
    6754          10 :             if (bHasFlagValues && adfValues[nIdxFlag] < 0)
    6755             :             {
    6756           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6757             :                          "Invalid value in flag_values[%d] = %f", nIdxFlag,
    6758           0 :                          adfValues[nIdxFlag]);
    6759           0 :                 return false;
    6760             :             }
    6761             : 
    6762          10 :             if (bHasFlagMasks && adfMasks[nIdxFlag] < 0)
    6763             :             {
    6764           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    6765             :                          "Invalid value in flag_masks[%d] = %f", nIdxFlag,
    6766           0 :                          adfMasks[nIdxFlag]);
    6767           0 :                 return false;
    6768             :             }
    6769             : 
    6770          10 :             if (bHasFlagValues)
    6771             :             {
    6772          12 :                 m_anValidFlagValues.push_back(
    6773           6 :                     static_cast<uint32_t>(adfValues[nIdxFlag]));
    6774             :             }
    6775             : 
    6776          10 :             if (bHasFlagMasks)
    6777             :             {
    6778          12 :                 m_anValidFlagMasks.push_back(
    6779           6 :                     static_cast<uint32_t>(adfMasks[nIdxFlag]));
    6780             :             }
    6781             :         }
    6782             :     }
    6783             : 
    6784          39 :     return true;
    6785             : }
    6786             : 
    6787             : /************************************************************************/
    6788             : /*                             IRead()                                  */
    6789             : /************************************************************************/
    6790             : 
    6791          48 : bool GDALMDArrayMask::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    6792             :                             const GInt64 *arrayStep,
    6793             :                             const GPtrDiff_t *bufferStride,
    6794             :                             const GDALExtendedDataType &bufferDataType,
    6795             :                             void *pDstBuffer) const
    6796             : {
    6797          48 :     size_t nElts = 1;
    6798          48 :     const size_t nDims = GetDimensionCount();
    6799          96 :     std::vector<GPtrDiff_t> tmpBufferStrideVector(nDims);
    6800         132 :     for (size_t i = 0; i < nDims; i++)
    6801          84 :         nElts *= count[i];
    6802          48 :     if (nDims > 0)
    6803             :     {
    6804          43 :         tmpBufferStrideVector.back() = 1;
    6805          84 :         for (size_t i = nDims - 1; i > 0;)
    6806             :         {
    6807          41 :             --i;
    6808          41 :             tmpBufferStrideVector[i] =
    6809          41 :                 tmpBufferStrideVector[i + 1] * count[i + 1];
    6810             :         }
    6811             :     }
    6812             : 
    6813             :     /* Optimized case: if we are an integer data type and that there is no */
    6814             :     /* attribute that can be used to set mask = 0, then fill the mask buffer */
    6815             :     /* directly */
    6816          46 :     if (!m_bHasMissingValue && !m_bHasFillValue && !m_bHasValidMin &&
    6817          70 :         !m_bHasValidMax && m_anValidFlagValues.empty() &&
    6818          32 :         m_anValidFlagMasks.empty() &&
    6819         103 :         m_poParent->GetRawNoDataValue() == nullptr &&
    6820           9 :         GDALDataTypeIsInteger(m_poParent->GetDataType().GetNumericDataType()))
    6821             :     {
    6822           7 :         if (bufferDataType == m_dt)  // Byte case
    6823             :         {
    6824           4 :             bool bContiguous = true;
    6825          10 :             for (size_t i = 0; i < nDims; i++)
    6826             :             {
    6827           7 :                 if (bufferStride[i] != tmpBufferStrideVector[i])
    6828             :                 {
    6829           1 :                     bContiguous = false;
    6830           1 :                     break;
    6831             :                 }
    6832             :             }
    6833           4 :             if (bContiguous)
    6834             :             {
    6835             :                 // CPLDebug("GDAL", "GetMask(): contiguous case");
    6836           3 :                 memset(pDstBuffer, 1, nElts);
    6837           3 :                 return true;
    6838             :             }
    6839             :         }
    6840             : 
    6841             :         struct Stack
    6842             :         {
    6843             :             size_t nIters = 0;
    6844             :             GByte *dst_ptr = nullptr;
    6845             :             GPtrDiff_t dst_inc_offset = 0;
    6846             :         };
    6847             : 
    6848           4 :         std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    6849           4 :         const size_t nBufferDTSize = bufferDataType.GetSize();
    6850          13 :         for (size_t i = 0; i < nDims; i++)
    6851             :         {
    6852           9 :             stack[i].dst_inc_offset =
    6853           9 :                 static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    6854             :         }
    6855           4 :         stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    6856             : 
    6857           4 :         size_t dimIdx = 0;
    6858           4 :         const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    6859           4 :         const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    6860             :         GByte abyOne[16];  // 16 is sizeof GDT_CFloat64
    6861           4 :         CPLAssert(nBufferDTSize <= 16);
    6862           4 :         const GByte flag = 1;
    6863             :         // Coverity misses that m_dt is of type Byte
    6864             :         // coverity[overrun-buffer-val]
    6865           4 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyOne, bufferDataType);
    6866             : 
    6867          28 :     lbl_next_depth:
    6868          28 :         if (dimIdx == nDimsMinus1)
    6869             :         {
    6870          19 :             auto nIters = nDims > 0 ? count[dimIdx] : 1;
    6871          19 :             GByte *dst_ptr = stack[dimIdx].dst_ptr;
    6872             : 
    6873             :             while (true)
    6874             :             {
    6875          73 :                 if (bBufferDataTypeIsByte)
    6876             :                 {
    6877          24 :                     *dst_ptr = flag;
    6878             :                 }
    6879             :                 else
    6880             :                 {
    6881          49 :                     memcpy(dst_ptr, abyOne, nBufferDTSize);
    6882             :                 }
    6883             : 
    6884          73 :                 if ((--nIters) == 0)
    6885          19 :                     break;
    6886          54 :                 dst_ptr += stack[dimIdx].dst_inc_offset;
    6887             :             }
    6888             :         }
    6889             :         else
    6890             :         {
    6891           9 :             stack[dimIdx].nIters = count[dimIdx];
    6892             :             while (true)
    6893             :             {
    6894          24 :                 dimIdx++;
    6895          24 :                 stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    6896          24 :                 goto lbl_next_depth;
    6897          24 :             lbl_return_to_caller:
    6898          24 :                 dimIdx--;
    6899          24 :                 if ((--stack[dimIdx].nIters) == 0)
    6900           9 :                     break;
    6901          15 :                 stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    6902             :             }
    6903             :         }
    6904          28 :         if (dimIdx > 0)
    6905          24 :             goto lbl_return_to_caller;
    6906             : 
    6907           4 :         return true;
    6908             :     }
    6909             : 
    6910             :     const auto oTmpBufferDT =
    6911          41 :         GDALDataTypeIsComplex(m_poParent->GetDataType().GetNumericDataType())
    6912             :             ? GDALExtendedDataType::Create(GDT_Float64)
    6913          82 :             : m_poParent->GetDataType();
    6914          41 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    6915          41 :     void *pTempBuffer = VSI_MALLOC2_VERBOSE(nTmpBufferDTSize, nElts);
    6916          41 :     if (!pTempBuffer)
    6917           0 :         return false;
    6918          82 :     if (!m_poParent->Read(arrayStartIdx, count, arrayStep,
    6919          41 :                           tmpBufferStrideVector.data(), oTmpBufferDT,
    6920             :                           pTempBuffer))
    6921             :     {
    6922           0 :         VSIFree(pTempBuffer);
    6923           0 :         return false;
    6924             :     }
    6925             : 
    6926          41 :     switch (oTmpBufferDT.GetNumericDataType())
    6927             :     {
    6928           6 :         case GDT_Byte:
    6929           6 :             ReadInternal<GByte>(count, bufferStride, bufferDataType, pDstBuffer,
    6930             :                                 pTempBuffer, oTmpBufferDT,
    6931             :                                 tmpBufferStrideVector);
    6932           6 :             break;
    6933             : 
    6934           0 :         case GDT_Int8:
    6935           0 :             ReadInternal<GInt8>(count, bufferStride, bufferDataType, pDstBuffer,
    6936             :                                 pTempBuffer, oTmpBufferDT,
    6937             :                                 tmpBufferStrideVector);
    6938           0 :             break;
    6939             : 
    6940           1 :         case GDT_UInt16:
    6941           1 :             ReadInternal<GUInt16>(count, bufferStride, bufferDataType,
    6942             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    6943             :                                   tmpBufferStrideVector);
    6944           1 :             break;
    6945             : 
    6946          14 :         case GDT_Int16:
    6947          14 :             ReadInternal<GInt16>(count, bufferStride, bufferDataType,
    6948             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    6949             :                                  tmpBufferStrideVector);
    6950          14 :             break;
    6951             : 
    6952           1 :         case GDT_UInt32:
    6953           1 :             ReadInternal<GUInt32>(count, bufferStride, bufferDataType,
    6954             :                                   pDstBuffer, pTempBuffer, oTmpBufferDT,
    6955             :                                   tmpBufferStrideVector);
    6956           1 :             break;
    6957             : 
    6958           5 :         case GDT_Int32:
    6959           5 :             ReadInternal<GInt32>(count, bufferStride, bufferDataType,
    6960             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    6961             :                                  tmpBufferStrideVector);
    6962           5 :             break;
    6963             : 
    6964           0 :         case GDT_UInt64:
    6965           0 :             ReadInternal<std::uint64_t>(count, bufferStride, bufferDataType,
    6966             :                                         pDstBuffer, pTempBuffer, oTmpBufferDT,
    6967             :                                         tmpBufferStrideVector);
    6968           0 :             break;
    6969             : 
    6970           0 :         case GDT_Int64:
    6971           0 :             ReadInternal<std::int64_t>(count, bufferStride, bufferDataType,
    6972             :                                        pDstBuffer, pTempBuffer, oTmpBufferDT,
    6973             :                                        tmpBufferStrideVector);
    6974           0 :             break;
    6975             : 
    6976           7 :         case GDT_Float32:
    6977           7 :             ReadInternal<float>(count, bufferStride, bufferDataType, pDstBuffer,
    6978             :                                 pTempBuffer, oTmpBufferDT,
    6979             :                                 tmpBufferStrideVector);
    6980           7 :             break;
    6981             : 
    6982           7 :         case GDT_Float64:
    6983           7 :             ReadInternal<double>(count, bufferStride, bufferDataType,
    6984             :                                  pDstBuffer, pTempBuffer, oTmpBufferDT,
    6985             :                                  tmpBufferStrideVector);
    6986           7 :             break;
    6987           0 :         case GDT_Unknown:
    6988             :         case GDT_CInt16:
    6989             :         case GDT_CInt32:
    6990             :         case GDT_CFloat32:
    6991             :         case GDT_CFloat64:
    6992             :         case GDT_TypeCount:
    6993           0 :             CPLAssert(false);
    6994             :             break;
    6995             :     }
    6996             : 
    6997          41 :     VSIFree(pTempBuffer);
    6998             : 
    6999          41 :     return true;
    7000             : }
    7001             : 
    7002             : /************************************************************************/
    7003             : /*                          IsValidForDT()                              */
    7004             : /************************************************************************/
    7005             : 
    7006          38 : template <typename Type> static bool IsValidForDT(double dfVal)
    7007             : {
    7008          38 :     if (std::isnan(dfVal))
    7009           0 :         return false;
    7010          38 :     if (dfVal < static_cast<double>(std::numeric_limits<Type>::lowest()))
    7011           0 :         return false;
    7012          38 :     if (dfVal > static_cast<double>(std::numeric_limits<Type>::max()))
    7013           0 :         return false;
    7014          38 :     return static_cast<double>(static_cast<Type>(dfVal)) == dfVal;
    7015             : }
    7016             : 
    7017           9 : template <> bool IsValidForDT<double>(double)
    7018             : {
    7019           9 :     return true;
    7020             : }
    7021             : 
    7022             : /************************************************************************/
    7023             : /*                              IsNan()                                 */
    7024             : /************************************************************************/
    7025             : 
    7026        1038 : template <typename Type> inline bool IsNan(Type)
    7027             : {
    7028        1038 :     return false;
    7029             : }
    7030             : 
    7031          25 : template <> bool IsNan<double>(double val)
    7032             : {
    7033          25 :     return std::isnan(val);
    7034             : }
    7035             : 
    7036          26 : template <> bool IsNan<float>(float val)
    7037             : {
    7038          26 :     return std::isnan(val);
    7039             : }
    7040             : 
    7041             : /************************************************************************/
    7042             : /*                         ReadInternal()                               */
    7043             : /************************************************************************/
    7044             : 
    7045             : template <typename Type>
    7046          41 : void GDALMDArrayMask::ReadInternal(
    7047             :     const size_t *count, const GPtrDiff_t *bufferStride,
    7048             :     const GDALExtendedDataType &bufferDataType, void *pDstBuffer,
    7049             :     const void *pTempBuffer, const GDALExtendedDataType &oTmpBufferDT,
    7050             :     const std::vector<GPtrDiff_t> &tmpBufferStrideVector) const
    7051             : {
    7052          41 :     const size_t nDims = GetDimensionCount();
    7053             : 
    7054         205 :     const auto castValue = [](bool &bHasVal, double dfVal) -> Type
    7055             :     {
    7056         205 :         if (bHasVal)
    7057             :         {
    7058          47 :             if (IsValidForDT<Type>(dfVal))
    7059             :             {
    7060          47 :                 return static_cast<Type>(dfVal);
    7061             :             }
    7062             :             else
    7063             :             {
    7064           0 :                 bHasVal = false;
    7065             :             }
    7066             :         }
    7067         158 :         return 0;
    7068             :     };
    7069             : 
    7070          41 :     const void *pSrcRawNoDataValue = m_poParent->GetRawNoDataValue();
    7071          41 :     bool bHasNodataValue = pSrcRawNoDataValue != nullptr;
    7072             :     const Type nNoDataValue =
    7073          41 :         castValue(bHasNodataValue, m_poParent->GetNoDataValueAsDouble());
    7074          41 :     bool bHasMissingValue = m_bHasMissingValue;
    7075          41 :     const Type nMissingValue = castValue(bHasMissingValue, m_dfMissingValue);
    7076          41 :     bool bHasFillValue = m_bHasFillValue;
    7077          41 :     const Type nFillValue = castValue(bHasFillValue, m_dfFillValue);
    7078          41 :     bool bHasValidMin = m_bHasValidMin;
    7079          41 :     const Type nValidMin = castValue(bHasValidMin, m_dfValidMin);
    7080          41 :     bool bHasValidMax = m_bHasValidMax;
    7081          41 :     const Type nValidMax = castValue(bHasValidMax, m_dfValidMax);
    7082          41 :     const bool bHasValidFlags =
    7083          41 :         !m_anValidFlagValues.empty() || !m_anValidFlagMasks.empty();
    7084             : 
    7085         348 :     const auto IsValidFlag = [this](Type v)
    7086             :     {
    7087          54 :         if (!m_anValidFlagValues.empty() && !m_anValidFlagMasks.empty())
    7088             :         {
    7089          20 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7090             :             {
    7091          12 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) ==
    7092             :                     m_anValidFlagValues[i])
    7093             :                 {
    7094           4 :                     return true;
    7095             :                 }
    7096             :             }
    7097             :         }
    7098          42 :         else if (!m_anValidFlagValues.empty())
    7099             :         {
    7100          49 :             for (size_t i = 0; i < m_anValidFlagValues.size(); ++i)
    7101             :             {
    7102          29 :                 if (static_cast<uint32_t>(v) == m_anValidFlagValues[i])
    7103             :                 {
    7104           4 :                     return true;
    7105             :                 }
    7106             :             }
    7107             :         }
    7108             :         else /* if( !m_anValidFlagMasks.empty() ) */
    7109             :         {
    7110          31 :             for (size_t i = 0; i < m_anValidFlagMasks.size(); ++i)
    7111             :             {
    7112          22 :                 if ((static_cast<uint32_t>(v) & m_anValidFlagMasks[i]) != 0)
    7113             :                 {
    7114           9 :                     return true;
    7115             :                 }
    7116             :             }
    7117             :         }
    7118          37 :         return false;
    7119             :     };
    7120             : 
    7121             : #define GET_MASK_FOR_SAMPLE(v)                                                 \
    7122             :     static_cast<GByte>(!IsNan(v) && !(bHasNodataValue && v == nNoDataValue) && \
    7123             :                        !(bHasMissingValue && v == nMissingValue) &&            \
    7124             :                        !(bHasFillValue && v == nFillValue) &&                  \
    7125             :                        !(bHasValidMin && v < nValidMin) &&                     \
    7126             :                        !(bHasValidMax && v > nValidMax) &&                     \
    7127             :                        (!bHasValidFlags || IsValidFlag(v)));
    7128             : 
    7129          41 :     const bool bBufferDataTypeIsByte = bufferDataType == m_dt;
    7130             :     /* Optimized case: Byte output and output buffer is contiguous */
    7131          41 :     if (bBufferDataTypeIsByte)
    7132             :     {
    7133          37 :         bool bContiguous = true;
    7134          96 :         for (size_t i = 0; i < nDims; i++)
    7135             :         {
    7136          60 :             if (bufferStride[i] != tmpBufferStrideVector[i])
    7137             :             {
    7138           1 :                 bContiguous = false;
    7139           1 :                 break;
    7140             :             }
    7141             :         }
    7142          37 :         if (bContiguous)
    7143             :         {
    7144          36 :             size_t nElts = 1;
    7145          95 :             for (size_t i = 0; i < nDims; i++)
    7146          59 :                 nElts *= count[i];
    7147             : 
    7148         670 :             for (size_t i = 0; i < nElts; i++)
    7149             :             {
    7150         634 :                 const Type *pSrc = static_cast<const Type *>(pTempBuffer) + i;
    7151         634 :                 static_cast<GByte *>(pDstBuffer)[i] =
    7152         634 :                     GET_MASK_FOR_SAMPLE(*pSrc);
    7153             :             }
    7154          36 :             return;
    7155             :         }
    7156             :     }
    7157             : 
    7158           5 :     const size_t nTmpBufferDTSize = oTmpBufferDT.GetSize();
    7159             : 
    7160             :     struct Stack
    7161             :     {
    7162             :         size_t nIters = 0;
    7163             :         const GByte *src_ptr = nullptr;
    7164             :         GByte *dst_ptr = nullptr;
    7165             :         GPtrDiff_t src_inc_offset = 0;
    7166             :         GPtrDiff_t dst_inc_offset = 0;
    7167             :     };
    7168             : 
    7169          10 :     std::vector<Stack> stack(std::max(static_cast<size_t>(1), nDims));
    7170           5 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    7171          15 :     for (size_t i = 0; i < nDims; i++)
    7172             :     {
    7173          20 :         stack[i].src_inc_offset = static_cast<GPtrDiff_t>(
    7174          10 :             tmpBufferStrideVector[i] * nTmpBufferDTSize);
    7175          10 :         stack[i].dst_inc_offset =
    7176          10 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    7177             :     }
    7178           5 :     stack[0].src_ptr = static_cast<const GByte *>(pTempBuffer);
    7179           5 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    7180             : 
    7181           5 :     size_t dimIdx = 0;
    7182           5 :     const size_t nDimsMinus1 = nDims > 0 ? nDims - 1 : 0;
    7183             :     GByte abyZeroOrOne[2][16];  // 16 is sizeof GDT_CFloat64
    7184           5 :     CPLAssert(nBufferDTSize <= 16);
    7185          15 :     for (GByte flag = 0; flag <= 1; flag++)
    7186             :     {
    7187             :         // Coverity misses that m_dt is of type Byte
    7188             :         // coverity[overrun-buffer-val]
    7189          10 :         GDALExtendedDataType::CopyValue(&flag, m_dt, abyZeroOrOne[flag],
    7190             :                                         bufferDataType);
    7191             :     }
    7192             : 
    7193          43 : lbl_next_depth:
    7194          43 :     if (dimIdx == nDimsMinus1)
    7195             :     {
    7196          35 :         auto nIters = nDims > 0 ? count[dimIdx] : 1;
    7197          35 :         const GByte *src_ptr = stack[dimIdx].src_ptr;
    7198          35 :         GByte *dst_ptr = stack[dimIdx].dst_ptr;
    7199             : 
    7200         420 :         while (true)
    7201             :         {
    7202         455 :             const Type *pSrc = reinterpret_cast<const Type *>(src_ptr);
    7203         455 :             const GByte flag = GET_MASK_FOR_SAMPLE(*pSrc);
    7204             : 
    7205         455 :             if (bBufferDataTypeIsByte)
    7206             :             {
    7207          24 :                 *dst_ptr = flag;
    7208             :             }
    7209             :             else
    7210             :             {
    7211         431 :                 memcpy(dst_ptr, abyZeroOrOne[flag], nBufferDTSize);
    7212             :             }
    7213             : 
    7214         455 :             if ((--nIters) == 0)
    7215          35 :                 break;
    7216         420 :             src_ptr += stack[dimIdx].src_inc_offset;
    7217         420 :             dst_ptr += stack[dimIdx].dst_inc_offset;
    7218             :         }
    7219             :     }
    7220             :     else
    7221             :     {
    7222           8 :         stack[dimIdx].nIters = count[dimIdx];
    7223             :         while (true)
    7224             :         {
    7225          38 :             dimIdx++;
    7226          38 :             stack[dimIdx].src_ptr = stack[dimIdx - 1].src_ptr;
    7227          38 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    7228          38 :             goto lbl_next_depth;
    7229          38 :         lbl_return_to_caller:
    7230          38 :             dimIdx--;
    7231          38 :             if ((--stack[dimIdx].nIters) == 0)
    7232           8 :                 break;
    7233          30 :             stack[dimIdx].src_ptr += stack[dimIdx].src_inc_offset;
    7234          30 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    7235             :         }
    7236             :     }
    7237          43 :     if (dimIdx > 0)
    7238          38 :         goto lbl_return_to_caller;
    7239             : }
    7240             : 
    7241             : /************************************************************************/
    7242             : /*                            GetMask()                                 */
    7243             : /************************************************************************/
    7244             : 
    7245             : /** Return an array that is a mask for the current array
    7246             : 
    7247             :  This array will be of type Byte, with values set to 0 to indicate invalid
    7248             :  pixels of the current array, and values set to 1 to indicate valid pixels.
    7249             : 
    7250             :  The generic implementation honours the NoDataValue, as well as various
    7251             :  netCDF CF attributes: missing_value, _FillValue, valid_min, valid_max
    7252             :  and valid_range.
    7253             : 
    7254             :  Starting with GDAL 3.8, option UNMASK_FLAGS=flag_meaning_1[,flag_meaning_2,...]
    7255             :  can be used to specify strings of the "flag_meanings" attribute
    7256             :  (cf https://cfconventions.org/cf-conventions/cf-conventions.html#flags)
    7257             :  for which pixels matching any of those flags will be set at 1 in the mask array,
    7258             :  and pixels matching none of those flags will be set at 0.
    7259             :  For example, let's consider the following netCDF variable defined with:
    7260             :  \verbatim
    7261             :  l2p_flags:valid_min = 0s ;
    7262             :  l2p_flags:valid_max = 256s ;
    7263             :  l2p_flags:flag_meanings = "microwave land ice lake river reserved_for_future_use unused_currently unused_currently unused_currently" ;
    7264             :  l2p_flags:flag_masks = 1s, 2s, 4s, 8s, 16s, 32s, 64s, 128s, 256s ;
    7265             :  \endverbatim
    7266             : 
    7267             :  GetMask(["UNMASK_FLAGS=microwave,land"]) will return an array such that:
    7268             :  - for pixel values *outside* valid_range [0,256], the mask value will be 0.
    7269             :  - for a pixel value with bit 0 or bit 1 at 1 within [0,256], the mask value
    7270             :    will be 1.
    7271             :  - for a pixel value with bit 0 and bit 1 at 0 within [0,256], the mask value
    7272             :    will be 0.
    7273             : 
    7274             :  This is the same as the C function GDALMDArrayGetMask().
    7275             : 
    7276             :  @param papszOptions NULL-terminated list of options, or NULL.
    7277             : 
    7278             :  @return a new array, that holds a reference to the original one, and thus is
    7279             :  a view of it (not a copy), or nullptr in case of error.
    7280             : */
    7281             : std::shared_ptr<GDALMDArray>
    7282          46 : GDALMDArray::GetMask(CSLConstList papszOptions) const
    7283             : {
    7284          92 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    7285          46 :     if (!self)
    7286             :     {
    7287           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    7288             :                  "Driver implementation issue: m_pSelf not set !");
    7289           0 :         return nullptr;
    7290             :     }
    7291          46 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    7292             :     {
    7293           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    7294             :                  "GetMask() only supports numeric data type");
    7295           1 :         return nullptr;
    7296             :     }
    7297          45 :     return GDALMDArrayMask::Create(self, papszOptions);
    7298             : }
    7299             : 
    7300             : /************************************************************************/
    7301             : /*                         IsRegularlySpaced()                          */
    7302             : /************************************************************************/
    7303             : 
    7304             : /** Returns whether an array is a 1D regularly spaced array.
    7305             :  *
    7306             :  * @param[out] dfStart     First value in the array
    7307             :  * @param[out] dfIncrement Increment/spacing between consecutive values.
    7308             :  * @return true if the array is regularly spaced.
    7309             :  */
    7310         175 : bool GDALMDArray::IsRegularlySpaced(double &dfStart, double &dfIncrement) const
    7311             : {
    7312         175 :     dfStart = 0;
    7313         175 :     dfIncrement = 0;
    7314         175 :     if (GetDimensionCount() != 1 || GetDataType().GetClass() != GEDTC_NUMERIC)
    7315           0 :         return false;
    7316         175 :     const auto nSize = GetDimensions()[0]->GetSize();
    7317         175 :     if (nSize <= 1 || nSize > 10 * 1000 * 1000)
    7318           2 :         return false;
    7319             : 
    7320         173 :     size_t nCount = static_cast<size_t>(nSize);
    7321         346 :     std::vector<double> adfTmp;
    7322             :     try
    7323             :     {
    7324         173 :         adfTmp.resize(nCount);
    7325             :     }
    7326           0 :     catch (const std::exception &)
    7327             :     {
    7328           0 :         return false;
    7329             :     }
    7330             : 
    7331         173 :     GUInt64 anStart[1] = {0};
    7332         173 :     size_t anCount[1] = {nCount};
    7333             : 
    7334             :     const auto IsRegularlySpacedInternal =
    7335       79790 :         [&dfStart, &dfIncrement, &anCount, &adfTmp]()
    7336             :     {
    7337         245 :         dfStart = adfTmp[0];
    7338         245 :         dfIncrement = (adfTmp[anCount[0] - 1] - adfTmp[0]) / (anCount[0] - 1);
    7339         245 :         if (dfIncrement == 0)
    7340             :         {
    7341           3 :             return false;
    7342             :         }
    7343       19884 :         for (size_t i = 1; i < anCount[0]; i++)
    7344             :         {
    7345       19642 :             if (fabs((adfTmp[i] - adfTmp[i - 1]) - dfIncrement) >
    7346       19642 :                 1e-3 * fabs(dfIncrement))
    7347             :             {
    7348           0 :                 return false;
    7349             :             }
    7350             :         }
    7351         242 :         return true;
    7352         173 :     };
    7353             : 
    7354             :     // First try with the first block(s). This can avoid excessive processing
    7355             :     // time, for example with Zarr datasets.
    7356             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=37636 and
    7357             :     // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=39273
    7358         173 :     const auto nBlockSize = GetBlockSize()[0];
    7359         173 :     if (nCount >= 5 && nBlockSize <= nCount / 2)
    7360             :     {
    7361             :         size_t nReducedCount =
    7362          75 :             std::max<size_t>(3, static_cast<size_t>(nBlockSize));
    7363         436 :         while (nReducedCount < 256 && nReducedCount <= (nCount - 2) / 2)
    7364         361 :             nReducedCount *= 2;
    7365          75 :         anCount[0] = nReducedCount;
    7366          75 :         if (!Read(anStart, anCount, nullptr, nullptr,
    7367         150 :                   GDALExtendedDataType::Create(GDT_Float64), &adfTmp[0]))
    7368             :         {
    7369           0 :             return false;
    7370             :         }
    7371          75 :         if (!IsRegularlySpacedInternal())
    7372             :         {
    7373           3 :             return false;
    7374             :         }
    7375             : 
    7376             :         // Get next values
    7377          72 :         anStart[0] = nReducedCount;
    7378          72 :         anCount[0] = nCount - nReducedCount;
    7379             :     }
    7380             : 
    7381         170 :     if (!Read(anStart, anCount, nullptr, nullptr,
    7382         340 :               GDALExtendedDataType::Create(GDT_Float64),
    7383         170 :               &adfTmp[static_cast<size_t>(anStart[0])]))
    7384             :     {
    7385           0 :         return false;
    7386             :     }
    7387             : 
    7388         170 :     return IsRegularlySpacedInternal();
    7389             : }
    7390             : 
    7391             : /************************************************************************/
    7392             : /*                         GuessGeoTransform()                          */
    7393             : /************************************************************************/
    7394             : 
    7395             : /** Returns whether 2 specified dimensions form a geotransform
    7396             :  *
    7397             :  * @param nDimX                Index of the X axis.
    7398             :  * @param nDimY                Index of the Y axis.
    7399             :  * @param bPixelIsPoint        Whether the geotransform should be returned
    7400             :  *                             with the pixel-is-point (pixel-center) convention
    7401             :  *                             (bPixelIsPoint = true), or with the pixel-is-area
    7402             :  *                             (top left corner convention)
    7403             :  *                             (bPixelIsPoint = false)
    7404             :  * @param[out] adfGeoTransform Computed geotransform
    7405             :  * @return true if a geotransform could be computed.
    7406             :  */
    7407         183 : bool GDALMDArray::GuessGeoTransform(size_t nDimX, size_t nDimY,
    7408             :                                     bool bPixelIsPoint,
    7409             :                                     double adfGeoTransform[6]) const
    7410             : {
    7411         183 :     const auto &dims(GetDimensions());
    7412         366 :     auto poVarX = dims[nDimX]->GetIndexingVariable();
    7413         366 :     auto poVarY = dims[nDimY]->GetIndexingVariable();
    7414         183 :     double dfXStart = 0.0;
    7415         183 :     double dfXSpacing = 0.0;
    7416         183 :     double dfYStart = 0.0;
    7417         183 :     double dfYSpacing = 0.0;
    7418         415 :     if (poVarX && poVarX->GetDimensionCount() == 1 &&
    7419         232 :         poVarX->GetDimensions()[0]->GetSize() == dims[nDimX]->GetSize() &&
    7420         292 :         poVarY && poVarY->GetDimensionCount() == 1 &&
    7421          88 :         poVarY->GetDimensions()[0]->GetSize() == dims[nDimY]->GetSize() &&
    7422         382 :         poVarX->IsRegularlySpaced(dfXStart, dfXSpacing) &&
    7423          83 :         poVarY->IsRegularlySpaced(dfYStart, dfYSpacing))
    7424             :     {
    7425          83 :         adfGeoTransform[0] = dfXStart - (bPixelIsPoint ? 0 : dfXSpacing / 2);
    7426          83 :         adfGeoTransform[1] = dfXSpacing;
    7427          83 :         adfGeoTransform[2] = 0;
    7428          83 :         adfGeoTransform[3] = dfYStart - (bPixelIsPoint ? 0 : dfYSpacing / 2);
    7429          83 :         adfGeoTransform[4] = 0;
    7430          83 :         adfGeoTransform[5] = dfYSpacing;
    7431          83 :         return true;
    7432             :     }
    7433         100 :     return false;
    7434             : }
    7435             : 
    7436             : /************************************************************************/
    7437             : /*                       GDALMDArrayResampled                           */
    7438             : /************************************************************************/
    7439             : 
    7440             : class GDALMDArrayResampledDataset;
    7441             : 
    7442             : class GDALMDArrayResampledDatasetRasterBand final : public GDALRasterBand
    7443             : {
    7444             :   protected:
    7445             :     CPLErr IReadBlock(int, int, void *) override;
    7446             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    7447             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    7448             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    7449             :                      GSpacing nLineSpaceBuf,
    7450             :                      GDALRasterIOExtraArg *psExtraArg) override;
    7451             : 
    7452             :   public:
    7453             :     explicit GDALMDArrayResampledDatasetRasterBand(
    7454             :         GDALMDArrayResampledDataset *poDSIn);
    7455             : 
    7456             :     double GetNoDataValue(int *pbHasNoData) override;
    7457             : };
    7458             : 
    7459             : class GDALMDArrayResampledDataset final : public GDALPamDataset
    7460             : {
    7461             :     friend class GDALMDArrayResampled;
    7462             :     friend class GDALMDArrayResampledDatasetRasterBand;
    7463             : 
    7464             :     std::shared_ptr<GDALMDArray> m_poArray;
    7465             :     const size_t m_iXDim;
    7466             :     const size_t m_iYDim;
    7467             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    7468             :     bool m_bHasGT = false;
    7469             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7470             : 
    7471             :     std::vector<GUInt64> m_anOffset{};
    7472             :     std::vector<size_t> m_anCount{};
    7473             :     std::vector<GPtrDiff_t> m_anStride{};
    7474             : 
    7475             :     std::string m_osFilenameLong{};
    7476             :     std::string m_osFilenameLat{};
    7477             : 
    7478             :   public:
    7479          24 :     GDALMDArrayResampledDataset(const std::shared_ptr<GDALMDArray> &array,
    7480             :                                 size_t iXDim, size_t iYDim)
    7481          24 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim),
    7482          24 :           m_anOffset(m_poArray->GetDimensionCount(), 0),
    7483          24 :           m_anCount(m_poArray->GetDimensionCount(), 1),
    7484          72 :           m_anStride(m_poArray->GetDimensionCount(), 0)
    7485             :     {
    7486          24 :         const auto &dims(m_poArray->GetDimensions());
    7487             : 
    7488          24 :         nRasterYSize = static_cast<int>(
    7489          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iYDim]->GetSize()));
    7490          24 :         nRasterXSize = static_cast<int>(
    7491          24 :             std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    7492             : 
    7493          24 :         m_bHasGT = m_poArray->GuessGeoTransform(m_iXDim, m_iYDim, false,
    7494          24 :                                                 m_adfGeoTransform);
    7495             : 
    7496          24 :         SetBand(1, new GDALMDArrayResampledDatasetRasterBand(this));
    7497          24 :     }
    7498             : 
    7499          48 :     ~GDALMDArrayResampledDataset()
    7500          24 :     {
    7501          24 :         if (!m_osFilenameLong.empty())
    7502           5 :             VSIUnlink(m_osFilenameLong.c_str());
    7503          24 :         if (!m_osFilenameLat.empty())
    7504           5 :             VSIUnlink(m_osFilenameLat.c_str());
    7505          48 :     }
    7506             : 
    7507          43 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    7508             :     {
    7509          43 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    7510          43 :         return m_bHasGT ? CE_None : CE_Failure;
    7511             :     }
    7512             : 
    7513         105 :     const OGRSpatialReference *GetSpatialRef() const override
    7514             :     {
    7515         105 :         m_poSRS = m_poArray->GetSpatialRef();
    7516         105 :         if (m_poSRS)
    7517             :         {
    7518          79 :             m_poSRS.reset(m_poSRS->Clone());
    7519         158 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    7520         237 :             for (auto &m : axisMapping)
    7521             :             {
    7522         158 :                 if (m == static_cast<int>(m_iXDim) + 1)
    7523          79 :                     m = 1;
    7524          79 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    7525          79 :                     m = 2;
    7526             :             }
    7527          79 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    7528             :         }
    7529         105 :         return m_poSRS.get();
    7530             :     }
    7531             : 
    7532           5 :     void SetGeolocationArray(const std::string &osFilenameLong,
    7533             :                              const std::string &osFilenameLat)
    7534             :     {
    7535           5 :         m_osFilenameLong = osFilenameLong;
    7536           5 :         m_osFilenameLat = osFilenameLat;
    7537          10 :         CPLStringList aosGeoLoc;
    7538           5 :         aosGeoLoc.SetNameValue("LINE_OFFSET", "0");
    7539           5 :         aosGeoLoc.SetNameValue("LINE_STEP", "1");
    7540           5 :         aosGeoLoc.SetNameValue("PIXEL_OFFSET", "0");
    7541           5 :         aosGeoLoc.SetNameValue("PIXEL_STEP", "1");
    7542           5 :         aosGeoLoc.SetNameValue("SRS", SRS_WKT_WGS84_LAT_LONG);  // FIXME?
    7543           5 :         aosGeoLoc.SetNameValue("X_BAND", "1");
    7544           5 :         aosGeoLoc.SetNameValue("X_DATASET", m_osFilenameLong.c_str());
    7545           5 :         aosGeoLoc.SetNameValue("Y_BAND", "1");
    7546           5 :         aosGeoLoc.SetNameValue("Y_DATASET", m_osFilenameLat.c_str());
    7547           5 :         aosGeoLoc.SetNameValue("GEOREFERENCING_CONVENTION", "PIXEL_CENTER");
    7548           5 :         SetMetadata(aosGeoLoc.List(), "GEOLOCATION");
    7549           5 :     }
    7550             : };
    7551             : 
    7552             : /************************************************************************/
    7553             : /*                   GDALMDArrayResampledDatasetRasterBand()            */
    7554             : /************************************************************************/
    7555             : 
    7556          24 : GDALMDArrayResampledDatasetRasterBand::GDALMDArrayResampledDatasetRasterBand(
    7557          24 :     GDALMDArrayResampledDataset *poDSIn)
    7558             : {
    7559          24 :     const auto &poArray(poDSIn->m_poArray);
    7560          24 :     const auto blockSize(poArray->GetBlockSize());
    7561          24 :     nBlockYSize = (blockSize[poDSIn->m_iYDim])
    7562          24 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7563          13 :                                                   blockSize[poDSIn->m_iYDim]))
    7564          24 :                       : 1;
    7565          24 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    7566          13 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    7567          13 :                                                   blockSize[poDSIn->m_iXDim]))
    7568          24 :                       : poDSIn->GetRasterXSize();
    7569          24 :     eDataType = poArray->GetDataType().GetNumericDataType();
    7570          24 :     eAccess = poDSIn->eAccess;
    7571          24 : }
    7572             : 
    7573             : /************************************************************************/
    7574             : /*                           GetNoDataValue()                           */
    7575             : /************************************************************************/
    7576             : 
    7577          46 : double GDALMDArrayResampledDatasetRasterBand::GetNoDataValue(int *pbHasNoData)
    7578             : {
    7579          46 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7580          46 :     const auto &poArray(l_poDS->m_poArray);
    7581          46 :     bool bHasNodata = false;
    7582          46 :     double dfRes = poArray->GetNoDataValueAsDouble(&bHasNodata);
    7583          46 :     if (pbHasNoData)
    7584          44 :         *pbHasNoData = bHasNodata;
    7585          46 :     return dfRes;
    7586             : }
    7587             : 
    7588             : /************************************************************************/
    7589             : /*                            IReadBlock()                              */
    7590             : /************************************************************************/
    7591             : 
    7592           0 : CPLErr GDALMDArrayResampledDatasetRasterBand::IReadBlock(int nBlockXOff,
    7593             :                                                          int nBlockYOff,
    7594             :                                                          void *pImage)
    7595             : {
    7596           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    7597           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    7598           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    7599           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    7600           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    7601             :     GDALRasterIOExtraArg sExtraArg;
    7602           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    7603           0 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    7604             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    7605           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    7606             : }
    7607             : 
    7608             : /************************************************************************/
    7609             : /*                            IRasterIO()                               */
    7610             : /************************************************************************/
    7611             : 
    7612          35 : CPLErr GDALMDArrayResampledDatasetRasterBand::IRasterIO(
    7613             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    7614             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    7615             :     GSpacing nPixelSpaceBuf, GSpacing nLineSpaceBuf,
    7616             :     GDALRasterIOExtraArg *psExtraArg)
    7617             : {
    7618          35 :     auto l_poDS(cpl::down_cast<GDALMDArrayResampledDataset *>(poDS));
    7619          35 :     const auto &poArray(l_poDS->m_poArray);
    7620          35 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    7621          35 :     if (eRWFlag == GF_Read && nXSize == nBufXSize && nYSize == nBufYSize &&
    7622          35 :         nBufferDTSize > 0 && (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    7623          35 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    7624             :     {
    7625          35 :         l_poDS->m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    7626          35 :         l_poDS->m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    7627          70 :         l_poDS->m_anStride[l_poDS->m_iXDim] =
    7628          35 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    7629             : 
    7630          35 :         l_poDS->m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    7631          35 :         l_poDS->m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    7632          70 :         l_poDS->m_anStride[l_poDS->m_iYDim] =
    7633          35 :             static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    7634             : 
    7635          70 :         return poArray->Read(l_poDS->m_anOffset.data(),
    7636          35 :                              l_poDS->m_anCount.data(), nullptr,
    7637          35 :                              l_poDS->m_anStride.data(),
    7638          70 :                              GDALExtendedDataType::Create(eBufType), pData)
    7639          35 :                    ? CE_None
    7640          35 :                    : CE_Failure;
    7641             :     }
    7642           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    7643             :                                      pData, nBufXSize, nBufYSize, eBufType,
    7644           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    7645             : }
    7646             : 
    7647             : class GDALMDArrayResampled final : public GDALPamMDArray
    7648             : {
    7649             :   private:
    7650             :     std::shared_ptr<GDALMDArray> m_poParent{};
    7651             :     std::vector<std::shared_ptr<GDALDimension>> m_apoDims;
    7652             :     std::vector<GUInt64> m_anBlockSize;
    7653             :     GDALExtendedDataType m_dt;
    7654             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    7655             :     std::shared_ptr<GDALMDArray> m_poVarX{};
    7656             :     std::shared_ptr<GDALMDArray> m_poVarY{};
    7657             :     std::unique_ptr<GDALMDArrayResampledDataset> m_poParentDS{};
    7658             :     std::unique_ptr<GDALDataset> m_poReprojectedDS{};
    7659             : 
    7660             :   protected:
    7661          21 :     GDALMDArrayResampled(
    7662             :         const std::shared_ptr<GDALMDArray> &poParent,
    7663             :         const std::vector<std::shared_ptr<GDALDimension>> &apoDims,
    7664             :         const std::vector<GUInt64> &anBlockSize)
    7665          42 :         : GDALAbstractMDArray(std::string(),
    7666          42 :                               "Resampled view of " + poParent->GetFullName()),
    7667             :           GDALPamMDArray(
    7668          42 :               std::string(), "Resampled view of " + poParent->GetFullName(),
    7669          42 :               GDALPamMultiDim::GetPAM(poParent), poParent->GetContext()),
    7670          21 :           m_poParent(std::move(poParent)), m_apoDims(apoDims),
    7671         105 :           m_anBlockSize(anBlockSize), m_dt(m_poParent->GetDataType())
    7672             :     {
    7673          21 :         CPLAssert(apoDims.size() == m_poParent->GetDimensionCount());
    7674          21 :         CPLAssert(anBlockSize.size() == m_poParent->GetDimensionCount());
    7675          21 :     }
    7676             : 
    7677             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    7678             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    7679             :                const GDALExtendedDataType &bufferDataType,
    7680             :                void *pDstBuffer) const override;
    7681             : 
    7682             :   public:
    7683             :     static std::shared_ptr<GDALMDArray>
    7684             :     Create(const std::shared_ptr<GDALMDArray> &poParent,
    7685             :            const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    7686             :            GDALRIOResampleAlg resampleAlg,
    7687             :            const OGRSpatialReference *poTargetSRS, CSLConstList papszOptions);
    7688             : 
    7689          42 :     ~GDALMDArrayResampled()
    7690          21 :     {
    7691             :         // First close the warped VRT
    7692          21 :         m_poReprojectedDS.reset();
    7693          21 :         m_poParentDS.reset();
    7694          42 :     }
    7695             : 
    7696          11 :     bool IsWritable() const override
    7697             :     {
    7698          11 :         return false;
    7699             :     }
    7700             : 
    7701          74 :     const std::string &GetFilename() const override
    7702             :     {
    7703          74 :         return m_poParent->GetFilename();
    7704             :     }
    7705             : 
    7706             :     const std::vector<std::shared_ptr<GDALDimension>> &
    7707         257 :     GetDimensions() const override
    7708             :     {
    7709         257 :         return m_apoDims;
    7710             :     }
    7711             : 
    7712         109 :     const GDALExtendedDataType &GetDataType() const override
    7713             :     {
    7714         109 :         return m_dt;
    7715             :     }
    7716             : 
    7717          21 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    7718             :     {
    7719          21 :         return m_poSRS;
    7720             :     }
    7721             : 
    7722          12 :     std::vector<GUInt64> GetBlockSize() const override
    7723             :     {
    7724          12 :         return m_anBlockSize;
    7725             :     }
    7726             : 
    7727             :     std::shared_ptr<GDALAttribute>
    7728           1 :     GetAttribute(const std::string &osName) const override
    7729             :     {
    7730           1 :         return m_poParent->GetAttribute(osName);
    7731             :     }
    7732             : 
    7733             :     std::vector<std::shared_ptr<GDALAttribute>>
    7734          12 :     GetAttributes(CSLConstList papszOptions = nullptr) const override
    7735             :     {
    7736          12 :         return m_poParent->GetAttributes(papszOptions);
    7737             :     }
    7738             : 
    7739           1 :     const std::string &GetUnit() const override
    7740             :     {
    7741           1 :         return m_poParent->GetUnit();
    7742             :     }
    7743             : 
    7744           1 :     const void *GetRawNoDataValue() const override
    7745             :     {
    7746           1 :         return m_poParent->GetRawNoDataValue();
    7747             :     }
    7748             : 
    7749           1 :     double GetOffset(bool *pbHasOffset,
    7750             :                      GDALDataType *peStorageType) const override
    7751             :     {
    7752           1 :         return m_poParent->GetOffset(pbHasOffset, peStorageType);
    7753             :     }
    7754             : 
    7755           1 :     double GetScale(bool *pbHasScale,
    7756             :                     GDALDataType *peStorageType) const override
    7757             :     {
    7758           1 :         return m_poParent->GetScale(pbHasScale, peStorageType);
    7759             :     }
    7760             : };
    7761             : 
    7762             : /************************************************************************/
    7763             : /*                   GDALMDArrayResampled::Create()                     */
    7764             : /************************************************************************/
    7765             : 
    7766          29 : std::shared_ptr<GDALMDArray> GDALMDArrayResampled::Create(
    7767             :     const std::shared_ptr<GDALMDArray> &poParent,
    7768             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDimsIn,
    7769             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    7770             :     CSLConstList /* papszOptions */)
    7771             : {
    7772          29 :     const char *pszResampleAlg = "nearest";
    7773          29 :     bool unsupported = false;
    7774          29 :     switch (resampleAlg)
    7775             :     {
    7776          16 :         case GRIORA_NearestNeighbour:
    7777          16 :             pszResampleAlg = "nearest";
    7778          16 :             break;
    7779           2 :         case GRIORA_Bilinear:
    7780           2 :             pszResampleAlg = "bilinear";
    7781           2 :             break;
    7782           5 :         case GRIORA_Cubic:
    7783           5 :             pszResampleAlg = "cubic";
    7784           5 :             break;
    7785           1 :         case GRIORA_CubicSpline:
    7786           1 :             pszResampleAlg = "cubicspline";
    7787           1 :             break;
    7788           1 :         case GRIORA_Lanczos:
    7789           1 :             pszResampleAlg = "lanczos";
    7790           1 :             break;
    7791           1 :         case GRIORA_Average:
    7792           1 :             pszResampleAlg = "average";
    7793           1 :             break;
    7794           1 :         case GRIORA_Mode:
    7795           1 :             pszResampleAlg = "mode";
    7796           1 :             break;
    7797           1 :         case GRIORA_Gauss:
    7798           1 :             unsupported = true;
    7799           1 :             break;
    7800           0 :         case GRIORA_RESERVED_START:
    7801           0 :             unsupported = true;
    7802           0 :             break;
    7803           0 :         case GRIORA_RESERVED_END:
    7804           0 :             unsupported = true;
    7805           0 :             break;
    7806           1 :         case GRIORA_RMS:
    7807           1 :             pszResampleAlg = "rms";
    7808           1 :             break;
    7809             :     }
    7810          29 :     if (unsupported)
    7811             :     {
    7812           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7813             :                  "Unsupported resample method for GetResampled()");
    7814           1 :         return nullptr;
    7815             :     }
    7816             : 
    7817          28 :     if (poParent->GetDimensionCount() < 2)
    7818             :     {
    7819           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    7820             :                  "GetResampled() only supports 2 dimensions or more");
    7821           1 :         return nullptr;
    7822             :     }
    7823             : 
    7824          27 :     const auto &aoParentDims = poParent->GetDimensions();
    7825          27 :     if (apoNewDimsIn.size() != aoParentDims.size())
    7826             :     {
    7827           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    7828             :                  "GetResampled(): apoNewDims size should be the same as "
    7829             :                  "GetDimensionCount()");
    7830           2 :         return nullptr;
    7831             :     }
    7832             : 
    7833          50 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims;
    7834          25 :     apoNewDims.reserve(apoNewDimsIn.size());
    7835             : 
    7836          50 :     std::vector<GUInt64> anBlockSize;
    7837          25 :     anBlockSize.reserve(apoNewDimsIn.size());
    7838          50 :     const auto &anParentBlockSize = poParent->GetBlockSize();
    7839             : 
    7840          50 :     auto apoParentDims = poParent->GetDimensions();
    7841             :     // Special case for NASA EMIT datasets
    7842          30 :     const bool bYXBandOrder = (apoParentDims.size() == 3 &&
    7843           7 :                                apoParentDims[0]->GetName() == "downtrack" &&
    7844          32 :                                apoParentDims[1]->GetName() == "crosstrack" &&
    7845           2 :                                apoParentDims[2]->GetName() == "bands");
    7846             : 
    7847             :     const size_t iYDimParent =
    7848          25 :         bYXBandOrder ? 0 : poParent->GetDimensionCount() - 2;
    7849             :     const size_t iXDimParent =
    7850          25 :         bYXBandOrder ? 1 : poParent->GetDimensionCount() - 1;
    7851             : 
    7852          77 :     for (unsigned i = 0; i < apoNewDimsIn.size(); ++i)
    7853             :     {
    7854          53 :         if (i == iYDimParent || i == iXDimParent)
    7855          48 :             continue;
    7856           5 :         if (apoNewDimsIn[i] == nullptr)
    7857             :         {
    7858           3 :             apoNewDims.emplace_back(aoParentDims[i]);
    7859             :         }
    7860           3 :         else if (apoNewDimsIn[i]->GetSize() != aoParentDims[i]->GetSize() ||
    7861           1 :                  apoNewDimsIn[i]->GetName() != aoParentDims[i]->GetName())
    7862             :         {
    7863           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    7864             :                      "GetResampled(): apoNewDims[%u] should be the same "
    7865             :                      "as its parent",
    7866             :                      i);
    7867           1 :             return nullptr;
    7868             :         }
    7869             :         else
    7870             :         {
    7871           1 :             apoNewDims.emplace_back(aoParentDims[i]);
    7872             :         }
    7873           4 :         anBlockSize.emplace_back(anParentBlockSize[i]);
    7874             :     }
    7875             : 
    7876             :     std::unique_ptr<GDALMDArrayResampledDataset> poParentDS(
    7877          48 :         new GDALMDArrayResampledDataset(poParent, iXDimParent, iYDimParent));
    7878             : 
    7879          24 :     double dfXStart = 0.0;
    7880          24 :     double dfXSpacing = 0.0;
    7881          24 :     bool gotXSpacing = false;
    7882          48 :     auto poNewDimX = apoNewDimsIn[iXDimParent];
    7883          24 :     if (poNewDimX)
    7884             :     {
    7885           4 :         if (poNewDimX->GetSize() > static_cast<GUInt64>(INT_MAX))
    7886             :         {
    7887           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    7888             :                      "Too big size for X dimension");
    7889           0 :             return nullptr;
    7890             :         }
    7891           4 :         auto var = poNewDimX->GetIndexingVariable();
    7892           4 :         if (var)
    7893             :         {
    7894           2 :             if (var->GetDimensionCount() != 1 ||
    7895           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimX->GetSize() ||
    7896           1 :                 !var->IsRegularlySpaced(dfXStart, dfXSpacing))
    7897             :             {
    7898           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    7899             :                          "New X dimension should be indexed by a regularly "
    7900             :                          "spaced variable");
    7901           0 :                 return nullptr;
    7902             :             }
    7903           1 :             gotXSpacing = true;
    7904             :         }
    7905             :     }
    7906             : 
    7907          24 :     double dfYStart = 0.0;
    7908          24 :     double dfYSpacing = 0.0;
    7909          48 :     auto poNewDimY = apoNewDimsIn[iYDimParent];
    7910          24 :     bool gotYSpacing = false;
    7911          24 :     if (poNewDimY)
    7912             :     {
    7913           4 :         if (poNewDimY->GetSize() > static_cast<GUInt64>(INT_MAX))
    7914             :         {
    7915           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    7916             :                      "Too big size for Y dimension");
    7917           0 :             return nullptr;
    7918             :         }
    7919           4 :         auto var = poNewDimY->GetIndexingVariable();
    7920           4 :         if (var)
    7921             :         {
    7922           2 :             if (var->GetDimensionCount() != 1 ||
    7923           2 :                 var->GetDimensions()[0]->GetSize() != poNewDimY->GetSize() ||
    7924           1 :                 !var->IsRegularlySpaced(dfYStart, dfYSpacing))
    7925             :             {
    7926           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    7927             :                          "New Y dimension should be indexed by a regularly "
    7928             :                          "spaced variable");
    7929           0 :                 return nullptr;
    7930             :             }
    7931           1 :             gotYSpacing = true;
    7932             :         }
    7933             :     }
    7934             : 
    7935             :     // This limitation could probably be removed
    7936          24 :     if ((gotXSpacing && !gotYSpacing) || (!gotXSpacing && gotYSpacing))
    7937             :     {
    7938           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    7939             :                  "Either none of new X or Y dimension should have an indexing "
    7940             :                  "variable, or both should both should have one.");
    7941           0 :         return nullptr;
    7942             :     }
    7943             : 
    7944          48 :     std::string osDstWKT;
    7945          24 :     if (poTargetSRS)
    7946             :     {
    7947           2 :         char *pszDstWKT = nullptr;
    7948           2 :         if (poTargetSRS->exportToWkt(&pszDstWKT) != OGRERR_NONE)
    7949             :         {
    7950           0 :             CPLFree(pszDstWKT);
    7951           0 :             return nullptr;
    7952             :         }
    7953           2 :         osDstWKT = pszDstWKT;
    7954           2 :         CPLFree(pszDstWKT);
    7955             :     }
    7956             : 
    7957             :     // Use coordinate variables for geolocation array
    7958          48 :     const auto apoCoordinateVars = poParent->GetCoordinateVariables();
    7959          24 :     bool useGeolocationArray = false;
    7960          24 :     if (apoCoordinateVars.size() >= 2)
    7961             :     {
    7962           0 :         std::shared_ptr<GDALMDArray> poLongVar;
    7963           0 :         std::shared_ptr<GDALMDArray> poLatVar;
    7964          15 :         for (const auto &poCoordVar : apoCoordinateVars)
    7965             :         {
    7966          10 :             const auto &osName = poCoordVar->GetName();
    7967          30 :             const auto poAttr = poCoordVar->GetAttribute("standard_name");
    7968          20 :             std::string osStandardName;
    7969          12 :             if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING &&
    7970           2 :                 poAttr->GetDimensionCount() == 0)
    7971             :             {
    7972           2 :                 const char *pszStandardName = poAttr->ReadAsString();
    7973           2 :                 if (pszStandardName)
    7974           2 :                     osStandardName = pszStandardName;
    7975             :             }
    7976          21 :             if (osName == "lon" || osName == "longitude" ||
    7977          21 :                 osName == "Longitude" || osStandardName == "longitude")
    7978             :             {
    7979           5 :                 poLongVar = poCoordVar;
    7980             :             }
    7981           6 :             else if (osName == "lat" || osName == "latitude" ||
    7982           6 :                      osName == "Latitude" || osStandardName == "latitude")
    7983             :             {
    7984           5 :                 poLatVar = poCoordVar;
    7985             :             }
    7986             :         }
    7987           5 :         if (poLatVar != nullptr && poLongVar != nullptr)
    7988             :         {
    7989           5 :             const auto longDimCount = poLongVar->GetDimensionCount();
    7990           5 :             const auto &longDims = poLongVar->GetDimensions();
    7991           5 :             const auto latDimCount = poLatVar->GetDimensionCount();
    7992           5 :             const auto &latDims = poLatVar->GetDimensions();
    7993           5 :             const auto xDimSize = aoParentDims[iXDimParent]->GetSize();
    7994           5 :             const auto yDimSize = aoParentDims[iYDimParent]->GetSize();
    7995           0 :             if (longDimCount == 1 && longDims[0]->GetSize() == xDimSize &&
    7996           5 :                 latDimCount == 1 && latDims[0]->GetSize() == yDimSize)
    7997             :             {
    7998             :                 // Geolocation arrays are 1D, and of consistent size with
    7999             :                 // the variable
    8000           0 :                 useGeolocationArray = true;
    8001             :             }
    8002           1 :             else if ((longDimCount == 2 ||
    8003           6 :                       (longDimCount == 3 && longDims[0]->GetSize() == 1)) &&
    8004          10 :                      longDims[longDimCount - 2]->GetSize() == yDimSize &&
    8005          10 :                      longDims[longDimCount - 1]->GetSize() == xDimSize &&
    8006           1 :                      (latDimCount == 2 ||
    8007           6 :                       (latDimCount == 3 && latDims[0]->GetSize() == 1)) &&
    8008          15 :                      latDims[latDimCount - 2]->GetSize() == yDimSize &&
    8009           5 :                      latDims[latDimCount - 1]->GetSize() == xDimSize)
    8010             : 
    8011             :             {
    8012             :                 // Geolocation arrays are 2D (or 3D with first dimension of
    8013             :                 // size 1, as found in Sentinel 5P products), and of consistent
    8014             :                 // size with the variable
    8015           5 :                 useGeolocationArray = true;
    8016             :             }
    8017             :             else
    8018             :             {
    8019           0 :                 CPLDebug(
    8020             :                     "GDAL",
    8021             :                     "Longitude and latitude coordinate variables found, "
    8022             :                     "but their characteristics are not compatible of using "
    8023             :                     "them as geolocation arrays");
    8024             :             }
    8025           5 :             if (useGeolocationArray)
    8026             :             {
    8027          10 :                 CPLDebug("GDAL",
    8028             :                          "Setting geolocation array from variables %s and %s",
    8029           5 :                          poLongVar->GetName().c_str(),
    8030           5 :                          poLatVar->GetName().c_str());
    8031             :                 std::string osFilenameLong =
    8032           5 :                     CPLSPrintf("/vsimem/%p/longitude.tif", poParent.get());
    8033             :                 std::string osFilenameLat =
    8034           5 :                     CPLSPrintf("/vsimem/%p/latitude.tif", poParent.get());
    8035             :                 std::unique_ptr<GDALDataset> poTmpLongDS(
    8036             :                     longDimCount == 1
    8037           0 :                         ? poLongVar->AsClassicDataset(0, 0)
    8038          20 :                         : poLongVar->AsClassicDataset(longDimCount - 1,
    8039          15 :                                                       longDimCount - 2));
    8040           5 :                 auto hTIFFLongDS = GDALTranslate(
    8041             :                     osFilenameLong.c_str(),
    8042             :                     GDALDataset::ToHandle(poTmpLongDS.get()), nullptr, nullptr);
    8043             :                 std::unique_ptr<GDALDataset> poTmpLatDS(
    8044           0 :                     latDimCount == 1 ? poLatVar->AsClassicDataset(0, 0)
    8045          20 :                                      : poLatVar->AsClassicDataset(
    8046          15 :                                            latDimCount - 1, latDimCount - 2));
    8047           5 :                 auto hTIFFLatDS = GDALTranslate(
    8048             :                     osFilenameLat.c_str(),
    8049             :                     GDALDataset::ToHandle(poTmpLatDS.get()), nullptr, nullptr);
    8050           5 :                 const bool bError =
    8051           5 :                     (hTIFFLatDS == nullptr || hTIFFLongDS == nullptr);
    8052           5 :                 GDALClose(hTIFFLongDS);
    8053           5 :                 GDALClose(hTIFFLatDS);
    8054           5 :                 if (bError)
    8055             :                 {
    8056           0 :                     VSIUnlink(osFilenameLong.c_str());
    8057           0 :                     VSIUnlink(osFilenameLat.c_str());
    8058           0 :                     return nullptr;
    8059             :                 }
    8060             : 
    8061           5 :                 poParentDS->SetGeolocationArray(osFilenameLong, osFilenameLat);
    8062             :             }
    8063             :         }
    8064             :         else
    8065             :         {
    8066           0 :             CPLDebug("GDAL",
    8067             :                      "Coordinate variables available for %s, but "
    8068             :                      "longitude and/or latitude variables were not identified",
    8069           0 :                      poParent->GetName().c_str());
    8070             :         }
    8071             :     }
    8072             : 
    8073             :     // Build gdalwarp arguments
    8074          48 :     CPLStringList aosArgv;
    8075             : 
    8076          24 :     aosArgv.AddString("-of");
    8077          24 :     aosArgv.AddString("VRT");
    8078             : 
    8079          24 :     aosArgv.AddString("-r");
    8080          24 :     aosArgv.AddString(pszResampleAlg);
    8081             : 
    8082          24 :     if (!osDstWKT.empty())
    8083             :     {
    8084           2 :         aosArgv.AddString("-t_srs");
    8085           2 :         aosArgv.AddString(osDstWKT.c_str());
    8086             :     }
    8087             : 
    8088          24 :     if (useGeolocationArray)
    8089           5 :         aosArgv.AddString("-geoloc");
    8090             : 
    8091          24 :     if (gotXSpacing && gotYSpacing)
    8092             :     {
    8093           1 :         const double dfXMin = dfXStart - dfXSpacing / 2;
    8094             :         const double dfXMax =
    8095           1 :             dfXMin + dfXSpacing * static_cast<double>(poNewDimX->GetSize());
    8096           1 :         const double dfYMax = dfYStart - dfYSpacing / 2;
    8097             :         const double dfYMin =
    8098           1 :             dfYMax + dfYSpacing * static_cast<double>(poNewDimY->GetSize());
    8099           1 :         aosArgv.AddString("-te");
    8100           1 :         aosArgv.AddString(CPLSPrintf("%.18g", dfXMin));
    8101           1 :         aosArgv.AddString(CPLSPrintf("%.18g", dfYMin));
    8102           1 :         aosArgv.AddString(CPLSPrintf("%.18g", dfXMax));
    8103           1 :         aosArgv.AddString(CPLSPrintf("%.18g", dfYMax));
    8104             :     }
    8105             : 
    8106          24 :     if (poNewDimX && poNewDimY)
    8107             :     {
    8108           3 :         aosArgv.AddString("-ts");
    8109             :         aosArgv.AddString(
    8110           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8111             :         aosArgv.AddString(
    8112           3 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8113             :     }
    8114          21 :     else if (poNewDimX)
    8115             :     {
    8116           1 :         aosArgv.AddString("-ts");
    8117             :         aosArgv.AddString(
    8118           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimX->GetSize())));
    8119           1 :         aosArgv.AddString("0");
    8120             :     }
    8121          20 :     else if (poNewDimY)
    8122             :     {
    8123           1 :         aosArgv.AddString("-ts");
    8124           1 :         aosArgv.AddString("0");
    8125             :         aosArgv.AddString(
    8126           1 :             CPLSPrintf("%d", static_cast<int>(poNewDimY->GetSize())));
    8127             :     }
    8128             : 
    8129             :     // Create a warped VRT dataset
    8130             :     GDALWarpAppOptions *psOptions =
    8131          24 :         GDALWarpAppOptionsNew(aosArgv.List(), nullptr);
    8132          24 :     GDALDatasetH hSrcDS = GDALDataset::ToHandle(poParentDS.get());
    8133             :     std::unique_ptr<GDALDataset> poReprojectedDS(GDALDataset::FromHandle(
    8134          48 :         GDALWarp("", nullptr, 1, &hSrcDS, psOptions, nullptr)));
    8135          24 :     GDALWarpAppOptionsFree(psOptions);
    8136          24 :     if (poReprojectedDS == nullptr)
    8137           3 :         return nullptr;
    8138             : 
    8139             :     int nBlockXSize;
    8140             :     int nBlockYSize;
    8141          21 :     poReprojectedDS->GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    8142          21 :     anBlockSize.emplace_back(nBlockYSize);
    8143          21 :     anBlockSize.emplace_back(nBlockXSize);
    8144             : 
    8145          21 :     double adfGeoTransform[6] = {0, 0, 0, 0, 0, 0};
    8146          21 :     CPLErr eErr = poReprojectedDS->GetGeoTransform(adfGeoTransform);
    8147          21 :     CPLAssert(eErr == CE_None);
    8148          21 :     CPL_IGNORE_RET_VAL(eErr);
    8149             : 
    8150             :     auto poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    8151           0 :         std::string(), "dimY", GDAL_DIM_TYPE_HORIZONTAL_Y, "NORTH",
    8152          42 :         poReprojectedDS->GetRasterYSize());
    8153             :     auto varY = GDALMDArrayRegularlySpaced::Create(
    8154          63 :         std::string(), poDimY->GetName(), poDimY,
    8155          84 :         adfGeoTransform[3] + adfGeoTransform[5] / 2, adfGeoTransform[5], 0);
    8156          21 :     poDimY->SetIndexingVariable(varY);
    8157             : 
    8158             :     auto poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    8159           0 :         std::string(), "dimX", GDAL_DIM_TYPE_HORIZONTAL_X, "EAST",
    8160          42 :         poReprojectedDS->GetRasterXSize());
    8161             :     auto varX = GDALMDArrayRegularlySpaced::Create(
    8162          63 :         std::string(), poDimX->GetName(), poDimX,
    8163          84 :         adfGeoTransform[0] + adfGeoTransform[1] / 2, adfGeoTransform[1], 0);
    8164          21 :     poDimX->SetIndexingVariable(varX);
    8165             : 
    8166          21 :     apoNewDims.emplace_back(poDimY);
    8167          21 :     apoNewDims.emplace_back(poDimX);
    8168             :     auto newAr(std::shared_ptr<GDALMDArrayResampled>(
    8169          42 :         new GDALMDArrayResampled(poParent, apoNewDims, anBlockSize)));
    8170          21 :     newAr->SetSelf(newAr);
    8171          21 :     if (poTargetSRS)
    8172             :     {
    8173           2 :         newAr->m_poSRS.reset(poTargetSRS->Clone());
    8174             :     }
    8175             :     else
    8176             :     {
    8177          19 :         newAr->m_poSRS = poParent->GetSpatialRef();
    8178             :     }
    8179          21 :     newAr->m_poVarX = varX;
    8180          21 :     newAr->m_poVarY = varY;
    8181          21 :     newAr->m_poReprojectedDS = std::move(poReprojectedDS);
    8182          21 :     newAr->m_poParentDS = std::move(poParentDS);
    8183             : 
    8184             :     // If the input array is y,x,band ordered, the above newAr is
    8185             :     // actually band,y,x ordered as it is more convenient for
    8186             :     // GDALMDArrayResampled::IRead() implementation. But transpose that
    8187             :     // array to the order of the input array
    8188          21 :     if (bYXBandOrder)
    8189           4 :         return newAr->Transpose(std::vector<int>{1, 2, 0});
    8190             : 
    8191          19 :     return newAr;
    8192             : }
    8193             : 
    8194             : /************************************************************************/
    8195             : /*                   GDALMDArrayResampled::IRead()                      */
    8196             : /************************************************************************/
    8197             : 
    8198          29 : bool GDALMDArrayResampled::IRead(const GUInt64 *arrayStartIdx,
    8199             :                                  const size_t *count, const GInt64 *arrayStep,
    8200             :                                  const GPtrDiff_t *bufferStride,
    8201             :                                  const GDALExtendedDataType &bufferDataType,
    8202             :                                  void *pDstBuffer) const
    8203             : {
    8204          29 :     if (bufferDataType.GetClass() != GEDTC_NUMERIC)
    8205           0 :         return false;
    8206             : 
    8207             :     struct Stack
    8208             :     {
    8209             :         size_t nIters = 0;
    8210             :         GByte *dst_ptr = nullptr;
    8211             :         GPtrDiff_t dst_inc_offset = 0;
    8212             :     };
    8213             : 
    8214          29 :     const auto nDims = GetDimensionCount();
    8215          58 :     std::vector<Stack> stack(nDims + 1);  // +1 to avoid -Wnull-dereference
    8216          29 :     const size_t nBufferDTSize = bufferDataType.GetSize();
    8217          92 :     for (size_t i = 0; i < nDims; i++)
    8218             :     {
    8219          63 :         stack[i].dst_inc_offset =
    8220          63 :             static_cast<GPtrDiff_t>(bufferStride[i] * nBufferDTSize);
    8221             :     }
    8222          29 :     stack[0].dst_ptr = static_cast<GByte *>(pDstBuffer);
    8223             : 
    8224          29 :     size_t dimIdx = 0;
    8225          29 :     const size_t iDimY = nDims - 2;
    8226          29 :     const size_t iDimX = nDims - 1;
    8227             :     // Use an array to avoid a false positive warning from CLang Static
    8228             :     // Analyzer about flushCaches being never read
    8229          29 :     bool flushCaches[] = {false};
    8230             :     const bool bYXBandOrder =
    8231          29 :         m_poParentDS->m_iYDim == 0 && m_poParentDS->m_iXDim == 1;
    8232             : 
    8233          38 : lbl_next_depth:
    8234          38 :     if (dimIdx == iDimY)
    8235             :     {
    8236          33 :         if (flushCaches[0])
    8237             :         {
    8238           5 :             flushCaches[0] = false;
    8239             :             // When changing of 2D slice, flush GDAL 2D buffers
    8240           5 :             m_poParentDS->FlushCache(false);
    8241           5 :             m_poReprojectedDS->FlushCache(false);
    8242             :         }
    8243             : 
    8244          33 :         if (!GDALMDRasterIOFromBand(m_poReprojectedDS->GetRasterBand(1),
    8245             :                                     GF_Read, iDimX, iDimY, arrayStartIdx, count,
    8246             :                                     arrayStep, bufferStride, bufferDataType,
    8247          33 :                                     stack[dimIdx].dst_ptr))
    8248             :         {
    8249           0 :             return false;
    8250             :         }
    8251             :     }
    8252             :     else
    8253             :     {
    8254           5 :         stack[dimIdx].nIters = count[dimIdx];
    8255           5 :         if (m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] !=
    8256           5 :             arrayStartIdx[dimIdx])
    8257             :         {
    8258           1 :             flushCaches[0] = true;
    8259             :         }
    8260           5 :         m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx] =
    8261           5 :             arrayStartIdx[dimIdx];
    8262             :         while (true)
    8263             :         {
    8264           9 :             dimIdx++;
    8265           9 :             stack[dimIdx].dst_ptr = stack[dimIdx - 1].dst_ptr;
    8266           9 :             goto lbl_next_depth;
    8267           9 :         lbl_return_to_caller:
    8268           9 :             dimIdx--;
    8269           9 :             if ((--stack[dimIdx].nIters) == 0)
    8270           5 :                 break;
    8271           4 :             flushCaches[0] = true;
    8272           4 :             ++m_poParentDS->m_anOffset[bYXBandOrder ? 2 : dimIdx];
    8273           4 :             stack[dimIdx].dst_ptr += stack[dimIdx].dst_inc_offset;
    8274             :         }
    8275             :     }
    8276          38 :     if (dimIdx > 0)
    8277           9 :         goto lbl_return_to_caller;
    8278             : 
    8279          29 :     return true;
    8280             : }
    8281             : 
    8282             : /************************************************************************/
    8283             : /*                           GetResampled()                             */
    8284             : /************************************************************************/
    8285             : 
    8286             : /** Return an array that is a resampled / reprojected view of the current array
    8287             :  *
    8288             :  * This is the same as the C function GDALMDArrayGetResampled().
    8289             :  *
    8290             :  * Currently this method can only resample along the last 2 dimensions, unless
    8291             :  * orthorectifying a NASA EMIT dataset.
    8292             :  *
    8293             :  * For NASA EMIT datasets, if apoNewDims[] and poTargetSRS is NULL, the
    8294             :  * geometry lookup table (GLT) is used for fast orthorectification.
    8295             :  *
    8296             :  * @param apoNewDims New dimensions. Its size should be GetDimensionCount().
    8297             :  *                   apoNewDims[i] can be NULL to let the method automatically
    8298             :  *                   determine it.
    8299             :  * @param resampleAlg Resampling algorithm
    8300             :  * @param poTargetSRS Target SRS, or nullptr
    8301             :  * @param papszOptions NULL-terminated list of options, or NULL.
    8302             :  *
    8303             :  * @return a new array, that holds a reference to the original one, and thus is
    8304             :  * a view of it (not a copy), or nullptr in case of error.
    8305             :  *
    8306             :  * @since 3.4
    8307             :  */
    8308          34 : std::shared_ptr<GDALMDArray> GDALMDArray::GetResampled(
    8309             :     const std::vector<std::shared_ptr<GDALDimension>> &apoNewDims,
    8310             :     GDALRIOResampleAlg resampleAlg, const OGRSpatialReference *poTargetSRS,
    8311             :     CSLConstList papszOptions) const
    8312             : {
    8313          68 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    8314          34 :     if (!self)
    8315             :     {
    8316           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8317             :                  "Driver implementation issue: m_pSelf not set !");
    8318           0 :         return nullptr;
    8319             :     }
    8320          34 :     if (GetDataType().GetClass() != GEDTC_NUMERIC)
    8321             :     {
    8322           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8323             :                  "GetResampled() only supports numeric data type");
    8324           0 :         return nullptr;
    8325             :     }
    8326             : 
    8327             :     // Special case for NASA EMIT datasets
    8328          68 :     auto apoDims = GetDimensions();
    8329          32 :     if (poTargetSRS == nullptr &&
    8330          47 :         ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    8331          12 :           apoDims[1]->GetName() == "crosstrack" &&
    8332           6 :           apoDims[2]->GetName() == "bands" &&
    8333          40 :           (apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(3) ||
    8334           1 :            apoNewDims ==
    8335          38 :                std::vector<std::shared_ptr<GDALDimension>>{nullptr, nullptr,
    8336          30 :                                                            apoDims[2]})) ||
    8337          51 :          (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    8338           3 :           apoDims[1]->GetName() == "crosstrack" &&
    8339          69 :           apoNewDims == std::vector<std::shared_ptr<GDALDimension>>(2))) &&
    8340           9 :         CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8341             :                                          "EMIT_ORTHORECTIFICATION", "YES")))
    8342             :     {
    8343           5 :         auto poRootGroup = GetRootGroup();
    8344           5 :         if (poRootGroup)
    8345             :         {
    8346          10 :             auto poAttrGeotransform = poRootGroup->GetAttribute("geotransform");
    8347          10 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    8348           5 :             if (poAttrGeotransform &&
    8349           5 :                 poAttrGeotransform->GetDataType().GetClass() == GEDTC_NUMERIC &&
    8350           5 :                 poAttrGeotransform->GetDimensionCount() == 1 &&
    8351          15 :                 poAttrGeotransform->GetDimensionsSize()[0] == 6 &&
    8352           5 :                 poLocationGroup)
    8353             :             {
    8354          10 :                 auto poGLT_X = poLocationGroup->OpenMDArray("glt_x");
    8355          10 :                 auto poGLT_Y = poLocationGroup->OpenMDArray("glt_y");
    8356          15 :                 if (poGLT_X && poGLT_X->GetDimensionCount() == 2 &&
    8357          10 :                     poGLT_X->GetDimensions()[0]->GetName() == "ortho_y" &&
    8358          10 :                     poGLT_X->GetDimensions()[1]->GetName() == "ortho_x" &&
    8359          15 :                     poGLT_Y && poGLT_Y->GetDimensionCount() == 2 &&
    8360          15 :                     poGLT_Y->GetDimensions()[0]->GetName() == "ortho_y" &&
    8361           5 :                     poGLT_Y->GetDimensions()[1]->GetName() == "ortho_x")
    8362             :                 {
    8363             :                     return CreateGLTOrthorectified(
    8364             :                         self, poGLT_X, poGLT_Y,
    8365             :                         /* nGLTIndexOffset = */ -1,
    8366          10 :                         poAttrGeotransform->ReadAsDoubleArray());
    8367             :                 }
    8368             :             }
    8369             :         }
    8370             :     }
    8371             : 
    8372          29 :     if (CPLTestBool(CSLFetchNameValueDef(papszOptions,
    8373             :                                          "EMIT_ORTHORECTIFICATION", "NO")))
    8374             :     {
    8375           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    8376             :                  "EMIT_ORTHORECTIFICATION required, but dataset and/or "
    8377             :                  "parameters are not compatible with it");
    8378           0 :         return nullptr;
    8379             :     }
    8380             : 
    8381             :     return GDALMDArrayResampled::Create(self, apoNewDims, resampleAlg,
    8382          29 :                                         poTargetSRS, papszOptions);
    8383             : }
    8384             : 
    8385             : /************************************************************************/
    8386             : /*                         GDALDatasetFromArray()                       */
    8387             : /************************************************************************/
    8388             : 
    8389             : class GDALDatasetFromArray;
    8390             : 
    8391             : namespace
    8392             : {
    8393             : struct MetadataItem
    8394             : {
    8395             :     std::shared_ptr<GDALMDArray> poArray{};
    8396             :     std::string osName{};
    8397             :     std::string osDefinition{};
    8398             :     bool bDefinitionUsesPctForG = false;
    8399             : };
    8400             : }  // namespace
    8401             : 
    8402             : class GDALRasterBandFromArray final : public GDALPamRasterBand
    8403             : {
    8404             :     std::vector<GUInt64> m_anOffset{};
    8405             :     std::vector<size_t> m_anCount{};
    8406             :     std::vector<GPtrDiff_t> m_anStride{};
    8407             : 
    8408             :   protected:
    8409             :     CPLErr IReadBlock(int, int, void *) override;
    8410             :     CPLErr IWriteBlock(int, int, void *) override;
    8411             :     CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
    8412             :                      int nYSize, void *pData, int nBufXSize, int nBufYSize,
    8413             :                      GDALDataType eBufType, GSpacing nPixelSpaceBuf,
    8414             :                      GSpacing nLineSpaceBuf,
    8415             :                      GDALRasterIOExtraArg *psExtraArg) override;
    8416             : 
    8417             :   public:
    8418             :     explicit GDALRasterBandFromArray(
    8419             :         GDALDatasetFromArray *poDSIn,
    8420             :         const std::vector<GUInt64> &anOtherDimCoord,
    8421             :         const std::vector<std::vector<MetadataItem>>
    8422             :             &aoBandParameterMetadataItems,
    8423             :         double dfDelay, time_t nStartTime, bool &bHasWarned);
    8424             : 
    8425             :     double GetNoDataValue(int *pbHasNoData) override;
    8426             :     int64_t GetNoDataValueAsInt64(int *pbHasNoData) override;
    8427             :     uint64_t GetNoDataValueAsUInt64(int *pbHasNoData) override;
    8428             :     double GetOffset(int *pbHasOffset) override;
    8429             :     double GetScale(int *pbHasScale) override;
    8430             :     const char *GetUnitType() override;
    8431             :     GDALColorInterp GetColorInterpretation() override;
    8432             : };
    8433             : 
    8434             : class GDALDatasetFromArray final : public GDALPamDataset
    8435             : {
    8436             :     friend class GDALRasterBandFromArray;
    8437             : 
    8438             :     std::shared_ptr<GDALMDArray> m_poArray;
    8439             :     size_t m_iXDim;
    8440             :     size_t m_iYDim;
    8441             :     double m_adfGeoTransform[6]{0, 1, 0, 0, 0, 1};
    8442             :     bool m_bHasGT = false;
    8443             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
    8444             :     GDALMultiDomainMetadata m_oMDD{};
    8445             :     std::string m_osOvrFilename{};
    8446             : 
    8447             :   public:
    8448         159 :     GDALDatasetFromArray(const std::shared_ptr<GDALMDArray> &array,
    8449             :                          size_t iXDim, size_t iYDim)
    8450         159 :         : m_poArray(array), m_iXDim(iXDim), m_iYDim(iYDim)
    8451             :     {
    8452             :         // Initialize an overview filename from the filename of the array
    8453             :         // and its name.
    8454         159 :         const std::string &osFilename = m_poArray->GetFilename();
    8455         159 :         if (!osFilename.empty())
    8456             :         {
    8457         142 :             m_osOvrFilename = osFilename;
    8458         142 :             m_osOvrFilename += '.';
    8459        4258 :             for (char ch : m_poArray->GetName())
    8460             :             {
    8461        4116 :                 if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') ||
    8462        3659 :                     (ch >= 'a' && ch <= 'z') || ch == '_')
    8463             :                 {
    8464        3296 :                     m_osOvrFilename += ch;
    8465             :                 }
    8466             :                 else
    8467             :                 {
    8468         820 :                     m_osOvrFilename += '_';
    8469             :                 }
    8470             :             }
    8471         142 :             m_osOvrFilename += ".ovr";
    8472         142 :             oOvManager.Initialize(this);
    8473             :         }
    8474         159 :     }
    8475             : 
    8476             :     static GDALDatasetFromArray *
    8477             :     Create(const std::shared_ptr<GDALMDArray> &array, size_t iXDim,
    8478             :            size_t iYDim, const std::shared_ptr<GDALGroup> &poRootGroup,
    8479             :            CSLConstList papszOptions);
    8480             : 
    8481         318 :     ~GDALDatasetFromArray()
    8482         159 :     {
    8483         159 :         GDALDatasetFromArray::Close();
    8484         318 :     }
    8485             : 
    8486         269 :     CPLErr Close() override
    8487             :     {
    8488         269 :         CPLErr eErr = CE_None;
    8489         269 :         if (nOpenFlags != OPEN_FLAGS_CLOSED)
    8490             :         {
    8491         269 :             if (GDALDatasetFromArray::FlushCache(/*bAtClosing=*/true) !=
    8492             :                 CE_None)
    8493           0 :                 eErr = CE_Failure;
    8494         269 :             m_poArray.reset();
    8495             :         }
    8496         269 :         return eErr;
    8497             :     }
    8498             : 
    8499          49 :     CPLErr GetGeoTransform(double *padfGeoTransform) override
    8500             :     {
    8501          49 :         memcpy(padfGeoTransform, m_adfGeoTransform, 6 * sizeof(double));
    8502          49 :         return m_bHasGT ? CE_None : CE_Failure;
    8503             :     }
    8504             : 
    8505          53 :     const OGRSpatialReference *GetSpatialRef() const override
    8506             :     {
    8507          53 :         if (m_poArray->GetDimensionCount() < 2)
    8508           3 :             return nullptr;
    8509          50 :         m_poSRS = m_poArray->GetSpatialRef();
    8510          50 :         if (m_poSRS)
    8511             :         {
    8512          16 :             m_poSRS.reset(m_poSRS->Clone());
    8513          32 :             auto axisMapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    8514          48 :             for (auto &m : axisMapping)
    8515             :             {
    8516          32 :                 if (m == static_cast<int>(m_iXDim) + 1)
    8517          16 :                     m = 1;
    8518          16 :                 else if (m == static_cast<int>(m_iYDim) + 1)
    8519          16 :                     m = 2;
    8520             :             }
    8521          16 :             m_poSRS->SetDataAxisToSRSAxisMapping(axisMapping);
    8522             :         }
    8523          50 :         return m_poSRS.get();
    8524             :     }
    8525             : 
    8526           4 :     CPLErr SetMetadata(char **papszMetadata, const char *pszDomain) override
    8527             :     {
    8528           4 :         return m_oMDD.SetMetadata(papszMetadata, pszDomain);
    8529             :     }
    8530             : 
    8531         144 :     char **GetMetadata(const char *pszDomain) override
    8532             :     {
    8533         144 :         return m_oMDD.GetMetadata(pszDomain);
    8534             :     }
    8535             : 
    8536         200 :     const char *GetMetadataItem(const char *pszName,
    8537             :                                 const char *pszDomain) override
    8538             :     {
    8539         370 :         if (!m_osOvrFilename.empty() && pszName &&
    8540         382 :             EQUAL(pszName, "OVERVIEW_FILE") && pszDomain &&
    8541          12 :             EQUAL(pszDomain, "OVERVIEWS"))
    8542             :         {
    8543          12 :             return m_osOvrFilename.c_str();
    8544             :         }
    8545         188 :         return m_oMDD.GetMetadataItem(pszName, pszDomain);
    8546             :     }
    8547             : };
    8548             : 
    8549             : /************************************************************************/
    8550             : /*                      GDALRasterBandFromArray()                       */
    8551             : /************************************************************************/
    8552             : 
    8553         215 : GDALRasterBandFromArray::GDALRasterBandFromArray(
    8554             :     GDALDatasetFromArray *poDSIn, const std::vector<GUInt64> &anOtherDimCoord,
    8555             :     const std::vector<std::vector<MetadataItem>> &aoBandParameterMetadataItems,
    8556         215 :     double dfDelay, time_t nStartTime, bool &bHasWarned)
    8557             : {
    8558         215 :     const auto &poArray(poDSIn->m_poArray);
    8559         215 :     const auto &dims(poArray->GetDimensions());
    8560         215 :     const auto nDimCount(dims.size());
    8561         430 :     const auto blockSize(poArray->GetBlockSize());
    8562         204 :     nBlockYSize = (nDimCount >= 2 && blockSize[poDSIn->m_iYDim])
    8563         419 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8564         123 :                                                   blockSize[poDSIn->m_iYDim]))
    8565             :                       : 1;
    8566         215 :     nBlockXSize = blockSize[poDSIn->m_iXDim]
    8567         134 :                       ? static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    8568         134 :                                                   blockSize[poDSIn->m_iXDim]))
    8569         215 :                       : poDSIn->GetRasterXSize();
    8570         215 :     eDataType = poArray->GetDataType().GetNumericDataType();
    8571         215 :     eAccess = poDSIn->eAccess;
    8572         215 :     m_anOffset.resize(nDimCount);
    8573         215 :     m_anCount.resize(nDimCount, 1);
    8574         215 :     m_anStride.resize(nDimCount);
    8575         731 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    8576             :     {
    8577         516 :         if (i != poDSIn->m_iXDim && !(nDimCount >= 2 && i == poDSIn->m_iYDim))
    8578             :         {
    8579         194 :             std::string dimName(dims[i]->GetName());
    8580          97 :             GUInt64 nIndex = anOtherDimCoord[j];
    8581             :             // Detect subset_{orig_dim_name}_{start}_{incr}_{size} names of
    8582             :             // subsetted dimensions as generated by GetView()
    8583          97 :             if (STARTS_WITH(dimName.c_str(), "subset_"))
    8584             :             {
    8585             :                 CPLStringList aosTokens(
    8586          12 :                     CSLTokenizeString2(dimName.c_str(), "_", 0));
    8587           6 :                 if (aosTokens.size() == 5)
    8588             :                 {
    8589           6 :                     dimName = aosTokens[1];
    8590          18 :                     const auto nStartDim = static_cast<GUInt64>(CPLScanUIntBig(
    8591           6 :                         aosTokens[2], static_cast<int>(strlen(aosTokens[2]))));
    8592           6 :                     const auto nIncrDim = CPLAtoGIntBig(aosTokens[3]);
    8593           6 :                     nIndex = nIncrDim > 0 ? nStartDim + nIndex * nIncrDim
    8594           0 :                                           : nStartDim - (nIndex * -nIncrDim);
    8595             :                 }
    8596             :             }
    8597          97 :             if (nDimCount != 3 || dimName != "Band")
    8598             :             {
    8599          44 :                 SetMetadataItem(
    8600             :                     CPLSPrintf("DIM_%s_INDEX", dimName.c_str()),
    8601             :                     CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nIndex)));
    8602             :             }
    8603             : 
    8604          97 :             auto indexingVar = dims[i]->GetIndexingVariable();
    8605             : 
    8606             :             // If the indexing variable is also listed in band parameter arrays,
    8607             :             // then don't use our default formatting
    8608          97 :             if (indexingVar)
    8609             :             {
    8610          34 :                 for (const auto &oItem : aoBandParameterMetadataItems[j])
    8611             :                 {
    8612          12 :                     if (oItem.poArray->GetFullName() ==
    8613          12 :                         indexingVar->GetFullName())
    8614             :                     {
    8615          12 :                         indexingVar.reset();
    8616          12 :                         break;
    8617             :                     }
    8618             :                 }
    8619             :             }
    8620             : 
    8621         119 :             if (indexingVar && indexingVar->GetDimensionCount() == 1 &&
    8622          22 :                 indexingVar->GetDimensions()[0]->GetSize() ==
    8623          22 :                     dims[i]->GetSize())
    8624             :             {
    8625          22 :                 if (dfDelay >= 0 && time(nullptr) - nStartTime > dfDelay)
    8626             :                 {
    8627           0 :                     if (!bHasWarned)
    8628             :                     {
    8629           0 :                         CPLError(
    8630             :                             CE_Warning, CPLE_AppDefined,
    8631             :                             "Maximum delay to load band metadata from "
    8632             :                             "dimension indexing variables has expired. "
    8633             :                             "Increase the value of the "
    8634             :                             "LOAD_EXTRA_DIM_METADATA_DELAY "
    8635             :                             "option of GDALMDArray::AsClassicDataset() "
    8636             :                             "(also accessible as the "
    8637             :                             "GDAL_LOAD_EXTRA_DIM_METADATA_DELAY "
    8638             :                             "configuration option), "
    8639             :                             "or set it to 'unlimited' for unlimited delay. ");
    8640           0 :                         bHasWarned = true;
    8641             :                     }
    8642             :                 }
    8643             :                 else
    8644             :                 {
    8645          22 :                     size_t nCount = 1;
    8646          22 :                     const auto &dt(indexingVar->GetDataType());
    8647          44 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8648          44 :                     if (indexingVar->Read(&(anOtherDimCoord[j]), &nCount,
    8649          22 :                                           nullptr, nullptr, dt, &abyTmp[0]))
    8650             :                     {
    8651          22 :                         char *pszTmp = nullptr;
    8652          22 :                         GDALExtendedDataType::CopyValue(
    8653          22 :                             &abyTmp[0], dt, &pszTmp,
    8654          44 :                             GDALExtendedDataType::CreateString());
    8655          22 :                         if (pszTmp)
    8656             :                         {
    8657          22 :                             SetMetadataItem(
    8658             :                                 CPLSPrintf("DIM_%s_VALUE", dimName.c_str()),
    8659             :                                 pszTmp);
    8660          22 :                             CPLFree(pszTmp);
    8661             :                         }
    8662             : 
    8663          22 :                         const auto &unit(indexingVar->GetUnit());
    8664          22 :                         if (!unit.empty())
    8665             :                         {
    8666          12 :                             SetMetadataItem(
    8667             :                                 CPLSPrintf("DIM_%s_UNIT", dimName.c_str()),
    8668             :                                 unit.c_str());
    8669             :                         }
    8670             :                     }
    8671             :                 }
    8672             :             }
    8673             : 
    8674         113 :             for (const auto &oItem : aoBandParameterMetadataItems[j])
    8675             :             {
    8676          32 :                 CPLString osVal;
    8677             : 
    8678          16 :                 size_t nCount = 1;
    8679          16 :                 const auto &dt(oItem.poArray->GetDataType());
    8680          16 :                 if (oItem.bDefinitionUsesPctForG)
    8681             :                 {
    8682             :                     // There is one and only one %[x][.y]f|g in osDefinition
    8683          12 :                     std::vector<GByte> abyTmp(dt.GetSize());
    8684          12 :                     if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8685           6 :                                             nullptr, nullptr, dt, &abyTmp[0]))
    8686             :                     {
    8687           6 :                         double dfVal = 0;
    8688           6 :                         GDALExtendedDataType::CopyValue(
    8689           6 :                             &abyTmp[0], dt, &dfVal,
    8690          12 :                             GDALExtendedDataType::Create(GDT_Float64));
    8691           6 :                         osVal.Printf(oItem.osDefinition.c_str(), dfVal);
    8692             :                     }
    8693             :                 }
    8694             :                 else
    8695             :                 {
    8696             :                     // There should be zero or one %s in osDefinition
    8697          10 :                     char *pszValue = nullptr;
    8698          10 :                     if (dt.GetClass() == GEDTC_STRING)
    8699             :                     {
    8700           4 :                         CPL_IGNORE_RET_VAL(oItem.poArray->Read(
    8701           2 :                             &(anOtherDimCoord[j]), &nCount, nullptr, nullptr,
    8702             :                             dt, &pszValue));
    8703             :                     }
    8704             :                     else
    8705             :                     {
    8706          16 :                         std::vector<GByte> abyTmp(dt.GetSize());
    8707          16 :                         if (oItem.poArray->Read(&(anOtherDimCoord[j]), &nCount,
    8708             :                                                 nullptr, nullptr, dt,
    8709           8 :                                                 &abyTmp[0]))
    8710             :                         {
    8711           8 :                             GDALExtendedDataType::CopyValue(
    8712           8 :                                 &abyTmp[0], dt, &pszValue,
    8713          16 :                                 GDALExtendedDataType::CreateString());
    8714             :                         }
    8715             :                     }
    8716             : 
    8717          10 :                     if (pszValue)
    8718             :                     {
    8719          10 :                         osVal.Printf(oItem.osDefinition.c_str(), pszValue);
    8720          10 :                         CPLFree(pszValue);
    8721             :                     }
    8722             :                 }
    8723          16 :                 if (!osVal.empty())
    8724          16 :                     SetMetadataItem(oItem.osName.c_str(), osVal);
    8725             :             }
    8726             : 
    8727          97 :             m_anOffset[i] = anOtherDimCoord[j];
    8728          97 :             j++;
    8729             :         }
    8730             :     }
    8731         215 : }
    8732             : 
    8733             : /************************************************************************/
    8734             : /*                           GetNoDataValue()                           */
    8735             : /************************************************************************/
    8736             : 
    8737          86 : double GDALRasterBandFromArray::GetNoDataValue(int *pbHasNoData)
    8738             : {
    8739          86 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8740          86 :     const auto &poArray(l_poDS->m_poArray);
    8741          86 :     bool bHasNodata = false;
    8742          86 :     const auto res = poArray->GetNoDataValueAsDouble(&bHasNodata);
    8743          86 :     if (pbHasNoData)
    8744          76 :         *pbHasNoData = bHasNodata;
    8745          86 :     return res;
    8746             : }
    8747             : 
    8748             : /************************************************************************/
    8749             : /*                       GetNoDataValueAsInt64()                        */
    8750             : /************************************************************************/
    8751             : 
    8752           1 : int64_t GDALRasterBandFromArray::GetNoDataValueAsInt64(int *pbHasNoData)
    8753             : {
    8754           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8755           1 :     const auto &poArray(l_poDS->m_poArray);
    8756           1 :     bool bHasNodata = false;
    8757           1 :     const auto nodata = poArray->GetNoDataValueAsInt64(&bHasNodata);
    8758           1 :     if (pbHasNoData)
    8759           1 :         *pbHasNoData = bHasNodata;
    8760           1 :     return nodata;
    8761             : }
    8762             : 
    8763             : /************************************************************************/
    8764             : /*                      GetNoDataValueAsUInt64()                        */
    8765             : /************************************************************************/
    8766             : 
    8767           1 : uint64_t GDALRasterBandFromArray::GetNoDataValueAsUInt64(int *pbHasNoData)
    8768             : {
    8769           1 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8770           1 :     const auto &poArray(l_poDS->m_poArray);
    8771           1 :     bool bHasNodata = false;
    8772           1 :     const auto nodata = poArray->GetNoDataValueAsUInt64(&bHasNodata);
    8773           1 :     if (pbHasNoData)
    8774           1 :         *pbHasNoData = bHasNodata;
    8775           1 :     return nodata;
    8776             : }
    8777             : 
    8778             : /************************************************************************/
    8779             : /*                             GetOffset()                              */
    8780             : /************************************************************************/
    8781             : 
    8782          29 : double GDALRasterBandFromArray::GetOffset(int *pbHasOffset)
    8783             : {
    8784          29 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8785          29 :     const auto &poArray(l_poDS->m_poArray);
    8786          29 :     bool bHasValue = false;
    8787          29 :     double dfRes = poArray->GetOffset(&bHasValue);
    8788          29 :     if (pbHasOffset)
    8789          17 :         *pbHasOffset = bHasValue;
    8790          29 :     return dfRes;
    8791             : }
    8792             : 
    8793             : /************************************************************************/
    8794             : /*                           GetUnitType()                              */
    8795             : /************************************************************************/
    8796             : 
    8797          36 : const char *GDALRasterBandFromArray::GetUnitType()
    8798             : {
    8799          36 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8800          36 :     const auto &poArray(l_poDS->m_poArray);
    8801          36 :     return poArray->GetUnit().c_str();
    8802             : }
    8803             : 
    8804             : /************************************************************************/
    8805             : /*                             GetScale()                              */
    8806             : /************************************************************************/
    8807             : 
    8808          27 : double GDALRasterBandFromArray::GetScale(int *pbHasScale)
    8809             : {
    8810          27 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8811          27 :     const auto &poArray(l_poDS->m_poArray);
    8812          27 :     bool bHasValue = false;
    8813          27 :     double dfRes = poArray->GetScale(&bHasValue);
    8814          27 :     if (pbHasScale)
    8815          15 :         *pbHasScale = bHasValue;
    8816          27 :     return dfRes;
    8817             : }
    8818             : 
    8819             : /************************************************************************/
    8820             : /*                            IReadBlock()                              */
    8821             : /************************************************************************/
    8822             : 
    8823          64 : CPLErr GDALRasterBandFromArray::IReadBlock(int nBlockXOff, int nBlockYOff,
    8824             :                                            void *pImage)
    8825             : {
    8826          64 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    8827          64 :     const int nXOff = nBlockXOff * nBlockXSize;
    8828          64 :     const int nYOff = nBlockYOff * nBlockYSize;
    8829          64 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    8830          64 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    8831             :     GDALRasterIOExtraArg sExtraArg;
    8832          64 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    8833         128 :     return IRasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    8834             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    8835         128 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    8836             : }
    8837             : 
    8838             : /************************************************************************/
    8839             : /*                            IWriteBlock()                             */
    8840             : /************************************************************************/
    8841             : 
    8842           0 : CPLErr GDALRasterBandFromArray::IWriteBlock(int nBlockXOff, int nBlockYOff,
    8843             :                                             void *pImage)
    8844             : {
    8845           0 :     const int nDTSize(GDALGetDataTypeSizeBytes(eDataType));
    8846           0 :     const int nXOff = nBlockXOff * nBlockXSize;
    8847           0 :     const int nYOff = nBlockYOff * nBlockYSize;
    8848           0 :     const int nReqXSize = std::min(nRasterXSize - nXOff, nBlockXSize);
    8849           0 :     const int nReqYSize = std::min(nRasterYSize - nYOff, nBlockYSize);
    8850             :     GDALRasterIOExtraArg sExtraArg;
    8851           0 :     INIT_RASTERIO_EXTRA_ARG(sExtraArg);
    8852           0 :     return IRasterIO(GF_Write, nXOff, nYOff, nReqXSize, nReqYSize, pImage,
    8853             :                      nReqXSize, nReqYSize, eDataType, nDTSize,
    8854           0 :                      static_cast<GSpacing>(nDTSize) * nBlockXSize, &sExtraArg);
    8855             : }
    8856             : 
    8857             : /************************************************************************/
    8858             : /*                            IRasterIO()                               */
    8859             : /************************************************************************/
    8860             : 
    8861         308 : CPLErr GDALRasterBandFromArray::IRasterIO(GDALRWFlag eRWFlag, int nXOff,
    8862             :                                           int nYOff, int nXSize, int nYSize,
    8863             :                                           void *pData, int nBufXSize,
    8864             :                                           int nBufYSize, GDALDataType eBufType,
    8865             :                                           GSpacing nPixelSpaceBuf,
    8866             :                                           GSpacing nLineSpaceBuf,
    8867             :                                           GDALRasterIOExtraArg *psExtraArg)
    8868             : {
    8869         308 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8870         308 :     const auto &poArray(l_poDS->m_poArray);
    8871         308 :     const int nBufferDTSize(GDALGetDataTypeSizeBytes(eBufType));
    8872         308 :     if (nXSize == nBufXSize && nYSize == nBufYSize && nBufferDTSize > 0 &&
    8873         308 :         (nPixelSpaceBuf % nBufferDTSize) == 0 &&
    8874         308 :         (nLineSpaceBuf % nBufferDTSize) == 0)
    8875             :     {
    8876         308 :         m_anOffset[l_poDS->m_iXDim] = static_cast<GUInt64>(nXOff);
    8877         308 :         m_anCount[l_poDS->m_iXDim] = static_cast<size_t>(nXSize);
    8878         616 :         m_anStride[l_poDS->m_iXDim] =
    8879         308 :             static_cast<GPtrDiff_t>(nPixelSpaceBuf / nBufferDTSize);
    8880         308 :         if (poArray->GetDimensionCount() >= 2)
    8881             :         {
    8882         299 :             m_anOffset[l_poDS->m_iYDim] = static_cast<GUInt64>(nYOff);
    8883         299 :             m_anCount[l_poDS->m_iYDim] = static_cast<size_t>(nYSize);
    8884         299 :             m_anStride[l_poDS->m_iYDim] =
    8885         299 :                 static_cast<GPtrDiff_t>(nLineSpaceBuf / nBufferDTSize);
    8886             :         }
    8887         308 :         if (eRWFlag == GF_Read)
    8888             :         {
    8889         608 :             return poArray->Read(m_anOffset.data(), m_anCount.data(), nullptr,
    8890         304 :                                  m_anStride.data(),
    8891         608 :                                  GDALExtendedDataType::Create(eBufType), pData)
    8892         304 :                        ? CE_None
    8893         304 :                        : CE_Failure;
    8894             :         }
    8895             :         else
    8896             :         {
    8897           8 :             return poArray->Write(m_anOffset.data(), m_anCount.data(), nullptr,
    8898           4 :                                   m_anStride.data(),
    8899           8 :                                   GDALExtendedDataType::Create(eBufType), pData)
    8900           4 :                        ? CE_None
    8901           4 :                        : CE_Failure;
    8902             :         }
    8903             :     }
    8904           0 :     return GDALRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    8905             :                                      pData, nBufXSize, nBufYSize, eBufType,
    8906           0 :                                      nPixelSpaceBuf, nLineSpaceBuf, psExtraArg);
    8907             : }
    8908             : 
    8909             : /************************************************************************/
    8910             : /*                      GetColorInterpretation()                        */
    8911             : /************************************************************************/
    8912             : 
    8913          44 : GDALColorInterp GDALRasterBandFromArray::GetColorInterpretation()
    8914             : {
    8915          44 :     auto l_poDS(cpl::down_cast<GDALDatasetFromArray *>(poDS));
    8916          44 :     const auto &poArray(l_poDS->m_poArray);
    8917         132 :     auto poAttr = poArray->GetAttribute("COLOR_INTERPRETATION");
    8918          44 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    8919             :     {
    8920           6 :         bool bOK = false;
    8921           6 :         GUInt64 nStartIndex = 0;
    8922           6 :         if (poArray->GetDimensionCount() == 2 &&
    8923           0 :             poAttr->GetDimensionCount() == 0)
    8924             :         {
    8925           0 :             bOK = true;
    8926             :         }
    8927           6 :         else if (poArray->GetDimensionCount() == 3)
    8928             :         {
    8929           6 :             uint64_t nExtraDimSamples = 1;
    8930           6 :             const auto &apoDims = poArray->GetDimensions();
    8931          24 :             for (size_t i = 0; i < apoDims.size(); ++i)
    8932             :             {
    8933          18 :                 if (i != l_poDS->m_iXDim && i != l_poDS->m_iYDim)
    8934           6 :                     nExtraDimSamples *= apoDims[i]->GetSize();
    8935             :             }
    8936           6 :             if (poAttr->GetDimensionsSize() ==
    8937          12 :                 std::vector<GUInt64>{static_cast<GUInt64>(nExtraDimSamples)})
    8938             :             {
    8939           6 :                 bOK = true;
    8940             :             }
    8941           6 :             nStartIndex = nBand - 1;
    8942             :         }
    8943           6 :         if (bOK)
    8944             :         {
    8945           6 :             const auto oStringDT = GDALExtendedDataType::CreateString();
    8946           6 :             const size_t nCount = 1;
    8947           6 :             const GInt64 arrayStep = 1;
    8948           6 :             const GPtrDiff_t bufferStride = 1;
    8949           6 :             char *pszValue = nullptr;
    8950           6 :             poAttr->Read(&nStartIndex, &nCount, &arrayStep, &bufferStride,
    8951           6 :                          oStringDT, &pszValue);
    8952           6 :             if (pszValue)
    8953             :             {
    8954             :                 const auto eColorInterp =
    8955           6 :                     GDALGetColorInterpretationByName(pszValue);
    8956           6 :                 CPLFree(pszValue);
    8957           6 :                 return eColorInterp;
    8958             :             }
    8959             :         }
    8960             :     }
    8961          38 :     return GCI_Undefined;
    8962             : }
    8963             : 
    8964             : /************************************************************************/
    8965             : /*                    GDALDatasetFromArray::Create()                    */
    8966             : /************************************************************************/
    8967             : 
    8968         182 : GDALDatasetFromArray *GDALDatasetFromArray::Create(
    8969             :     const std::shared_ptr<GDALMDArray> &array, size_t iXDim, size_t iYDim,
    8970             :     const std::shared_ptr<GDALGroup> &poRootGroup, CSLConstList papszOptions)
    8971             : 
    8972             : {
    8973         182 :     const auto nDimCount(array->GetDimensionCount());
    8974         182 :     if (nDimCount == 0)
    8975             :     {
    8976           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8977             :                  "Unsupported number of dimensions");
    8978           1 :         return nullptr;
    8979             :     }
    8980         361 :     if (array->GetDataType().GetClass() != GEDTC_NUMERIC ||
    8981         180 :         array->GetDataType().GetNumericDataType() == GDT_Unknown)
    8982             :     {
    8983           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    8984             :                  "Only arrays with numeric data types "
    8985             :                  "can be exposed as classic GDALDataset");
    8986           1 :         return nullptr;
    8987             :     }
    8988         180 :     if (iXDim >= nDimCount ||
    8989         166 :         (nDimCount >= 2 && (iYDim >= nDimCount || iXDim == iYDim)))
    8990             :     {
    8991           6 :         CPLError(CE_Failure, CPLE_NotSupported, "Invalid iXDim and/or iYDim");
    8992           6 :         return nullptr;
    8993             :     }
    8994         174 :     GUInt64 nTotalBands = 1;
    8995         174 :     const auto &dims(array->GetDimensions());
    8996         559 :     for (size_t i = 0; i < nDimCount; ++i)
    8997             :     {
    8998         386 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    8999             :         {
    9000          51 :             if (dims[i]->GetSize() > 65536 / nTotalBands)
    9001             :             {
    9002           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9003             :                          "Too many bands. Operate on a sliced view");
    9004           1 :                 return nullptr;
    9005             :             }
    9006          50 :             nTotalBands *= dims[i]->GetSize();
    9007             :         }
    9008             :     }
    9009             : 
    9010             :     const char *pszBandMetadata =
    9011         173 :         CSLFetchNameValue(papszOptions, "BAND_METADATA");
    9012         173 :     const size_t nNewDimCount = nDimCount >= 2 ? nDimCount - 2 : 0;
    9013             :     std::vector<std::vector<MetadataItem>> aoBandParameterMetadataItems(
    9014         346 :         nNewDimCount);
    9015             : 
    9016         173 :     if (pszBandMetadata)
    9017             :     {
    9018          21 :         if (!poRootGroup)
    9019             :         {
    9020           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9021             :                      "Root group should be provided when BAND_METADATA is set");
    9022          14 :             return nullptr;
    9023             :         }
    9024          20 :         CPLJSONDocument oDoc;
    9025          20 :         if (!oDoc.LoadMemory(pszBandMetadata))
    9026             :         {
    9027           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9028             :                      "Invalid JSON content for BAND_METADATA");
    9029           1 :             return nullptr;
    9030             :         }
    9031          19 :         auto oRoot = oDoc.GetRoot();
    9032          19 :         if (oRoot.GetType() != CPLJSONObject::Type::Array)
    9033             :         {
    9034           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    9035             :                      "Value of BAND_METADATA should be an array");
    9036           1 :             return nullptr;
    9037             :         }
    9038             : 
    9039          18 :         std::map<std::string, size_t> oMapArrayDimNameToExtraDimIdx;
    9040          72 :         for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9041             :         {
    9042          54 :             if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9043             :             {
    9044          18 :                 oMapArrayDimNameToExtraDimIdx[dims[i]->GetName()] = j;
    9045          18 :                 ++j;
    9046             :             }
    9047             :         }
    9048             : 
    9049          18 :         auto oArray = oRoot.ToArray();
    9050          26 :         for (int j = 0; j < oArray.Size(); ++j)
    9051             :         {
    9052          19 :             const auto oJsonItem = oArray[j];
    9053          19 :             MetadataItem oItem;
    9054             : 
    9055          38 :             auto osBandArrayFullname = oJsonItem.GetString("array");
    9056          19 :             if (osBandArrayFullname.empty())
    9057             :             {
    9058           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9059             :                          "BAND_METADATA[%d][\"array\"] is missing", j);
    9060           1 :                 return nullptr;
    9061             :             }
    9062             :             oItem.poArray =
    9063          18 :                 poRootGroup->OpenMDArrayFromFullname(osBandArrayFullname);
    9064          18 :             if (!oItem.poArray)
    9065             :             {
    9066           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9067             :                          "Array %s cannot be found",
    9068             :                          osBandArrayFullname.c_str());
    9069           1 :                 return nullptr;
    9070             :             }
    9071          17 :             if (oItem.poArray->GetDimensionCount() != 1)
    9072             :             {
    9073           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9074             :                          "Array %s is not a 1D array",
    9075             :                          osBandArrayFullname.c_str());
    9076           1 :                 return nullptr;
    9077             :             }
    9078             :             const auto &osAuxArrayDimName =
    9079          16 :                 oItem.poArray->GetDimensions()[0]->GetName();
    9080          16 :             auto oIter = oMapArrayDimNameToExtraDimIdx.find(osAuxArrayDimName);
    9081          16 :             if (oIter == oMapArrayDimNameToExtraDimIdx.end())
    9082             :             {
    9083           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9084             :                          "Dimension %s of array %s is not a non-X/Y dimension "
    9085             :                          "of array %s",
    9086             :                          osAuxArrayDimName.c_str(), osBandArrayFullname.c_str(),
    9087           1 :                          array->GetName().c_str());
    9088           1 :                 return nullptr;
    9089             :             }
    9090          15 :             const size_t iExtraDimIdx = oIter->second;
    9091          15 :             CPLAssert(iExtraDimIdx < nNewDimCount);
    9092             : 
    9093          15 :             oItem.osName = oJsonItem.GetString("item_name");
    9094          15 :             if (oItem.osName.empty())
    9095             :             {
    9096           1 :                 CPLError(CE_Failure, CPLE_AppDefined,
    9097             :                          "BAND_METADATA[%d][\"item_name\"] is missing", j);
    9098           1 :                 return nullptr;
    9099             :             }
    9100             : 
    9101          28 :             const auto osDefinition = oJsonItem.GetString("item_value", "%s");
    9102             : 
    9103             :             // Check correctness of definition
    9104          14 :             bool bFirstNumericFormatter = true;
    9105          14 :             std::string osModDefinition;
    9106          14 :             bool bDefinitionUsesPctForG = false;
    9107          72 :             for (size_t k = 0; k < osDefinition.size(); ++k)
    9108             :             {
    9109          64 :                 if (osDefinition[k] == '%')
    9110             :                 {
    9111          13 :                     osModDefinition += osDefinition[k];
    9112          13 :                     if (k + 1 == osDefinition.size())
    9113             :                     {
    9114           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9115             :                                  "Value of "
    9116             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9117             :                                  "%s is invalid at offset %d",
    9118             :                                  osAuxArrayDimName.c_str(), j,
    9119             :                                  osDefinition.c_str(), int(k));
    9120           1 :                         return nullptr;
    9121             :                     }
    9122          12 :                     ++k;
    9123          12 :                     if (osDefinition[k] == '%')
    9124             :                     {
    9125           1 :                         osModDefinition += osDefinition[k];
    9126           1 :                         continue;
    9127             :                     }
    9128          11 :                     if (!bFirstNumericFormatter)
    9129             :                     {
    9130           1 :                         CPLError(
    9131             :                             CE_Failure, CPLE_AppDefined,
    9132             :                             "Value of "
    9133             :                             "BAND_METADATA[\"%s\"][%d][\"item_value\"] = %s is "
    9134             :                             "invalid at offset %d: %%[x][.y]f|g or %%s "
    9135             :                             "formatters should be specified at most once",
    9136             :                             osAuxArrayDimName.c_str(), j, osDefinition.c_str(),
    9137             :                             int(k));
    9138           1 :                         return nullptr;
    9139             :                     }
    9140          10 :                     bFirstNumericFormatter = false;
    9141          13 :                     for (; k < osDefinition.size(); ++k)
    9142             :                     {
    9143          13 :                         osModDefinition += osDefinition[k];
    9144          26 :                         if (!((osDefinition[k] >= '0' &&
    9145          12 :                                osDefinition[k] <= '9') ||
    9146          11 :                               osDefinition[k] == '.'))
    9147          10 :                             break;
    9148             :                     }
    9149          20 :                     if (k == osDefinition.size() ||
    9150          10 :                         (osDefinition[k] != 'f' && osDefinition[k] != 'g' &&
    9151           5 :                          osDefinition[k] != 's'))
    9152             :                     {
    9153           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9154             :                                  "Value of "
    9155             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9156             :                                  "%s is invalid at offset %d: only "
    9157             :                                  "%%[x][.y]f|g or %%s formatters are accepted",
    9158             :                                  osAuxArrayDimName.c_str(), j,
    9159             :                                  osDefinition.c_str(), int(k));
    9160           1 :                         return nullptr;
    9161             :                     }
    9162           9 :                     bDefinitionUsesPctForG =
    9163           9 :                         (osDefinition[k] == 'f' || osDefinition[k] == 'g');
    9164           9 :                     if (bDefinitionUsesPctForG)
    9165             :                     {
    9166           5 :                         if (oItem.poArray->GetDataType().GetClass() !=
    9167             :                             GEDTC_NUMERIC)
    9168             :                         {
    9169           1 :                             CPLError(CE_Failure, CPLE_AppDefined,
    9170             :                                      "Data type of %s array is not numeric",
    9171             :                                      osAuxArrayDimName.c_str());
    9172           1 :                             return nullptr;
    9173             :                         }
    9174             :                     }
    9175             :                 }
    9176          56 :                 else if (osDefinition[k] == '$' &&
    9177          56 :                          k + 1 < osDefinition.size() &&
    9178           5 :                          osDefinition[k + 1] == '{')
    9179             :                 {
    9180           5 :                     const auto nPos = osDefinition.find('}', k);
    9181           5 :                     if (nPos == std::string::npos)
    9182             :                     {
    9183           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9184             :                                  "Value of "
    9185             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9186             :                                  "%s is invalid at offset %d",
    9187             :                                  osAuxArrayDimName.c_str(), j,
    9188             :                                  osDefinition.c_str(), int(k));
    9189           2 :                         return nullptr;
    9190             :                     }
    9191             :                     const auto osAttrName =
    9192           4 :                         osDefinition.substr(k + 2, nPos - (k + 2));
    9193           4 :                     auto poAttr = oItem.poArray->GetAttribute(osAttrName);
    9194           4 :                     if (!poAttr)
    9195             :                     {
    9196           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9197             :                                  "Value of "
    9198             :                                  "BAND_METADATA[\"%s\"][%d][\"item_value\"] = "
    9199             :                                  "%s is invalid: %s is not an attribute of %s",
    9200             :                                  osAuxArrayDimName.c_str(), j,
    9201             :                                  osDefinition.c_str(), osAttrName.c_str(),
    9202             :                                  osAuxArrayDimName.c_str());
    9203           1 :                         return nullptr;
    9204             :                     }
    9205           3 :                     k = nPos;
    9206           3 :                     const char *pszValue = poAttr->ReadAsString();
    9207           3 :                     if (!pszValue)
    9208             :                     {
    9209           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    9210             :                                  "Cannot get value of attribute %s of %s as a "
    9211             :                                  "string",
    9212             :                                  osAttrName.c_str(), osAuxArrayDimName.c_str());
    9213           0 :                         return nullptr;
    9214             :                     }
    9215           3 :                     osModDefinition += pszValue;
    9216             :                 }
    9217             :                 else
    9218             :                 {
    9219          46 :                     osModDefinition += osDefinition[k];
    9220             :                 }
    9221             :             }
    9222             : 
    9223           8 :             oItem.osDefinition = std::move(osModDefinition);
    9224           8 :             oItem.bDefinitionUsesPctForG = bDefinitionUsesPctForG;
    9225             : 
    9226           8 :             aoBandParameterMetadataItems[iExtraDimIdx].emplace_back(
    9227           8 :                 std::move(oItem));
    9228             :         }
    9229             :     }
    9230             : 
    9231         318 :     auto poDS = std::make_unique<GDALDatasetFromArray>(array, iXDim, iYDim);
    9232             : 
    9233         159 :     poDS->eAccess = array->IsWritable() ? GA_Update : GA_ReadOnly;
    9234             : 
    9235         159 :     poDS->nRasterYSize =
    9236         159 :         nDimCount < 2 ? 1
    9237         148 :                       : static_cast<int>(std::min(static_cast<GUInt64>(INT_MAX),
    9238         148 :                                                   dims[iYDim]->GetSize()));
    9239         318 :     poDS->nRasterXSize = static_cast<int>(
    9240         159 :         std::min(static_cast<GUInt64>(INT_MAX), dims[iXDim]->GetSize()));
    9241             : 
    9242         318 :     std::vector<GUInt64> anOtherDimCoord(nNewDimCount);
    9243         318 :     std::vector<GUInt64> anStackIters(nDimCount);
    9244         318 :     std::vector<size_t> anMapNewToOld(nNewDimCount);
    9245         502 :     for (size_t i = 0, j = 0; i < nDimCount; ++i)
    9246             :     {
    9247         343 :         if (i != iXDim && !(nDimCount >= 2 && i == iYDim))
    9248             :         {
    9249          36 :             anMapNewToOld[j] = i;
    9250          36 :             j++;
    9251             :         }
    9252             :     }
    9253             : 
    9254         318 :     poDS->m_bHasGT =
    9255         159 :         array->GuessGeoTransform(iXDim, iYDim, false, poDS->m_adfGeoTransform);
    9256             : 
    9257         318 :     const auto attrs(array->GetAttributes());
    9258         228 :     for (const auto &attr : attrs)
    9259             :     {
    9260          69 :         if (attr->GetName() != "COLOR_INTERPRETATION")
    9261             :         {
    9262         132 :             auto stringArray = attr->ReadAsStringArray();
    9263         132 :             std::string val;
    9264          66 :             if (stringArray.size() > 1)
    9265             :             {
    9266          22 :                 val += '{';
    9267             :             }
    9268         154 :             for (int i = 0; i < stringArray.size(); ++i)
    9269             :             {
    9270          88 :                 if (i > 0)
    9271          22 :                     val += ',';
    9272          88 :                 val += stringArray[i];
    9273             :             }
    9274          66 :             if (stringArray.size() > 1)
    9275             :             {
    9276          22 :                 val += '}';
    9277             :             }
    9278          66 :             poDS->m_oMDD.SetMetadataItem(attr->GetName().c_str(), val.c_str());
    9279             :         }
    9280             :     }
    9281             : 
    9282         159 :     const char *pszDelay = CSLFetchNameValueDef(
    9283             :         papszOptions, "LOAD_EXTRA_DIM_METADATA_DELAY",
    9284             :         CPLGetConfigOption("GDAL_LOAD_EXTRA_DIM_METADATA_DELAY", "5"));
    9285             :     const double dfDelay =
    9286         159 :         EQUAL(pszDelay, "unlimited") ? -1 : CPLAtof(pszDelay);
    9287         159 :     const auto nStartTime = time(nullptr);
    9288         159 :     bool bHasWarned = false;
    9289             :     // Instantiate bands by iterating over non-XY variables
    9290         159 :     size_t iDim = 0;
    9291         159 :     int nCurBand = 1;
    9292         253 : lbl_next_depth:
    9293         253 :     if (iDim < nNewDimCount)
    9294             :     {
    9295          38 :         anStackIters[iDim] = dims[anMapNewToOld[iDim]]->GetSize();
    9296          38 :         anOtherDimCoord[iDim] = 0;
    9297             :         while (true)
    9298             :         {
    9299          94 :             ++iDim;
    9300          94 :             goto lbl_next_depth;
    9301          94 :         lbl_return_to_caller:
    9302          94 :             --iDim;
    9303          94 :             --anStackIters[iDim];
    9304          94 :             if (anStackIters[iDim] == 0)
    9305          38 :                 break;
    9306          56 :             ++anOtherDimCoord[iDim];
    9307             :         }
    9308             :     }
    9309             :     else
    9310             :     {
    9311         430 :         poDS->SetBand(nCurBand, new GDALRasterBandFromArray(
    9312         215 :                                     poDS.get(), anOtherDimCoord,
    9313             :                                     aoBandParameterMetadataItems, dfDelay,
    9314         215 :                                     nStartTime, bHasWarned));
    9315         215 :         ++nCurBand;
    9316             :     }
    9317         253 :     if (iDim > 0)
    9318          94 :         goto lbl_return_to_caller;
    9319             : 
    9320         159 :     if (!array->GetFilename().empty())
    9321             :     {
    9322         142 :         poDS->SetPhysicalFilename(array->GetFilename().c_str());
    9323             :         std::string osDerivedDatasetName(
    9324             :             CPLSPrintf("AsClassicDataset(%d,%d) view of %s", int(iXDim),
    9325         284 :                        int(iYDim), array->GetFullName().c_str()));
    9326         142 :         if (!array->GetContext().empty())
    9327             :         {
    9328           2 :             osDerivedDatasetName += " with context ";
    9329           2 :             osDerivedDatasetName += array->GetContext();
    9330             :         }
    9331         142 :         poDS->SetDerivedDatasetName(osDerivedDatasetName.c_str());
    9332         142 :         poDS->TryLoadXML();
    9333             :     }
    9334             : 
    9335         159 :     return poDS.release();
    9336             : }
    9337             : 
    9338             : /************************************************************************/
    9339             : /*                          AsClassicDataset()                         */
    9340             : /************************************************************************/
    9341             : 
    9342             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
    9343             :  *
    9344             :  * In the case of > 2D arrays, additional dimensions will be represented as
    9345             :  * raster bands.
    9346             :  *
    9347             :  * The "reverse" method is GDALRasterBand::AsMDArray().
    9348             :  *
    9349             :  * This is the same as the C function GDALMDArrayAsClassicDataset().
    9350             :  *
    9351             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
    9352             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
    9353             :  *              Ignored if the dimension count is 1.
    9354             :  * @param poRootGroup (Added in GDAL 3.8) Root group. Used with the BAND_METADATA
    9355             :  *                    option.
    9356             :  * @param papszOptions (Added in GDAL 3.8) Null-terminated list of options, or
    9357             :  *                     nullptr. Current supported options are:
    9358             :  *                     <ul>
    9359             :  *                     <li>BAND_METADATA: JSON serialized array defining which
    9360             :  *                         arrays of the poRootGroup, indexed by non-X and Y
    9361             :  *                         dimensions, should be mapped as band metadata items.
    9362             :  *                         Each array item should be an object with the
    9363             :  *                         following members:
    9364             :  *                         - "array": full name of a band parameter array.
    9365             :  *                           Such array must be a one
    9366             :  *                           dimensional array, and its dimension must be one of
    9367             :  *                           the dimensions of the array on which the method is
    9368             :  *                           called (excluding the X and Y dimensons).
    9369             :  *                         - "item_name": band metadata item name
    9370             :  *                         - "item_value": (optional) String, where "%[x][.y]f",
    9371             :  *                           "%[x][.y]g" or "%s" printf-like formatting can be
    9372             :  *                           used to format the corresponding value of the
    9373             :  *                           parameter array. The percentage character should be
    9374             :  *                           repeated: "%%"
    9375             :  *                           "${attribute_name}" can also be used to include the
    9376             :  *                           value of an attribute for the array.
    9377             :  *                           If "item_value" is not provided, a default formatting
    9378             :  *                           of the value will be applied.
    9379             :  *
    9380             :  *                         Example:
    9381             :  *                         [
    9382             :  *                            {
    9383             :  *                              "array": "/sensor_band_parameters/wavelengths",
    9384             :  *                              "item_name": "WAVELENGTH",
    9385             :  *                              "item_value": "%.1f ${units}"
    9386             :  *                            },
    9387             :  *                            {
    9388             :  *                              "array": "/sensor_band_parameters/fwhm",
    9389             :  *                              "item_name": "FWHM"
    9390             :  *                            },
    9391             :  *                            {
    9392             :  *                              "array": "/sensor_band_parameters/fwhm",
    9393             :  *                              "item_name": "FWHM_UNIT",
    9394             :  *                              "item_value": "${units}"
    9395             :  *                            }
    9396             :  *                         ]
    9397             :  *                     </li>
    9398             :  *                     <li>LOAD_EXTRA_DIM_METADATA_DELAY: Maximum delay in
    9399             :  *                         seconds allowed to set the DIM_{dimname}_VALUE band
    9400             :  *                         metadata items from the indexing variable of the
    9401             :  *                         dimensions.
    9402             :  *                         Default value is 5. 'unlimited' can be used to mean
    9403             :  *                         unlimited delay. Can also be defined globally with
    9404             :  *                         the GDAL_LOAD_EXTRA_DIM_METADATA_DELAY configuration
    9405             :  *                         option.</li>
    9406             :  *                     </ul>
    9407             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
    9408             :  */
    9409             : GDALDataset *
    9410         182 : GDALMDArray::AsClassicDataset(size_t iXDim, size_t iYDim,
    9411             :                               const std::shared_ptr<GDALGroup> &poRootGroup,
    9412             :                               CSLConstList papszOptions) const
    9413             : {
    9414         364 :     auto self = std::dynamic_pointer_cast<GDALMDArray>(m_pSelf.lock());
    9415         182 :     if (!self)
    9416             :     {
    9417           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    9418             :                  "Driver implementation issue: m_pSelf not set !");
    9419           0 :         return nullptr;
    9420             :     }
    9421         182 :     return GDALDatasetFromArray::Create(self, iXDim, iYDim, poRootGroup,
    9422         182 :                                         papszOptions);
    9423             : }
    9424             : 
    9425             : /************************************************************************/
    9426             : /*                           GetStatistics()                            */
    9427             : /************************************************************************/
    9428             : 
    9429             : /**
    9430             :  * \brief Fetch statistics.
    9431             :  *
    9432             :  * Returns the minimum, maximum, mean and standard deviation of all
    9433             :  * pixel values in this array.
    9434             :  *
    9435             :  * If bForce is FALSE results will only be returned if it can be done
    9436             :  * quickly (i.e. without scanning the data).  If bForce is FALSE and
    9437             :  * results cannot be returned efficiently, the method will return CE_Warning
    9438             :  * but no warning will have been issued.   This is a non-standard use of
    9439             :  * the CE_Warning return value to indicate "nothing done".
    9440             :  *
    9441             :  * When cached statistics are not available, and bForce is TRUE,
    9442             :  * ComputeStatistics() is called.
    9443             :  *
    9444             :  * Note that file formats using PAM (Persistent Auxiliary Metadata) services
    9445             :  * will generally cache statistics in the .aux.xml file allowing fast fetch
    9446             :  * after the first request.
    9447             :  *
    9448             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9449             :  *
    9450             :  * This method is the same as the C function GDALMDArrayGetStatistics().
    9451             :  *
    9452             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9453             :  * if statistics on the whole array are wished, or to false if a subset of it
    9454             :  * may be used.
    9455             :  *
    9456             :  * @param bForce If false statistics will only be returned if it can
    9457             :  * be done without rescanning the image.
    9458             :  *
    9459             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9460             :  *
    9461             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9462             :  *
    9463             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9464             :  *
    9465             :  * @param pdfStdDev Location into which to load image standard deviation
    9466             :  * (may be NULL).
    9467             :  *
    9468             :  * @param pnValidCount Number of samples whose value is different from the
    9469             :  * nodata value. (may be NULL)
    9470             :  *
    9471             :  * @param pfnProgress a function to call to report progress, or NULL.
    9472             :  *
    9473             :  * @param pProgressData application data to pass to the progress function.
    9474             :  *
    9475             :  * @return CE_None on success, CE_Warning if no values returned,
    9476             :  * CE_Failure if an error occurs.
    9477             :  *
    9478             :  * @since GDAL 3.2
    9479             :  */
    9480             : 
    9481           7 : CPLErr GDALMDArray::GetStatistics(bool bApproxOK, bool bForce, double *pdfMin,
    9482             :                                   double *pdfMax, double *pdfMean,
    9483             :                                   double *pdfStdDev, GUInt64 *pnValidCount,
    9484             :                                   GDALProgressFunc pfnProgress,
    9485             :                                   void *pProgressData)
    9486             : {
    9487           7 :     if (!bForce)
    9488           1 :         return CE_Warning;
    9489             : 
    9490          12 :     return ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean, pdfStdDev,
    9491           6 :                              pnValidCount, pfnProgress, pProgressData, nullptr)
    9492           6 :                ? CE_None
    9493           6 :                : CE_Failure;
    9494             : }
    9495             : 
    9496             : /************************************************************************/
    9497             : /*                         ComputeStatistics()                          */
    9498             : /************************************************************************/
    9499             : 
    9500             : /**
    9501             :  * \brief Compute statistics.
    9502             :  *
    9503             :  * Returns the minimum, maximum, mean and standard deviation of all
    9504             :  * pixel values in this array.
    9505             :  *
    9506             :  * Pixels taken into account in statistics are those whose mask value
    9507             :  * (as determined by GetMask()) is non-zero.
    9508             :  *
    9509             :  * Once computed, the statistics will generally be "set" back on the
    9510             :  * owing dataset.
    9511             :  *
    9512             :  * Cached statistics can be cleared with GDALDataset::ClearStatistics().
    9513             :  *
    9514             :  * This method is the same as the C functions GDALMDArrayComputeStatistics().
    9515             :  * and GDALMDArrayComputeStatisticsEx().
    9516             :  *
    9517             :  * @param bApproxOK Currently ignored. In the future, should be set to true
    9518             :  * if statistics on the whole array are wished, or to false if a subset of it
    9519             :  * may be used.
    9520             :  *
    9521             :  * @param pdfMin Location into which to load image minimum (may be NULL).
    9522             :  *
    9523             :  * @param pdfMax Location into which to load image maximum (may be NULL).-
    9524             :  *
    9525             :  * @param pdfMean Location into which to load image mean (may be NULL).
    9526             :  *
    9527             :  * @param pdfStdDev Location into which to load image standard deviation
    9528             :  * (may be NULL).
    9529             :  *
    9530             :  * @param pnValidCount Number of samples whose value is different from the
    9531             :  * nodata value. (may be NULL)
    9532             :  *
    9533             :  * @param pfnProgress a function to call to report progress, or NULL.
    9534             :  *
    9535             :  * @param pProgressData application data to pass to the progress function.
    9536             :  *
    9537             :  * @param papszOptions NULL-terminated list of options, of NULL. Added in 3.8.
    9538             :  *                     Options are driver specific. For now the netCDF and Zarr
    9539             :  *                     drivers recognize UPDATE_METADATA=YES, whose effect is
    9540             :  *                     to add or update the actual_range attribute with the
    9541             :  *                     computed min/max, only if done on the full array, in non
    9542             :  *                     approximate mode, and the dataset is opened in update
    9543             :  *                     mode.
    9544             :  *
    9545             :  * @return true on success
    9546             :  *
    9547             :  * @since GDAL 3.2
    9548             :  */
    9549             : 
    9550          10 : bool GDALMDArray::ComputeStatistics(bool bApproxOK, double *pdfMin,
    9551             :                                     double *pdfMax, double *pdfMean,
    9552             :                                     double *pdfStdDev, GUInt64 *pnValidCount,
    9553             :                                     GDALProgressFunc pfnProgress,
    9554             :                                     void *pProgressData,
    9555             :                                     CSLConstList papszOptions)
    9556             : {
    9557             :     struct StatsPerChunkType
    9558             :     {
    9559             :         const GDALMDArray *array = nullptr;
    9560             :         std::shared_ptr<GDALMDArray> poMask{};
    9561             :         double dfMin = std::numeric_limits<double>::max();
    9562             :         double dfMax = -std::numeric_limits<double>::max();
    9563             :         double dfMean = 0.0;
    9564             :         double dfM2 = 0.0;
    9565             :         GUInt64 nValidCount = 0;
    9566             :         std::vector<GByte> abyData{};
    9567             :         std::vector<double> adfData{};
    9568             :         std::vector<GByte> abyMaskData{};
    9569             :         GDALProgressFunc pfnProgress = nullptr;
    9570             :         void *pProgressData = nullptr;
    9571             :     };
    9572             : 
    9573          10 :     const auto PerChunkFunc = [](GDALAbstractMDArray *,
    9574             :                                  const GUInt64 *chunkArrayStartIdx,
    9575             :                                  const size_t *chunkCount, GUInt64 iCurChunk,
    9576             :                                  GUInt64 nChunkCount, void *pUserData)
    9577             :     {
    9578          10 :         StatsPerChunkType *data = static_cast<StatsPerChunkType *>(pUserData);
    9579          10 :         const GDALMDArray *array = data->array;
    9580          10 :         const GDALMDArray *poMask = data->poMask.get();
    9581          10 :         const size_t nDims = array->GetDimensionCount();
    9582          10 :         size_t nVals = 1;
    9583          27 :         for (size_t i = 0; i < nDims; i++)
    9584          17 :             nVals *= chunkCount[i];
    9585             : 
    9586             :         // Get mask
    9587          10 :         data->abyMaskData.resize(nVals);
    9588          10 :         if (!(poMask->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9589          10 :                            poMask->GetDataType(), &data->abyMaskData[0])))
    9590             :         {
    9591           0 :             return false;
    9592             :         }
    9593             : 
    9594             :         // Get data
    9595          10 :         const auto &oType = array->GetDataType();
    9596          10 :         if (oType.GetNumericDataType() == GDT_Float64)
    9597             :         {
    9598           4 :             data->adfData.resize(nVals);
    9599           4 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9600           4 :                              oType, &data->adfData[0]))
    9601             :             {
    9602           0 :                 return false;
    9603             :             }
    9604             :         }
    9605             :         else
    9606             :         {
    9607           6 :             data->abyData.resize(nVals * oType.GetSize());
    9608           6 :             if (!array->Read(chunkArrayStartIdx, chunkCount, nullptr, nullptr,
    9609           6 :                              oType, &data->abyData[0]))
    9610             :             {
    9611           0 :                 return false;
    9612             :             }
    9613           6 :             data->adfData.resize(nVals);
    9614           6 :             GDALCopyWords64(&data->abyData[0], oType.GetNumericDataType(),
    9615           6 :                             static_cast<int>(oType.GetSize()),
    9616           6 :                             &data->adfData[0], GDT_Float64,
    9617             :                             static_cast<int>(sizeof(double)),
    9618             :                             static_cast<GPtrDiff_t>(nVals));
    9619             :         }
    9620         469 :         for (size_t i = 0; i < nVals; i++)
    9621             :         {
    9622         459 :             if (data->abyMaskData[i])
    9623             :             {
    9624         454 :                 const double dfValue = data->adfData[i];
    9625         454 :                 data->dfMin = std::min(data->dfMin, dfValue);
    9626         454 :                 data->dfMax = std::max(data->dfMax, dfValue);
    9627         454 :                 data->nValidCount++;
    9628         454 :                 const double dfDelta = dfValue - data->dfMean;
    9629         454 :                 data->dfMean += dfDelta / data->nValidCount;
    9630         454 :                 data->dfM2 += dfDelta * (dfValue - data->dfMean);
    9631             :             }
    9632             :         }
    9633          10 :         if (data->pfnProgress &&
    9634           0 :             !data->pfnProgress(static_cast<double>(iCurChunk + 1) / nChunkCount,
    9635             :                                "", data->pProgressData))
    9636             :         {
    9637           0 :             return false;
    9638             :         }
    9639          10 :         return true;
    9640             :     };
    9641             : 
    9642          10 :     const auto &oType = GetDataType();
    9643          20 :     if (oType.GetClass() != GEDTC_NUMERIC ||
    9644          10 :         GDALDataTypeIsComplex(oType.GetNumericDataType()))
    9645             :     {
    9646           0 :         CPLError(
    9647             :             CE_Failure, CPLE_NotSupported,
    9648             :             "Statistics can only be computed on non-complex numeric data type");
    9649           0 :         return false;
    9650             :     }
    9651             : 
    9652          10 :     const size_t nDims = GetDimensionCount();
    9653          20 :     std::vector<GUInt64> arrayStartIdx(nDims);
    9654          20 :     std::vector<GUInt64> count(nDims);
    9655          10 :     const auto &poDims = GetDimensions();
    9656          27 :     for (size_t i = 0; i < nDims; i++)
    9657             :     {
    9658          17 :         count[i] = poDims[i]->GetSize();
    9659             :     }
    9660          10 :     const char *pszSwathSize = CPLGetConfigOption("GDAL_SWATH_SIZE", nullptr);
    9661             :     const size_t nMaxChunkSize =
    9662             :         pszSwathSize
    9663          10 :             ? static_cast<size_t>(
    9664           0 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    9665           0 :                            CPLAtoGIntBig(pszSwathSize)))
    9666             :             : static_cast<size_t>(
    9667          10 :                   std::min(GIntBig(std::numeric_limits<size_t>::max() / 2),
    9668          10 :                            GDALGetCacheMax64() / 4));
    9669          20 :     StatsPerChunkType sData;
    9670          10 :     sData.array = this;
    9671          10 :     sData.poMask = GetMask(nullptr);
    9672          10 :     if (sData.poMask == nullptr)
    9673             :     {
    9674           0 :         return false;
    9675             :     }
    9676          10 :     sData.pfnProgress = pfnProgress;
    9677          10 :     sData.pProgressData = pProgressData;
    9678          10 :     if (!ProcessPerChunk(arrayStartIdx.data(), count.data(),
    9679          20 :                          GetProcessingChunkSize(nMaxChunkSize).data(),
    9680          10 :                          PerChunkFunc, &sData))
    9681             :     {
    9682           0 :         return false;
    9683             :     }
    9684             : 
    9685          10 :     if (pdfMin)
    9686          10 :         *pdfMin = sData.dfMin;
    9687             : 
    9688          10 :     if (pdfMax)
    9689          10 :         *pdfMax = sData.dfMax;
    9690             : 
    9691          10 :     if (pdfMean)
    9692           8 :         *pdfMean = sData.dfMean;
    9693             : 
    9694             :     const double dfStdDev =
    9695          10 :         sData.nValidCount > 0 ? sqrt(sData.dfM2 / sData.nValidCount) : 0.0;
    9696          10 :     if (pdfStdDev)
    9697           8 :         *pdfStdDev = dfStdDev;
    9698             : 
    9699          10 :     if (pnValidCount)
    9700           8 :         *pnValidCount = sData.nValidCount;
    9701             : 
    9702          10 :     SetStatistics(bApproxOK, sData.dfMin, sData.dfMax, sData.dfMean, dfStdDev,
    9703          10 :                   sData.nValidCount, papszOptions);
    9704             : 
    9705          10 :     return true;
    9706             : }
    9707             : 
    9708             : /************************************************************************/
    9709             : /*                            SetStatistics()                           */
    9710             : /************************************************************************/
    9711             : //! @cond Doxygen_Suppress
    9712           5 : bool GDALMDArray::SetStatistics(bool /* bApproxStats */, double /* dfMin */,
    9713             :                                 double /* dfMax */, double /* dfMean */,
    9714             :                                 double /* dfStdDev */,
    9715             :                                 GUInt64 /* nValidCount */,
    9716             :                                 CSLConstList /* papszOptions */)
    9717             : {
    9718           5 :     CPLDebug("GDAL", "Cannot save statistics on a non-PAM MDArray");
    9719           5 :     return false;
    9720             : }
    9721             : 
    9722             : //! @endcond
    9723             : 
    9724             : /************************************************************************/
    9725             : /*                           ClearStatistics()                          */
    9726             : /************************************************************************/
    9727             : 
    9728             : /**
    9729             :  * \brief Clear statistics.
    9730             :  *
    9731             :  * @since GDAL 3.4
    9732             :  */
    9733           0 : void GDALMDArray::ClearStatistics()
    9734             : {
    9735           0 : }
    9736             : 
    9737             : /************************************************************************/
    9738             : /*                      GetCoordinateVariables()                        */
    9739             : /************************************************************************/
    9740             : 
    9741             : /**
    9742             :  * \brief Return coordinate variables.
    9743             :  *
    9744             :  * Coordinate variables are an alternate way of indexing an array that can
    9745             :  * be sometimes used. For example, an array collected through remote sensing
    9746             :  * might be indexed by (scanline, pixel). But there can be
    9747             :  * a longitude and latitude arrays alongside that are also both indexed by
    9748             :  * (scanline, pixel), and are referenced from operational arrays for
    9749             :  * reprojection purposes.
    9750             :  *
    9751             :  * For netCDF, this will return the arrays referenced by the "coordinates"
    9752             :  * attribute.
    9753             :  *
    9754             :  * This method is the same as the C function
    9755             :  * GDALMDArrayGetCoordinateVariables().
    9756             :  *
    9757             :  * @return a vector of arrays
    9758             :  *
    9759             :  * @since GDAL 3.4
    9760             :  */
    9761             : 
    9762             : std::vector<std::shared_ptr<GDALMDArray>>
    9763          13 : GDALMDArray::GetCoordinateVariables() const
    9764             : {
    9765          13 :     return {};
    9766             : }
    9767             : 
    9768             : /************************************************************************/
    9769             : /*                       ~GDALExtendedDataType()                        */
    9770             : /************************************************************************/
    9771             : 
    9772             : GDALExtendedDataType::~GDALExtendedDataType() = default;
    9773             : 
    9774             : /************************************************************************/
    9775             : /*                        GDALExtendedDataType()                        */
    9776             : /************************************************************************/
    9777             : 
    9778        7578 : GDALExtendedDataType::GDALExtendedDataType(size_t nMaxStringLength,
    9779        7578 :                                            GDALExtendedDataTypeSubType eSubType)
    9780             :     : m_eClass(GEDTC_STRING), m_eSubType(eSubType), m_nSize(sizeof(char *)),
    9781        7578 :       m_nMaxStringLength(nMaxStringLength)
    9782             : {
    9783        7578 : }
    9784             : 
    9785             : /************************************************************************/
    9786             : /*                        GDALExtendedDataType()                        */
    9787             : /************************************************************************/
    9788             : 
    9789       30819 : GDALExtendedDataType::GDALExtendedDataType(GDALDataType eType)
    9790             :     : m_eClass(GEDTC_NUMERIC), m_eNumericDT(eType),
    9791       30819 :       m_nSize(GDALGetDataTypeSizeBytes(eType))
    9792             : {
    9793       30819 : }
    9794             : 
    9795             : /************************************************************************/
    9796             : /*                        GDALExtendedDataType()                        */
    9797             : /************************************************************************/
    9798             : 
    9799         571 : GDALExtendedDataType::GDALExtendedDataType(
    9800             :     const std::string &osName, size_t nTotalSize,
    9801         571 :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
    9802             :     : m_osName(osName), m_eClass(GEDTC_COMPOUND),
    9803         571 :       m_aoComponents(std::move(components)), m_nSize(nTotalSize)
    9804             : {
    9805         571 : }
    9806             : 
    9807             : /************************************************************************/
    9808             : /*                        GDALExtendedDataType()                        */
    9809             : /************************************************************************/
    9810             : 
    9811             : /** Copy constructor. */
    9812       15042 : GDALExtendedDataType::GDALExtendedDataType(const GDALExtendedDataType &other)
    9813       30084 :     : m_osName(other.m_osName), m_eClass(other.m_eClass),
    9814       15042 :       m_eSubType(other.m_eSubType), m_eNumericDT(other.m_eNumericDT),
    9815       15042 :       m_nSize(other.m_nSize), m_nMaxStringLength(other.m_nMaxStringLength)
    9816             : {
    9817       15042 :     if (m_eClass == GEDTC_COMPOUND)
    9818             :     {
    9819         431 :         for (const auto &elt : other.m_aoComponents)
    9820             :         {
    9821         281 :             m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
    9822             :         }
    9823             :     }
    9824       15042 : }
    9825             : 
    9826             : /************************************************************************/
    9827             : /*                            operator= ()                              */
    9828             : /************************************************************************/
    9829             : 
    9830             : /** Copy assignment. */
    9831             : GDALExtendedDataType &
    9832         606 : GDALExtendedDataType::operator=(const GDALExtendedDataType &other)
    9833             : {
    9834         606 :     if (this != &other)
    9835             :     {
    9836         606 :         m_osName = other.m_osName;
    9837         606 :         m_eClass = other.m_eClass;
    9838         606 :         m_eSubType = other.m_eSubType;
    9839         606 :         m_eNumericDT = other.m_eNumericDT;
    9840         606 :         m_nSize = other.m_nSize;
    9841         606 :         m_nMaxStringLength = other.m_nMaxStringLength;
    9842         606 :         m_aoComponents.clear();
    9843         606 :         if (m_eClass == GEDTC_COMPOUND)
    9844             :         {
    9845           0 :             for (const auto &elt : other.m_aoComponents)
    9846             :             {
    9847           0 :                 m_aoComponents.emplace_back(new GDALEDTComponent(*elt));
    9848             :             }
    9849             :         }
    9850             :     }
    9851         606 :     return *this;
    9852             : }
    9853             : 
    9854             : /************************************************************************/
    9855             : /*                            operator= ()                              */
    9856             : /************************************************************************/
    9857             : 
    9858             : /** Move assignment. */
    9859             : GDALExtendedDataType &
    9860       12464 : GDALExtendedDataType::operator=(GDALExtendedDataType &&other)
    9861             : {
    9862       12464 :     m_osName = std::move(other.m_osName);
    9863       12464 :     m_eClass = other.m_eClass;
    9864       12464 :     m_eSubType = other.m_eSubType;
    9865       12464 :     m_eNumericDT = other.m_eNumericDT;
    9866       12464 :     m_nSize = other.m_nSize;
    9867       12464 :     m_nMaxStringLength = other.m_nMaxStringLength;
    9868       12464 :     m_aoComponents = std::move(other.m_aoComponents);
    9869       12464 :     other.m_eClass = GEDTC_NUMERIC;
    9870       12464 :     other.m_eNumericDT = GDT_Unknown;
    9871       12464 :     other.m_nSize = 0;
    9872       12464 :     other.m_nMaxStringLength = 0;
    9873       12464 :     return *this;
    9874             : }
    9875             : 
    9876             : /************************************************************************/
    9877             : /*                           Create()                                   */
    9878             : /************************************************************************/
    9879             : 
    9880             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
    9881             :  *
    9882             :  * This is the same as the C function GDALExtendedDataTypeCreate()
    9883             :  *
    9884             :  * @param eType Numeric data type.
    9885             :  */
    9886       30813 : GDALExtendedDataType GDALExtendedDataType::Create(GDALDataType eType)
    9887             : {
    9888       30813 :     return GDALExtendedDataType(eType);
    9889             : }
    9890             : 
    9891             : /************************************************************************/
    9892             : /*                           Create()                                   */
    9893             : /************************************************************************/
    9894             : 
    9895             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
    9896             :  *
    9897             :  * This is the same as the C function GDALExtendedDataTypeCreateCompound()
    9898             :  *
    9899             :  * @param osName Type name.
    9900             :  * @param nTotalSize Total size of the type in bytes.
    9901             :  *                   Should be large enough to store all components.
    9902             :  * @param components Components of the compound type.
    9903             :  */
    9904         577 : GDALExtendedDataType GDALExtendedDataType::Create(
    9905             :     const std::string &osName, size_t nTotalSize,
    9906             :     std::vector<std::unique_ptr<GDALEDTComponent>> &&components)
    9907             : {
    9908         577 :     size_t nLastOffset = 0;
    9909             :     // Some arbitrary threshold to avoid potential integer overflows
    9910         577 :     if (nTotalSize > static_cast<size_t>(std::numeric_limits<int>::max() / 2))
    9911             :     {
    9912           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
    9913           2 :         return GDALExtendedDataType(GDT_Unknown);
    9914             :     }
    9915        2966 :     for (const auto &comp : components)
    9916             :     {
    9917             :         // Check alignment too ?
    9918        2392 :         if (comp->GetOffset() < nLastOffset)
    9919             :         {
    9920           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
    9921           1 :             return GDALExtendedDataType(GDT_Unknown);
    9922             :         }
    9923        2391 :         nLastOffset = comp->GetOffset() + comp->GetType().GetSize();
    9924             :     }
    9925         574 :     if (nTotalSize < nLastOffset)
    9926             :     {
    9927           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid offset/size");
    9928           1 :         return GDALExtendedDataType(GDT_Unknown);
    9929             :     }
    9930         573 :     if (nTotalSize == 0 || components.empty())
    9931             :     {
    9932           2 :         CPLError(CE_Failure, CPLE_AppDefined, "Empty compound not allowed");
    9933           2 :         return GDALExtendedDataType(GDT_Unknown);
    9934             :     }
    9935         571 :     return GDALExtendedDataType(osName, nTotalSize, std::move(components));
    9936             : }
    9937             : 
    9938             : /************************************************************************/
    9939             : /*                           Create()                                   */
    9940             : /************************************************************************/
    9941             : 
    9942             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
    9943             :  *
    9944             :  * This is the same as the C function GDALExtendedDataTypeCreateString().
    9945             :  *
    9946             :  * @param nMaxStringLength maximum length of a string in bytes. 0 if
    9947             :  * unknown/unlimited
    9948             :  * @param eSubType Subtype.
    9949             :  */
    9950             : GDALExtendedDataType
    9951        7578 : GDALExtendedDataType::CreateString(size_t nMaxStringLength,
    9952             :                                    GDALExtendedDataTypeSubType eSubType)
    9953             : {
    9954        7578 :     return GDALExtendedDataType(nMaxStringLength, eSubType);
    9955             : }
    9956             : 
    9957             : /************************************************************************/
    9958             : /*                           operator==()                               */
    9959             : /************************************************************************/
    9960             : 
    9961             : /** Equality operator.
    9962             :  *
    9963             :  * This is the same as the C function GDALExtendedDataTypeEquals().
    9964             :  */
    9965        2051 : bool GDALExtendedDataType::operator==(const GDALExtendedDataType &other) const
    9966             : {
    9967        2024 :     if (m_eClass != other.m_eClass || m_eSubType != other.m_eSubType ||
    9968        4075 :         m_nSize != other.m_nSize || m_osName != other.m_osName)
    9969             :     {
    9970         166 :         return false;
    9971             :     }
    9972        1885 :     if (m_eClass == GEDTC_NUMERIC)
    9973             :     {
    9974         805 :         return m_eNumericDT == other.m_eNumericDT;
    9975             :     }
    9976        1080 :     if (m_eClass == GEDTC_STRING)
    9977             :     {
    9978         931 :         return true;
    9979             :     }
    9980         149 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
    9981         149 :     if (m_aoComponents.size() != other.m_aoComponents.size())
    9982             :     {
    9983           2 :         return false;
    9984             :     }
    9985         668 :     for (size_t i = 0; i < m_aoComponents.size(); i++)
    9986             :     {
    9987         521 :         if (!(*m_aoComponents[i] == *other.m_aoComponents[i]))
    9988             :         {
    9989           0 :             return false;
    9990             :         }
    9991             :     }
    9992         147 :     return true;
    9993             : }
    9994             : 
    9995             : /************************************************************************/
    9996             : /*                        CanConvertTo()                                */
    9997             : /************************************************************************/
    9998             : 
    9999             : /** Return whether this data type can be converted to the other one.
   10000             :  *
   10001             :  * This is the same as the C function GDALExtendedDataTypeCanConvertTo().
   10002             :  *
   10003             :  * @param other Target data type for the conversion being considered.
   10004             :  */
   10005        7521 : bool GDALExtendedDataType::CanConvertTo(const GDALExtendedDataType &other) const
   10006             : {
   10007        7521 :     if (m_eClass == GEDTC_NUMERIC)
   10008             :     {
   10009        5337 :         if (m_eNumericDT == GDT_Unknown)
   10010           0 :             return false;
   10011        5337 :         if (other.m_eClass == GEDTC_NUMERIC &&
   10012        5263 :             other.m_eNumericDT == GDT_Unknown)
   10013           0 :             return false;
   10014        5411 :         return other.m_eClass == GEDTC_NUMERIC ||
   10015        5411 :                other.m_eClass == GEDTC_STRING;
   10016             :     }
   10017        2184 :     if (m_eClass == GEDTC_STRING)
   10018             :     {
   10019        2067 :         return other.m_eClass == m_eClass;
   10020             :     }
   10021         117 :     CPLAssert(m_eClass == GEDTC_COMPOUND);
   10022         117 :     if (other.m_eClass != GEDTC_COMPOUND)
   10023           0 :         return false;
   10024             :     std::map<std::string, const std::unique_ptr<GDALEDTComponent> *>
   10025         234 :         srcComponents;
   10026         482 :     for (const auto &srcComp : m_aoComponents)
   10027             :     {
   10028         365 :         srcComponents[srcComp->GetName()] = &srcComp;
   10029             :     }
   10030         355 :     for (const auto &dstComp : other.m_aoComponents)
   10031             :     {
   10032         239 :         auto oIter = srcComponents.find(dstComp->GetName());
   10033         239 :         if (oIter == srcComponents.end())
   10034           1 :             return false;
   10035         238 :         if (!(*(oIter->second))->GetType().CanConvertTo(dstComp->GetType()))
   10036           0 :             return false;
   10037             :     }
   10038         116 :     return true;
   10039             : }
   10040             : 
   10041             : /************************************************************************/
   10042             : /*                     NeedsFreeDynamicMemory()                         */
   10043             : /************************************************************************/
   10044             : 
   10045             : /** Return whether the data type holds dynamically allocated memory, that
   10046             :  * needs to be freed with FreeDynamicMemory().
   10047             :  *
   10048             :  */
   10049        3335 : bool GDALExtendedDataType::NeedsFreeDynamicMemory() const
   10050             : {
   10051        3335 :     switch (m_eClass)
   10052             :     {
   10053         804 :         case GEDTC_STRING:
   10054         804 :             return true;
   10055             : 
   10056        2470 :         case GEDTC_NUMERIC:
   10057        2470 :             return false;
   10058             : 
   10059          61 :         case GEDTC_COMPOUND:
   10060             :         {
   10061         154 :             for (const auto &comp : m_aoComponents)
   10062             :             {
   10063         140 :                 if (comp->GetType().NeedsFreeDynamicMemory())
   10064          47 :                     return true;
   10065             :             }
   10066             :         }
   10067             :     }
   10068          14 :     return false;
   10069             : }
   10070             : 
   10071             : /************************************************************************/
   10072             : /*                        FreeDynamicMemory()                           */
   10073             : /************************************************************************/
   10074             : 
   10075             : /** Release the dynamic memory (strings typically) from a raw value.
   10076             :  *
   10077             :  * This is the same as the C function GDALExtendedDataTypeFreeDynamicMemory().
   10078             :  *
   10079             :  * @param pBuffer Raw buffer of a single element of an attribute or array value.
   10080             :  */
   10081        3031 : void GDALExtendedDataType::FreeDynamicMemory(void *pBuffer) const
   10082             : {
   10083        3031 :     switch (m_eClass)
   10084             :     {
   10085        2179 :         case GEDTC_STRING:
   10086             :         {
   10087             :             char *pszStr;
   10088        2179 :             memcpy(&pszStr, pBuffer, sizeof(char *));
   10089        2179 :             if (pszStr)
   10090             :             {
   10091        1701 :                 VSIFree(pszStr);
   10092             :             }
   10093        2179 :             break;
   10094             :         }
   10095             : 
   10096         735 :         case GEDTC_NUMERIC:
   10097             :         {
   10098         735 :             break;
   10099             :         }
   10100             : 
   10101         117 :         case GEDTC_COMPOUND:
   10102             :         {
   10103         117 :             GByte *pabyBuffer = static_cast<GByte *>(pBuffer);
   10104         507 :             for (const auto &comp : m_aoComponents)
   10105             :             {
   10106         780 :                 comp->GetType().FreeDynamicMemory(pabyBuffer +
   10107         390 :                                                   comp->GetOffset());
   10108             :             }
   10109         117 :             break;
   10110             :         }
   10111             :     }
   10112        3031 : }
   10113             : 
   10114             : /************************************************************************/
   10115             : /*                      ~GDALEDTComponent()                             */
   10116             : /************************************************************************/
   10117             : 
   10118             : GDALEDTComponent::~GDALEDTComponent() = default;
   10119             : 
   10120             : /************************************************************************/
   10121             : /*                      GDALEDTComponent()                              */
   10122             : /************************************************************************/
   10123             : 
   10124             : /** constructor of a GDALEDTComponent
   10125             :  *
   10126             :  * This is the same as the C function GDALEDTComponendCreate()
   10127             :  *
   10128             :  * @param name Component name
   10129             :  * @param offset Offset in byte of the component in the compound data type.
   10130             :  *               In case of nesting of compound data type, this should be
   10131             :  *               the offset to the immediate belonging data type, not to the
   10132             :  *               higher level one.
   10133             :  * @param type   Component data type.
   10134             :  */
   10135        2383 : GDALEDTComponent::GDALEDTComponent(const std::string &name, size_t offset,
   10136        2383 :                                    const GDALExtendedDataType &type)
   10137        2383 :     : m_osName(name), m_nOffset(offset), m_oType(type)
   10138             : {
   10139        2383 : }
   10140             : 
   10141             : /************************************************************************/
   10142             : /*                      GDALEDTComponent()                              */
   10143             : /************************************************************************/
   10144             : 
   10145             : /** Copy constructor. */
   10146             : GDALEDTComponent::GDALEDTComponent(const GDALEDTComponent &) = default;
   10147             : 
   10148             : /************************************************************************/
   10149             : /*                           operator==()                               */
   10150             : /************************************************************************/
   10151             : 
   10152             : /** Equality operator.
   10153             :  */
   10154         521 : bool GDALEDTComponent::operator==(const GDALEDTComponent &other) const
   10155             : {
   10156        1042 :     return m_osName == other.m_osName && m_nOffset == other.m_nOffset &&
   10157        1042 :            m_oType == other.m_oType;
   10158             : }
   10159             : 
   10160             : /************************************************************************/
   10161             : /*                        ~GDALDimension()                              */
   10162             : /************************************************************************/
   10163             : 
   10164             : GDALDimension::~GDALDimension() = default;
   10165             : 
   10166             : /************************************************************************/
   10167             : /*                         GDALDimension()                              */
   10168             : /************************************************************************/
   10169             : 
   10170             : //! @cond Doxygen_Suppress
   10171             : /** Constructor.
   10172             :  *
   10173             :  * @param osParentName Parent name
   10174             :  * @param osName name
   10175             :  * @param osType type. See GetType().
   10176             :  * @param osDirection direction. See GetDirection().
   10177             :  * @param nSize size.
   10178             :  */
   10179        7902 : GDALDimension::GDALDimension(const std::string &osParentName,
   10180             :                              const std::string &osName,
   10181             :                              const std::string &osType,
   10182        7902 :                              const std::string &osDirection, GUInt64 nSize)
   10183             :     : m_osName(osName),
   10184             :       m_osFullName(
   10185        7902 :           !osParentName.empty()
   10186       11709 :               ? ((osParentName == "/" ? "/" : osParentName + "/") + osName)
   10187             :               : osName),
   10188       27513 :       m_osType(osType), m_osDirection(osDirection), m_nSize(nSize)
   10189             : {
   10190        7902 : }
   10191             : 
   10192             : //! @endcond
   10193             : 
   10194             : /************************************************************************/
   10195             : /*                         GetIndexingVariable()                        */
   10196             : /************************************************************************/
   10197             : 
   10198             : /** Return the variable that is used to index the dimension (if there is one).
   10199             :  *
   10200             :  * This is the array, typically one-dimensional, describing the values taken
   10201             :  * by the dimension.
   10202             :  */
   10203          29 : std::shared_ptr<GDALMDArray> GDALDimension::GetIndexingVariable() const
   10204             : {
   10205          29 :     return nullptr;
   10206             : }
   10207             : 
   10208             : /************************************************************************/
   10209             : /*                         SetIndexingVariable()                        */
   10210             : /************************************************************************/
   10211             : 
   10212             : /** Set the variable that is used to index the dimension.
   10213             :  *
   10214             :  * This is the array, typically one-dimensional, describing the values taken
   10215             :  * by the dimension.
   10216             :  *
   10217             :  * Optionally implemented by drivers.
   10218             :  *
   10219             :  * Drivers known to implement it: MEM.
   10220             :  *
   10221             :  * @param poArray Variable to use to index the dimension.
   10222             :  * @return true in case of success.
   10223             :  */
   10224           3 : bool GDALDimension::SetIndexingVariable(
   10225             :     CPL_UNUSED std::shared_ptr<GDALMDArray> poArray)
   10226             : {
   10227           3 :     CPLError(CE_Failure, CPLE_NotSupported,
   10228             :              "SetIndexingVariable() not implemented");
   10229           3 :     return false;
   10230             : }
   10231             : 
   10232             : /************************************************************************/
   10233             : /*                            Rename()                                  */
   10234             : /************************************************************************/
   10235             : 
   10236             : /** Rename the dimension.
   10237             :  *
   10238             :  * This is not implemented by all drivers.
   10239             :  *
   10240             :  * Drivers known to implement it: MEM, netCDF, ZARR.
   10241             :  *
   10242             :  * This is the same as the C function GDALDimensionRename().
   10243             :  *
   10244             :  * @param osNewName New name.
   10245             :  *
   10246             :  * @return true in case of success
   10247             :  * @since GDAL 3.8
   10248             :  */
   10249           0 : bool GDALDimension::Rename(CPL_UNUSED const std::string &osNewName)
   10250             : {
   10251           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Rename() not implemented");
   10252           0 :     return false;
   10253             : }
   10254             : 
   10255             : /************************************************************************/
   10256             : /*                         BaseRename()                                 */
   10257             : /************************************************************************/
   10258             : 
   10259             : //! @cond Doxygen_Suppress
   10260           8 : void GDALDimension::BaseRename(const std::string &osNewName)
   10261             : {
   10262           8 :     m_osFullName.resize(m_osFullName.size() - m_osName.size());
   10263           8 :     m_osFullName += osNewName;
   10264           8 :     m_osName = osNewName;
   10265           8 : }
   10266             : 
   10267             : //! @endcond
   10268             : 
   10269             : //! @cond Doxygen_Suppress
   10270             : /************************************************************************/
   10271             : /*                          ParentRenamed()                             */
   10272             : /************************************************************************/
   10273             : 
   10274           8 : void GDALDimension::ParentRenamed(const std::string &osNewParentFullName)
   10275             : {
   10276           8 :     m_osFullName = osNewParentFullName;
   10277           8 :     m_osFullName += "/";
   10278           8 :     m_osFullName += m_osName;
   10279           8 : }
   10280             : 
   10281             : //! @endcond
   10282             : 
   10283             : //! @cond Doxygen_Suppress
   10284             : /************************************************************************/
   10285             : /*                          ParentDeleted()                             */
   10286             : /************************************************************************/
   10287             : 
   10288           4 : void GDALDimension::ParentDeleted()
   10289             : {
   10290           4 : }
   10291             : 
   10292             : //! @endcond
   10293             : 
   10294             : /************************************************************************/
   10295             : /************************************************************************/
   10296             : /************************************************************************/
   10297             : /*                              C API                                   */
   10298             : /************************************************************************/
   10299             : /************************************************************************/
   10300             : /************************************************************************/
   10301             : 
   10302             : /************************************************************************/
   10303             : /*                      GDALExtendedDataTypeCreate()                    */
   10304             : /************************************************************************/
   10305             : 
   10306             : /** Return a new GDALExtendedDataType of class GEDTC_NUMERIC.
   10307             :  *
   10308             :  * This is the same as the C++ method GDALExtendedDataType::Create()
   10309             :  *
   10310             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10311             :  *
   10312             :  * @param eType Numeric data type.
   10313             :  *
   10314             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10315             :  */
   10316        1940 : GDALExtendedDataTypeH GDALExtendedDataTypeCreate(GDALDataType eType)
   10317             : {
   10318             :     return new GDALExtendedDataTypeHS(
   10319        1940 :         new GDALExtendedDataType(GDALExtendedDataType::Create(eType)));
   10320             : }
   10321             : 
   10322             : /************************************************************************/
   10323             : /*                    GDALExtendedDataTypeCreateString()                */
   10324             : /************************************************************************/
   10325             : 
   10326             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10327             :  *
   10328             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10329             :  *
   10330             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10331             :  *
   10332             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10333             :  */
   10334           0 : GDALExtendedDataTypeH GDALExtendedDataTypeCreateString(size_t nMaxStringLength)
   10335             : {
   10336           0 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10337           0 :         GDALExtendedDataType::CreateString(nMaxStringLength)));
   10338             : }
   10339             : 
   10340             : /************************************************************************/
   10341             : /*                   GDALExtendedDataTypeCreateStringEx()               */
   10342             : /************************************************************************/
   10343             : 
   10344             : /** Return a new GDALExtendedDataType of class GEDTC_STRING.
   10345             :  *
   10346             :  * This is the same as the C++ method GDALExtendedDataType::CreateString()
   10347             :  *
   10348             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10349             :  *
   10350             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10351             :  * @since GDAL 3.4
   10352             :  */
   10353             : GDALExtendedDataTypeH
   10354         185 : GDALExtendedDataTypeCreateStringEx(size_t nMaxStringLength,
   10355             :                                    GDALExtendedDataTypeSubType eSubType)
   10356             : {
   10357         185 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(
   10358         185 :         GDALExtendedDataType::CreateString(nMaxStringLength, eSubType)));
   10359             : }
   10360             : 
   10361             : /************************************************************************/
   10362             : /*                   GDALExtendedDataTypeCreateCompound()               */
   10363             : /************************************************************************/
   10364             : 
   10365             : /** Return a new GDALExtendedDataType of class GEDTC_COMPOUND.
   10366             :  *
   10367             :  * This is the same as the C++ method GDALExtendedDataType::Create(const
   10368             :  * std::string&, size_t, std::vector<std::unique_ptr<GDALEDTComponent>>&&)
   10369             :  *
   10370             :  * The returned handle should be freed with GDALExtendedDataTypeRelease().
   10371             :  *
   10372             :  * @param pszName Type name.
   10373             :  * @param nTotalSize Total size of the type in bytes.
   10374             :  *                   Should be large enough to store all components.
   10375             :  * @param nComponents Number of components in comps array.
   10376             :  * @param comps Components.
   10377             :  * @return a new GDALExtendedDataTypeH handle, or nullptr.
   10378             :  */
   10379             : GDALExtendedDataTypeH
   10380          22 : GDALExtendedDataTypeCreateCompound(const char *pszName, size_t nTotalSize,
   10381             :                                    size_t nComponents,
   10382             :                                    const GDALEDTComponentH *comps)
   10383             : {
   10384          44 :     std::vector<std::unique_ptr<GDALEDTComponent>> compsCpp;
   10385          54 :     for (size_t i = 0; i < nComponents; i++)
   10386             :     {
   10387          64 :         compsCpp.emplace_back(std::unique_ptr<GDALEDTComponent>(
   10388          64 :             new GDALEDTComponent(*(comps[i]->m_poImpl.get()))));
   10389             :     }
   10390             :     auto dt = GDALExtendedDataType::Create(pszName ? pszName : "", nTotalSize,
   10391          66 :                                            std::move(compsCpp));
   10392          22 :     if (dt.GetClass() != GEDTC_COMPOUND)
   10393           6 :         return nullptr;
   10394          16 :     return new GDALExtendedDataTypeHS(new GDALExtendedDataType(dt));
   10395             : }
   10396             : 
   10397             : /************************************************************************/
   10398             : /*                     GDALExtendedDataTypeRelease()                    */
   10399             : /************************************************************************/
   10400             : 
   10401             : /** Release the GDAL in-memory object associated with a GDALExtendedDataTypeH.
   10402             :  *
   10403             :  * Note: when applied on a object coming from a driver, this does not
   10404             :  * destroy the object in the file, database, etc...
   10405             :  */
   10406        6371 : void GDALExtendedDataTypeRelease(GDALExtendedDataTypeH hEDT)
   10407             : {
   10408        6371 :     delete hEDT;
   10409        6371 : }
   10410             : 
   10411             : /************************************************************************/
   10412             : /*                     GDALExtendedDataTypeGetName()                    */
   10413             : /************************************************************************/
   10414             : 
   10415             : /** Return type name.
   10416             :  *
   10417             :  * This is the same as the C++ method GDALExtendedDataType::GetName()
   10418             :  */
   10419           7 : const char *GDALExtendedDataTypeGetName(GDALExtendedDataTypeH hEDT)
   10420             : {
   10421           7 :     VALIDATE_POINTER1(hEDT, __func__, "");
   10422           7 :     return hEDT->m_poImpl->GetName().c_str();
   10423             : }
   10424             : 
   10425             : /************************************************************************/
   10426             : /*                     GDALExtendedDataTypeGetClass()                    */
   10427             : /************************************************************************/
   10428             : 
   10429             : /** Return type class.
   10430             :  *
   10431             :  * This is the same as the C++ method GDALExtendedDataType::GetClass()
   10432             :  */
   10433             : GDALExtendedDataTypeClass
   10434        7401 : GDALExtendedDataTypeGetClass(GDALExtendedDataTypeH hEDT)
   10435             : {
   10436        7401 :     VALIDATE_POINTER1(hEDT, __func__, GEDTC_NUMERIC);
   10437        7401 :     return hEDT->m_poImpl->GetClass();
   10438             : }
   10439             : 
   10440             : /************************************************************************/
   10441             : /*               GDALExtendedDataTypeGetNumericDataType()               */
   10442             : /************************************************************************/
   10443             : 
   10444             : /** Return numeric data type (only valid when GetClass() == GEDTC_NUMERIC)
   10445             :  *
   10446             :  * This is the same as the C++ method GDALExtendedDataType::GetNumericDataType()
   10447             :  */
   10448         510 : GDALDataType GDALExtendedDataTypeGetNumericDataType(GDALExtendedDataTypeH hEDT)
   10449             : {
   10450         510 :     VALIDATE_POINTER1(hEDT, __func__, GDT_Unknown);
   10451         510 :     return hEDT->m_poImpl->GetNumericDataType();
   10452             : }
   10453             : 
   10454             : /************************************************************************/
   10455             : /*                   GDALExtendedDataTypeGetSize()                      */
   10456             : /************************************************************************/
   10457             : 
   10458             : /** Return data type size in bytes.
   10459             :  *
   10460             :  * This is the same as the C++ method GDALExtendedDataType::GetSize()
   10461             :  */
   10462        2460 : size_t GDALExtendedDataTypeGetSize(GDALExtendedDataTypeH hEDT)
   10463             : {
   10464        2460 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10465        2460 :     return hEDT->m_poImpl->GetSize();
   10466             : }
   10467             : 
   10468             : /************************************************************************/
   10469             : /*              GDALExtendedDataTypeGetMaxStringLength()                */
   10470             : /************************************************************************/
   10471             : 
   10472             : /** Return the maximum length of a string in bytes.
   10473             :  *
   10474             :  * 0 indicates unknown/unlimited string.
   10475             :  *
   10476             :  * This is the same as the C++ method GDALExtendedDataType::GetMaxStringLength()
   10477             :  */
   10478           3 : size_t GDALExtendedDataTypeGetMaxStringLength(GDALExtendedDataTypeH hEDT)
   10479             : {
   10480           3 :     VALIDATE_POINTER1(hEDT, __func__, 0);
   10481           3 :     return hEDT->m_poImpl->GetMaxStringLength();
   10482             : }
   10483             : 
   10484             : /************************************************************************/
   10485             : /*                    GDALExtendedDataTypeCanConvertTo()                */
   10486             : /************************************************************************/
   10487             : 
   10488             : /** Return whether this data type can be converted to the other one.
   10489             :  *
   10490             :  * This is the same as the C function GDALExtendedDataType::CanConvertTo()
   10491             :  *
   10492             :  * @param hSourceEDT Source data type for the conversion being considered.
   10493             :  * @param hTargetEDT Target data type for the conversion being considered.
   10494             :  * @return TRUE if hSourceEDT can be convert to hTargetEDT. FALSE otherwise.
   10495             :  */
   10496           7 : int GDALExtendedDataTypeCanConvertTo(GDALExtendedDataTypeH hSourceEDT,
   10497             :                                      GDALExtendedDataTypeH hTargetEDT)
   10498             : {
   10499           7 :     VALIDATE_POINTER1(hSourceEDT, __func__, FALSE);
   10500           7 :     VALIDATE_POINTER1(hTargetEDT, __func__, FALSE);
   10501           7 :     return hSourceEDT->m_poImpl->CanConvertTo(*(hTargetEDT->m_poImpl));
   10502             : }
   10503             : 
   10504             : /************************************************************************/
   10505             : /*                        GDALExtendedDataTypeEquals()                  */
   10506             : /************************************************************************/
   10507             : 
   10508             : /** Return whether this data type is equal to another one.
   10509             :  *
   10510             :  * This is the same as the C++ method GDALExtendedDataType::operator==()
   10511             :  *
   10512             :  * @param hFirstEDT First data type.
   10513             :  * @param hSecondEDT Second data type.
   10514             :  * @return TRUE if they are equal. FALSE otherwise.
   10515             :  */
   10516          98 : int GDALExtendedDataTypeEquals(GDALExtendedDataTypeH hFirstEDT,
   10517             :                                GDALExtendedDataTypeH hSecondEDT)
   10518             : {
   10519          98 :     VALIDATE_POINTER1(hFirstEDT, __func__, FALSE);
   10520          98 :     VALIDATE_POINTER1(hSecondEDT, __func__, FALSE);
   10521          98 :     return *(hFirstEDT->m_poImpl) == *(hSecondEDT->m_poImpl);
   10522             : }
   10523             : 
   10524             : /************************************************************************/
   10525             : /*                    GDALExtendedDataTypeGetSubType()                  */
   10526             : /************************************************************************/
   10527             : 
   10528             : /** Return the subtype of a type.
   10529             :  *
   10530             :  * This is the same as the C++ method GDALExtendedDataType::GetSubType()
   10531             :  *
   10532             :  * @param hEDT Data type.
   10533             :  * @return subtype.
   10534             :  * @since 3.4
   10535             :  */
   10536             : GDALExtendedDataTypeSubType
   10537         104 : GDALExtendedDataTypeGetSubType(GDALExtendedDataTypeH hEDT)
   10538             : {
   10539         104 :     VALIDATE_POINTER1(hEDT, __func__, GEDTST_NONE);
   10540         104 :     return hEDT->m_poImpl->GetSubType();
   10541             : }
   10542             : 
   10543             : /************************************************************************/
   10544             : /*                     GDALExtendedDataTypeGetComponents()              */
   10545             : /************************************************************************/
   10546             : 
   10547             : /** Return the components of the data type (only valid when GetClass() ==
   10548             :  * GEDTC_COMPOUND)
   10549             :  *
   10550             :  * The returned array and its content must be freed with
   10551             :  * GDALExtendedDataTypeFreeComponents(). If only the array itself needs to be
   10552             :  * freed, CPLFree() should be called (and GDALExtendedDataTypeRelease() on
   10553             :  * individual array members).
   10554             :  *
   10555             :  * This is the same as the C++ method GDALExtendedDataType::GetComponents()
   10556             :  *
   10557             :  * @param hEDT Data type
   10558             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   10559             :  * @return an array of *pnCount components.
   10560             :  */
   10561          44 : GDALEDTComponentH *GDALExtendedDataTypeGetComponents(GDALExtendedDataTypeH hEDT,
   10562             :                                                      size_t *pnCount)
   10563             : {
   10564          44 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   10565          44 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   10566          44 :     const auto &components = hEDT->m_poImpl->GetComponents();
   10567             :     auto ret = static_cast<GDALEDTComponentH *>(
   10568          44 :         CPLMalloc(sizeof(GDALEDTComponentH) * components.size()));
   10569         131 :     for (size_t i = 0; i < components.size(); i++)
   10570             :     {
   10571          87 :         ret[i] = new GDALEDTComponentHS(*components[i].get());
   10572             :     }
   10573          44 :     *pnCount = components.size();
   10574          44 :     return ret;
   10575             : }
   10576             : 
   10577             : /************************************************************************/
   10578             : /*                     GDALExtendedDataTypeFreeComponents()             */
   10579             : /************************************************************************/
   10580             : 
   10581             : /** Free the return of GDALExtendedDataTypeGetComponents().
   10582             :  *
   10583             :  * @param components return value of GDALExtendedDataTypeGetComponents()
   10584             :  * @param nCount *pnCount value returned by GDALExtendedDataTypeGetComponents()
   10585             :  */
   10586          44 : void GDALExtendedDataTypeFreeComponents(GDALEDTComponentH *components,
   10587             :                                         size_t nCount)
   10588             : {
   10589         131 :     for (size_t i = 0; i < nCount; i++)
   10590             :     {
   10591          87 :         delete components[i];
   10592             :     }
   10593          44 :     CPLFree(components);
   10594          44 : }
   10595             : 
   10596             : /************************************************************************/
   10597             : /*                         GDALEDTComponentCreate()                     */
   10598             : /************************************************************************/
   10599             : 
   10600             : /** Create a new GDALEDTComponent.
   10601             :  *
   10602             :  * The returned value must be freed with GDALEDTComponentRelease().
   10603             :  *
   10604             :  * This is the same as the C++ constructor GDALEDTComponent::GDALEDTComponent().
   10605             :  */
   10606          20 : GDALEDTComponentH GDALEDTComponentCreate(const char *pszName, size_t nOffset,
   10607             :                                          GDALExtendedDataTypeH hType)
   10608             : {
   10609          20 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   10610          20 :     VALIDATE_POINTER1(hType, __func__, nullptr);
   10611             :     return new GDALEDTComponentHS(
   10612          20 :         GDALEDTComponent(pszName, nOffset, *(hType->m_poImpl.get())));
   10613             : }
   10614             : 
   10615             : /************************************************************************/
   10616             : /*                         GDALEDTComponentRelease()                    */
   10617             : /************************************************************************/
   10618             : 
   10619             : /** Release the GDAL in-memory object associated with a GDALEDTComponentH.
   10620             :  *
   10621             :  * Note: when applied on a object coming from a driver, this does not
   10622             :  * destroy the object in the file, database, etc...
   10623             :  */
   10624          61 : void GDALEDTComponentRelease(GDALEDTComponentH hComp)
   10625             : {
   10626          61 :     delete hComp;
   10627          61 : }
   10628             : 
   10629             : /************************************************************************/
   10630             : /*                         GDALEDTComponentGetName()                    */
   10631             : /************************************************************************/
   10632             : 
   10633             : /** Return the name.
   10634             :  *
   10635             :  * The returned pointer is valid until hComp is released.
   10636             :  *
   10637             :  * This is the same as the C++ method GDALEDTComponent::GetName().
   10638             :  */
   10639          33 : const char *GDALEDTComponentGetName(GDALEDTComponentH hComp)
   10640             : {
   10641          33 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   10642          33 :     return hComp->m_poImpl->GetName().c_str();
   10643             : }
   10644             : 
   10645             : /************************************************************************/
   10646             : /*                       GDALEDTComponentGetOffset()                    */
   10647             : /************************************************************************/
   10648             : 
   10649             : /** Return the offset (in bytes) of the component in the compound data type.
   10650             :  *
   10651             :  * This is the same as the C++ method GDALEDTComponent::GetOffset().
   10652             :  */
   10653          31 : size_t GDALEDTComponentGetOffset(GDALEDTComponentH hComp)
   10654             : {
   10655          31 :     VALIDATE_POINTER1(hComp, __func__, 0);
   10656          31 :     return hComp->m_poImpl->GetOffset();
   10657             : }
   10658             : 
   10659             : /************************************************************************/
   10660             : /*                       GDALEDTComponentGetType()                      */
   10661             : /************************************************************************/
   10662             : 
   10663             : /** Return the data type of the component.
   10664             :  *
   10665             :  * This is the same as the C++ method GDALEDTComponent::GetType().
   10666             :  */
   10667          93 : GDALExtendedDataTypeH GDALEDTComponentGetType(GDALEDTComponentH hComp)
   10668             : {
   10669          93 :     VALIDATE_POINTER1(hComp, __func__, nullptr);
   10670             :     return new GDALExtendedDataTypeHS(
   10671          93 :         new GDALExtendedDataType(hComp->m_poImpl->GetType()));
   10672             : }
   10673             : 
   10674             : /************************************************************************/
   10675             : /*                           GDALGroupRelease()                         */
   10676             : /************************************************************************/
   10677             : 
   10678             : /** Release the GDAL in-memory object associated with a GDALGroupH.
   10679             :  *
   10680             :  * Note: when applied on a object coming from a driver, this does not
   10681             :  * destroy the object in the file, database, etc...
   10682             :  */
   10683        1375 : void GDALGroupRelease(GDALGroupH hGroup)
   10684             : {
   10685        1375 :     delete hGroup;
   10686        1375 : }
   10687             : 
   10688             : /************************************************************************/
   10689             : /*                           GDALGroupGetName()                         */
   10690             : /************************************************************************/
   10691             : 
   10692             : /** Return the name of the group.
   10693             :  *
   10694             :  * The returned pointer is valid until hGroup is released.
   10695             :  *
   10696             :  * This is the same as the C++ method GDALGroup::GetName().
   10697             :  */
   10698          87 : const char *GDALGroupGetName(GDALGroupH hGroup)
   10699             : {
   10700          87 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10701          87 :     return hGroup->m_poImpl->GetName().c_str();
   10702             : }
   10703             : 
   10704             : /************************************************************************/
   10705             : /*                         GDALGroupGetFullName()                       */
   10706             : /************************************************************************/
   10707             : 
   10708             : /** Return the full name of the group.
   10709             :  *
   10710             :  * The returned pointer is valid until hGroup is released.
   10711             :  *
   10712             :  * This is the same as the C++ method GDALGroup::GetFullName().
   10713             :  */
   10714          41 : const char *GDALGroupGetFullName(GDALGroupH hGroup)
   10715             : {
   10716          41 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10717          41 :     return hGroup->m_poImpl->GetFullName().c_str();
   10718             : }
   10719             : 
   10720             : /************************************************************************/
   10721             : /*                          GDALGroupGetMDArrayNames()                  */
   10722             : /************************************************************************/
   10723             : 
   10724             : /** Return the list of multidimensional array names contained in this group.
   10725             :  *
   10726             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   10727             :  *
   10728             :  * @return the array names, to be freed with CSLDestroy()
   10729             :  */
   10730         314 : char **GDALGroupGetMDArrayNames(GDALGroupH hGroup, CSLConstList papszOptions)
   10731             : {
   10732         314 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10733         628 :     auto names = hGroup->m_poImpl->GetMDArrayNames(papszOptions);
   10734         628 :     CPLStringList res;
   10735         798 :     for (const auto &name : names)
   10736             :     {
   10737         484 :         res.AddString(name.c_str());
   10738             :     }
   10739         314 :     return res.StealList();
   10740             : }
   10741             : 
   10742             : /************************************************************************/
   10743             : /*                          GDALGroupOpenMDArray()                      */
   10744             : /************************************************************************/
   10745             : 
   10746             : /** Open and return a multidimensional array.
   10747             :  *
   10748             :  * This is the same as the C++ method GDALGroup::OpenMDArray().
   10749             :  *
   10750             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   10751             :  */
   10752         755 : GDALMDArrayH GDALGroupOpenMDArray(GDALGroupH hGroup, const char *pszMDArrayName,
   10753             :                                   CSLConstList papszOptions)
   10754             : {
   10755         755 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10756         755 :     VALIDATE_POINTER1(pszMDArrayName, __func__, nullptr);
   10757        2265 :     auto array = hGroup->m_poImpl->OpenMDArray(std::string(pszMDArrayName),
   10758        2265 :                                                papszOptions);
   10759         755 :     if (!array)
   10760          28 :         return nullptr;
   10761         727 :     return new GDALMDArrayHS(array);
   10762             : }
   10763             : 
   10764             : /************************************************************************/
   10765             : /*                  GDALGroupOpenMDArrayFromFullname()                  */
   10766             : /************************************************************************/
   10767             : 
   10768             : /** Open and return a multidimensional array from its fully qualified name.
   10769             :  *
   10770             :  * This is the same as the C++ method GDALGroup::OpenMDArrayFromFullname().
   10771             :  *
   10772             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   10773             :  *
   10774             :  * @since GDAL 3.2
   10775             :  */
   10776          16 : GDALMDArrayH GDALGroupOpenMDArrayFromFullname(GDALGroupH hGroup,
   10777             :                                               const char *pszFullname,
   10778             :                                               CSLConstList papszOptions)
   10779             : {
   10780          16 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10781          16 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   10782          16 :     auto array = hGroup->m_poImpl->OpenMDArrayFromFullname(
   10783          48 :         std::string(pszFullname), papszOptions);
   10784          16 :     if (!array)
   10785           2 :         return nullptr;
   10786          14 :     return new GDALMDArrayHS(array);
   10787             : }
   10788             : 
   10789             : /************************************************************************/
   10790             : /*                      GDALGroupResolveMDArray()                       */
   10791             : /************************************************************************/
   10792             : 
   10793             : /** Locate an array in a group and its subgroups by name.
   10794             :  *
   10795             :  * See GDALGroup::ResolveMDArray() for description of the behavior.
   10796             :  * @since GDAL 3.2
   10797             :  */
   10798          19 : GDALMDArrayH GDALGroupResolveMDArray(GDALGroupH hGroup, const char *pszName,
   10799             :                                      const char *pszStartingPoint,
   10800             :                                      CSLConstList papszOptions)
   10801             : {
   10802          19 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10803          19 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   10804          19 :     VALIDATE_POINTER1(pszStartingPoint, __func__, nullptr);
   10805          19 :     auto array = hGroup->m_poImpl->ResolveMDArray(
   10806          57 :         std::string(pszName), std::string(pszStartingPoint), papszOptions);
   10807          19 :     if (!array)
   10808           2 :         return nullptr;
   10809          17 :     return new GDALMDArrayHS(array);
   10810             : }
   10811             : 
   10812             : /************************************************************************/
   10813             : /*                        GDALGroupGetGroupNames()                      */
   10814             : /************************************************************************/
   10815             : 
   10816             : /** Return the list of sub-groups contained in this group.
   10817             :  *
   10818             :  * This is the same as the C++ method GDALGroup::GetGroupNames().
   10819             :  *
   10820             :  * @return the group names, to be freed with CSLDestroy()
   10821             :  */
   10822          95 : char **GDALGroupGetGroupNames(GDALGroupH hGroup, CSLConstList papszOptions)
   10823             : {
   10824          95 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10825         190 :     auto names = hGroup->m_poImpl->GetGroupNames(papszOptions);
   10826         190 :     CPLStringList res;
   10827         215 :     for (const auto &name : names)
   10828             :     {
   10829         120 :         res.AddString(name.c_str());
   10830             :     }
   10831          95 :     return res.StealList();
   10832             : }
   10833             : 
   10834             : /************************************************************************/
   10835             : /*                           GDALGroupOpenGroup()                       */
   10836             : /************************************************************************/
   10837             : 
   10838             : /** Open and return a sub-group.
   10839             :  *
   10840             :  * This is the same as the C++ method GDALGroup::OpenGroup().
   10841             :  *
   10842             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   10843             :  */
   10844         157 : GDALGroupH GDALGroupOpenGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   10845             :                               CSLConstList papszOptions)
   10846             : {
   10847         157 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10848         157 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   10849             :     auto subGroup =
   10850         471 :         hGroup->m_poImpl->OpenGroup(std::string(pszSubGroupName), papszOptions);
   10851         157 :     if (!subGroup)
   10852          28 :         return nullptr;
   10853         129 :     return new GDALGroupHS(subGroup);
   10854             : }
   10855             : 
   10856             : /************************************************************************/
   10857             : /*                   GDALGroupGetVectorLayerNames()                     */
   10858             : /************************************************************************/
   10859             : 
   10860             : /** Return the list of layer names contained in this group.
   10861             :  *
   10862             :  * This is the same as the C++ method GDALGroup::GetVectorLayerNames().
   10863             :  *
   10864             :  * @return the group names, to be freed with CSLDestroy()
   10865             :  * @since 3.4
   10866             :  */
   10867           8 : char **GDALGroupGetVectorLayerNames(GDALGroupH hGroup,
   10868             :                                     CSLConstList papszOptions)
   10869             : {
   10870           8 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10871          16 :     auto names = hGroup->m_poImpl->GetVectorLayerNames(papszOptions);
   10872          16 :     CPLStringList res;
   10873          18 :     for (const auto &name : names)
   10874             :     {
   10875          10 :         res.AddString(name.c_str());
   10876             :     }
   10877           8 :     return res.StealList();
   10878             : }
   10879             : 
   10880             : /************************************************************************/
   10881             : /*                      GDALGroupOpenVectorLayer()                      */
   10882             : /************************************************************************/
   10883             : 
   10884             : /** Open and return a vector layer.
   10885             :  *
   10886             :  * This is the same as the C++ method GDALGroup::OpenVectorLayer().
   10887             :  *
   10888             :  * Note that the vector layer is owned by its parent GDALDatasetH, and thus
   10889             :  * the returned handled if only valid while the parent GDALDatasetH is kept
   10890             :  * opened.
   10891             :  *
   10892             :  * @return the vector layer, or nullptr.
   10893             :  * @since 3.4
   10894             :  */
   10895          12 : OGRLayerH GDALGroupOpenVectorLayer(GDALGroupH hGroup,
   10896             :                                    const char *pszVectorLayerName,
   10897             :                                    CSLConstList papszOptions)
   10898             : {
   10899          12 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10900          12 :     VALIDATE_POINTER1(pszVectorLayerName, __func__, nullptr);
   10901          24 :     return OGRLayer::ToHandle(hGroup->m_poImpl->OpenVectorLayer(
   10902          24 :         std::string(pszVectorLayerName), papszOptions));
   10903             : }
   10904             : 
   10905             : /************************************************************************/
   10906             : /*                       GDALGroupOpenMDArrayFromFullname()             */
   10907             : /************************************************************************/
   10908             : 
   10909             : /** Open and return a sub-group from its fully qualified name.
   10910             :  *
   10911             :  * This is the same as the C++ method GDALGroup::OpenGroupFromFullname().
   10912             :  *
   10913             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   10914             :  *
   10915             :  * @since GDAL 3.2
   10916             :  */
   10917           3 : GDALGroupH GDALGroupOpenGroupFromFullname(GDALGroupH hGroup,
   10918             :                                           const char *pszFullname,
   10919             :                                           CSLConstList papszOptions)
   10920             : {
   10921           3 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10922           3 :     VALIDATE_POINTER1(pszFullname, __func__, nullptr);
   10923           3 :     auto subGroup = hGroup->m_poImpl->OpenGroupFromFullname(
   10924           9 :         std::string(pszFullname), papszOptions);
   10925           3 :     if (!subGroup)
   10926           2 :         return nullptr;
   10927           1 :     return new GDALGroupHS(subGroup);
   10928             : }
   10929             : 
   10930             : /************************************************************************/
   10931             : /*                         GDALGroupGetDimensions()                     */
   10932             : /************************************************************************/
   10933             : 
   10934             : /** Return the list of dimensions contained in this group and used by its
   10935             :  * arrays.
   10936             :  *
   10937             :  * The returned array must be freed with GDALReleaseDimensions().  If only the
   10938             :  * array itself needs to be freed, CPLFree() should be called (and
   10939             :  * GDALDimensionRelease() on individual array members).
   10940             :  *
   10941             :  * This is the same as the C++ method GDALGroup::GetDimensions().
   10942             :  *
   10943             :  * @param hGroup Group.
   10944             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   10945             :  * @param papszOptions Driver specific options determining how dimensions
   10946             :  * should be retrieved. Pass nullptr for default behavior.
   10947             :  *
   10948             :  * @return an array of *pnCount dimensions.
   10949             :  */
   10950          73 : GDALDimensionH *GDALGroupGetDimensions(GDALGroupH hGroup, size_t *pnCount,
   10951             :                                        CSLConstList papszOptions)
   10952             : {
   10953          73 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10954          73 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   10955          73 :     auto dims = hGroup->m_poImpl->GetDimensions(papszOptions);
   10956             :     auto ret = static_cast<GDALDimensionH *>(
   10957          73 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   10958         230 :     for (size_t i = 0; i < dims.size(); i++)
   10959             :     {
   10960         157 :         ret[i] = new GDALDimensionHS(dims[i]);
   10961             :     }
   10962          73 :     *pnCount = dims.size();
   10963          73 :     return ret;
   10964             : }
   10965             : 
   10966             : /************************************************************************/
   10967             : /*                          GDALGroupGetAttribute()                     */
   10968             : /************************************************************************/
   10969             : 
   10970             : /** Return an attribute by its name.
   10971             :  *
   10972             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   10973             :  *
   10974             :  * The returned attribute must be freed with GDALAttributeRelease().
   10975             :  */
   10976          63 : GDALAttributeH GDALGroupGetAttribute(GDALGroupH hGroup, const char *pszName)
   10977             : {
   10978          63 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   10979          63 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   10980         189 :     auto attr = hGroup->m_poImpl->GetAttribute(std::string(pszName));
   10981          63 :     if (attr)
   10982          59 :         return new GDALAttributeHS(attr);
   10983           4 :     return nullptr;
   10984             : }
   10985             : 
   10986             : /************************************************************************/
   10987             : /*                         GDALGroupGetAttributes()                     */
   10988             : /************************************************************************/
   10989             : 
   10990             : /** Return the list of attributes contained in this group.
   10991             :  *
   10992             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   10993             :  * array itself needs to be freed, CPLFree() should be called (and
   10994             :  * GDALAttributeRelease() on individual array members).
   10995             :  *
   10996             :  * This is the same as the C++ method GDALGroup::GetAttributes().
   10997             :  *
   10998             :  * @param hGroup Group.
   10999             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11000             :  * @param papszOptions Driver specific options determining how attributes
   11001             :  * should be retrieved. Pass nullptr for default behavior.
   11002             :  *
   11003             :  * @return an array of *pnCount attributes.
   11004             :  */
   11005          67 : GDALAttributeH *GDALGroupGetAttributes(GDALGroupH hGroup, size_t *pnCount,
   11006             :                                        CSLConstList papszOptions)
   11007             : {
   11008          67 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11009          67 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11010          67 :     auto attrs = hGroup->m_poImpl->GetAttributes(papszOptions);
   11011             :     auto ret = static_cast<GDALAttributeH *>(
   11012          67 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11013         221 :     for (size_t i = 0; i < attrs.size(); i++)
   11014             :     {
   11015         154 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11016             :     }
   11017          67 :     *pnCount = attrs.size();
   11018          67 :     return ret;
   11019             : }
   11020             : 
   11021             : /************************************************************************/
   11022             : /*                     GDALGroupGetStructuralInfo()                     */
   11023             : /************************************************************************/
   11024             : 
   11025             : /** Return structural information on the group.
   11026             :  *
   11027             :  * This may be the compression, etc..
   11028             :  *
   11029             :  * The return value should not be freed and is valid until GDALGroup is
   11030             :  * released or this function called again.
   11031             :  *
   11032             :  * This is the same as the C++ method GDALGroup::GetStructuralInfo().
   11033             :  */
   11034           4 : CSLConstList GDALGroupGetStructuralInfo(GDALGroupH hGroup)
   11035             : {
   11036           4 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11037           4 :     return hGroup->m_poImpl->GetStructuralInfo();
   11038             : }
   11039             : 
   11040             : /************************************************************************/
   11041             : /*                         GDALReleaseAttributes()                      */
   11042             : /************************************************************************/
   11043             : 
   11044             : /** Free the return of GDALGroupGetAttributes() or GDALMDArrayGetAttributes()
   11045             :  *
   11046             :  * @param attributes return pointer of above methods
   11047             :  * @param nCount *pnCount value returned by above methods
   11048             :  */
   11049         124 : void GDALReleaseAttributes(GDALAttributeH *attributes, size_t nCount)
   11050             : {
   11051         406 :     for (size_t i = 0; i < nCount; i++)
   11052             :     {
   11053         282 :         delete attributes[i];
   11054             :     }
   11055         124 :     CPLFree(attributes);
   11056         124 : }
   11057             : 
   11058             : /************************************************************************/
   11059             : /*                         GDALGroupCreateGroup()                       */
   11060             : /************************************************************************/
   11061             : 
   11062             : /** Create a sub-group within a group.
   11063             :  *
   11064             :  * This is the same as the C++ method GDALGroup::CreateGroup().
   11065             :  *
   11066             :  * @return the sub-group, to be freed with GDALGroupRelease(), or nullptr.
   11067             :  */
   11068         173 : GDALGroupH GDALGroupCreateGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11069             :                                 CSLConstList papszOptions)
   11070             : {
   11071         173 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11072         173 :     VALIDATE_POINTER1(pszSubGroupName, __func__, nullptr);
   11073         519 :     auto ret = hGroup->m_poImpl->CreateGroup(std::string(pszSubGroupName),
   11074         519 :                                              papszOptions);
   11075         173 :     if (!ret)
   11076          49 :         return nullptr;
   11077         124 :     return new GDALGroupHS(ret);
   11078             : }
   11079             : 
   11080             : /************************************************************************/
   11081             : /*                         GDALGroupDeleteGroup()                       */
   11082             : /************************************************************************/
   11083             : 
   11084             : /** Delete a sub-group from a group.
   11085             :  *
   11086             :  * After this call, if a previously obtained instance of the deleted object
   11087             :  * is still alive, no method other than for freeing it should be invoked.
   11088             :  *
   11089             :  * This is the same as the C++ method GDALGroup::DeleteGroup().
   11090             :  *
   11091             :  * @return true in case of success.
   11092             :  * @since GDAL 3.8
   11093             :  */
   11094          20 : bool GDALGroupDeleteGroup(GDALGroupH hGroup, const char *pszSubGroupName,
   11095             :                           CSLConstList papszOptions)
   11096             : {
   11097          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11098          20 :     VALIDATE_POINTER1(pszSubGroupName, __func__, false);
   11099          40 :     return hGroup->m_poImpl->DeleteGroup(std::string(pszSubGroupName),
   11100          20 :                                          papszOptions);
   11101             : }
   11102             : 
   11103             : /************************************************************************/
   11104             : /*                      GDALGroupCreateDimension()                      */
   11105             : /************************************************************************/
   11106             : 
   11107             : /** Create a dimension within a group.
   11108             :  *
   11109             :  * This is the same as the C++ method GDALGroup::CreateDimension().
   11110             :  *
   11111             :  * @return the dimension, to be freed with GDALDimensionRelease(), or nullptr.
   11112             :  */
   11113         652 : GDALDimensionH GDALGroupCreateDimension(GDALGroupH hGroup, const char *pszName,
   11114             :                                         const char *pszType,
   11115             :                                         const char *pszDirection, GUInt64 nSize,
   11116             :                                         CSLConstList papszOptions)
   11117             : {
   11118         652 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11119         652 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11120         652 :     auto ret = hGroup->m_poImpl->CreateDimension(
   11121        1304 :         std::string(pszName), std::string(pszType ? pszType : ""),
   11122        2608 :         std::string(pszDirection ? pszDirection : ""), nSize, papszOptions);
   11123         652 :     if (!ret)
   11124           9 :         return nullptr;
   11125         643 :     return new GDALDimensionHS(ret);
   11126             : }
   11127             : 
   11128             : /************************************************************************/
   11129             : /*                      GDALGroupCreateMDArray()                        */
   11130             : /************************************************************************/
   11131             : 
   11132             : /** Create a multidimensional array within a group.
   11133             :  *
   11134             :  * This is the same as the C++ method GDALGroup::CreateMDArray().
   11135             :  *
   11136             :  * @return the array, to be freed with GDALMDArrayRelease(), or nullptr.
   11137             :  */
   11138         590 : GDALMDArrayH GDALGroupCreateMDArray(GDALGroupH hGroup, const char *pszName,
   11139             :                                     size_t nDimensions,
   11140             :                                     GDALDimensionH *pahDimensions,
   11141             :                                     GDALExtendedDataTypeH hEDT,
   11142             :                                     CSLConstList papszOptions)
   11143             : {
   11144         590 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11145         590 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11146         590 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11147        1180 :     std::vector<std::shared_ptr<GDALDimension>> dims;
   11148         590 :     dims.reserve(nDimensions);
   11149        1393 :     for (size_t i = 0; i < nDimensions; i++)
   11150         803 :         dims.push_back(pahDimensions[i]->m_poImpl);
   11151        1770 :     auto ret = hGroup->m_poImpl->CreateMDArray(std::string(pszName), dims,
   11152        1770 :                                                *(hEDT->m_poImpl), papszOptions);
   11153         590 :     if (!ret)
   11154          65 :         return nullptr;
   11155         525 :     return new GDALMDArrayHS(ret);
   11156             : }
   11157             : 
   11158             : /************************************************************************/
   11159             : /*                         GDALGroupDeleteMDArray()                     */
   11160             : /************************************************************************/
   11161             : 
   11162             : /** Delete an array from a group.
   11163             :  *
   11164             :  * After this call, if a previously obtained instance of the deleted object
   11165             :  * is still alive, no method other than for freeing it should be invoked.
   11166             :  *
   11167             :  * This is the same as the C++ method GDALGroup::DeleteMDArray().
   11168             :  *
   11169             :  * @return true in case of success.
   11170             :  * @since GDAL 3.8
   11171             :  */
   11172          20 : bool GDALGroupDeleteMDArray(GDALGroupH hGroup, const char *pszName,
   11173             :                             CSLConstList papszOptions)
   11174             : {
   11175          20 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11176          20 :     VALIDATE_POINTER1(pszName, __func__, false);
   11177          20 :     return hGroup->m_poImpl->DeleteMDArray(std::string(pszName), papszOptions);
   11178             : }
   11179             : 
   11180             : /************************************************************************/
   11181             : /*                      GDALGroupCreateAttribute()                      */
   11182             : /************************************************************************/
   11183             : 
   11184             : /** Create a attribute within a group.
   11185             :  *
   11186             :  * This is the same as the C++ method GDALGroup::CreateAttribute().
   11187             :  *
   11188             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11189             :  */
   11190         105 : GDALAttributeH GDALGroupCreateAttribute(GDALGroupH hGroup, const char *pszName,
   11191             :                                         size_t nDimensions,
   11192             :                                         const GUInt64 *panDimensions,
   11193             :                                         GDALExtendedDataTypeH hEDT,
   11194             :                                         CSLConstList papszOptions)
   11195             : {
   11196         105 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11197         105 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11198         210 :     std::vector<GUInt64> dims;
   11199         105 :     dims.reserve(nDimensions);
   11200         142 :     for (size_t i = 0; i < nDimensions; i++)
   11201          37 :         dims.push_back(panDimensions[i]);
   11202         105 :     auto ret = hGroup->m_poImpl->CreateAttribute(
   11203         315 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11204         105 :     if (!ret)
   11205          14 :         return nullptr;
   11206          91 :     return new GDALAttributeHS(ret);
   11207             : }
   11208             : 
   11209             : /************************************************************************/
   11210             : /*                         GDALGroupDeleteAttribute()                   */
   11211             : /************************************************************************/
   11212             : 
   11213             : /** Delete an attribute from a group.
   11214             :  *
   11215             :  * After this call, if a previously obtained instance of the deleted object
   11216             :  * is still alive, no method other than for freeing it should be invoked.
   11217             :  *
   11218             :  * This is the same as the C++ method GDALGroup::DeleteAttribute().
   11219             :  *
   11220             :  * @return true in case of success.
   11221             :  * @since GDAL 3.8
   11222             :  */
   11223          25 : bool GDALGroupDeleteAttribute(GDALGroupH hGroup, const char *pszName,
   11224             :                               CSLConstList papszOptions)
   11225             : {
   11226          25 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11227          25 :     VALIDATE_POINTER1(pszName, __func__, false);
   11228          50 :     return hGroup->m_poImpl->DeleteAttribute(std::string(pszName),
   11229          25 :                                              papszOptions);
   11230             : }
   11231             : 
   11232             : /************************************************************************/
   11233             : /*                          GDALGroupRename()                           */
   11234             : /************************************************************************/
   11235             : 
   11236             : /** Rename the group.
   11237             :  *
   11238             :  * This is not implemented by all drivers.
   11239             :  *
   11240             :  * Drivers known to implement it: MEM, netCDF.
   11241             :  *
   11242             :  * This is the same as the C++ method GDALGroup::Rename()
   11243             :  *
   11244             :  * @return true in case of success
   11245             :  * @since GDAL 3.8
   11246             :  */
   11247          45 : bool GDALGroupRename(GDALGroupH hGroup, const char *pszNewName)
   11248             : {
   11249          45 :     VALIDATE_POINTER1(hGroup, __func__, false);
   11250          45 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   11251          45 :     return hGroup->m_poImpl->Rename(pszNewName);
   11252             : }
   11253             : 
   11254             : /************************************************************************/
   11255             : /*                 GDALGroupSubsetDimensionFromSelection()              */
   11256             : /************************************************************************/
   11257             : 
   11258             : /** Return a virtual group whose one dimension has been subset according to a
   11259             :  * selection.
   11260             :  *
   11261             :  * This is the same as the C++ method GDALGroup::SubsetDimensionFromSelection().
   11262             :  *
   11263             :  * @return a virtual group, to be freed with GDALGroupRelease(), or nullptr.
   11264             :  */
   11265             : GDALGroupH
   11266          14 : GDALGroupSubsetDimensionFromSelection(GDALGroupH hGroup,
   11267             :                                       const char *pszSelection,
   11268             :                                       CPL_UNUSED CSLConstList papszOptions)
   11269             : {
   11270          14 :     VALIDATE_POINTER1(hGroup, __func__, nullptr);
   11271          14 :     VALIDATE_POINTER1(pszSelection, __func__, nullptr);
   11272          14 :     auto hNewGroup = hGroup->m_poImpl->SubsetDimensionFromSelection(
   11273          42 :         std::string(pszSelection));
   11274          14 :     if (!hNewGroup)
   11275           8 :         return nullptr;
   11276           6 :     return new GDALGroupHS(hNewGroup);
   11277             : }
   11278             : 
   11279             : /************************************************************************/
   11280             : /*                        GDALMDArrayRelease()                          */
   11281             : /************************************************************************/
   11282             : 
   11283             : /** Release the GDAL in-memory object associated with a GDALMDArray.
   11284             :  *
   11285             :  * Note: when applied on a object coming from a driver, this does not
   11286             :  * destroy the object in the file, database, etc...
   11287             :  */
   11288        1935 : void GDALMDArrayRelease(GDALMDArrayH hMDArray)
   11289             : {
   11290        1935 :     delete hMDArray;
   11291        1935 : }
   11292             : 
   11293             : /************************************************************************/
   11294             : /*                        GDALMDArrayGetName()                          */
   11295             : /************************************************************************/
   11296             : 
   11297             : /** Return array name.
   11298             :  *
   11299             :  * This is the same as the C++ method GDALMDArray::GetName()
   11300             :  */
   11301          83 : const char *GDALMDArrayGetName(GDALMDArrayH hArray)
   11302             : {
   11303          83 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11304          83 :     return hArray->m_poImpl->GetName().c_str();
   11305             : }
   11306             : 
   11307             : /************************************************************************/
   11308             : /*                    GDALMDArrayGetFullName()                          */
   11309             : /************************************************************************/
   11310             : 
   11311             : /** Return array full name.
   11312             :  *
   11313             :  * This is the same as the C++ method GDALMDArray::GetFullName()
   11314             :  */
   11315          50 : const char *GDALMDArrayGetFullName(GDALMDArrayH hArray)
   11316             : {
   11317          50 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11318          50 :     return hArray->m_poImpl->GetFullName().c_str();
   11319             : }
   11320             : 
   11321             : /************************************************************************/
   11322             : /*                        GDALMDArrayGetName()                          */
   11323             : /************************************************************************/
   11324             : 
   11325             : /** Return the total number of values in the array.
   11326             :  *
   11327             :  * This is the same as the C++ method
   11328             :  * GDALAbstractMDArray::GetTotalElementsCount()
   11329             :  */
   11330           6 : GUInt64 GDALMDArrayGetTotalElementsCount(GDALMDArrayH hArray)
   11331             : {
   11332           6 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11333           6 :     return hArray->m_poImpl->GetTotalElementsCount();
   11334             : }
   11335             : 
   11336             : /************************************************************************/
   11337             : /*                        GDALMDArrayGetDimensionCount()                */
   11338             : /************************************************************************/
   11339             : 
   11340             : /** Return the number of dimensions.
   11341             :  *
   11342             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   11343             :  */
   11344       10002 : size_t GDALMDArrayGetDimensionCount(GDALMDArrayH hArray)
   11345             : {
   11346       10002 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11347       10002 :     return hArray->m_poImpl->GetDimensionCount();
   11348             : }
   11349             : 
   11350             : /************************************************************************/
   11351             : /*                        GDALMDArrayGetDimensions()                    */
   11352             : /************************************************************************/
   11353             : 
   11354             : /** Return the dimensions of the array
   11355             :  *
   11356             :  * The returned array must be freed with GDALReleaseDimensions(). If only the
   11357             :  * array itself needs to be freed, CPLFree() should be called (and
   11358             :  * GDALDimensionRelease() on individual array members).
   11359             :  *
   11360             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensions()
   11361             :  *
   11362             :  * @param hArray Array.
   11363             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11364             :  *
   11365             :  * @return an array of *pnCount dimensions.
   11366             :  */
   11367        2199 : GDALDimensionH *GDALMDArrayGetDimensions(GDALMDArrayH hArray, size_t *pnCount)
   11368             : {
   11369        2199 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11370        2199 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11371        2199 :     const auto &dims(hArray->m_poImpl->GetDimensions());
   11372             :     auto ret = static_cast<GDALDimensionH *>(
   11373        2199 :         CPLMalloc(sizeof(GDALDimensionH) * dims.size()));
   11374        6201 :     for (size_t i = 0; i < dims.size(); i++)
   11375             :     {
   11376        4002 :         ret[i] = new GDALDimensionHS(dims[i]);
   11377             :     }
   11378        2199 :     *pnCount = dims.size();
   11379        2199 :     return ret;
   11380             : }
   11381             : 
   11382             : /************************************************************************/
   11383             : /*                        GDALReleaseDimensions()                       */
   11384             : /************************************************************************/
   11385             : 
   11386             : /** Free the return of GDALGroupGetDimensions() or GDALMDArrayGetDimensions()
   11387             :  *
   11388             :  * @param dims return pointer of above methods
   11389             :  * @param nCount *pnCount value returned by above methods
   11390             :  */
   11391        2272 : void GDALReleaseDimensions(GDALDimensionH *dims, size_t nCount)
   11392             : {
   11393        6431 :     for (size_t i = 0; i < nCount; i++)
   11394             :     {
   11395        4159 :         delete dims[i];
   11396             :     }
   11397        2272 :     CPLFree(dims);
   11398        2272 : }
   11399             : 
   11400             : /************************************************************************/
   11401             : /*                        GDALMDArrayGetDataType()                     */
   11402             : /************************************************************************/
   11403             : 
   11404             : /** Return the data type
   11405             :  *
   11406             :  * The return must be freed with GDALExtendedDataTypeRelease().
   11407             :  */
   11408        3750 : GDALExtendedDataTypeH GDALMDArrayGetDataType(GDALMDArrayH hArray)
   11409             : {
   11410        3750 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11411             :     return new GDALExtendedDataTypeHS(
   11412        3750 :         new GDALExtendedDataType(hArray->m_poImpl->GetDataType()));
   11413             : }
   11414             : 
   11415             : /************************************************************************/
   11416             : /*                          GDALMDArrayRead()                           */
   11417             : /************************************************************************/
   11418             : 
   11419             : /** Read part or totality of a multidimensional array.
   11420             :  *
   11421             :  * This is the same as the C++ method GDALAbstractMDArray::Read()
   11422             :  *
   11423             :  * @return TRUE in case of success.
   11424             :  */
   11425        1882 : int GDALMDArrayRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11426             :                     const size_t *count, const GInt64 *arrayStep,
   11427             :                     const GPtrDiff_t *bufferStride,
   11428             :                     GDALExtendedDataTypeH bufferDataType, void *pDstBuffer,
   11429             :                     const void *pDstBufferAllocStart,
   11430             :                     size_t nDstBufferAllocSize)
   11431             : {
   11432        1882 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11433        1882 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11434           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11435             :     {
   11436           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11437           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11438             :     }
   11439        1882 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11440        1882 :     VALIDATE_POINTER1(pDstBuffer, __func__, FALSE);
   11441             :     // coverity[var_deref_model]
   11442        3764 :     return hArray->m_poImpl->Read(arrayStartIdx, count, arrayStep, bufferStride,
   11443        1882 :                                   *(bufferDataType->m_poImpl), pDstBuffer,
   11444        1882 :                                   pDstBufferAllocStart, nDstBufferAllocSize);
   11445             : }
   11446             : 
   11447             : /************************************************************************/
   11448             : /*                          GDALMDArrayWrite()                           */
   11449             : /************************************************************************/
   11450             : 
   11451             : /** Write part or totality of a multidimensional array.
   11452             :  *
   11453             :  * This is the same as the C++ method GDALAbstractMDArray::Write()
   11454             :  *
   11455             :  * @return TRUE in case of success.
   11456             :  */
   11457         547 : int GDALMDArrayWrite(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11458             :                      const size_t *count, const GInt64 *arrayStep,
   11459             :                      const GPtrDiff_t *bufferStride,
   11460             :                      GDALExtendedDataTypeH bufferDataType,
   11461             :                      const void *pSrcBuffer, const void *pSrcBufferAllocStart,
   11462             :                      size_t nSrcBufferAllocSize)
   11463             : {
   11464         547 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11465         547 :     if ((arrayStartIdx == nullptr || count == nullptr) &&
   11466           0 :         hArray->m_poImpl->GetDimensionCount() > 0)
   11467             :     {
   11468           0 :         VALIDATE_POINTER1(arrayStartIdx, __func__, FALSE);
   11469           0 :         VALIDATE_POINTER1(count, __func__, FALSE);
   11470             :     }
   11471         547 :     VALIDATE_POINTER1(bufferDataType, __func__, FALSE);
   11472         547 :     VALIDATE_POINTER1(pSrcBuffer, __func__, FALSE);
   11473             :     // coverity[var_deref_model]
   11474        1094 :     return hArray->m_poImpl->Write(arrayStartIdx, count, arrayStep,
   11475         547 :                                    bufferStride, *(bufferDataType->m_poImpl),
   11476             :                                    pSrcBuffer, pSrcBufferAllocStart,
   11477         547 :                                    nSrcBufferAllocSize);
   11478             : }
   11479             : 
   11480             : /************************************************************************/
   11481             : /*                       GDALMDArrayAdviseRead()                        */
   11482             : /************************************************************************/
   11483             : 
   11484             : /** Advise driver of upcoming read requests.
   11485             :  *
   11486             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11487             :  *
   11488             :  * @return TRUE in case of success.
   11489             :  *
   11490             :  * @since GDAL 3.2
   11491             :  */
   11492           0 : int GDALMDArrayAdviseRead(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11493             :                           const size_t *count)
   11494             : {
   11495           0 :     return GDALMDArrayAdviseReadEx(hArray, arrayStartIdx, count, nullptr);
   11496             : }
   11497             : 
   11498             : /************************************************************************/
   11499             : /*                      GDALMDArrayAdviseReadEx()                       */
   11500             : /************************************************************************/
   11501             : 
   11502             : /** Advise driver of upcoming read requests.
   11503             :  *
   11504             :  * This is the same as the C++ method GDALMDArray::AdviseRead()
   11505             :  *
   11506             :  * @return TRUE in case of success.
   11507             :  *
   11508             :  * @since GDAL 3.4
   11509             :  */
   11510          22 : int GDALMDArrayAdviseReadEx(GDALMDArrayH hArray, const GUInt64 *arrayStartIdx,
   11511             :                             const size_t *count, CSLConstList papszOptions)
   11512             : {
   11513          22 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11514             :     // coverity[var_deref_model]
   11515          22 :     return hArray->m_poImpl->AdviseRead(arrayStartIdx, count, papszOptions);
   11516             : }
   11517             : 
   11518             : /************************************************************************/
   11519             : /*                         GDALMDArrayGetAttribute()                    */
   11520             : /************************************************************************/
   11521             : 
   11522             : /** Return an attribute by its name.
   11523             :  *
   11524             :  * This is the same as the C++ method GDALIHasAttribute::GetAttribute()
   11525             :  *
   11526             :  * The returned attribute must be freed with GDALAttributeRelease().
   11527             :  */
   11528         118 : GDALAttributeH GDALMDArrayGetAttribute(GDALMDArrayH hArray, const char *pszName)
   11529             : {
   11530         118 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11531         118 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11532         354 :     auto attr = hArray->m_poImpl->GetAttribute(std::string(pszName));
   11533         118 :     if (attr)
   11534         109 :         return new GDALAttributeHS(attr);
   11535           9 :     return nullptr;
   11536             : }
   11537             : 
   11538             : /************************************************************************/
   11539             : /*                        GDALMDArrayGetAttributes()                    */
   11540             : /************************************************************************/
   11541             : 
   11542             : /** Return the list of attributes contained in this array.
   11543             :  *
   11544             :  * The returned array must be freed with GDALReleaseAttributes(). If only the
   11545             :  * array itself needs to be freed, CPLFree() should be called (and
   11546             :  * GDALAttributeRelease() on individual array members).
   11547             :  *
   11548             :  * This is the same as the C++ method GDALMDArray::GetAttributes().
   11549             :  *
   11550             :  * @param hArray Array.
   11551             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   11552             :  * @param papszOptions Driver specific options determining how attributes
   11553             :  * should be retrieved. Pass nullptr for default behavior.
   11554             :  *
   11555             :  * @return an array of *pnCount attributes.
   11556             :  */
   11557          57 : GDALAttributeH *GDALMDArrayGetAttributes(GDALMDArrayH hArray, size_t *pnCount,
   11558             :                                          CSLConstList papszOptions)
   11559             : {
   11560          57 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11561          57 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   11562          57 :     auto attrs = hArray->m_poImpl->GetAttributes(papszOptions);
   11563             :     auto ret = static_cast<GDALAttributeH *>(
   11564          57 :         CPLMalloc(sizeof(GDALAttributeH) * attrs.size()));
   11565         185 :     for (size_t i = 0; i < attrs.size(); i++)
   11566             :     {
   11567         128 :         ret[i] = new GDALAttributeHS(attrs[i]);
   11568             :     }
   11569          57 :     *pnCount = attrs.size();
   11570          57 :     return ret;
   11571             : }
   11572             : 
   11573             : /************************************************************************/
   11574             : /*                       GDALMDArrayCreateAttribute()                   */
   11575             : /************************************************************************/
   11576             : 
   11577             : /** Create a attribute within an array.
   11578             :  *
   11579             :  * This is the same as the C++ method GDALMDArray::CreateAttribute().
   11580             :  *
   11581             :  * @return the attribute, to be freed with GDALAttributeRelease(), or nullptr.
   11582             :  */
   11583         148 : GDALAttributeH GDALMDArrayCreateAttribute(GDALMDArrayH hArray,
   11584             :                                           const char *pszName,
   11585             :                                           size_t nDimensions,
   11586             :                                           const GUInt64 *panDimensions,
   11587             :                                           GDALExtendedDataTypeH hEDT,
   11588             :                                           CSLConstList papszOptions)
   11589             : {
   11590         148 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11591         148 :     VALIDATE_POINTER1(pszName, __func__, nullptr);
   11592         148 :     VALIDATE_POINTER1(hEDT, __func__, nullptr);
   11593         296 :     std::vector<GUInt64> dims;
   11594         148 :     dims.reserve(nDimensions);
   11595         173 :     for (size_t i = 0; i < nDimensions; i++)
   11596          25 :         dims.push_back(panDimensions[i]);
   11597         148 :     auto ret = hArray->m_poImpl->CreateAttribute(
   11598         444 :         std::string(pszName), dims, *(hEDT->m_poImpl), papszOptions);
   11599         148 :     if (!ret)
   11600           9 :         return nullptr;
   11601         139 :     return new GDALAttributeHS(ret);
   11602             : }
   11603             : 
   11604             : /************************************************************************/
   11605             : /*                       GDALMDArrayDeleteAttribute()                   */
   11606             : /************************************************************************/
   11607             : 
   11608             : /** Delete an attribute from an array.
   11609             :  *
   11610             :  * After this call, if a previously obtained instance of the deleted object
   11611             :  * is still alive, no method other than for freeing it should be invoked.
   11612             :  *
   11613             :  * This is the same as the C++ method GDALMDArray::DeleteAttribute().
   11614             :  *
   11615             :  * @return true in case of success.
   11616             :  * @since GDAL 3.8
   11617             :  */
   11618          24 : bool GDALMDArrayDeleteAttribute(GDALMDArrayH hArray, const char *pszName,
   11619             :                                 CSLConstList papszOptions)
   11620             : {
   11621          24 :     VALIDATE_POINTER1(hArray, __func__, false);
   11622          24 :     VALIDATE_POINTER1(pszName, __func__, false);
   11623          48 :     return hArray->m_poImpl->DeleteAttribute(std::string(pszName),
   11624          24 :                                              papszOptions);
   11625             : }
   11626             : 
   11627             : /************************************************************************/
   11628             : /*                       GDALMDArrayGetRawNoDataValue()                 */
   11629             : /************************************************************************/
   11630             : 
   11631             : /** Return the nodata value as a "raw" value.
   11632             :  *
   11633             :  * The value returned might be nullptr in case of no nodata value. When
   11634             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   11635             :  * bytes is GetDataType().GetSize().
   11636             :  *
   11637             :  * The returned value should not be modified or freed.
   11638             :  *
   11639             :  * This is the same as the ++ method GDALMDArray::GetRawNoDataValue().
   11640             :  *
   11641             :  * @return nullptr or a pointer to GetDataType().GetSize() bytes.
   11642             :  */
   11643          74 : const void *GDALMDArrayGetRawNoDataValue(GDALMDArrayH hArray)
   11644             : {
   11645          74 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   11646          74 :     return hArray->m_poImpl->GetRawNoDataValue();
   11647             : }
   11648             : 
   11649             : /************************************************************************/
   11650             : /*                      GDALMDArrayGetNoDataValueAsDouble()             */
   11651             : /************************************************************************/
   11652             : 
   11653             : /** Return the nodata value as a double.
   11654             :  *
   11655             :  * The value returned might be nullptr in case of no nodata value. When
   11656             :  * a nodata value is registered, a non-nullptr will be returned whose size in
   11657             :  * bytes is GetDataType().GetSize().
   11658             :  *
   11659             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsDouble().
   11660             :  *
   11661             :  * @param hArray Array handle.
   11662             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   11663             :  * if a nodata value exists and can be converted to double. Might be nullptr.
   11664             :  *
   11665             :  * @return the nodata value as a double. A 0.0 value might also indicate the
   11666             :  * absence of a nodata value or an error in the conversion (*pbHasNoDataValue
   11667             :  * will be set to false then).
   11668             :  */
   11669         117 : double GDALMDArrayGetNoDataValueAsDouble(GDALMDArrayH hArray,
   11670             :                                          int *pbHasNoDataValue)
   11671             : {
   11672         117 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11673         117 :     bool bHasNodataValue = false;
   11674         117 :     double ret = hArray->m_poImpl->GetNoDataValueAsDouble(&bHasNodataValue);
   11675         117 :     if (pbHasNoDataValue)
   11676         117 :         *pbHasNoDataValue = bHasNodataValue;
   11677         117 :     return ret;
   11678             : }
   11679             : 
   11680             : /************************************************************************/
   11681             : /*                      GDALMDArrayGetNoDataValueAsInt64()              */
   11682             : /************************************************************************/
   11683             : 
   11684             : /** Return the nodata value as a Int64.
   11685             :  *
   11686             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   11687             :  *
   11688             :  * @param hArray Array handle.
   11689             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   11690             :  * if a nodata value exists and can be converted to Int64. Might be nullptr.
   11691             :  *
   11692             :  * @return the nodata value as a Int64.
   11693             :  * @since GDAL 3.5
   11694             :  */
   11695          11 : int64_t GDALMDArrayGetNoDataValueAsInt64(GDALMDArrayH hArray,
   11696             :                                          int *pbHasNoDataValue)
   11697             : {
   11698          11 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11699          11 :     bool bHasNodataValue = false;
   11700          11 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsInt64(&bHasNodataValue);
   11701          11 :     if (pbHasNoDataValue)
   11702          11 :         *pbHasNoDataValue = bHasNodataValue;
   11703          11 :     return ret;
   11704             : }
   11705             : 
   11706             : /************************************************************************/
   11707             : /*                      GDALMDArrayGetNoDataValueAsUInt64()              */
   11708             : /************************************************************************/
   11709             : 
   11710             : /** Return the nodata value as a UInt64.
   11711             :  *
   11712             :  * This is the same as the C++ method GDALMDArray::GetNoDataValueAsInt64().
   11713             :  *
   11714             :  * @param hArray Array handle.
   11715             :  * @param pbHasNoDataValue Pointer to a output boolean that will be set to true
   11716             :  * if a nodata value exists and can be converted to UInt64. Might be nullptr.
   11717             :  *
   11718             :  * @return the nodata value as a UInt64.
   11719             :  * @since GDAL 3.5
   11720             :  */
   11721           7 : uint64_t GDALMDArrayGetNoDataValueAsUInt64(GDALMDArrayH hArray,
   11722             :                                            int *pbHasNoDataValue)
   11723             : {
   11724           7 :     VALIDATE_POINTER1(hArray, __func__, 0);
   11725           7 :     bool bHasNodataValue = false;
   11726           7 :     const auto ret = hArray->m_poImpl->GetNoDataValueAsUInt64(&bHasNodataValue);
   11727           7 :     if (pbHasNoDataValue)
   11728           7 :         *pbHasNoDataValue = bHasNodataValue;
   11729           7 :     return ret;
   11730             : }
   11731             : 
   11732             : /************************************************************************/
   11733             : /*                     GDALMDArraySetRawNoDataValue()                   */
   11734             : /************************************************************************/
   11735             : 
   11736             : /** Set the nodata value as a "raw" value.
   11737             :  *
   11738             :  * This is the same as the C++ method GDALMDArray::SetRawNoDataValue(const
   11739             :  * void*).
   11740             :  *
   11741             :  * @return TRUE in case of success.
   11742             :  */
   11743          14 : int GDALMDArraySetRawNoDataValue(GDALMDArrayH hArray, const void *pNoData)
   11744             : {
   11745          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11746          14 :     return hArray->m_poImpl->SetRawNoDataValue(pNoData);
   11747             : }
   11748             : 
   11749             : /************************************************************************/
   11750             : /*                   GDALMDArraySetNoDataValueAsDouble()                */
   11751             : /************************************************************************/
   11752             : 
   11753             : /** Set the nodata value as a double.
   11754             :  *
   11755             :  * If the natural data type of the attribute/array is not double, type
   11756             :  * conversion will occur to the type returned by GetDataType().
   11757             :  *
   11758             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(double).
   11759             :  *
   11760             :  * @return TRUE in case of success.
   11761             :  */
   11762          50 : int GDALMDArraySetNoDataValueAsDouble(GDALMDArrayH hArray, double dfNoDataValue)
   11763             : {
   11764          50 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11765          50 :     return hArray->m_poImpl->SetNoDataValue(dfNoDataValue);
   11766             : }
   11767             : 
   11768             : /************************************************************************/
   11769             : /*                   GDALMDArraySetNoDataValueAsInt64()                 */
   11770             : /************************************************************************/
   11771             : 
   11772             : /** Set the nodata value as a Int64.
   11773             :  *
   11774             :  * If the natural data type of the attribute/array is not Int64, type conversion
   11775             :  * will occur to the type returned by GetDataType().
   11776             :  *
   11777             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(int64_t).
   11778             :  *
   11779             :  * @return TRUE in case of success.
   11780             :  * @since GDAL 3.5
   11781             :  */
   11782           1 : int GDALMDArraySetNoDataValueAsInt64(GDALMDArrayH hArray, int64_t nNoDataValue)
   11783             : {
   11784           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11785           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   11786             : }
   11787             : 
   11788             : /************************************************************************/
   11789             : /*                   GDALMDArraySetNoDataValueAsUInt64()                */
   11790             : /************************************************************************/
   11791             : 
   11792             : /** Set the nodata value as a UInt64.
   11793             :  *
   11794             :  * If the natural data type of the attribute/array is not UInt64, type
   11795             :  * conversion will occur to the type returned by GetDataType().
   11796             :  *
   11797             :  * This is the same as the C++ method GDALMDArray::SetNoDataValue(uint64_t).
   11798             :  *
   11799             :  * @return TRUE in case of success.
   11800             :  * @since GDAL 3.5
   11801             :  */
   11802           1 : int GDALMDArraySetNoDataValueAsUInt64(GDALMDArrayH hArray,
   11803             :                                       uint64_t nNoDataValue)
   11804             : {
   11805           1 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11806           1 :     return hArray->m_poImpl->SetNoDataValue(nNoDataValue);
   11807             : }
   11808             : 
   11809             : /************************************************************************/
   11810             : /*                        GDALMDArrayResize()                           */
   11811             : /************************************************************************/
   11812             : 
   11813             : /** Resize an array to new dimensions.
   11814             :  *
   11815             :  * Not all drivers may allow this operation, and with restrictions (e.g.
   11816             :  * for netCDF, this is limited to growing of "unlimited" dimensions)
   11817             :  *
   11818             :  * Resizing a dimension used in other arrays will cause those other arrays
   11819             :  * to be resized.
   11820             :  *
   11821             :  * This is the same as the C++ method GDALMDArray::Resize().
   11822             :  *
   11823             :  * @param hArray Array.
   11824             :  * @param panNewDimSizes Array of GetDimensionCount() values containing the
   11825             :  *                       new size of each indexing dimension.
   11826             :  * @param papszOptions Options. (Driver specific)
   11827             :  * @return true in case of success.
   11828             :  * @since GDAL 3.7
   11829             :  */
   11830          42 : bool GDALMDArrayResize(GDALMDArrayH hArray, const GUInt64 *panNewDimSizes,
   11831             :                        CSLConstList papszOptions)
   11832             : {
   11833          42 :     VALIDATE_POINTER1(hArray, __func__, false);
   11834          42 :     VALIDATE_POINTER1(panNewDimSizes, __func__, false);
   11835          84 :     std::vector<GUInt64> anNewDimSizes(hArray->m_poImpl->GetDimensionCount());
   11836         125 :     for (size_t i = 0; i < anNewDimSizes.size(); ++i)
   11837             :     {
   11838          83 :         anNewDimSizes[i] = panNewDimSizes[i];
   11839             :     }
   11840          42 :     return hArray->m_poImpl->Resize(anNewDimSizes, papszOptions);
   11841             : }
   11842             : 
   11843             : /************************************************************************/
   11844             : /*                          GDALMDArraySetScale()                       */
   11845             : /************************************************************************/
   11846             : 
   11847             : /** Set the scale value to apply to raw values.
   11848             :  *
   11849             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   11850             :  *
   11851             :  * This is the same as the C++ method GDALMDArray::SetScale().
   11852             :  *
   11853             :  * @return TRUE in case of success.
   11854             :  */
   11855           0 : int GDALMDArraySetScale(GDALMDArrayH hArray, double dfScale)
   11856             : {
   11857           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11858           0 :     return hArray->m_poImpl->SetScale(dfScale);
   11859             : }
   11860             : 
   11861             : /************************************************************************/
   11862             : /*                        GDALMDArraySetScaleEx()                       */
   11863             : /************************************************************************/
   11864             : 
   11865             : /** Set the scale value to apply to raw values.
   11866             :  *
   11867             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   11868             :  *
   11869             :  * This is the same as the C++ method GDALMDArray::SetScale().
   11870             :  *
   11871             :  * @return TRUE in case of success.
   11872             :  * @since GDAL 3.3
   11873             :  */
   11874          20 : int GDALMDArraySetScaleEx(GDALMDArrayH hArray, double dfScale,
   11875             :                           GDALDataType eStorageType)
   11876             : {
   11877          20 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11878          20 :     return hArray->m_poImpl->SetScale(dfScale, eStorageType);
   11879             : }
   11880             : 
   11881             : /************************************************************************/
   11882             : /*                          GDALMDArraySetOffset()                       */
   11883             : /************************************************************************/
   11884             : 
   11885             : /** Set the scale value to apply to raw values.
   11886             :  *
   11887             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   11888             :  *
   11889             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   11890             :  *
   11891             :  * @return TRUE in case of success.
   11892             :  */
   11893           0 : int GDALMDArraySetOffset(GDALMDArrayH hArray, double dfOffset)
   11894             : {
   11895           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11896           0 :     return hArray->m_poImpl->SetOffset(dfOffset);
   11897             : }
   11898             : 
   11899             : /************************************************************************/
   11900             : /*                       GDALMDArraySetOffsetEx()                       */
   11901             : /************************************************************************/
   11902             : 
   11903             : /** Set the scale value to apply to raw values.
   11904             :  *
   11905             :  * unscaled_value = raw_value * GetOffset() + GetOffset()
   11906             :  *
   11907             :  * This is the same as the C++ method GDALMDArray::SetOffset().
   11908             :  *
   11909             :  * @return TRUE in case of success.
   11910             :  * @since GDAL 3.3
   11911             :  */
   11912          20 : int GDALMDArraySetOffsetEx(GDALMDArrayH hArray, double dfOffset,
   11913             :                            GDALDataType eStorageType)
   11914             : {
   11915          20 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   11916          20 :     return hArray->m_poImpl->SetOffset(dfOffset, eStorageType);
   11917             : }
   11918             : 
   11919             : /************************************************************************/
   11920             : /*                          GDALMDArrayGetScale()                       */
   11921             : /************************************************************************/
   11922             : 
   11923             : /** Get the scale value to apply to raw values.
   11924             :  *
   11925             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   11926             :  *
   11927             :  * This is the same as the C++ method GDALMDArray::GetScale().
   11928             :  *
   11929             :  * @return the scale value
   11930             :  */
   11931         102 : double GDALMDArrayGetScale(GDALMDArrayH hArray, int *pbHasValue)
   11932             : {
   11933         102 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   11934         102 :     bool bHasValue = false;
   11935         102 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue);
   11936         102 :     if (pbHasValue)
   11937         102 :         *pbHasValue = bHasValue;
   11938         102 :     return dfRet;
   11939             : }
   11940             : 
   11941             : /************************************************************************/
   11942             : /*                        GDALMDArrayGetScaleEx()                       */
   11943             : /************************************************************************/
   11944             : 
   11945             : /** Get the scale value to apply to raw values.
   11946             :  *
   11947             :  * unscaled_value = raw_value * GetScale() + GetScale()
   11948             :  *
   11949             :  * This is the same as the C++ method GDALMDArray::GetScale().
   11950             :  *
   11951             :  * @return the scale value
   11952             :  * @since GDAL 3.3
   11953             :  */
   11954           5 : double GDALMDArrayGetScaleEx(GDALMDArrayH hArray, int *pbHasValue,
   11955             :                              GDALDataType *peStorageType)
   11956             : {
   11957           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   11958           5 :     bool bHasValue = false;
   11959           5 :     double dfRet = hArray->m_poImpl->GetScale(&bHasValue, peStorageType);
   11960           5 :     if (pbHasValue)
   11961           5 :         *pbHasValue = bHasValue;
   11962           5 :     return dfRet;
   11963             : }
   11964             : 
   11965             : /************************************************************************/
   11966             : /*                          GDALMDArrayGetOffset()                      */
   11967             : /************************************************************************/
   11968             : 
   11969             : /** Get the scale value to apply to raw values.
   11970             :  *
   11971             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   11972             :  *
   11973             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   11974             :  *
   11975             :  * @return the scale value
   11976             :  */
   11977          99 : double GDALMDArrayGetOffset(GDALMDArrayH hArray, int *pbHasValue)
   11978             : {
   11979          99 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   11980          99 :     bool bHasValue = false;
   11981          99 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue);
   11982          99 :     if (pbHasValue)
   11983          99 :         *pbHasValue = bHasValue;
   11984          99 :     return dfRet;
   11985             : }
   11986             : 
   11987             : /************************************************************************/
   11988             : /*                        GDALMDArrayGetOffsetEx()                      */
   11989             : /************************************************************************/
   11990             : 
   11991             : /** Get the scale value to apply to raw values.
   11992             :  *
   11993             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   11994             :  *
   11995             :  * This is the same as the C++ method GDALMDArray::GetOffset().
   11996             :  *
   11997             :  * @return the scale value
   11998             :  * @since GDAL 3.3
   11999             :  */
   12000           5 : double GDALMDArrayGetOffsetEx(GDALMDArrayH hArray, int *pbHasValue,
   12001             :                               GDALDataType *peStorageType)
   12002             : {
   12003           5 :     VALIDATE_POINTER1(hArray, __func__, 0.0);
   12004           5 :     bool bHasValue = false;
   12005           5 :     double dfRet = hArray->m_poImpl->GetOffset(&bHasValue, peStorageType);
   12006           5 :     if (pbHasValue)
   12007           5 :         *pbHasValue = bHasValue;
   12008           5 :     return dfRet;
   12009             : }
   12010             : 
   12011             : /************************************************************************/
   12012             : /*                      GDALMDArrayGetBlockSize()                       */
   12013             : /************************************************************************/
   12014             : 
   12015             : /** Return the "natural" block size of the array along all dimensions.
   12016             :  *
   12017             :  * Some drivers might organize the array in tiles/blocks and reading/writing
   12018             :  * aligned on those tile/block boundaries will be more efficient.
   12019             :  *
   12020             :  * The returned number of elements in the vector is the same as
   12021             :  * GetDimensionCount(). A value of 0 should be interpreted as no hint regarding
   12022             :  * the natural block size along the considered dimension.
   12023             :  * "Flat" arrays will typically return a vector of values set to 0.
   12024             :  *
   12025             :  * The default implementation will return a vector of values set to 0.
   12026             :  *
   12027             :  * This method is used by GetProcessingChunkSize().
   12028             :  *
   12029             :  * Pedantic note: the returned type is GUInt64, so in the highly unlikeley
   12030             :  * theoretical case of a 32-bit platform, this might exceed its size_t
   12031             :  * allocation capabilities.
   12032             :  *
   12033             :  * This is the same as the C++ method GDALAbstractMDArray::GetBlockSize().
   12034             :  *
   12035             :  * @return the block size, in number of elements along each dimension.
   12036             :  */
   12037          93 : GUInt64 *GDALMDArrayGetBlockSize(GDALMDArrayH hArray, size_t *pnCount)
   12038             : {
   12039          93 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12040          93 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12041          93 :     auto res = hArray->m_poImpl->GetBlockSize();
   12042          93 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * res.size()));
   12043         285 :     for (size_t i = 0; i < res.size(); i++)
   12044             :     {
   12045         192 :         ret[i] = res[i];
   12046             :     }
   12047          93 :     *pnCount = res.size();
   12048          93 :     return ret;
   12049             : }
   12050             : 
   12051             : /***********************************************************************/
   12052             : /*                   GDALMDArrayGetProcessingChunkSize()               */
   12053             : /************************************************************************/
   12054             : 
   12055             : /** \brief Return an optimal chunk size for read/write operations, given the
   12056             :  * natural block size and memory constraints specified.
   12057             :  *
   12058             :  * This method will use GetBlockSize() to define a chunk whose dimensions are
   12059             :  * multiple of those returned by GetBlockSize() (unless the block define by
   12060             :  * GetBlockSize() is larger than nMaxChunkMemory, in which case it will be
   12061             :  * returned by this method).
   12062             :  *
   12063             :  * This is the same as the C++ method
   12064             :  * GDALAbstractMDArray::GetProcessingChunkSize().
   12065             :  *
   12066             :  * @param hArray Array.
   12067             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12068             :  * @param nMaxChunkMemory Maximum amount of memory, in bytes, to use for the
   12069             :  * chunk.
   12070             :  *
   12071             :  * @return the chunk size, in number of elements along each dimension.
   12072             :  */
   12073             : 
   12074           1 : size_t *GDALMDArrayGetProcessingChunkSize(GDALMDArrayH hArray, size_t *pnCount,
   12075             :                                           size_t nMaxChunkMemory)
   12076             : {
   12077           1 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12078           1 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12079           1 :     auto res = hArray->m_poImpl->GetProcessingChunkSize(nMaxChunkMemory);
   12080           1 :     auto ret = static_cast<size_t *>(CPLMalloc(sizeof(size_t) * res.size()));
   12081           3 :     for (size_t i = 0; i < res.size(); i++)
   12082             :     {
   12083           2 :         ret[i] = res[i];
   12084             :     }
   12085           1 :     *pnCount = res.size();
   12086           1 :     return ret;
   12087             : }
   12088             : 
   12089             : /************************************************************************/
   12090             : /*                     GDALMDArrayGetStructuralInfo()                   */
   12091             : /************************************************************************/
   12092             : 
   12093             : /** Return structural information on the array.
   12094             :  *
   12095             :  * This may be the compression, etc..
   12096             :  *
   12097             :  * The return value should not be freed and is valid until GDALMDArray is
   12098             :  * released or this function called again.
   12099             :  *
   12100             :  * This is the same as the C++ method GDALMDArray::GetStructuralInfo().
   12101             :  */
   12102           5 : CSLConstList GDALMDArrayGetStructuralInfo(GDALMDArrayH hArray)
   12103             : {
   12104           5 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12105           5 :     return hArray->m_poImpl->GetStructuralInfo();
   12106             : }
   12107             : 
   12108             : /************************************************************************/
   12109             : /*                        GDALMDArrayGetView()                          */
   12110             : /************************************************************************/
   12111             : 
   12112             : /** Return a view of the array using slicing or field access.
   12113             :  *
   12114             :  * The returned object should be released with GDALMDArrayRelease().
   12115             :  *
   12116             :  * This is the same as the C++ method GDALMDArray::GetView().
   12117             :  */
   12118         430 : GDALMDArrayH GDALMDArrayGetView(GDALMDArrayH hArray, const char *pszViewExpr)
   12119             : {
   12120         430 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12121         430 :     VALIDATE_POINTER1(pszViewExpr, __func__, nullptr);
   12122        1290 :     auto sliced = hArray->m_poImpl->GetView(std::string(pszViewExpr));
   12123         430 :     if (!sliced)
   12124          22 :         return nullptr;
   12125         408 :     return new GDALMDArrayHS(sliced);
   12126             : }
   12127             : 
   12128             : /************************************************************************/
   12129             : /*                       GDALMDArrayTranspose()                         */
   12130             : /************************************************************************/
   12131             : 
   12132             : /** Return a view of the array whose axis have been reordered.
   12133             :  *
   12134             :  * The returned object should be released with GDALMDArrayRelease().
   12135             :  *
   12136             :  * This is the same as the C++ method GDALMDArray::Transpose().
   12137             :  */
   12138          44 : GDALMDArrayH GDALMDArrayTranspose(GDALMDArrayH hArray, size_t nNewAxisCount,
   12139             :                                   const int *panMapNewAxisToOldAxis)
   12140             : {
   12141          44 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12142          88 :     std::vector<int> anMapNewAxisToOldAxis(nNewAxisCount);
   12143          44 :     if (nNewAxisCount)
   12144             :     {
   12145          43 :         memcpy(&anMapNewAxisToOldAxis[0], panMapNewAxisToOldAxis,
   12146             :                nNewAxisCount * sizeof(int));
   12147             :     }
   12148          88 :     auto reordered = hArray->m_poImpl->Transpose(anMapNewAxisToOldAxis);
   12149          44 :     if (!reordered)
   12150           7 :         return nullptr;
   12151          37 :     return new GDALMDArrayHS(reordered);
   12152             : }
   12153             : 
   12154             : /************************************************************************/
   12155             : /*                      GDALMDArrayGetUnscaled()                        */
   12156             : /************************************************************************/
   12157             : 
   12158             : /** Return an array that is the unscaled version of the current one.
   12159             :  *
   12160             :  * That is each value of the unscaled array will be
   12161             :  * unscaled_value = raw_value * GetScale() + GetOffset()
   12162             :  *
   12163             :  * Starting with GDAL 3.3, the Write() method is implemented and will convert
   12164             :  * from unscaled values to raw values.
   12165             :  *
   12166             :  * The returned object should be released with GDALMDArrayRelease().
   12167             :  *
   12168             :  * This is the same as the C++ method GDALMDArray::GetUnscaled().
   12169             :  */
   12170          13 : GDALMDArrayH GDALMDArrayGetUnscaled(GDALMDArrayH hArray)
   12171             : {
   12172          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12173          26 :     auto unscaled = hArray->m_poImpl->GetUnscaled();
   12174          13 :     if (!unscaled)
   12175           0 :         return nullptr;
   12176          13 :     return new GDALMDArrayHS(unscaled);
   12177             : }
   12178             : 
   12179             : /************************************************************************/
   12180             : /*                          GDALMDArrayGetMask()                         */
   12181             : /************************************************************************/
   12182             : 
   12183             : /** Return an array that is a mask for the current array
   12184             :  *
   12185             :  * This array will be of type Byte, with values set to 0 to indicate invalid
   12186             :  * pixels of the current array, and values set to 1 to indicate valid pixels.
   12187             :  *
   12188             :  * The returned object should be released with GDALMDArrayRelease().
   12189             :  *
   12190             :  * This is the same as the C++ method GDALMDArray::GetMask().
   12191             :  */
   12192          35 : GDALMDArrayH GDALMDArrayGetMask(GDALMDArrayH hArray, CSLConstList papszOptions)
   12193             : {
   12194          35 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12195          70 :     auto unscaled = hArray->m_poImpl->GetMask(papszOptions);
   12196          35 :     if (!unscaled)
   12197           7 :         return nullptr;
   12198          28 :     return new GDALMDArrayHS(unscaled);
   12199             : }
   12200             : 
   12201             : /************************************************************************/
   12202             : /*                   GDALMDArrayGetResampled()                          */
   12203             : /************************************************************************/
   12204             : 
   12205             : /** Return an array that is a resampled / reprojected view of the current array
   12206             :  *
   12207             :  * This is the same as the C++ method GDALMDArray::GetResampled().
   12208             :  *
   12209             :  * Currently this method can only resample along the last 2 dimensions, unless
   12210             :  * orthorectifying a NASA EMIT dataset.
   12211             :  *
   12212             :  * The returned object should be released with GDALMDArrayRelease().
   12213             :  *
   12214             :  * @since 3.4
   12215             :  */
   12216          30 : GDALMDArrayH GDALMDArrayGetResampled(GDALMDArrayH hArray, size_t nNewDimCount,
   12217             :                                      const GDALDimensionH *pahNewDims,
   12218             :                                      GDALRIOResampleAlg resampleAlg,
   12219             :                                      OGRSpatialReferenceH hTargetSRS,
   12220             :                                      CSLConstList papszOptions)
   12221             : {
   12222          30 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12223          30 :     VALIDATE_POINTER1(pahNewDims, __func__, nullptr);
   12224          60 :     std::vector<std::shared_ptr<GDALDimension>> apoNewDims(nNewDimCount);
   12225          96 :     for (size_t i = 0; i < nNewDimCount; ++i)
   12226             :     {
   12227          66 :         if (pahNewDims[i])
   12228           8 :             apoNewDims[i] = pahNewDims[i]->m_poImpl;
   12229             :     }
   12230          30 :     auto poNewArray = hArray->m_poImpl->GetResampled(
   12231          30 :         apoNewDims, resampleAlg, OGRSpatialReference::FromHandle(hTargetSRS),
   12232          60 :         papszOptions);
   12233          30 :     if (!poNewArray)
   12234           8 :         return nullptr;
   12235          22 :     return new GDALMDArrayHS(poNewArray);
   12236             : }
   12237             : 
   12238             : /************************************************************************/
   12239             : /*                      GDALMDArraySetUnit()                            */
   12240             : /************************************************************************/
   12241             : 
   12242             : /** Set the variable unit.
   12243             :  *
   12244             :  * Values should conform as much as possible with those allowed by
   12245             :  * the NetCDF CF conventions:
   12246             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12247             :  * but others might be returned.
   12248             :  *
   12249             :  * Few examples are "meter", "degrees", "second", ...
   12250             :  * Empty value means unknown.
   12251             :  *
   12252             :  * This is the same as the C function GDALMDArraySetUnit()
   12253             :  *
   12254             :  * @param hArray array.
   12255             :  * @param pszUnit unit name.
   12256             :  * @return TRUE in case of success.
   12257             :  */
   12258          14 : int GDALMDArraySetUnit(GDALMDArrayH hArray, const char *pszUnit)
   12259             : {
   12260          14 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12261          14 :     return hArray->m_poImpl->SetUnit(pszUnit ? pszUnit : "");
   12262             : }
   12263             : 
   12264             : /************************************************************************/
   12265             : /*                      GDALMDArrayGetUnit()                            */
   12266             : /************************************************************************/
   12267             : 
   12268             : /** Return the array unit.
   12269             :  *
   12270             :  * Values should conform as much as possible with those allowed by
   12271             :  * the NetCDF CF conventions:
   12272             :  * http://cfconventions.org/Data/cf-conventions/cf-conventions-1.7/cf-conventions.html#units
   12273             :  * but others might be returned.
   12274             :  *
   12275             :  * Few examples are "meter", "degrees", "second", ...
   12276             :  * Empty value means unknown.
   12277             :  *
   12278             :  * The return value should not be freed and is valid until GDALMDArray is
   12279             :  * released or this function called again.
   12280             :  *
   12281             :  * This is the same as the C++ method GDALMDArray::GetUnit().
   12282             :  */
   12283         110 : const char *GDALMDArrayGetUnit(GDALMDArrayH hArray)
   12284             : {
   12285         110 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12286         110 :     return hArray->m_poImpl->GetUnit().c_str();
   12287             : }
   12288             : 
   12289             : /************************************************************************/
   12290             : /*                      GDALMDArrayGetSpatialRef()                      */
   12291             : /************************************************************************/
   12292             : 
   12293             : /** Assign a spatial reference system object to the array.
   12294             :  *
   12295             :  * This is the same as the C++ method GDALMDArray::SetSpatialRef().
   12296             :  * @return TRUE in case of success.
   12297             :  */
   12298          30 : int GDALMDArraySetSpatialRef(GDALMDArrayH hArray, OGRSpatialReferenceH hSRS)
   12299             : {
   12300          30 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12301          60 :     return hArray->m_poImpl->SetSpatialRef(
   12302          60 :         OGRSpatialReference::FromHandle(hSRS));
   12303             : }
   12304             : 
   12305             : /************************************************************************/
   12306             : /*                      GDALMDArrayGetSpatialRef()                      */
   12307             : /************************************************************************/
   12308             : 
   12309             : /** Return the spatial reference system object associated with the array.
   12310             :  *
   12311             :  * This is the same as the C++ method GDALMDArray::GetSpatialRef().
   12312             :  *
   12313             :  * The returned object must be freed with OSRDestroySpatialReference().
   12314             :  */
   12315          77 : OGRSpatialReferenceH GDALMDArrayGetSpatialRef(GDALMDArrayH hArray)
   12316             : {
   12317          77 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12318          77 :     auto poSRS = hArray->m_poImpl->GetSpatialRef();
   12319          77 :     return poSRS ? OGRSpatialReference::ToHandle(poSRS->Clone()) : nullptr;
   12320             : }
   12321             : 
   12322             : /************************************************************************/
   12323             : /*                      GDALMDArrayGetStatistics()                      */
   12324             : /************************************************************************/
   12325             : 
   12326             : /**
   12327             :  * \brief Fetch statistics.
   12328             :  *
   12329             :  * This is the same as the C++ method GDALMDArray::GetStatistics().
   12330             :  *
   12331             :  * @since GDAL 3.2
   12332             :  */
   12333             : 
   12334          15 : CPLErr GDALMDArrayGetStatistics(GDALMDArrayH hArray, GDALDatasetH /*hDS*/,
   12335             :                                 int bApproxOK, int bForce, double *pdfMin,
   12336             :                                 double *pdfMax, double *pdfMean,
   12337             :                                 double *pdfStdDev, GUInt64 *pnValidCount,
   12338             :                                 GDALProgressFunc pfnProgress,
   12339             :                                 void *pProgressData)
   12340             : {
   12341          15 :     VALIDATE_POINTER1(hArray, __func__, CE_Failure);
   12342          30 :     return hArray->m_poImpl->GetStatistics(
   12343          15 :         CPL_TO_BOOL(bApproxOK), CPL_TO_BOOL(bForce), pdfMin, pdfMax, pdfMean,
   12344          15 :         pdfStdDev, pnValidCount, pfnProgress, pProgressData);
   12345             : }
   12346             : 
   12347             : /************************************************************************/
   12348             : /*                      GDALMDArrayComputeStatistics()                  */
   12349             : /************************************************************************/
   12350             : 
   12351             : /**
   12352             :  * \brief Compute statistics.
   12353             :  *
   12354             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12355             :  *
   12356             :  * @since GDAL 3.2
   12357             :  * @see GDALMDArrayComputeStatisticsEx()
   12358             :  */
   12359             : 
   12360           0 : int GDALMDArrayComputeStatistics(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12361             :                                  int bApproxOK, double *pdfMin, double *pdfMax,
   12362             :                                  double *pdfMean, double *pdfStdDev,
   12363             :                                  GUInt64 *pnValidCount,
   12364             :                                  GDALProgressFunc pfnProgress,
   12365             :                                  void *pProgressData)
   12366             : {
   12367           0 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12368           0 :     return hArray->m_poImpl->ComputeStatistics(
   12369           0 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12370           0 :         pnValidCount, pfnProgress, pProgressData, nullptr);
   12371             : }
   12372             : 
   12373             : /************************************************************************/
   12374             : /*                     GDALMDArrayComputeStatisticsEx()                 */
   12375             : /************************************************************************/
   12376             : 
   12377             : /**
   12378             :  * \brief Compute statistics.
   12379             :  *
   12380             :  * Same as GDALMDArrayComputeStatistics() with extra papszOptions argument.
   12381             :  *
   12382             :  * This is the same as the C++ method GDALMDArray::ComputeStatistics().
   12383             :  *
   12384             :  * @since GDAL 3.8
   12385             :  */
   12386             : 
   12387           4 : int GDALMDArrayComputeStatisticsEx(GDALMDArrayH hArray, GDALDatasetH /* hDS */,
   12388             :                                    int bApproxOK, double *pdfMin,
   12389             :                                    double *pdfMax, double *pdfMean,
   12390             :                                    double *pdfStdDev, GUInt64 *pnValidCount,
   12391             :                                    GDALProgressFunc pfnProgress,
   12392             :                                    void *pProgressData,
   12393             :                                    CSLConstList papszOptions)
   12394             : {
   12395           4 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12396           8 :     return hArray->m_poImpl->ComputeStatistics(
   12397           4 :         CPL_TO_BOOL(bApproxOK), pdfMin, pdfMax, pdfMean, pdfStdDev,
   12398           8 :         pnValidCount, pfnProgress, pProgressData, papszOptions);
   12399             : }
   12400             : 
   12401             : /************************************************************************/
   12402             : /*                 GDALMDArrayGetCoordinateVariables()                  */
   12403             : /************************************************************************/
   12404             : 
   12405             : /** Return coordinate variables.
   12406             :  *
   12407             :  * The returned array must be freed with GDALReleaseArrays(). If only the array
   12408             :  * itself needs to be freed, CPLFree() should be called (and
   12409             :  * GDALMDArrayRelease() on individual array members).
   12410             :  *
   12411             :  * This is the same as the C++ method GDALMDArray::GetCoordinateVariables()
   12412             :  *
   12413             :  * @param hArray Array.
   12414             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12415             :  *
   12416             :  * @return an array of *pnCount arrays.
   12417             :  * @since 3.4
   12418             :  */
   12419          13 : GDALMDArrayH *GDALMDArrayGetCoordinateVariables(GDALMDArrayH hArray,
   12420             :                                                 size_t *pnCount)
   12421             : {
   12422          13 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12423          13 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12424          13 :     const auto coordinates(hArray->m_poImpl->GetCoordinateVariables());
   12425             :     auto ret = static_cast<GDALMDArrayH *>(
   12426          13 :         CPLMalloc(sizeof(GDALMDArrayH) * coordinates.size()));
   12427          29 :     for (size_t i = 0; i < coordinates.size(); i++)
   12428             :     {
   12429          16 :         ret[i] = new GDALMDArrayHS(coordinates[i]);
   12430             :     }
   12431          13 :     *pnCount = coordinates.size();
   12432          13 :     return ret;
   12433             : }
   12434             : 
   12435             : /************************************************************************/
   12436             : /*                     GDALMDArrayGetGridded()                          */
   12437             : /************************************************************************/
   12438             : 
   12439             : /** Return a gridded array from scattered point data, that is from an array
   12440             :  * whose last dimension is the indexing variable of X and Y arrays.
   12441             :  *
   12442             :  * The returned object should be released with GDALMDArrayRelease().
   12443             :  *
   12444             :  * This is the same as the C++ method GDALMDArray::GetGridded().
   12445             :  *
   12446             :  * @since GDAL 3.7
   12447             :  */
   12448          22 : GDALMDArrayH GDALMDArrayGetGridded(GDALMDArrayH hArray,
   12449             :                                    const char *pszGridOptions,
   12450             :                                    GDALMDArrayH hXArray, GDALMDArrayH hYArray,
   12451             :                                    CSLConstList papszOptions)
   12452             : {
   12453          22 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   12454          22 :     VALIDATE_POINTER1(pszGridOptions, __func__, nullptr);
   12455          22 :     auto gridded = hArray->m_poImpl->GetGridded(
   12456          44 :         pszGridOptions, hXArray ? hXArray->m_poImpl : nullptr,
   12457          88 :         hYArray ? hYArray->m_poImpl : nullptr, papszOptions);
   12458          22 :     if (!gridded)
   12459          19 :         return nullptr;
   12460           3 :     return new GDALMDArrayHS(gridded);
   12461             : }
   12462             : 
   12463             : /************************************************************************/
   12464             : /*                        GDALReleaseArrays()                           */
   12465             : /************************************************************************/
   12466             : 
   12467             : /** Free the return of GDALMDArrayGetCoordinateVariables()
   12468             :  *
   12469             :  * @param arrays return pointer of above methods
   12470             :  * @param nCount *pnCount value returned by above methods
   12471             :  */
   12472          13 : void GDALReleaseArrays(GDALMDArrayH *arrays, size_t nCount)
   12473             : {
   12474          29 :     for (size_t i = 0; i < nCount; i++)
   12475             :     {
   12476          16 :         delete arrays[i];
   12477             :     }
   12478          13 :     CPLFree(arrays);
   12479          13 : }
   12480             : 
   12481             : /************************************************************************/
   12482             : /*                           GDALMDArrayCache()                         */
   12483             : /************************************************************************/
   12484             : 
   12485             : /**
   12486             :  * \brief Cache the content of the array into an auxiliary filename.
   12487             :  *
   12488             :  * This is the same as the C++ method GDALMDArray::Cache().
   12489             :  *
   12490             :  * @since GDAL 3.4
   12491             :  */
   12492             : 
   12493           6 : int GDALMDArrayCache(GDALMDArrayH hArray, CSLConstList papszOptions)
   12494             : {
   12495           6 :     VALIDATE_POINTER1(hArray, __func__, FALSE);
   12496           6 :     return hArray->m_poImpl->Cache(papszOptions);
   12497             : }
   12498             : 
   12499             : /************************************************************************/
   12500             : /*                       GDALMDArrayRename()                           */
   12501             : /************************************************************************/
   12502             : 
   12503             : /** Rename the array.
   12504             :  *
   12505             :  * This is not implemented by all drivers.
   12506             :  *
   12507             :  * Drivers known to implement it: MEM, netCDF, Zarr.
   12508             :  *
   12509             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   12510             :  *
   12511             :  * @return true in case of success
   12512             :  * @since GDAL 3.8
   12513             :  */
   12514          28 : bool GDALMDArrayRename(GDALMDArrayH hArray, const char *pszNewName)
   12515             : {
   12516          28 :     VALIDATE_POINTER1(hArray, __func__, false);
   12517          28 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12518          28 :     return hArray->m_poImpl->Rename(pszNewName);
   12519             : }
   12520             : 
   12521             : /************************************************************************/
   12522             : /*                        GDALAttributeRelease()                        */
   12523             : /************************************************************************/
   12524             : 
   12525             : /** Release the GDAL in-memory object associated with a GDALAttribute.
   12526             :  *
   12527             :  * Note: when applied on a object coming from a driver, this does not
   12528             :  * destroy the object in the file, database, etc...
   12529             :  */
   12530         680 : void GDALAttributeRelease(GDALAttributeH hAttr)
   12531             : {
   12532         680 :     delete hAttr;
   12533         680 : }
   12534             : 
   12535             : /************************************************************************/
   12536             : /*                        GDALAttributeGetName()                        */
   12537             : /************************************************************************/
   12538             : 
   12539             : /** Return the name of the attribute.
   12540             :  *
   12541             :  * The returned pointer is valid until hAttr is released.
   12542             :  *
   12543             :  * This is the same as the C++ method GDALAttribute::GetName().
   12544             :  */
   12545         361 : const char *GDALAttributeGetName(GDALAttributeH hAttr)
   12546             : {
   12547         361 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12548         361 :     return hAttr->m_poImpl->GetName().c_str();
   12549             : }
   12550             : 
   12551             : /************************************************************************/
   12552             : /*                      GDALAttributeGetFullName()                      */
   12553             : /************************************************************************/
   12554             : 
   12555             : /** Return the full name of the attribute.
   12556             :  *
   12557             :  * The returned pointer is valid until hAttr is released.
   12558             :  *
   12559             :  * This is the same as the C++ method GDALAttribute::GetFullName().
   12560             :  */
   12561          49 : const char *GDALAttributeGetFullName(GDALAttributeH hAttr)
   12562             : {
   12563          49 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12564          49 :     return hAttr->m_poImpl->GetFullName().c_str();
   12565             : }
   12566             : 
   12567             : /************************************************************************/
   12568             : /*                   GDALAttributeGetTotalElementsCount()               */
   12569             : /************************************************************************/
   12570             : 
   12571             : /** Return the total number of values in the attribute.
   12572             :  *
   12573             :  * This is the same as the C++ method
   12574             :  * GDALAbstractMDArray::GetTotalElementsCount()
   12575             :  */
   12576         176 : GUInt64 GDALAttributeGetTotalElementsCount(GDALAttributeH hAttr)
   12577             : {
   12578         176 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12579         176 :     return hAttr->m_poImpl->GetTotalElementsCount();
   12580             : }
   12581             : 
   12582             : /************************************************************************/
   12583             : /*                    GDALAttributeGetDimensionCount()                */
   12584             : /************************************************************************/
   12585             : 
   12586             : /** Return the number of dimensions.
   12587             :  *
   12588             :  * This is the same as the C++ method GDALAbstractMDArray::GetDimensionCount()
   12589             :  */
   12590          12 : size_t GDALAttributeGetDimensionCount(GDALAttributeH hAttr)
   12591             : {
   12592          12 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12593          12 :     return hAttr->m_poImpl->GetDimensionCount();
   12594             : }
   12595             : 
   12596             : /************************************************************************/
   12597             : /*                       GDALAttributeGetDimensionsSize()                */
   12598             : /************************************************************************/
   12599             : 
   12600             : /** Return the dimension sizes of the attribute.
   12601             :  *
   12602             :  * The returned array must be freed with CPLFree()
   12603             :  *
   12604             :  * @param hAttr Attribute.
   12605             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12606             :  *
   12607             :  * @return an array of *pnCount values.
   12608             :  */
   12609          11 : GUInt64 *GDALAttributeGetDimensionsSize(GDALAttributeH hAttr, size_t *pnCount)
   12610             : {
   12611          11 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12612          11 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12613          11 :     const auto &dims = hAttr->m_poImpl->GetDimensions();
   12614          11 :     auto ret = static_cast<GUInt64 *>(CPLMalloc(sizeof(GUInt64) * dims.size()));
   12615          22 :     for (size_t i = 0; i < dims.size(); i++)
   12616             :     {
   12617          11 :         ret[i] = dims[i]->GetSize();
   12618             :     }
   12619          11 :     *pnCount = dims.size();
   12620          11 :     return ret;
   12621             : }
   12622             : 
   12623             : /************************************************************************/
   12624             : /*                       GDALAttributeGetDataType()                     */
   12625             : /************************************************************************/
   12626             : 
   12627             : /** Return the data type
   12628             :  *
   12629             :  * The return must be freed with GDALExtendedDataTypeRelease().
   12630             :  */
   12631         411 : GDALExtendedDataTypeH GDALAttributeGetDataType(GDALAttributeH hAttr)
   12632             : {
   12633         411 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12634             :     return new GDALExtendedDataTypeHS(
   12635         411 :         new GDALExtendedDataType(hAttr->m_poImpl->GetDataType()));
   12636             : }
   12637             : 
   12638             : /************************************************************************/
   12639             : /*                       GDALAttributeReadAsRaw()                       */
   12640             : /************************************************************************/
   12641             : 
   12642             : /** Return the raw value of an attribute.
   12643             :  *
   12644             :  * This is the same as the C++ method GDALAttribute::ReadAsRaw().
   12645             :  *
   12646             :  * The returned buffer must be freed with GDALAttributeFreeRawResult()
   12647             :  *
   12648             :  * @param hAttr Attribute.
   12649             :  * @param pnSize Pointer to the number of bytes returned. Must NOT be NULL.
   12650             :  *
   12651             :  * @return a buffer of *pnSize bytes.
   12652             :  */
   12653           6 : GByte *GDALAttributeReadAsRaw(GDALAttributeH hAttr, size_t *pnSize)
   12654             : {
   12655           6 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12656           6 :     VALIDATE_POINTER1(pnSize, __func__, nullptr);
   12657          12 :     auto res(hAttr->m_poImpl->ReadAsRaw());
   12658           6 :     *pnSize = res.size();
   12659           6 :     auto ret = res.StealData();
   12660           6 :     if (!ret)
   12661             :     {
   12662           0 :         *pnSize = 0;
   12663           0 :         return nullptr;
   12664             :     }
   12665           6 :     return ret;
   12666             : }
   12667             : 
   12668             : /************************************************************************/
   12669             : /*                       GDALAttributeFreeRawResult()                   */
   12670             : /************************************************************************/
   12671             : 
   12672             : /** Free the return of GDALAttributeAsRaw()
   12673             :  */
   12674           6 : void GDALAttributeFreeRawResult(GDALAttributeH hAttr, GByte *raw,
   12675             :                                 CPL_UNUSED size_t nSize)
   12676             : {
   12677           6 :     VALIDATE_POINTER0(hAttr, __func__);
   12678           6 :     if (raw)
   12679             :     {
   12680           6 :         const auto &dt(hAttr->m_poImpl->GetDataType());
   12681           6 :         const auto nDTSize(dt.GetSize());
   12682           6 :         GByte *pabyPtr = raw;
   12683           6 :         const auto nEltCount(hAttr->m_poImpl->GetTotalElementsCount());
   12684           6 :         CPLAssert(nSize == nDTSize * nEltCount);
   12685          12 :         for (size_t i = 0; i < nEltCount; ++i)
   12686             :         {
   12687           6 :             dt.FreeDynamicMemory(pabyPtr);
   12688           6 :             pabyPtr += nDTSize;
   12689             :         }
   12690           6 :         CPLFree(raw);
   12691             :     }
   12692             : }
   12693             : 
   12694             : /************************************************************************/
   12695             : /*                       GDALAttributeReadAsString()                    */
   12696             : /************************************************************************/
   12697             : 
   12698             : /** Return the value of an attribute as a string.
   12699             :  *
   12700             :  * The returned string should not be freed, and its lifetime does not
   12701             :  * excess a next call to ReadAsString() on the same object, or the deletion
   12702             :  * of the object itself.
   12703             :  *
   12704             :  * This function will only return the first element if there are several.
   12705             :  *
   12706             :  * This is the same as the C++ method GDALAttribute::ReadAsString()
   12707             :  *
   12708             :  * @return a string, or nullptr.
   12709             :  */
   12710         107 : const char *GDALAttributeReadAsString(GDALAttributeH hAttr)
   12711             : {
   12712         107 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12713         107 :     return hAttr->m_poImpl->ReadAsString();
   12714             : }
   12715             : 
   12716             : /************************************************************************/
   12717             : /*                      GDALAttributeReadAsInt()                        */
   12718             : /************************************************************************/
   12719             : 
   12720             : /** Return the value of an attribute as a integer.
   12721             :  *
   12722             :  * This function will only return the first element if there are several.
   12723             :  *
   12724             :  * It can fail if its value can be converted to integer.
   12725             :  *
   12726             :  * This is the same as the C++ method GDALAttribute::ReadAsInt()
   12727             :  *
   12728             :  * @return a integer, or INT_MIN in case of error.
   12729             :  */
   12730          19 : int GDALAttributeReadAsInt(GDALAttributeH hAttr)
   12731             : {
   12732          19 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12733          19 :     return hAttr->m_poImpl->ReadAsInt();
   12734             : }
   12735             : 
   12736             : /************************************************************************/
   12737             : /*                       GDALAttributeReadAsDouble()                    */
   12738             : /************************************************************************/
   12739             : 
   12740             : /** Return the value of an attribute as a double.
   12741             :  *
   12742             :  * This function will only return the first element if there are several.
   12743             :  *
   12744             :  * It can fail if its value can be converted to double.
   12745             :  *
   12746             :  * This is the same as the C++ method GDALAttribute::ReadAsDouble()
   12747             :  *
   12748             :  * @return a double value.
   12749             :  */
   12750          37 : double GDALAttributeReadAsDouble(GDALAttributeH hAttr)
   12751             : {
   12752          37 :     VALIDATE_POINTER1(hAttr, __func__, 0);
   12753          37 :     return hAttr->m_poImpl->ReadAsDouble();
   12754             : }
   12755             : 
   12756             : /************************************************************************/
   12757             : /*                     GDALAttributeReadAsStringArray()                 */
   12758             : /************************************************************************/
   12759             : 
   12760             : /** Return the value of an attribute as an array of strings.
   12761             :  *
   12762             :  * This is the same as the C++ method GDALAttribute::ReadAsStringArray()
   12763             :  *
   12764             :  * The return value must be freed with CSLDestroy().
   12765             :  */
   12766          19 : char **GDALAttributeReadAsStringArray(GDALAttributeH hAttr)
   12767             : {
   12768          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12769          19 :     return hAttr->m_poImpl->ReadAsStringArray().StealList();
   12770             : }
   12771             : 
   12772             : /************************************************************************/
   12773             : /*                     GDALAttributeReadAsIntArray()                    */
   12774             : /************************************************************************/
   12775             : 
   12776             : /** Return the value of an attribute as an array of integers.
   12777             :  *
   12778             :  * This is the same as the C++ method GDALAttribute::ReadAsIntArray()
   12779             :  *
   12780             :  * @param hAttr Attribute
   12781             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12782             :  * @return array to be freed with CPLFree(), or nullptr.
   12783             :  */
   12784          15 : int *GDALAttributeReadAsIntArray(GDALAttributeH hAttr, size_t *pnCount)
   12785             : {
   12786          15 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12787          15 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12788          15 :     *pnCount = 0;
   12789          30 :     auto tmp(hAttr->m_poImpl->ReadAsIntArray());
   12790          15 :     if (tmp.empty())
   12791           0 :         return nullptr;
   12792          15 :     auto ret = static_cast<int *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(int)));
   12793          15 :     if (!ret)
   12794           0 :         return nullptr;
   12795          15 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(int));
   12796          15 :     *pnCount = tmp.size();
   12797          15 :     return ret;
   12798             : }
   12799             : 
   12800             : /************************************************************************/
   12801             : /*                     GDALAttributeReadAsDoubleArray()                 */
   12802             : /************************************************************************/
   12803             : 
   12804             : /** Return the value of an attribute as an array of doubles.
   12805             :  *
   12806             :  * This is the same as the C++ method GDALAttribute::ReadAsDoubleArray()
   12807             :  *
   12808             :  * @param hAttr Attribute
   12809             :  * @param pnCount Pointer to the number of values returned. Must NOT be NULL.
   12810             :  * @return array to be freed with CPLFree(), or nullptr.
   12811             :  */
   12812          19 : double *GDALAttributeReadAsDoubleArray(GDALAttributeH hAttr, size_t *pnCount)
   12813             : {
   12814          19 :     VALIDATE_POINTER1(hAttr, __func__, nullptr);
   12815          19 :     VALIDATE_POINTER1(pnCount, __func__, nullptr);
   12816          19 :     *pnCount = 0;
   12817          38 :     auto tmp(hAttr->m_poImpl->ReadAsDoubleArray());
   12818          19 :     if (tmp.empty())
   12819           0 :         return nullptr;
   12820             :     auto ret =
   12821          19 :         static_cast<double *>(VSI_MALLOC2_VERBOSE(tmp.size(), sizeof(double)));
   12822          19 :     if (!ret)
   12823           0 :         return nullptr;
   12824          19 :     memcpy(ret, tmp.data(), tmp.size() * sizeof(double));
   12825          19 :     *pnCount = tmp.size();
   12826          19 :     return ret;
   12827             : }
   12828             : 
   12829             : /************************************************************************/
   12830             : /*                     GDALAttributeWriteRaw()                          */
   12831             : /************************************************************************/
   12832             : 
   12833             : /** Write an attribute from raw values expressed in GetDataType()
   12834             :  *
   12835             :  * The values should be provided in the type of GetDataType() and there should
   12836             :  * be exactly GetTotalElementsCount() of them.
   12837             :  * If GetDataType() is a string, each value should be a char* pointer.
   12838             :  *
   12839             :  * This is the same as the C++ method GDALAttribute::Write(const void*, size_t).
   12840             :  *
   12841             :  * @param hAttr Attribute
   12842             :  * @param pabyValue Buffer of nLen bytes.
   12843             :  * @param nLength Size of pabyValue in bytes. Should be equal to
   12844             :  *             GetTotalElementsCount() * GetDataType().GetSize()
   12845             :  * @return TRUE in case of success.
   12846             :  */
   12847           5 : int GDALAttributeWriteRaw(GDALAttributeH hAttr, const void *pabyValue,
   12848             :                           size_t nLength)
   12849             : {
   12850           5 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   12851           5 :     return hAttr->m_poImpl->Write(pabyValue, nLength);
   12852             : }
   12853             : 
   12854             : /************************************************************************/
   12855             : /*                     GDALAttributeWriteString()                       */
   12856             : /************************************************************************/
   12857             : 
   12858             : /** Write an attribute from a string value.
   12859             :  *
   12860             :  * Type conversion will be performed if needed. If the attribute contains
   12861             :  * multiple values, only the first one will be updated.
   12862             :  *
   12863             :  * This is the same as the C++ method GDALAttribute::Write(const char*)
   12864             :  *
   12865             :  * @param hAttr Attribute
   12866             :  * @param pszVal Pointer to a string.
   12867             :  * @return TRUE in case of success.
   12868             :  */
   12869         174 : int GDALAttributeWriteString(GDALAttributeH hAttr, const char *pszVal)
   12870             : {
   12871         174 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   12872         174 :     return hAttr->m_poImpl->Write(pszVal);
   12873             : }
   12874             : 
   12875             : /************************************************************************/
   12876             : /*                        GDALAttributeWriteInt()                       */
   12877             : /************************************************************************/
   12878             : 
   12879             : /** Write an attribute from a integer value.
   12880             :  *
   12881             :  * Type conversion will be performed if needed. If the attribute contains
   12882             :  * multiple values, only the first one will be updated.
   12883             :  *
   12884             :  * This is the same as the C++ method GDALAttribute::WriteInt()
   12885             :  *
   12886             :  * @param hAttr Attribute
   12887             :  * @param nVal Value.
   12888             :  * @return TRUE in case of success.
   12889             :  */
   12890          22 : int GDALAttributeWriteInt(GDALAttributeH hAttr, int nVal)
   12891             : {
   12892          22 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   12893          22 :     return hAttr->m_poImpl->WriteInt(nVal);
   12894             : }
   12895             : 
   12896             : /************************************************************************/
   12897             : /*                        GDALAttributeWriteDouble()                    */
   12898             : /************************************************************************/
   12899             : 
   12900             : /** Write an attribute from a double value.
   12901             :  *
   12902             :  * Type conversion will be performed if needed. If the attribute contains
   12903             :  * multiple values, only the first one will be updated.
   12904             :  *
   12905             :  * This is the same as the C++ method GDALAttribute::Write(double);
   12906             :  *
   12907             :  * @param hAttr Attribute
   12908             :  * @param dfVal Value.
   12909             :  *
   12910             :  * @return TRUE in case of success.
   12911             :  */
   12912          16 : int GDALAttributeWriteDouble(GDALAttributeH hAttr, double dfVal)
   12913             : {
   12914          16 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   12915          16 :     return hAttr->m_poImpl->Write(dfVal);
   12916             : }
   12917             : 
   12918             : /************************************************************************/
   12919             : /*                       GDALAttributeWriteStringArray()                */
   12920             : /************************************************************************/
   12921             : 
   12922             : /** Write an attribute from an array of strings.
   12923             :  *
   12924             :  * Type conversion will be performed if needed.
   12925             :  *
   12926             :  * Exactly GetTotalElementsCount() strings must be provided
   12927             :  *
   12928             :  * This is the same as the C++ method GDALAttribute::Write(CSLConstList)
   12929             :  *
   12930             :  * @param hAttr Attribute
   12931             :  * @param papszValues Array of strings.
   12932             :  * @return TRUE in case of success.
   12933             :  */
   12934           8 : int GDALAttributeWriteStringArray(GDALAttributeH hAttr,
   12935             :                                   CSLConstList papszValues)
   12936             : {
   12937           8 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   12938           8 :     return hAttr->m_poImpl->Write(papszValues);
   12939             : }
   12940             : 
   12941             : /************************************************************************/
   12942             : /*                       GDALAttributeWriteDoubleArray()                */
   12943             : /************************************************************************/
   12944             : 
   12945             : /** Write an attribute from an array of double.
   12946             :  *
   12947             :  * Type conversion will be performed if needed.
   12948             :  *
   12949             :  * Exactly GetTotalElementsCount() strings must be provided
   12950             :  *
   12951             :  * This is the same as the C++ method GDALAttribute::Write(const double *,
   12952             :  * size_t)
   12953             :  *
   12954             :  * @param hAttr Attribute
   12955             :  * @param padfValues Array of double.
   12956             :  * @param nCount Should be equal to GetTotalElementsCount().
   12957             :  * @return TRUE in case of success.
   12958             :  */
   12959          17 : int GDALAttributeWriteDoubleArray(GDALAttributeH hAttr,
   12960             :                                   const double *padfValues, size_t nCount)
   12961             : {
   12962          17 :     VALIDATE_POINTER1(hAttr, __func__, FALSE);
   12963          17 :     return hAttr->m_poImpl->Write(padfValues, nCount);
   12964             : }
   12965             : 
   12966             : /************************************************************************/
   12967             : /*                      GDALAttributeRename()                           */
   12968             : /************************************************************************/
   12969             : 
   12970             : /** Rename the attribute.
   12971             :  *
   12972             :  * This is not implemented by all drivers.
   12973             :  *
   12974             :  * Drivers known to implement it: MEM, netCDF.
   12975             :  *
   12976             :  * This is the same as the C++ method GDALAbstractMDArray::Rename()
   12977             :  *
   12978             :  * @return true in case of success
   12979             :  * @since GDAL 3.8
   12980             :  */
   12981          27 : bool GDALAttributeRename(GDALAttributeH hAttr, const char *pszNewName)
   12982             : {
   12983          27 :     VALIDATE_POINTER1(hAttr, __func__, false);
   12984          27 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   12985          27 :     return hAttr->m_poImpl->Rename(pszNewName);
   12986             : }
   12987             : 
   12988             : /************************************************************************/
   12989             : /*                        GDALDimensionRelease()                        */
   12990             : /************************************************************************/
   12991             : 
   12992             : /** Release the GDAL in-memory object associated with a GDALDimension.
   12993             :  *
   12994             :  * Note: when applied on a object coming from a driver, this does not
   12995             :  * destroy the object in the file, database, etc...
   12996             :  */
   12997        4732 : void GDALDimensionRelease(GDALDimensionH hDim)
   12998             : {
   12999        4732 :     delete hDim;
   13000        4732 : }
   13001             : 
   13002             : /************************************************************************/
   13003             : /*                        GDALDimensionGetName()                        */
   13004             : /************************************************************************/
   13005             : 
   13006             : /** Return dimension name.
   13007             :  *
   13008             :  * This is the same as the C++ method GDALDimension::GetName()
   13009             :  */
   13010         284 : const char *GDALDimensionGetName(GDALDimensionH hDim)
   13011             : {
   13012         284 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13013         284 :     return hDim->m_poImpl->GetName().c_str();
   13014             : }
   13015             : 
   13016             : /************************************************************************/
   13017             : /*                      GDALDimensionGetFullName()                      */
   13018             : /************************************************************************/
   13019             : 
   13020             : /** Return dimension full name.
   13021             :  *
   13022             :  * This is the same as the C++ method GDALDimension::GetFullName()
   13023             :  */
   13024          80 : const char *GDALDimensionGetFullName(GDALDimensionH hDim)
   13025             : {
   13026          80 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13027          80 :     return hDim->m_poImpl->GetFullName().c_str();
   13028             : }
   13029             : 
   13030             : /************************************************************************/
   13031             : /*                        GDALDimensionGetType()                        */
   13032             : /************************************************************************/
   13033             : 
   13034             : /** Return dimension type.
   13035             :  *
   13036             :  * This is the same as the C++ method GDALDimension::GetType()
   13037             :  */
   13038          62 : const char *GDALDimensionGetType(GDALDimensionH hDim)
   13039             : {
   13040          62 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13041          62 :     return hDim->m_poImpl->GetType().c_str();
   13042             : }
   13043             : 
   13044             : /************************************************************************/
   13045             : /*                     GDALDimensionGetDirection()                      */
   13046             : /************************************************************************/
   13047             : 
   13048             : /** Return dimension direction.
   13049             :  *
   13050             :  * This is the same as the C++ method GDALDimension::GetDirection()
   13051             :  */
   13052          32 : const char *GDALDimensionGetDirection(GDALDimensionH hDim)
   13053             : {
   13054          32 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13055          32 :     return hDim->m_poImpl->GetDirection().c_str();
   13056             : }
   13057             : 
   13058             : /************************************************************************/
   13059             : /*                        GDALDimensionGetSize()                        */
   13060             : /************************************************************************/
   13061             : 
   13062             : /** Return the size, that is the number of values along the dimension.
   13063             :  *
   13064             :  * This is the same as the C++ method GDALDimension::GetSize()
   13065             :  */
   13066        3503 : GUInt64 GDALDimensionGetSize(GDALDimensionH hDim)
   13067             : {
   13068        3503 :     VALIDATE_POINTER1(hDim, __func__, 0);
   13069        3503 :     return hDim->m_poImpl->GetSize();
   13070             : }
   13071             : 
   13072             : /************************************************************************/
   13073             : /*                     GDALDimensionGetIndexingVariable()               */
   13074             : /************************************************************************/
   13075             : 
   13076             : /** Return the variable that is used to index the dimension (if there is one).
   13077             :  *
   13078             :  * This is the array, typically one-dimensional, describing the values taken
   13079             :  * by the dimension.
   13080             :  *
   13081             :  * The returned value should be freed with GDALMDArrayRelease().
   13082             :  *
   13083             :  * This is the same as the C++ method GDALDimension::GetIndexingVariable()
   13084             :  */
   13085         118 : GDALMDArrayH GDALDimensionGetIndexingVariable(GDALDimensionH hDim)
   13086             : {
   13087         118 :     VALIDATE_POINTER1(hDim, __func__, nullptr);
   13088         236 :     auto var(hDim->m_poImpl->GetIndexingVariable());
   13089         118 :     if (!var)
   13090          10 :         return nullptr;
   13091         108 :     return new GDALMDArrayHS(var);
   13092             : }
   13093             : 
   13094             : /************************************************************************/
   13095             : /*                      GDALDimensionSetIndexingVariable()              */
   13096             : /************************************************************************/
   13097             : 
   13098             : /** Set the variable that is used to index the dimension.
   13099             :  *
   13100             :  * This is the array, typically one-dimensional, describing the values taken
   13101             :  * by the dimension.
   13102             :  *
   13103             :  * This is the same as the C++ method GDALDimension::SetIndexingVariable()
   13104             :  *
   13105             :  * @return TRUE in case of success.
   13106             :  */
   13107          22 : int GDALDimensionSetIndexingVariable(GDALDimensionH hDim, GDALMDArrayH hArray)
   13108             : {
   13109          22 :     VALIDATE_POINTER1(hDim, __func__, FALSE);
   13110          66 :     return hDim->m_poImpl->SetIndexingVariable(hArray ? hArray->m_poImpl
   13111          44 :                                                       : nullptr);
   13112             : }
   13113             : 
   13114             : /************************************************************************/
   13115             : /*                      GDALDimensionRename()                           */
   13116             : /************************************************************************/
   13117             : 
   13118             : /** Rename the dimension.
   13119             :  *
   13120             :  * This is not implemented by all drivers.
   13121             :  *
   13122             :  * Drivers known to implement it: MEM, netCDF.
   13123             :  *
   13124             :  * This is the same as the C++ method GDALDimension::Rename()
   13125             :  *
   13126             :  * @return true in case of success
   13127             :  * @since GDAL 3.8
   13128             :  */
   13129          31 : bool GDALDimensionRename(GDALDimensionH hDim, const char *pszNewName)
   13130             : {
   13131          31 :     VALIDATE_POINTER1(hDim, __func__, false);
   13132          31 :     VALIDATE_POINTER1(pszNewName, __func__, false);
   13133          31 :     return hDim->m_poImpl->Rename(pszNewName);
   13134             : }
   13135             : 
   13136             : /************************************************************************/
   13137             : /*                       GDALDatasetGetRootGroup()                      */
   13138             : /************************************************************************/
   13139             : 
   13140             : /** Return the root GDALGroup of this dataset.
   13141             :  *
   13142             :  * Only valid for multidimensional datasets.
   13143             :  *
   13144             :  * The returned value must be freed with GDALGroupRelease().
   13145             :  *
   13146             :  * This is the same as the C++ method GDALDataset::GetRootGroup().
   13147             :  *
   13148             :  * @since GDAL 3.1
   13149             :  */
   13150        1119 : GDALGroupH GDALDatasetGetRootGroup(GDALDatasetH hDS)
   13151             : {
   13152        1119 :     VALIDATE_POINTER1(hDS, __func__, nullptr);
   13153        1119 :     auto poGroup(GDALDataset::FromHandle(hDS)->GetRootGroup());
   13154        1119 :     return poGroup ? new GDALGroupHS(poGroup) : nullptr;
   13155             : }
   13156             : 
   13157             : /************************************************************************/
   13158             : /*                      GDALRasterBandAsMDArray()                        */
   13159             : /************************************************************************/
   13160             : 
   13161             : /** Return a view of this raster band as a 2D multidimensional GDALMDArray.
   13162             :  *
   13163             :  * The band must be linked to a GDALDataset. If this dataset is not already
   13164             :  * marked as shared, it will be, so that the returned array holds a reference
   13165             :  * to it.
   13166             :  *
   13167             :  * If the dataset has a geotransform attached, the X and Y dimensions of the
   13168             :  * returned array will have an associated indexing variable.
   13169             :  *
   13170             :  * The returned pointer must be released with GDALMDArrayRelease().
   13171             :  *
   13172             :  * This is the same as the C++ method GDALRasterBand::AsMDArray().
   13173             :  *
   13174             :  * @return a new array, or NULL.
   13175             :  *
   13176             :  * @since GDAL 3.1
   13177             :  */
   13178          21 : GDALMDArrayH GDALRasterBandAsMDArray(GDALRasterBandH hBand)
   13179             : {
   13180          21 :     VALIDATE_POINTER1(hBand, __func__, nullptr);
   13181          42 :     auto poArray(GDALRasterBand::FromHandle(hBand)->AsMDArray());
   13182          21 :     if (!poArray)
   13183           0 :         return nullptr;
   13184          21 :     return new GDALMDArrayHS(poArray);
   13185             : }
   13186             : 
   13187             : /************************************************************************/
   13188             : /*                       GDALMDArrayAsClassicDataset()                  */
   13189             : /************************************************************************/
   13190             : 
   13191             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13192             :  *
   13193             :  * Only 2D or more arrays are supported.
   13194             :  *
   13195             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13196             :  * raster bands.
   13197             :  *
   13198             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13199             :  *
   13200             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13201             :  *
   13202             :  * @param hArray Array.
   13203             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13204             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13205             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13206             :  */
   13207           0 : GDALDatasetH GDALMDArrayAsClassicDataset(GDALMDArrayH hArray, size_t iXDim,
   13208             :                                          size_t iYDim)
   13209             : {
   13210           0 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13211           0 :     return GDALDataset::ToHandle(
   13212           0 :         hArray->m_poImpl->AsClassicDataset(iXDim, iYDim));
   13213             : }
   13214             : 
   13215             : /************************************************************************/
   13216             : /*                     GDALMDArrayAsClassicDatasetEx()                  */
   13217             : /************************************************************************/
   13218             : 
   13219             : /** Return a view of this array as a "classic" GDALDataset (ie 2D)
   13220             :  *
   13221             :  * Only 2D or more arrays are supported.
   13222             :  *
   13223             :  * In the case of > 2D arrays, additional dimensions will be represented as
   13224             :  * raster bands.
   13225             :  *
   13226             :  * The "reverse" method is GDALRasterBand::AsMDArray().
   13227             :  *
   13228             :  * This is the same as the C++ method GDALMDArray::AsClassicDataset().
   13229             :  * @param hArray Array.
   13230             :  * @param iXDim Index of the dimension that will be used as the X/width axis.
   13231             :  * @param iYDim Index of the dimension that will be used as the Y/height axis.
   13232             :  *              Ignored if the dimension count is 1.
   13233             :  * @param hRootGroup Root group, or NULL. Used with the BAND_METADATA option.
   13234             :  * @param papszOptions Cf GDALMDArray::AsClassicDataset()
   13235             :  * @return a new GDALDataset that must be freed with GDALClose(), or nullptr
   13236             :  * @since GDAL 3.8
   13237             :  */
   13238          56 : GDALDatasetH GDALMDArrayAsClassicDatasetEx(GDALMDArrayH hArray, size_t iXDim,
   13239             :                                            size_t iYDim, GDALGroupH hRootGroup,
   13240             :                                            CSLConstList papszOptions)
   13241             : {
   13242          56 :     VALIDATE_POINTER1(hArray, __func__, nullptr);
   13243         112 :     return GDALDataset::ToHandle(hArray->m_poImpl->AsClassicDataset(
   13244         112 :         iXDim, iYDim, hRootGroup ? hRootGroup->m_poImpl : nullptr,
   13245         112 :         papszOptions));
   13246             : }
   13247             : 
   13248             : //! @cond Doxygen_Suppress
   13249             : 
   13250         180 : GDALAttributeString::GDALAttributeString(const std::string &osParentName,
   13251             :                                          const std::string &osName,
   13252             :                                          const std::string &osValue,
   13253         180 :                                          GDALExtendedDataTypeSubType eSubType)
   13254             :     : GDALAbstractMDArray(osParentName, osName),
   13255             :       GDALAttribute(osParentName, osName),
   13256         180 :       m_dt(GDALExtendedDataType::CreateString(0, eSubType)), m_osValue(osValue)
   13257             : {
   13258         180 : }
   13259             : 
   13260             : const std::vector<std::shared_ptr<GDALDimension>> &
   13261          30 : GDALAttributeString::GetDimensions() const
   13262             : {
   13263          30 :     return m_dims;
   13264             : }
   13265             : 
   13266          21 : const GDALExtendedDataType &GDALAttributeString::GetDataType() const
   13267             : {
   13268          21 :     return m_dt;
   13269             : }
   13270             : 
   13271          10 : bool GDALAttributeString::IRead(const GUInt64 *, const size_t *, const GInt64 *,
   13272             :                                 const GPtrDiff_t *,
   13273             :                                 const GDALExtendedDataType &bufferDataType,
   13274             :                                 void *pDstBuffer) const
   13275             : {
   13276          10 :     if (bufferDataType.GetClass() != GEDTC_STRING)
   13277           0 :         return false;
   13278          10 :     char *pszStr = static_cast<char *>(VSIMalloc(m_osValue.size() + 1));
   13279          10 :     if (!pszStr)
   13280           0 :         return false;
   13281          10 :     memcpy(pszStr, m_osValue.c_str(), m_osValue.size() + 1);
   13282          10 :     *static_cast<char **>(pDstBuffer) = pszStr;
   13283          10 :     return true;
   13284             : }
   13285             : 
   13286          66 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13287             :                                            const std::string &osName,
   13288          66 :                                            double dfValue)
   13289             :     : GDALAbstractMDArray(osParentName, osName),
   13290             :       GDALAttribute(osParentName, osName),
   13291          66 :       m_dt(GDALExtendedDataType::Create(GDT_Float64)), m_dfValue(dfValue)
   13292             : {
   13293          66 : }
   13294             : 
   13295          27 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13296             :                                            const std::string &osName,
   13297          27 :                                            int nValue)
   13298             :     : GDALAbstractMDArray(osParentName, osName),
   13299             :       GDALAttribute(osParentName, osName),
   13300          27 :       m_dt(GDALExtendedDataType::Create(GDT_Int32)), m_nValue(nValue)
   13301             : {
   13302          27 : }
   13303             : 
   13304           7 : GDALAttributeNumeric::GDALAttributeNumeric(const std::string &osParentName,
   13305             :                                            const std::string &osName,
   13306           7 :                                            const std::vector<GUInt32> &anValues)
   13307             :     : GDALAbstractMDArray(osParentName, osName),
   13308             :       GDALAttribute(osParentName, osName),
   13309           7 :       m_dt(GDALExtendedDataType::Create(GDT_UInt32)), m_anValuesUInt32(anValues)
   13310             : {
   13311           7 :     m_dims.push_back(std::make_shared<GDALDimension>(
   13312          14 :         std::string(), "dim0", std::string(), std::string(),
   13313           7 :         m_anValuesUInt32.size()));
   13314           7 : }
   13315             : 
   13316             : const std::vector<std::shared_ptr<GDALDimension>> &
   13317          14 : GDALAttributeNumeric::GetDimensions() const
   13318             : {
   13319          14 :     return m_dims;
   13320             : }
   13321             : 
   13322           8 : const GDALExtendedDataType &GDALAttributeNumeric::GetDataType() const
   13323             : {
   13324           8 :     return m_dt;
   13325             : }
   13326             : 
   13327           4 : bool GDALAttributeNumeric::IRead(const GUInt64 *arrayStartIdx,
   13328             :                                  const size_t *count, const GInt64 *arrayStep,
   13329             :                                  const GPtrDiff_t *bufferStride,
   13330             :                                  const GDALExtendedDataType &bufferDataType,
   13331             :                                  void *pDstBuffer) const
   13332             : {
   13333           4 :     if (m_dims.empty())
   13334             :     {
   13335           3 :         if (m_dt.GetNumericDataType() == GDT_Float64)
   13336           0 :             GDALExtendedDataType::CopyValue(&m_dfValue, m_dt, pDstBuffer,
   13337             :                                             bufferDataType);
   13338             :         else
   13339             :         {
   13340           3 :             CPLAssert(m_dt.GetNumericDataType() == GDT_Int32);
   13341           3 :             GDALExtendedDataType::CopyValue(&m_nValue, m_dt, pDstBuffer,
   13342             :                                             bufferDataType);
   13343             :         }
   13344             :     }
   13345             :     else
   13346             :     {
   13347           1 :         CPLAssert(m_dt.GetNumericDataType() == GDT_UInt32);
   13348           1 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13349          30 :         for (size_t i = 0; i < count[0]; ++i)
   13350             :         {
   13351          29 :             GDALExtendedDataType::CopyValue(
   13352          29 :                 &m_anValuesUInt32[static_cast<size_t>(arrayStartIdx[0] +
   13353          29 :                                                       i * arrayStep[0])],
   13354          29 :                 m_dt, pabyDstBuffer, bufferDataType);
   13355          29 :             pabyDstBuffer += bufferDataType.GetSize() * bufferStride[0];
   13356             :         }
   13357             :     }
   13358           4 :     return true;
   13359             : }
   13360             : 
   13361         168 : GDALMDArrayRegularlySpaced::GDALMDArrayRegularlySpaced(
   13362             :     const std::string &osParentName, const std::string &osName,
   13363             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13364         168 :     double dfIncrement, double dfOffsetInIncrement)
   13365             :     : GDALAbstractMDArray(osParentName, osName),
   13366             :       GDALMDArray(osParentName, osName), m_dfStart(dfStart),
   13367             :       m_dfIncrement(dfIncrement),
   13368         336 :       m_dfOffsetInIncrement(dfOffsetInIncrement), m_dims{poDim}
   13369             : {
   13370         168 : }
   13371             : 
   13372         168 : std::shared_ptr<GDALMDArrayRegularlySpaced> GDALMDArrayRegularlySpaced::Create(
   13373             :     const std::string &osParentName, const std::string &osName,
   13374             :     const std::shared_ptr<GDALDimension> &poDim, double dfStart,
   13375             :     double dfIncrement, double dfOffsetInIncrement)
   13376             : {
   13377             :     auto poArray = std::make_shared<GDALMDArrayRegularlySpaced>(
   13378         168 :         osParentName, osName, poDim, dfStart, dfIncrement, dfOffsetInIncrement);
   13379         168 :     poArray->SetSelf(poArray);
   13380         168 :     return poArray;
   13381             : }
   13382             : 
   13383             : const std::vector<std::shared_ptr<GDALDimension>> &
   13384         754 : GDALMDArrayRegularlySpaced::GetDimensions() const
   13385             : {
   13386         754 :     return m_dims;
   13387             : }
   13388             : 
   13389         316 : const GDALExtendedDataType &GDALMDArrayRegularlySpaced::GetDataType() const
   13390             : {
   13391         316 :     return m_dt;
   13392             : }
   13393             : 
   13394             : std::vector<std::shared_ptr<GDALAttribute>>
   13395           4 : GDALMDArrayRegularlySpaced::GetAttributes(CSLConstList) const
   13396             : {
   13397           4 :     return m_attributes;
   13398             : }
   13399             : 
   13400           0 : void GDALMDArrayRegularlySpaced::AddAttribute(
   13401             :     const std::shared_ptr<GDALAttribute> &poAttr)
   13402             : {
   13403           0 :     m_attributes.emplace_back(poAttr);
   13404           0 : }
   13405             : 
   13406         188 : bool GDALMDArrayRegularlySpaced::IRead(
   13407             :     const GUInt64 *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
   13408             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
   13409             :     void *pDstBuffer) const
   13410             : {
   13411         188 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
   13412       14719 :     for (size_t i = 0; i < count[0]; i++)
   13413             :     {
   13414       14531 :         const double dfVal = m_dfStart + (arrayStartIdx[0] + i * arrayStep[0] +
   13415       14531 :                                           m_dfOffsetInIncrement) *
   13416       14531 :                                              m_dfIncrement;
   13417       14531 :         GDALExtendedDataType::CopyValue(&dfVal, m_dt, pabyDstBuffer,
   13418             :                                         bufferDataType);
   13419       14531 :         pabyDstBuffer += bufferStride[0] * bufferDataType.GetSize();
   13420             :     }
   13421         188 :     return true;
   13422             : }
   13423             : 
   13424        2780 : GDALDimensionWeakIndexingVar::GDALDimensionWeakIndexingVar(
   13425             :     const std::string &osParentName, const std::string &osName,
   13426        2780 :     const std::string &osType, const std::string &osDirection, GUInt64 nSize)
   13427        2780 :     : GDALDimension(osParentName, osName, osType, osDirection, nSize)
   13428             : {
   13429        2780 : }
   13430             : 
   13431             : std::shared_ptr<GDALMDArray>
   13432         653 : GDALDimensionWeakIndexingVar::GetIndexingVariable() const
   13433             : {
   13434         653 :     return m_poIndexingVariable.lock();
   13435             : }
   13436             : 
   13437             : // cppcheck-suppress passedByValue
   13438         443 : bool GDALDimensionWeakIndexingVar::SetIndexingVariable(
   13439             :     std::shared_ptr<GDALMDArray> poIndexingVariable)
   13440             : {
   13441         443 :     m_poIndexingVariable = poIndexingVariable;
   13442         443 :     return true;
   13443             : }
   13444             : 
   13445          33 : void GDALDimensionWeakIndexingVar::SetSize(GUInt64 nNewSize)
   13446             : {
   13447          33 :     m_nSize = nNewSize;
   13448          33 : }
   13449             : 
   13450             : /************************************************************************/
   13451             : /*                       GDALPamMultiDim::Private                       */
   13452             : /************************************************************************/
   13453             : 
   13454             : struct GDALPamMultiDim::Private
   13455             : {
   13456             :     std::string m_osFilename{};
   13457             :     std::string m_osPamFilename{};
   13458             : 
   13459             :     struct Statistics
   13460             :     {
   13461             :         bool bHasStats = false;
   13462             :         bool bApproxStats = false;
   13463             :         double dfMin = 0;
   13464             :         double dfMax = 0;
   13465             :         double dfMean = 0;
   13466             :         double dfStdDev = 0;
   13467             :         GUInt64 nValidCount = 0;
   13468             :     };
   13469             : 
   13470             :     struct ArrayInfo
   13471             :     {
   13472             :         std::shared_ptr<OGRSpatialReference> poSRS{};
   13473             :         // cppcheck-suppress unusedStructMember
   13474             :         Statistics stats{};
   13475             :     };
   13476             : 
   13477             :     typedef std::pair<std::string, std::string> NameContext;
   13478             :     std::map<NameContext, ArrayInfo> m_oMapArray{};
   13479             :     std::vector<CPLXMLTreeCloser> m_apoOtherNodes{};
   13480             :     bool m_bDirty = false;
   13481             :     bool m_bLoaded = false;
   13482             : };
   13483             : 
   13484             : /************************************************************************/
   13485             : /*                          GDALPamMultiDim                             */
   13486             : /************************************************************************/
   13487             : 
   13488        1342 : GDALPamMultiDim::GDALPamMultiDim(const std::string &osFilename)
   13489        1342 :     : d(new Private())
   13490             : {
   13491        1342 :     d->m_osFilename = osFilename;
   13492        1342 : }
   13493             : 
   13494             : /************************************************************************/
   13495             : /*                   GDALPamMultiDim::~GDALPamMultiDim()                */
   13496             : /************************************************************************/
   13497             : 
   13498        1342 : GDALPamMultiDim::~GDALPamMultiDim()
   13499             : {
   13500        1342 :     if (d->m_bDirty)
   13501          28 :         Save();
   13502        1342 : }
   13503             : 
   13504             : /************************************************************************/
   13505             : /*                          GDALPamMultiDim::Load()                     */
   13506             : /************************************************************************/
   13507             : 
   13508          98 : void GDALPamMultiDim::Load()
   13509             : {
   13510          98 :     if (d->m_bLoaded)
   13511          87 :         return;
   13512          43 :     d->m_bLoaded = true;
   13513             : 
   13514          43 :     const char *pszProxyPam = PamGetProxy(d->m_osFilename.c_str());
   13515          43 :     d->m_osPamFilename =
   13516          86 :         pszProxyPam ? std::string(pszProxyPam) : d->m_osFilename + ".aux.xml";
   13517          43 :     CPLXMLTreeCloser oTree(nullptr);
   13518             :     {
   13519          86 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   13520          43 :         oTree.reset(CPLParseXMLFile(d->m_osPamFilename.c_str()));
   13521             :     }
   13522          43 :     if (!oTree)
   13523             :     {
   13524          32 :         return;
   13525             :     }
   13526          11 :     const auto poPAMMultiDim = CPLGetXMLNode(oTree.get(), "=PAMDataset");
   13527          11 :     if (!poPAMMultiDim)
   13528           0 :         return;
   13529          34 :     for (CPLXMLNode *psIter = poPAMMultiDim->psChild; psIter;
   13530          23 :          psIter = psIter->psNext)
   13531             :     {
   13532          23 :         if (psIter->eType == CXT_Element &&
   13533          23 :             strcmp(psIter->pszValue, "Array") == 0)
   13534             :         {
   13535          13 :             const char *pszName = CPLGetXMLValue(psIter, "name", nullptr);
   13536          13 :             if (!pszName)
   13537           0 :                 continue;
   13538          13 :             const char *pszContext = CPLGetXMLValue(psIter, "context", "");
   13539             :             const auto oKey =
   13540          26 :                 std::pair<std::string, std::string>(pszName, pszContext);
   13541             : 
   13542             :             /* --------------------------------------------------------------------
   13543             :              */
   13544             :             /*      Check for an SRS node. */
   13545             :             /* --------------------------------------------------------------------
   13546             :              */
   13547          13 :             const CPLXMLNode *psSRSNode = CPLGetXMLNode(psIter, "SRS");
   13548          13 :             if (psSRSNode)
   13549             :             {
   13550             :                 std::shared_ptr<OGRSpatialReference> poSRS =
   13551           6 :                     std::make_shared<OGRSpatialReference>();
   13552           3 :                 poSRS->SetFromUserInput(
   13553             :                     CPLGetXMLValue(psSRSNode, nullptr, ""),
   13554             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
   13555           3 :                 const char *pszMapping = CPLGetXMLValue(
   13556             :                     psSRSNode, "dataAxisToSRSAxisMapping", nullptr);
   13557           3 :                 if (pszMapping)
   13558             :                 {
   13559             :                     char **papszTokens =
   13560           3 :                         CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
   13561           6 :                     std::vector<int> anMapping;
   13562           9 :                     for (int i = 0; papszTokens && papszTokens[i]; i++)
   13563             :                     {
   13564           6 :                         anMapping.push_back(atoi(papszTokens[i]));
   13565             :                     }
   13566           3 :                     CSLDestroy(papszTokens);
   13567           3 :                     poSRS->SetDataAxisToSRSAxisMapping(anMapping);
   13568             :                 }
   13569             :                 else
   13570             :                 {
   13571           0 :                     poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
   13572             :                 }
   13573             : 
   13574             :                 const char *pszCoordinateEpoch =
   13575           3 :                     CPLGetXMLValue(psSRSNode, "coordinateEpoch", nullptr);
   13576           3 :                 if (pszCoordinateEpoch)
   13577           3 :                     poSRS->SetCoordinateEpoch(CPLAtof(pszCoordinateEpoch));
   13578             : 
   13579           3 :                 d->m_oMapArray[oKey].poSRS = std::move(poSRS);
   13580             :             }
   13581             : 
   13582             :             const CPLXMLNode *psStatistics =
   13583          13 :                 CPLGetXMLNode(psIter, "Statistics");
   13584          13 :             if (psStatistics)
   13585             :             {
   13586           7 :                 Private::Statistics sStats;
   13587           7 :                 sStats.bHasStats = true;
   13588           7 :                 sStats.bApproxStats = CPLTestBool(
   13589             :                     CPLGetXMLValue(psStatistics, "ApproxStats", "false"));
   13590           7 :                 sStats.dfMin =
   13591           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Minimum", "0"));
   13592           7 :                 sStats.dfMax =
   13593           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Maximum", "0"));
   13594           7 :                 sStats.dfMean =
   13595           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "Mean", "0"));
   13596           7 :                 sStats.dfStdDev =
   13597           7 :                     CPLAtofM(CPLGetXMLValue(psStatistics, "StdDev", "0"));
   13598           7 :                 sStats.nValidCount = static_cast<GUInt64>(CPLAtoGIntBig(
   13599             :                     CPLGetXMLValue(psStatistics, "ValidSampleCount", "0")));
   13600           7 :                 d->m_oMapArray[oKey].stats = sStats;
   13601          13 :             }
   13602             :         }
   13603             :         else
   13604             :         {
   13605          10 :             CPLXMLNode *psNextBackup = psIter->psNext;
   13606          10 :             psIter->psNext = nullptr;
   13607          10 :             d->m_apoOtherNodes.emplace_back(
   13608          10 :                 CPLXMLTreeCloser(CPLCloneXMLTree(psIter)));
   13609          10 :             psIter->psNext = psNextBackup;
   13610             :         }
   13611             :     }
   13612             : }
   13613             : 
   13614             : /************************************************************************/
   13615             : /*                          GDALPamMultiDim::Save()                     */
   13616             : /************************************************************************/
   13617             : 
   13618          28 : void GDALPamMultiDim::Save()
   13619             : {
   13620             :     CPLXMLTreeCloser oTree(
   13621          56 :         CPLCreateXMLNode(nullptr, CXT_Element, "PAMDataset"));
   13622          32 :     for (const auto &poOtherNode : d->m_apoOtherNodes)
   13623             :     {
   13624           4 :         CPLAddXMLChild(oTree.get(), CPLCloneXMLTree(poOtherNode.get()));
   13625             :     }
   13626         104 :     for (const auto &kv : d->m_oMapArray)
   13627             :     {
   13628             :         CPLXMLNode *psArrayNode =
   13629          76 :             CPLCreateXMLNode(oTree.get(), CXT_Element, "Array");
   13630          76 :         CPLAddXMLAttributeAndValue(psArrayNode, "name", kv.first.first.c_str());
   13631          76 :         if (!kv.first.second.empty())
   13632             :         {
   13633           1 :             CPLAddXMLAttributeAndValue(psArrayNode, "context",
   13634             :                                        kv.first.second.c_str());
   13635             :         }
   13636          76 :         if (kv.second.poSRS)
   13637             :         {
   13638          68 :             char *pszWKT = nullptr;
   13639             :             {
   13640         136 :                 CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
   13641          68 :                 const char *const apszOptions[] = {"FORMAT=WKT2", nullptr};
   13642          68 :                 kv.second.poSRS->exportToWkt(&pszWKT, apszOptions);
   13643             :             }
   13644             :             CPLXMLNode *psSRSNode =
   13645          68 :                 CPLCreateXMLElementAndValue(psArrayNode, "SRS", pszWKT);
   13646          68 :             CPLFree(pszWKT);
   13647             :             const auto &mapping =
   13648          68 :                 kv.second.poSRS->GetDataAxisToSRSAxisMapping();
   13649         136 :             CPLString osMapping;
   13650         204 :             for (size_t i = 0; i < mapping.size(); ++i)
   13651             :             {
   13652         136 :                 if (!osMapping.empty())
   13653          68 :                     osMapping += ",";
   13654         136 :                 osMapping += CPLSPrintf("%d", mapping[i]);
   13655             :             }
   13656          68 :             CPLAddXMLAttributeAndValue(psSRSNode, "dataAxisToSRSAxisMapping",
   13657             :                                        osMapping.c_str());
   13658             : 
   13659             :             const double dfCoordinateEpoch =
   13660          68 :                 kv.second.poSRS->GetCoordinateEpoch();
   13661          68 :             if (dfCoordinateEpoch > 0)
   13662             :             {
   13663             :                 std::string osCoordinateEpoch =
   13664           2 :                     CPLSPrintf("%f", dfCoordinateEpoch);
   13665           1 :                 if (osCoordinateEpoch.find('.') != std::string::npos)
   13666             :                 {
   13667           6 :                     while (osCoordinateEpoch.back() == '0')
   13668           5 :                         osCoordinateEpoch.resize(osCoordinateEpoch.size() - 1);
   13669             :                 }
   13670           1 :                 CPLAddXMLAttributeAndValue(psSRSNode, "coordinateEpoch",
   13671             :                                            osCoordinateEpoch.c_str());
   13672             :             }
   13673             :         }
   13674             : 
   13675          76 :         if (kv.second.stats.bHasStats)
   13676             :         {
   13677             :             CPLXMLNode *psMDArray =
   13678           5 :                 CPLCreateXMLNode(psArrayNode, CXT_Element, "Statistics");
   13679           5 :             CPLCreateXMLElementAndValue(psMDArray, "ApproxStats",
   13680           5 :                                         kv.second.stats.bApproxStats ? "1"
   13681             :                                                                      : "0");
   13682           5 :             CPLCreateXMLElementAndValue(
   13683             :                 psMDArray, "Minimum",
   13684           5 :                 CPLSPrintf("%.18g", kv.second.stats.dfMin));
   13685           5 :             CPLCreateXMLElementAndValue(
   13686             :                 psMDArray, "Maximum",
   13687           5 :                 CPLSPrintf("%.18g", kv.second.stats.dfMax));
   13688           5 :             CPLCreateXMLElementAndValue(
   13689           5 :                 psMDArray, "Mean", CPLSPrintf("%.18g", kv.second.stats.dfMean));
   13690           5 :             CPLCreateXMLElementAndValue(
   13691             :                 psMDArray, "StdDev",
   13692           5 :                 CPLSPrintf("%.18g", kv.second.stats.dfStdDev));
   13693           5 :             CPLCreateXMLElementAndValue(
   13694             :                 psMDArray, "ValidSampleCount",
   13695           5 :                 CPLSPrintf(CPL_FRMT_GUIB, kv.second.stats.nValidCount));
   13696             :         }
   13697             :     }
   13698             : 
   13699          56 :     std::vector<CPLErrorHandlerAccumulatorStruct> aoErrors;
   13700          28 :     CPLInstallErrorHandlerAccumulator(aoErrors);
   13701             :     const int bSaved =
   13702          28 :         CPLSerializeXMLTreeToFile(oTree.get(), d->m_osPamFilename.c_str());
   13703          28 :     CPLUninstallErrorHandlerAccumulator();
   13704             : 
   13705          28 :     const char *pszNewPam = nullptr;
   13706          28 :     if (!bSaved && PamGetProxy(d->m_osFilename.c_str()) == nullptr &&
   13707           0 :         ((pszNewPam = PamAllocateProxy(d->m_osFilename.c_str())) != nullptr))
   13708             :     {
   13709           0 :         CPLErrorReset();
   13710           0 :         CPLSerializeXMLTreeToFile(oTree.get(), pszNewPam);
   13711             :     }
   13712             :     else
   13713             :     {
   13714          28 :         for (const auto &oError : aoErrors)
   13715             :         {
   13716           0 :             CPLError(oError.type, oError.no, "%s", oError.msg.c_str());
   13717             :         }
   13718             :     }
   13719          28 : }
   13720             : 
   13721             : /************************************************************************/
   13722             : /*                    GDALPamMultiDim::GetSpatialRef()                  */
   13723             : /************************************************************************/
   13724             : 
   13725             : std::shared_ptr<OGRSpatialReference>
   13726          10 : GDALPamMultiDim::GetSpatialRef(const std::string &osArrayFullName,
   13727             :                                const std::string &osContext)
   13728             : {
   13729          10 :     Load();
   13730             :     auto oIter =
   13731          10 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   13732          10 :     if (oIter != d->m_oMapArray.end())
   13733           2 :         return oIter->second.poSRS;
   13734           8 :     return nullptr;
   13735             : }
   13736             : 
   13737             : /************************************************************************/
   13738             : /*                    GDALPamMultiDim::SetSpatialRef()                  */
   13739             : /************************************************************************/
   13740             : 
   13741          69 : void GDALPamMultiDim::SetSpatialRef(const std::string &osArrayFullName,
   13742             :                                     const std::string &osContext,
   13743             :                                     const OGRSpatialReference *poSRS)
   13744             : {
   13745          69 :     Load();
   13746          69 :     d->m_bDirty = true;
   13747          69 :     if (poSRS && !poSRS->IsEmpty())
   13748          68 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].poSRS.reset(
   13749             :             poSRS->Clone());
   13750             :     else
   13751           2 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)]
   13752           1 :             .poSRS.reset();
   13753          69 : }
   13754             : 
   13755             : /************************************************************************/
   13756             : /*                           GetStatistics()                            */
   13757             : /************************************************************************/
   13758             : 
   13759          13 : CPLErr GDALPamMultiDim::GetStatistics(const std::string &osArrayFullName,
   13760             :                                       const std::string &osContext,
   13761             :                                       bool bApproxOK, double *pdfMin,
   13762             :                                       double *pdfMax, double *pdfMean,
   13763             :                                       double *pdfStdDev, GUInt64 *pnValidCount)
   13764             : {
   13765          13 :     Load();
   13766             :     auto oIter =
   13767          13 :         d->m_oMapArray.find(std::make_pair(osArrayFullName, osContext));
   13768          13 :     if (oIter == d->m_oMapArray.end())
   13769           6 :         return CE_Failure;
   13770           7 :     const auto &stats = oIter->second.stats;
   13771           7 :     if (!stats.bHasStats)
   13772           1 :         return CE_Failure;
   13773           6 :     if (!bApproxOK && stats.bApproxStats)
   13774           0 :         return CE_Failure;
   13775           6 :     if (pdfMin)
   13776           6 :         *pdfMin = stats.dfMin;
   13777           6 :     if (pdfMax)
   13778           6 :         *pdfMax = stats.dfMax;
   13779           6 :     if (pdfMean)
   13780           6 :         *pdfMean = stats.dfMean;
   13781           6 :     if (pdfStdDev)
   13782           6 :         *pdfStdDev = stats.dfStdDev;
   13783           6 :     if (pnValidCount)
   13784           6 :         *pnValidCount = stats.nValidCount;
   13785           6 :     return CE_None;
   13786             : }
   13787             : 
   13788             : /************************************************************************/
   13789             : /*                           SetStatistics()                            */
   13790             : /************************************************************************/
   13791             : 
   13792           5 : void GDALPamMultiDim::SetStatistics(const std::string &osArrayFullName,
   13793             :                                     const std::string &osContext,
   13794             :                                     bool bApproxStats, double dfMin,
   13795             :                                     double dfMax, double dfMean,
   13796             :                                     double dfStdDev, GUInt64 nValidCount)
   13797             : {
   13798           5 :     Load();
   13799           5 :     d->m_bDirty = true;
   13800             :     auto &stats =
   13801           5 :         d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats;
   13802           5 :     stats.bHasStats = true;
   13803           5 :     stats.bApproxStats = bApproxStats;
   13804           5 :     stats.dfMin = dfMin;
   13805           5 :     stats.dfMax = dfMax;
   13806           5 :     stats.dfMean = dfMean;
   13807           5 :     stats.dfStdDev = dfStdDev;
   13808           5 :     stats.nValidCount = nValidCount;
   13809           5 : }
   13810             : 
   13811             : /************************************************************************/
   13812             : /*                           ClearStatistics()                          */
   13813             : /************************************************************************/
   13814             : 
   13815           0 : void GDALPamMultiDim::ClearStatistics(const std::string &osArrayFullName,
   13816             :                                       const std::string &osContext)
   13817             : {
   13818           0 :     Load();
   13819           0 :     d->m_bDirty = true;
   13820           0 :     d->m_oMapArray[std::make_pair(osArrayFullName, osContext)].stats.bHasStats =
   13821             :         false;
   13822           0 : }
   13823             : 
   13824             : /************************************************************************/
   13825             : /*                           ClearStatistics()                          */
   13826             : /************************************************************************/
   13827             : 
   13828           1 : void GDALPamMultiDim::ClearStatistics()
   13829             : {
   13830           1 :     Load();
   13831           1 :     d->m_bDirty = true;
   13832           3 :     for (auto &kv : d->m_oMapArray)
   13833           2 :         kv.second.stats.bHasStats = false;
   13834           1 : }
   13835             : 
   13836             : /************************************************************************/
   13837             : /*                             GetPAM()                                 */
   13838             : /************************************************************************/
   13839             : 
   13840             : /*static*/ std::shared_ptr<GDALPamMultiDim>
   13841         748 : GDALPamMultiDim::GetPAM(const std::shared_ptr<GDALMDArray> &poParent)
   13842             : {
   13843         748 :     auto poPamArray = dynamic_cast<GDALPamMDArray *>(poParent.get());
   13844         748 :     if (poPamArray)
   13845         539 :         return poPamArray->GetPAM();
   13846         209 :     return nullptr;
   13847             : }
   13848             : 
   13849             : /************************************************************************/
   13850             : /*                           GDALPamMDArray                             */
   13851             : /************************************************************************/
   13852             : 
   13853        3506 : GDALPamMDArray::GDALPamMDArray(const std::string &osParentName,
   13854             :                                const std::string &osName,
   13855             :                                const std::shared_ptr<GDALPamMultiDim> &poPam,
   13856           0 :                                const std::string &osContext)
   13857             :     :
   13858             : #if !defined(COMPILER_WARNS_ABOUT_ABSTRACT_VBASE_INIT)
   13859             :       GDALAbstractMDArray(osParentName, osName),
   13860             : #endif
   13861        3506 :       GDALMDArray(osParentName, osName, osContext), m_poPam(poPam)
   13862             : {
   13863        3506 : }
   13864             : 
   13865             : /************************************************************************/
   13866             : /*                    GDALPamMDArray::SetSpatialRef()                   */
   13867             : /************************************************************************/
   13868             : 
   13869          69 : bool GDALPamMDArray::SetSpatialRef(const OGRSpatialReference *poSRS)
   13870             : {
   13871          69 :     if (!m_poPam)
   13872           0 :         return false;
   13873          69 :     m_poPam->SetSpatialRef(GetFullName(), GetContext(), poSRS);
   13874          69 :     return true;
   13875             : }
   13876             : 
   13877             : /************************************************************************/
   13878             : /*                    GDALPamMDArray::GetSpatialRef()                   */
   13879             : /************************************************************************/
   13880             : 
   13881          10 : std::shared_ptr<OGRSpatialReference> GDALPamMDArray::GetSpatialRef() const
   13882             : {
   13883          10 :     if (!m_poPam)
   13884           0 :         return nullptr;
   13885          10 :     return m_poPam->GetSpatialRef(GetFullName(), GetContext());
   13886             : }
   13887             : 
   13888             : /************************************************************************/
   13889             : /*                           GetStatistics()                            */
   13890             : /************************************************************************/
   13891             : 
   13892          13 : CPLErr GDALPamMDArray::GetStatistics(bool bApproxOK, bool bForce,
   13893             :                                      double *pdfMin, double *pdfMax,
   13894             :                                      double *pdfMean, double *pdfStdDev,
   13895             :                                      GUInt64 *pnValidCount,
   13896             :                                      GDALProgressFunc pfnProgress,
   13897             :                                      void *pProgressData)
   13898             : {
   13899          13 :     if (m_poPam && m_poPam->GetStatistics(GetFullName(), GetContext(),
   13900             :                                           bApproxOK, pdfMin, pdfMax, pdfMean,
   13901          13 :                                           pdfStdDev, pnValidCount) == CE_None)
   13902             :     {
   13903           6 :         return CE_None;
   13904             :     }
   13905           7 :     if (!bForce)
   13906           4 :         return CE_Warning;
   13907             : 
   13908           3 :     return GDALMDArray::GetStatistics(bApproxOK, bForce, pdfMin, pdfMax,
   13909             :                                       pdfMean, pdfStdDev, pnValidCount,
   13910           3 :                                       pfnProgress, pProgressData);
   13911             : }
   13912             : 
   13913             : /************************************************************************/
   13914             : /*                           SetStatistics()                            */
   13915             : /************************************************************************/
   13916             : 
   13917           5 : bool GDALPamMDArray::SetStatistics(bool bApproxStats, double dfMin,
   13918             :                                    double dfMax, double dfMean, double dfStdDev,
   13919             :                                    GUInt64 nValidCount,
   13920             :                                    CSLConstList /* papszOptions */)
   13921             : {
   13922           5 :     if (!m_poPam)
   13923           0 :         return false;
   13924           5 :     m_poPam->SetStatistics(GetFullName(), GetContext(), bApproxStats, dfMin,
   13925             :                            dfMax, dfMean, dfStdDev, nValidCount);
   13926           5 :     return true;
   13927             : }
   13928             : 
   13929             : /************************************************************************/
   13930             : /*                           ClearStatistics()                          */
   13931             : /************************************************************************/
   13932             : 
   13933           0 : void GDALPamMDArray::ClearStatistics()
   13934             : {
   13935           0 :     if (!m_poPam)
   13936           0 :         return;
   13937           0 :     m_poPam->ClearStatistics(GetFullName(), GetContext());
   13938             : }
   13939             : 
   13940             : //! @endcond

Generated by: LCOV version 1.14