LCOV - code coverage report
Current view: top level - frmts/netcdf - netcdfmultidim.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2161 2383 90.7 %
Date: 2024-05-04 12:52:34 Functions: 135 141 95.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  netCDF read/write Driver
       4             :  * Author:   Even Rouault <even.rouault at spatialys.com>
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
       8             :  *
       9             :  * Permission is hereby granted, free of charge, to any person obtaining a
      10             :  * copy of this software and associated documentation files (the "Software"),
      11             :  * to deal in the Software without restriction, including without limitation
      12             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      13             :  * and/or sell copies of the Software, and to permit persons to whom the
      14             :  * Software is furnished to do so, subject to the following conditions:
      15             :  *
      16             :  * The above copyright notice and this permission notice shall be included
      17             :  * in all copies or substantial portions of the Software.
      18             :  *
      19             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      20             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      22             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      24             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      25             :  * DEALINGS IN THE SOFTWARE.
      26             :  ****************************************************************************/
      27             : 
      28             : #include <algorithm>
      29             : #include <limits>
      30             : #include <map>
      31             : 
      32             : #include "netcdfdataset.h"
      33             : #include "netcdfdrivercore.h"
      34             : 
      35             : #include "netcdf_mem.h"
      36             : 
      37             : /************************************************************************/
      38             : /*                         netCDFSharedResources                        */
      39             : /************************************************************************/
      40             : 
      41             : class netCDFSharedResources
      42             : {
      43             :     friend class netCDFDataset;
      44             : 
      45             :     bool m_bImappIsInElements = true;
      46             :     bool m_bReadOnly = true;
      47             :     bool m_bIsNC4 = false;
      48             :     int m_cdfid = 0;
      49             : #ifdef ENABLE_NCDUMP
      50             :     bool m_bFileToDestroyAtClosing = false;
      51             : #endif
      52             :     CPLString m_osFilename{};
      53             : #ifdef ENABLE_UFFD
      54             :     cpl_uffd_context *m_pUffdCtx = nullptr;
      55             : #endif
      56             :     VSILFILE *m_fpVSIMEM = nullptr;
      57             :     bool m_bDefineMode = false;
      58             :     std::map<int, int> m_oMapDimIdToGroupId{};
      59             :     bool m_bIsInIndexingVariable = false;
      60             :     std::shared_ptr<GDALPamMultiDim> m_poPAM{};
      61             :     std::map<int, std::weak_ptr<GDALDimension>> m_oCachedDimensions{};
      62             : 
      63             :   public:
      64             :     explicit netCDFSharedResources(const std::string &osFilename);
      65             :     ~netCDFSharedResources();
      66             : 
      67         545 :     inline int GetCDFId() const
      68             :     {
      69         545 :         return m_cdfid;
      70             :     }
      71             : 
      72        1805 :     inline bool IsReadOnly() const
      73             :     {
      74        1805 :         return m_bReadOnly;
      75             :     }
      76             : 
      77         161 :     inline bool IsNC4() const
      78             :     {
      79         161 :         return m_bIsNC4;
      80             :     }
      81             : 
      82             :     bool SetDefineMode(bool bNewDefineMode);
      83             :     int GetBelongingGroupOfDim(int startgid, int dimid);
      84             : 
      85           7 :     inline bool GetImappIsInElements() const
      86             :     {
      87           7 :         return m_bImappIsInElements;
      88             :     }
      89             : 
      90        1066 :     void SetIsInGetIndexingVariable(bool b)
      91             :     {
      92        1066 :         m_bIsInIndexingVariable = b;
      93        1066 :     }
      94             : 
      95        1681 :     bool GetIsInIndexingVariable() const
      96             :     {
      97        1681 :         return m_bIsInIndexingVariable;
      98             :     }
      99             : 
     100         559 :     const std::string &GetFilename() const
     101             :     {
     102         559 :         return m_osFilename;
     103             :     }
     104             : 
     105        1587 :     const std::shared_ptr<GDALPamMultiDim> &GetPAM()
     106             :     {
     107        1587 :         return m_poPAM;
     108             :     }
     109             : 
     110        1536 :     void CacheDimension(int dimid, const std::shared_ptr<GDALDimension> &poDim)
     111             :     {
     112        1536 :         m_oCachedDimensions[dimid] = poDim;
     113        1536 :     }
     114             : 
     115        2223 :     std::shared_ptr<GDALDimension> GetCachedDimension(int dimid) const
     116             :     {
     117        2223 :         auto oIter = m_oCachedDimensions.find(dimid);
     118        2223 :         if (oIter == m_oCachedDimensions.end())
     119         384 :             return nullptr;
     120        1839 :         return oIter->second.lock();
     121             :     }
     122             : };
     123             : 
     124             : /************************************************************************/
     125             : /*                       netCDFSharedResources()                        */
     126             : /************************************************************************/
     127             : 
     128         224 : netCDFSharedResources::netCDFSharedResources(const std::string &osFilename)
     129             :     : m_bImappIsInElements(false), m_osFilename(osFilename),
     130         224 :       m_poPAM(std::make_shared<GDALPamMultiDim>(osFilename))
     131             : {
     132             :     // netcdf >= 4.4 uses imapp argument of nc_get/put_varm as a stride in
     133             :     // elements, whereas earlier versions use bytes.
     134             :     CPLStringList aosVersionNumbers(
     135         448 :         CSLTokenizeString2(nc_inq_libvers(), ".", 0));
     136         224 :     m_bImappIsInElements = false;
     137         224 :     if (aosVersionNumbers.size() >= 3)
     138             :     {
     139         224 :         m_bImappIsInElements =
     140         224 :             (atoi(aosVersionNumbers[0]) > 4 || atoi(aosVersionNumbers[1]) >= 4);
     141             :     }
     142         224 : }
     143             : 
     144             : /************************************************************************/
     145             : /*                       GetBelongingGroupOfDim()                       */
     146             : /************************************************************************/
     147             : 
     148        1478 : int netCDFSharedResources::GetBelongingGroupOfDim(int startgid, int dimid)
     149             : {
     150             :     // Am I missing a netCDF API to do this directly ?
     151        1478 :     auto oIter = m_oMapDimIdToGroupId.find(dimid);
     152        1478 :     if (oIter != m_oMapDimIdToGroupId.end())
     153        1192 :         return oIter->second;
     154             : 
     155         286 :     int gid = startgid;
     156             :     while (true)
     157             :     {
     158         295 :         int nbDims = 0;
     159         295 :         NCDF_ERR(nc_inq_ndims(gid, &nbDims));
     160         295 :         if (nbDims > 0)
     161             :         {
     162         286 :             std::vector<int> dimids(nbDims);
     163         286 :             NCDF_ERR(nc_inq_dimids(gid, &nbDims, &dimids[0], FALSE));
     164         605 :             for (int i = 0; i < nbDims; i++)
     165             :             {
     166         605 :                 m_oMapDimIdToGroupId[dimid] = gid;
     167         605 :                 if (dimids[i] == dimid)
     168         286 :                     return gid;
     169             :             }
     170             :         }
     171           9 :         int nParentGID = 0;
     172           9 :         if (nc_inq_grp_parent(gid, &nParentGID) != NC_NOERR)
     173           0 :             return startgid;
     174           9 :         gid = nParentGID;
     175           9 :     }
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*                            SetDefineMode()                           */
     180             : /************************************************************************/
     181             : 
     182         944 : bool netCDFSharedResources::SetDefineMode(bool bNewDefineMode)
     183             : {
     184             :     // Do nothing if already in new define mode
     185             :     // or if dataset is in read-only mode or if dataset is NC4 format.
     186         944 :     if (m_bDefineMode == bNewDefineMode || m_bReadOnly || m_bIsNC4)
     187         930 :         return true;
     188             : 
     189          14 :     CPLDebug("GDAL_netCDF", "SetDefineMode(%d) new=%d, old=%d", m_cdfid,
     190          14 :              static_cast<int>(bNewDefineMode), static_cast<int>(m_bDefineMode));
     191             : 
     192          14 :     m_bDefineMode = bNewDefineMode;
     193             : 
     194             :     int status;
     195          14 :     if (m_bDefineMode)
     196           8 :         status = nc_redef(m_cdfid);
     197             :     else
     198           6 :         status = nc_enddef(m_cdfid);
     199             : 
     200          14 :     NCDF_ERR(status);
     201          14 :     return status == NC_NOERR;
     202             : }
     203             : 
     204             : /************************************************************************/
     205             : /*                        netCDFAttributeHolder                         */
     206             : /************************************************************************/
     207             : 
     208             : class netCDFAttributeHolder CPL_NON_FINAL
     209             : {
     210             :   protected:
     211             :     std::map<std::string, GDALAttribute *> m_oMapAttributes{};
     212             : 
     213             :   public:
     214         653 :     void RegisterAttribute(GDALAttribute *poAttr)
     215             :     {
     216         653 :         m_oMapAttributes[poAttr->GetName()] = poAttr;
     217         653 :     }
     218             : 
     219         589 :     void UnRegisterAttribute(GDALAttribute *poAttr)
     220             :     {
     221         589 :         m_oMapAttributes.erase(poAttr->GetName());
     222         589 :     }
     223             : };
     224             : 
     225             : /************************************************************************/
     226             : /*                           netCDFGroup                                */
     227             : /************************************************************************/
     228             : 
     229             : class netCDFGroup final : public GDALGroup, public netCDFAttributeHolder
     230             : {
     231             :     std::shared_ptr<netCDFSharedResources> m_poShared;
     232             :     int m_gid = 0;
     233             :     CPLStringList m_aosStructuralInfo{};
     234             :     std::weak_ptr<netCDFGroup> m_poParent{};
     235             :     std::set<GDALGroup *> m_oSetGroups{};
     236             :     std::set<GDALDimension *> m_oSetDimensions{};
     237             :     std::set<GDALMDArray *> m_oSetArrays{};
     238             : 
     239         545 :     static std::string retrieveName(int gid)
     240             :     {
     241         545 :         CPLMutexHolderD(&hNCMutex);
     242         545 :         char szName[NC_MAX_NAME + 1] = {};
     243         545 :         NCDF_ERR(nc_inq_grpname(gid, szName));
     244        1090 :         return szName;
     245             :     }
     246             : 
     247          41 :     void RegisterSubGroup(GDALGroup *poSubGroup)
     248             :     {
     249          41 :         m_oSetGroups.insert(poSubGroup);
     250          41 :     }
     251             : 
     252          25 :     void UnRegisterSubGroup(GDALGroup *poSubGroup)
     253             :     {
     254          25 :         m_oSetGroups.erase(poSubGroup);
     255          25 :     }
     256             : 
     257             :   protected:
     258             :     friend class netCDFDimension;
     259             : 
     260         494 :     void RegisterDimension(GDALDimension *poDim)
     261             :     {
     262         494 :         m_oSetDimensions.insert(poDim);
     263         494 :     }
     264             : 
     265         228 :     void UnRegisterDimension(GDALDimension *poDim)
     266             :     {
     267         228 :         m_oSetDimensions.erase(poDim);
     268         228 :     }
     269             : 
     270             :     friend class netCDFVariable;
     271             : 
     272         516 :     void RegisterArray(GDALMDArray *poArray)
     273             :     {
     274         516 :         m_oSetArrays.insert(poArray);
     275         516 :     }
     276             : 
     277         320 :     void UnRegisterArray(GDALMDArray *poArray)
     278             :     {
     279         320 :         m_oSetArrays.erase(poArray);
     280         320 :     }
     281             : 
     282             :     void NotifyChildrenOfRenaming() override;
     283             : 
     284             :     netCDFGroup(const std::shared_ptr<netCDFSharedResources> &poShared,
     285             :                 int gid);
     286             : 
     287             :   public:
     288             :     ~netCDFGroup();
     289             : 
     290             :     static std::shared_ptr<netCDFGroup>
     291             :     Create(const std::shared_ptr<netCDFSharedResources> &poShared, int cdfid);
     292             : 
     293             :     static std::shared_ptr<netCDFGroup>
     294             :     Create(const std::shared_ptr<netCDFSharedResources> &poShared,
     295             :            const std::shared_ptr<netCDFGroup> &poParent, int nSubGroupId);
     296             : 
     297             :     std::vector<std::string>
     298             :     GetGroupNames(CSLConstList papszOptions) const override;
     299             :     std::shared_ptr<GDALGroup>
     300             :     OpenGroup(const std::string &osName,
     301             :               CSLConstList papszOptions = nullptr) const override;
     302             : 
     303             :     std::vector<std::string>
     304             :     GetMDArrayNames(CSLConstList papszOptions) const override;
     305             :     std::shared_ptr<GDALMDArray>
     306             :     OpenMDArray(const std::string &osName,
     307             :                 CSLConstList papszOptions) const override;
     308             : 
     309             :     std::vector<std::shared_ptr<GDALDimension>>
     310             :     GetDimensions(CSLConstList papszOptions) const override;
     311             : 
     312             :     std::shared_ptr<GDALAttribute>
     313             :     GetAttribute(const std::string &osName) const override;
     314             : 
     315             :     std::vector<std::shared_ptr<GDALAttribute>>
     316             :     GetAttributes(CSLConstList papszOptions) const override;
     317             : 
     318             :     std::shared_ptr<GDALGroup> CreateGroup(const std::string &osName,
     319             :                                            CSLConstList papszOptions) override;
     320             : 
     321             :     std::shared_ptr<GDALDimension>
     322             :     CreateDimension(const std::string &osName, const std::string &osType,
     323             :                     const std::string &osDirection, GUInt64 nSize,
     324             :                     CSLConstList papszOptions) override;
     325             : 
     326             :     std::shared_ptr<GDALMDArray> CreateMDArray(
     327             :         const std::string &osName,
     328             :         const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
     329             :         const GDALExtendedDataType &oDataType,
     330             :         CSLConstList papszOptions) override;
     331             : 
     332             :     std::shared_ptr<GDALAttribute>
     333             :     CreateAttribute(const std::string &osName,
     334             :                     const std::vector<GUInt64> &anDimensions,
     335             :                     const GDALExtendedDataType &oDataType,
     336             :                     CSLConstList papszOptions) override;
     337             : 
     338             :     bool DeleteAttribute(const std::string &osName,
     339             :                          CSLConstList papszOptions) override;
     340             : 
     341             :     CSLConstList GetStructuralInfo() const override;
     342             : 
     343             :     void ClearStatistics() override;
     344             : 
     345             :     bool Rename(const std::string &osNewName) override;
     346             : };
     347             : 
     348             : /************************************************************************/
     349             : /*                   netCDFVirtualGroupBySameDimension                  */
     350             : /************************************************************************/
     351             : 
     352             : class netCDFVirtualGroupBySameDimension final : public GDALGroup
     353             : {
     354             :     // the real group to which we derived this virtual group from
     355             :     std::shared_ptr<netCDFGroup> m_poGroup;
     356             :     std::string m_osDimName{};
     357             : 
     358             :   protected:
     359             :     netCDFVirtualGroupBySameDimension(
     360             :         const std::shared_ptr<netCDFGroup> &poGroup,
     361             :         const std::string &osDimName);
     362             : 
     363             :   public:
     364             :     static std::shared_ptr<netCDFVirtualGroupBySameDimension>
     365             :     Create(const std::shared_ptr<netCDFGroup> &poGroup,
     366             :            const std::string &osDimName);
     367             : 
     368             :     std::vector<std::string>
     369             :     GetMDArrayNames(CSLConstList papszOptions) const override;
     370             :     std::shared_ptr<GDALMDArray>
     371             :     OpenMDArray(const std::string &osName,
     372             :                 CSLConstList papszOptions) const override;
     373             : };
     374             : 
     375             : /************************************************************************/
     376             : /*                         netCDFDimension                              */
     377             : /************************************************************************/
     378             : 
     379             : class netCDFDimension final : public GDALDimension
     380             : {
     381             :     std::shared_ptr<netCDFSharedResources> m_poShared;
     382             :     int m_gid = 0;
     383             :     int m_dimid = 0;
     384             :     std::weak_ptr<netCDFGroup> m_poParent{};
     385             : 
     386        1673 :     static std::string retrieveName(int cfid, int dimid)
     387             :     {
     388        1673 :         CPLMutexHolderD(&hNCMutex);
     389        1673 :         char szName[NC_MAX_NAME + 1] = {};
     390        1673 :         NCDF_ERR(nc_inq_dimname(cfid, dimid, szName));
     391        3346 :         return szName;
     392             :     }
     393             : 
     394        1994 :     static GUInt64 retrieveSize(int cfid, int dimid)
     395             :     {
     396        1994 :         CPLMutexHolderD(&hNCMutex);
     397        1994 :         size_t nDimLen = 0;
     398        1994 :         NCDF_ERR(nc_inq_dimlen(cfid, dimid, &nDimLen));
     399        3988 :         return nDimLen;
     400             :     }
     401             : 
     402             :   public:
     403             :     netCDFDimension(const std::shared_ptr<netCDFSharedResources> &poShared,
     404             :                     int cfid, int dimid, size_t nForcedSize,
     405             :                     const std::string &osType);
     406             : 
     407             :     ~netCDFDimension();
     408             : 
     409             :     static std::shared_ptr<netCDFDimension>
     410             :     Create(const std::shared_ptr<netCDFSharedResources> &poShared,
     411             :            const std::shared_ptr<netCDFGroup> &poParent, int cfid, int dimid,
     412             :            size_t nForcedSize, const std::string &osType);
     413             : 
     414             :     std::shared_ptr<GDALMDArray> GetIndexingVariable() const override;
     415             : 
     416         199 :     int GetId() const
     417             :     {
     418         199 :         return m_dimid;
     419             :     }
     420             : 
     421         454 :     GUInt64 GetActualSize() const
     422             :     {
     423         454 :         return retrieveSize(m_gid, m_dimid);
     424             :     }
     425             : 
     426           3 :     void SetSize(GUInt64 nNewSize)
     427             :     {
     428           3 :         m_nSize = nNewSize;
     429           3 :     }
     430             : 
     431             :     bool Rename(const std::string &osNewName) override;
     432             : };
     433             : 
     434             : /************************************************************************/
     435             : /*                         netCDFAttribute                              */
     436             : /************************************************************************/
     437             : 
     438             : class netCDFVariable;
     439             : 
     440             : class netCDFAttribute final : public GDALAttribute
     441             : {
     442             :     std::shared_ptr<netCDFSharedResources> m_poShared;
     443             :     std::weak_ptr<netCDFAttributeHolder> m_poParent;
     444             :     int m_gid = 0;
     445             :     int m_varid = 0;
     446             :     size_t m_nTextLength = 0;
     447             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
     448             :     nc_type m_nAttType = NC_NAT;
     449             :     mutable std::unique_ptr<GDALExtendedDataType> m_dt;
     450             :     mutable bool m_bPerfectDataTypeMatch = false;
     451             : 
     452             :   protected:
     453             :     netCDFAttribute(const std::shared_ptr<netCDFSharedResources> &poShared,
     454             :                     int gid, int varid, const std::string &name);
     455             : 
     456             :     netCDFAttribute(const std::shared_ptr<netCDFSharedResources> &poShared,
     457             :                     int gid, int varid, const std::string &osName,
     458             :                     const std::vector<GUInt64> &anDimensions,
     459             :                     const GDALExtendedDataType &oDataType,
     460             :                     CSLConstList papszOptions);
     461             : 
     462             :     bool
     463             :     IRead(const GUInt64 *arrayStartIdx,    // array of size GetDimensionCount()
     464             :           const size_t *count,             // array of size GetDimensionCount()
     465             :           const GInt64 *arrayStep,         // step in elements
     466             :           const GPtrDiff_t *bufferStride,  // stride in elements
     467             :           const GDALExtendedDataType &bufferDataType,
     468             :           void *pDstBuffer) const override;
     469             : 
     470             :     bool
     471             :     IWrite(const GUInt64 *arrayStartIdx,    // array of size GetDimensionCount()
     472             :            const size_t *count,             // array of size GetDimensionCount()
     473             :            const GInt64 *arrayStep,         // step in elements
     474             :            const GPtrDiff_t *bufferStride,  // stride in elements
     475             :            const GDALExtendedDataType &bufferDataType,
     476             :            const void *pSrcBuffer) override;
     477             : 
     478             :   public:
     479             :     ~netCDFAttribute() override;
     480             : 
     481             :     static std::shared_ptr<netCDFAttribute>
     482             :     Create(const std::shared_ptr<netCDFSharedResources> &poShared,
     483             :            const std::shared_ptr<netCDFAttributeHolder> &poParent, int gid,
     484             :            int varid, const std::string &name);
     485             : 
     486             :     static std::shared_ptr<netCDFAttribute>
     487             :     Create(const std::shared_ptr<netCDFSharedResources> &poShared,
     488             :            const std::shared_ptr<netCDFAttributeHolder> &poParent, int gid,
     489             :            int varid, const std::string &osName,
     490             :            const std::vector<GUInt64> &anDimensions,
     491             :            const GDALExtendedDataType &oDataType, CSLConstList papszOptions);
     492             : 
     493             :     const std::vector<std::shared_ptr<GDALDimension>> &
     494        2358 :     GetDimensions() const override
     495             :     {
     496        2358 :         return m_dims;
     497             :     }
     498             : 
     499             :     const GDALExtendedDataType &GetDataType() const override;
     500             : 
     501             :     bool Rename(const std::string &osNewName) override;
     502             : };
     503             : 
     504             : /************************************************************************/
     505             : /*                         netCDFVariable                               */
     506             : /************************************************************************/
     507             : 
     508             : class netCDFVariable final : public GDALPamMDArray, public netCDFAttributeHolder
     509             : {
     510             :     std::shared_ptr<netCDFSharedResources> m_poShared;
     511             :     std::weak_ptr<netCDFGroup> m_poParent{};
     512             :     int m_gid = 0;
     513             :     int m_varid = 0;
     514             :     int m_nDims = 0;
     515             :     mutable std::vector<std::shared_ptr<GDALDimension>> m_dims{};
     516             :     mutable nc_type m_nVarType = NC_NAT;
     517             :     mutable std::unique_ptr<GDALExtendedDataType> m_dt;
     518             :     mutable bool m_bPerfectDataTypeMatch = false;
     519             :     mutable std::vector<GByte> m_abyNoData{};
     520             :     mutable bool m_bGetRawNoDataValueHasRun = false;
     521             :     bool m_bHasWrittenData = true;
     522             :     bool m_bUseDefaultFillAsNoData = false;
     523             :     std::string m_osUnit{};
     524             :     CPLStringList m_aosStructuralInfo{};
     525             :     mutable bool m_bSRSRead = false;
     526             :     mutable std::shared_ptr<OGRSpatialReference> m_poSRS{};
     527             :     bool m_bWriteGDALTags = true;
     528             :     size_t m_nTextLength = 0;
     529             :     mutable std::vector<GUInt64> m_cachedArrayStartIdx{};
     530             :     mutable std::vector<size_t> m_cachedCount{};
     531             :     mutable std::shared_ptr<GDALMDArray> m_poCachedArray{};
     532             : 
     533             :     void ConvertNCToGDAL(GByte *) const;
     534             :     void ConvertGDALToNC(GByte *) const;
     535             : 
     536             :     bool ReadOneElement(const GDALExtendedDataType &src_datatype,
     537             :                         const GDALExtendedDataType &bufferDataType,
     538             :                         const size_t *array_idx, void *pDstBuffer) const;
     539             : 
     540             :     bool WriteOneElement(const GDALExtendedDataType &dst_datatype,
     541             :                          const GDALExtendedDataType &bufferDataType,
     542             :                          const size_t *array_idx, const void *pSrcBuffer) const;
     543             : 
     544             :     template <typename BufferType, typename NCGetPutVar1FuncType,
     545             :               typename ReadOrWriteOneElementType>
     546             :     bool
     547             :     IReadWriteGeneric(const size_t *arrayStartIdx, const size_t *count,
     548             :                       const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
     549             :                       const GDALExtendedDataType &bufferDataType,
     550             :                       BufferType buffer, NCGetPutVar1FuncType NCGetPutVar1Func,
     551             :                       ReadOrWriteOneElementType ReadOrWriteOneElement) const;
     552             : 
     553             :     template <typename BufferType, typename NCGetPutVar1FuncType,
     554             :               typename NCGetPutVaraFuncType, typename NCGetPutVarmFuncType,
     555             :               typename ReadOrWriteOneElementType>
     556             :     bool IReadWrite(const bool bIsRead, const GUInt64 *arrayStartIdx,
     557             :                     const size_t *count, const GInt64 *arrayStep,
     558             :                     const GPtrDiff_t *bufferStride,
     559             :                     const GDALExtendedDataType &bufferDataType,
     560             :                     BufferType buffer, NCGetPutVar1FuncType NCGetPutVar1Func,
     561             :                     NCGetPutVaraFuncType NCGetPutVaraFunc,
     562             :                     NCGetPutVarmFuncType NCGetPutVarmFunc,
     563             :                     ReadOrWriteOneElementType ReadOrWriteOneElement) const;
     564             : 
     565             :   protected:
     566             :     netCDFVariable(const std::shared_ptr<netCDFSharedResources> &poShared,
     567             :                    int gid, int varid,
     568             :                    const std::vector<std::shared_ptr<GDALDimension>> &dims,
     569             :                    CSLConstList papszOptions);
     570             : 
     571             :     bool
     572             :     IRead(const GUInt64 *arrayStartIdx,    // array of size GetDimensionCount()
     573             :           const size_t *count,             // array of size GetDimensionCount()
     574             :           const GInt64 *arrayStep,         // step in elements
     575             :           const GPtrDiff_t *bufferStride,  // stride in elements
     576             :           const GDALExtendedDataType &bufferDataType,
     577             :           void *pDstBuffer) const override;
     578             : 
     579             :     bool
     580             :     IWrite(const GUInt64 *arrayStartIdx,    // array of size GetDimensionCount()
     581             :            const size_t *count,             // array of size GetDimensionCount()
     582             :            const GInt64 *arrayStep,         // step in elements
     583             :            const GPtrDiff_t *bufferStride,  // stride in elements
     584             :            const GDALExtendedDataType &bufferDataType,
     585             :            const void *pSrcBuffer) override;
     586             : 
     587             :     bool IAdviseRead(const GUInt64 *arrayStartIdx, const size_t *count,
     588             :                      CSLConstList papszOptions) const override;
     589             : 
     590             :     void NotifyChildrenOfRenaming() override;
     591             : 
     592             :     bool SetStatistics(bool bApproxStats, double dfMin, double dfMax,
     593             :                        double dfMean, double dfStdDev, GUInt64 nValidCount,
     594             :                        CSLConstList papszOptions) override;
     595             : 
     596             :   public:
     597             :     static std::shared_ptr<netCDFVariable>
     598        1586 :     Create(const std::shared_ptr<netCDFSharedResources> &poShared,
     599             :            const std::shared_ptr<netCDFGroup> &poParent, int gid, int varid,
     600             :            const std::vector<std::shared_ptr<GDALDimension>> &dims,
     601             :            CSLConstList papszOptions, bool bCreate)
     602             :     {
     603             :         auto var(std::shared_ptr<netCDFVariable>(
     604        1586 :             new netCDFVariable(poShared, gid, varid, dims, papszOptions)));
     605        1586 :         var->SetSelf(var);
     606        1586 :         var->m_poParent = poParent;
     607        1586 :         if (poParent)
     608         516 :             poParent->RegisterArray(var.get());
     609        1586 :         var->m_bHasWrittenData = !bCreate;
     610        1586 :         return var;
     611             :     }
     612             : 
     613             :     ~netCDFVariable() override;
     614             : 
     615        1184 :     void SetUseDefaultFillAsNoData(bool b)
     616             :     {
     617        1184 :         m_bUseDefaultFillAsNoData = b;
     618        1184 :     }
     619             : 
     620          34 :     bool IsWritable() const override
     621             :     {
     622          34 :         return !m_poShared->IsReadOnly();
     623             :     }
     624             : 
     625         559 :     const std::string &GetFilename() const override
     626             :     {
     627         559 :         return m_poShared->GetFilename();
     628             :     }
     629             : 
     630             :     const std::vector<std::shared_ptr<GDALDimension>> &
     631             :     GetDimensions() const override;
     632             : 
     633             :     const GDALExtendedDataType &GetDataType() const override;
     634             : 
     635             :     std::shared_ptr<GDALAttribute>
     636             :     GetAttribute(const std::string &osName) const override;
     637             : 
     638             :     std::vector<std::shared_ptr<GDALAttribute>>
     639             :     GetAttributes(CSLConstList papszOptions) const override;
     640             : 
     641             :     std::shared_ptr<GDALAttribute>
     642             :     CreateAttribute(const std::string &osName,
     643             :                     const std::vector<GUInt64> &anDimensions,
     644             :                     const GDALExtendedDataType &oDataType,
     645             :                     CSLConstList papszOptions) override;
     646             : 
     647             :     bool DeleteAttribute(const std::string &osName,
     648             :                          CSLConstList papszOptions) override;
     649             : 
     650             :     const void *GetRawNoDataValue() const override;
     651             : 
     652             :     bool SetRawNoDataValue(const void *) override;
     653             : 
     654             :     std::vector<GUInt64> GetBlockSize() const override;
     655             : 
     656             :     CSLConstList GetStructuralInfo() const override;
     657             : 
     658          73 :     const std::string &GetUnit() const override
     659             :     {
     660          73 :         return m_osUnit;
     661             :     }
     662             : 
     663             :     bool SetUnit(const std::string &osUnit) override;
     664             : 
     665             :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override;
     666             : 
     667             :     bool SetSpatialRef(const OGRSpatialReference *poSRS) override;
     668             : 
     669             :     double GetOffset(bool *pbHasOffset,
     670             :                      GDALDataType *peStorageType) const override;
     671             : 
     672             :     double GetScale(bool *pbHasScale,
     673             :                     GDALDataType *peStorageType) const override;
     674             : 
     675             :     bool SetOffset(double dfOffset, GDALDataType eStorageType) override;
     676             : 
     677             :     bool SetScale(double dfScale, GDALDataType eStorageType) override;
     678             : 
     679             :     std::vector<std::shared_ptr<GDALMDArray>>
     680             :     GetCoordinateVariables() const override;
     681             : 
     682             :     bool Resize(const std::vector<GUInt64> &anNewDimSizes,
     683             :                 CSLConstList) override;
     684             : 
     685         248 :     int GetGroupId() const
     686             :     {
     687         248 :         return m_gid;
     688             :     }
     689             : 
     690         224 :     int GetVarId() const
     691             :     {
     692         224 :         return m_varid;
     693             :     }
     694             : 
     695        4796 :     static std::string retrieveName(int gid, int varid)
     696             :     {
     697        4796 :         CPLMutexHolderD(&hNCMutex);
     698        4796 :         char szName[NC_MAX_NAME + 1] = {};
     699        4796 :         NCDF_ERR(nc_inq_varname(gid, varid, szName));
     700        9592 :         return szName;
     701             :     }
     702             : 
     703             :     bool Rename(const std::string &osNewName) override;
     704             : 
     705           4 :     std::shared_ptr<GDALGroup> GetRootGroup() const override
     706             :     {
     707           4 :         return netCDFGroup::Create(m_poShared, nullptr, m_gid);
     708             :     }
     709             : };
     710             : 
     711             : /************************************************************************/
     712             : /*                       ~netCDFSharedResources()                       */
     713             : /************************************************************************/
     714             : 
     715         224 : netCDFSharedResources::~netCDFSharedResources()
     716             : {
     717         448 :     CPLMutexHolderD(&hNCMutex);
     718             : 
     719         224 :     if (m_cdfid > 0)
     720             :     {
     721             : #ifdef NCDF_DEBUG
     722             :         CPLDebug("GDAL_netCDF", "calling nc_close( %d)", m_cdfid);
     723             : #endif
     724         221 :         int status = GDAL_nc_close(m_cdfid);
     725         221 :         NCDF_ERR(status);
     726             :     }
     727             : 
     728             : #ifdef ENABLE_UFFD
     729         224 :     if (m_pUffdCtx)
     730             :     {
     731           1 :         NETCDF_UFFD_UNMAP(m_pUffdCtx);
     732             :     }
     733             : #endif
     734             : 
     735         224 :     if (m_fpVSIMEM)
     736           4 :         VSIFCloseL(m_fpVSIMEM);
     737             : 
     738             : #ifdef ENABLE_NCDUMP
     739         224 :     if (m_bFileToDestroyAtClosing)
     740           0 :         VSIUnlink(m_osFilename);
     741             : #endif
     742         224 : }
     743             : 
     744             : /************************************************************************/
     745             : /*                     NCDFGetParentGroupName()                         */
     746             : /************************************************************************/
     747             : 
     748         545 : static CPLString NCDFGetParentGroupName(int gid)
     749             : {
     750         545 :     int nParentGID = 0;
     751         545 :     if (nc_inq_grp_parent(gid, &nParentGID) != NC_NOERR)
     752        1004 :         return std::string();
     753          43 :     return NCDFGetGroupFullName(nParentGID);
     754             : }
     755             : 
     756             : /************************************************************************/
     757             : /*                             netCDFGroup()                            */
     758             : /************************************************************************/
     759             : 
     760         545 : netCDFGroup::netCDFGroup(const std::shared_ptr<netCDFSharedResources> &poShared,
     761         545 :                          int gid)
     762        1090 :     : GDALGroup(NCDFGetParentGroupName(gid), retrieveName(gid)),
     763        1635 :       m_poShared(poShared), m_gid(gid)
     764             : {
     765         545 :     if (m_gid == m_poShared->GetCDFId())
     766             :     {
     767         502 :         int nFormat = 0;
     768         502 :         nc_inq_format(m_gid, &nFormat);
     769         502 :         if (nFormat == NC_FORMAT_CLASSIC)
     770             :         {
     771         144 :             m_aosStructuralInfo.SetNameValue("NC_FORMAT", "CLASSIC");
     772             :         }
     773             : #ifdef NC_FORMAT_64BIT_OFFSET
     774         358 :         else if (nFormat == NC_FORMAT_64BIT_OFFSET)
     775             :         {
     776           0 :             m_aosStructuralInfo.SetNameValue("NC_FORMAT", "64BIT_OFFSET");
     777             :         }
     778             : #endif
     779             : #ifdef NC_FORMAT_CDF5
     780         358 :         else if (nFormat == NC_FORMAT_CDF5)
     781             :         {
     782           0 :             m_aosStructuralInfo.SetNameValue("NC_FORMAT", "CDF5");
     783             :         }
     784             : #endif
     785         358 :         else if (nFormat == NC_FORMAT_NETCDF4)
     786             :         {
     787         357 :             m_aosStructuralInfo.SetNameValue("NC_FORMAT", "NETCDF4");
     788             :         }
     789           1 :         else if (nFormat == NC_FORMAT_NETCDF4_CLASSIC)
     790             :         {
     791           1 :             m_aosStructuralInfo.SetNameValue("NC_FORMAT", "NETCDF4_CLASSIC");
     792             :         }
     793             :     }
     794         545 : }
     795             : 
     796             : /************************************************************************/
     797             : /*                            ~netCDFGroup()                            */
     798             : /************************************************************************/
     799             : 
     800         818 : netCDFGroup::~netCDFGroup()
     801             : {
     802        1090 :     auto poParent = m_poParent.lock();
     803         545 :     if (poParent)
     804          25 :         poParent->UnRegisterSubGroup(this);
     805         818 : }
     806             : 
     807             : /************************************************************************/
     808             : /*                              Create()                                */
     809             : /************************************************************************/
     810             : 
     811             : /* static */
     812             : std::shared_ptr<netCDFGroup>
     813         273 : netCDFGroup::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
     814             :                     int cdfid)
     815             : {
     816             :     auto poGroup =
     817         273 :         std::shared_ptr<netCDFGroup>(new netCDFGroup(poShared, cdfid));
     818         273 :     poGroup->SetSelf(poGroup);
     819         273 :     return poGroup;
     820             : }
     821             : 
     822             : /************************************************************************/
     823             : /*                              Create()                                */
     824             : /************************************************************************/
     825             : 
     826             : /* static */
     827             : std::shared_ptr<netCDFGroup>
     828         106 : netCDFGroup::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
     829             :                     const std::shared_ptr<netCDFGroup> &poParent,
     830             :                     int nSubGroupId)
     831             : {
     832         106 :     auto poSubGroup = netCDFGroup::Create(poShared, nSubGroupId);
     833         106 :     poSubGroup->m_poParent = poParent;
     834         106 :     if (poParent)
     835          41 :         poParent->RegisterSubGroup(poSubGroup.get());
     836         106 :     return poSubGroup;
     837             : }
     838             : 
     839             : /************************************************************************/
     840             : /*                             CreateGroup()                            */
     841             : /************************************************************************/
     842             : 
     843             : std::shared_ptr<GDALGroup>
     844          16 : netCDFGroup::CreateGroup(const std::string &osName,
     845             :                          CSLConstList /*papszOptions*/)
     846             : {
     847          16 :     if (osName.empty())
     848             :     {
     849           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     850             :                  "Empty group name not supported");
     851           1 :         return nullptr;
     852             :     }
     853          30 :     CPLMutexHolderD(&hNCMutex);
     854          15 :     m_poShared->SetDefineMode(true);
     855          15 :     int nSubGroupId = -1;
     856          15 :     int ret = nc_def_grp(m_gid, osName.c_str(), &nSubGroupId);
     857          15 :     NCDF_ERR(ret);
     858          15 :     if (ret != NC_NOERR)
     859           3 :         return nullptr;
     860          12 :     return netCDFGroup::Create(
     861          24 :         m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
     862          12 :         nSubGroupId);
     863             : }
     864             : 
     865             : /************************************************************************/
     866             : /*                             CreateDimension()                        */
     867             : /************************************************************************/
     868             : 
     869             : std::shared_ptr<GDALDimension>
     870         138 : netCDFGroup::CreateDimension(const std::string &osName,
     871             :                              const std::string &osType, const std::string &,
     872             :                              GUInt64 nSize, CSLConstList papszOptions)
     873             : {
     874             :     const bool bUnlimited =
     875         138 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "UNLIMITED", "FALSE"));
     876             :     if (static_cast<size_t>(nSize) != nSize)
     877             :     {
     878             :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid size");
     879             :         return nullptr;
     880             :     }
     881         276 :     CPLMutexHolderD(&hNCMutex);
     882         138 :     m_poShared->SetDefineMode(true);
     883         138 :     int nDimId = -1;
     884         138 :     NCDF_ERR(nc_def_dim(m_gid, osName.c_str(),
     885             :                         static_cast<size_t>(bUnlimited ? 0 : nSize), &nDimId));
     886         138 :     if (nDimId < 0)
     887           3 :         return nullptr;
     888         135 :     return netCDFDimension::Create(
     889         270 :         m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
     890         135 :         m_gid, nDimId, static_cast<size_t>(nSize), osType);
     891             : }
     892             : 
     893             : /************************************************************************/
     894             : /*                     CreateOrGetComplexDataType()                     */
     895             : /************************************************************************/
     896             : 
     897           7 : static int CreateOrGetComplexDataType(int gid, GDALDataType eDT)
     898             : {
     899           7 :     const char *pszName = "";
     900           7 :     int nSubTypeId = NC_NAT;
     901           7 :     switch (eDT)
     902             :     {
     903           1 :         case GDT_CInt16:
     904           1 :             pszName = "ComplexInt16";
     905           1 :             nSubTypeId = NC_SHORT;
     906           1 :             break;
     907           1 :         case GDT_CInt32:
     908           1 :             pszName = "ComplexInt32";
     909           1 :             nSubTypeId = NC_INT;
     910           1 :             break;
     911           2 :         case GDT_CFloat32:
     912           2 :             pszName = "ComplexFloat32";
     913           2 :             nSubTypeId = NC_FLOAT;
     914           2 :             break;
     915           3 :         case GDT_CFloat64:
     916           3 :             pszName = "ComplexFloat64";
     917           3 :             nSubTypeId = NC_DOUBLE;
     918           3 :             break;
     919           0 :         default:
     920           0 :             CPLAssert(false);
     921             :             break;
     922             :     }
     923           7 :     int nTypeId = NC_NAT;
     924           7 :     if (nc_inq_typeid(gid, pszName, &nTypeId) == NC_NOERR)
     925             :     {
     926             :         // We could check that the type definition is really the one we want
     927           1 :         return nTypeId;
     928             :     }
     929           6 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
     930           6 :     NCDF_ERR(nc_def_compound(gid, nDTSize, pszName, &nTypeId));
     931           6 :     if (nTypeId != NC_NAT)
     932             :     {
     933           6 :         NCDF_ERR(nc_insert_compound(gid, nTypeId, "real", 0, nSubTypeId));
     934           6 :         NCDF_ERR(
     935             :             nc_insert_compound(gid, nTypeId, "imag", nDTSize / 2, nSubTypeId));
     936             :     }
     937           6 :     return nTypeId;
     938             : }
     939             : 
     940             : /************************************************************************/
     941             : /*                    CreateOrGetCompoundDataType()                     */
     942             : /************************************************************************/
     943             : 
     944             : static int CreateOrGetType(int gid, const GDALExtendedDataType &oType);
     945             : 
     946           3 : static int CreateOrGetCompoundDataType(int gid,
     947             :                                        const GDALExtendedDataType &oType)
     948             : {
     949           3 :     int nTypeId = NC_NAT;
     950           3 :     if (nc_inq_typeid(gid, oType.GetName().c_str(), &nTypeId) == NC_NOERR)
     951             :     {
     952             :         // We could check that the type definition is really the one we want
     953           2 :         return nTypeId;
     954             :     }
     955           1 :     NCDF_ERR(nc_def_compound(gid, oType.GetSize(), oType.GetName().c_str(),
     956             :                              &nTypeId));
     957           1 :     if (nTypeId != NC_NAT)
     958             :     {
     959           3 :         for (const auto &comp : oType.GetComponents())
     960             :         {
     961           2 :             int nSubTypeId = CreateOrGetType(gid, comp->GetType());
     962           2 :             if (nSubTypeId == NC_NAT)
     963           0 :                 return NC_NAT;
     964           2 :             NCDF_ERR(nc_insert_compound(gid, nTypeId, comp->GetName().c_str(),
     965             :                                         comp->GetOffset(), nSubTypeId));
     966             :         }
     967             :     }
     968           1 :     return nTypeId;
     969             : }
     970             : 
     971             : /************************************************************************/
     972             : /*                         CreateOrGetType()                            */
     973             : /************************************************************************/
     974             : 
     975         289 : static int CreateOrGetType(int gid, const GDALExtendedDataType &oType)
     976             : {
     977         289 :     int nTypeId = NC_NAT;
     978         289 :     const auto typeClass = oType.GetClass();
     979         289 :     if (typeClass == GEDTC_NUMERIC)
     980             :     {
     981         149 :         switch (oType.GetNumericDataType())
     982             :         {
     983          36 :             case GDT_Byte:
     984          36 :                 nTypeId = NC_UBYTE;
     985          36 :                 break;
     986           1 :             case GDT_Int8:
     987           1 :                 nTypeId = NC_BYTE;
     988           1 :                 break;
     989          11 :             case GDT_UInt16:
     990          11 :                 nTypeId = NC_USHORT;
     991          11 :                 break;
     992          10 :             case GDT_Int16:
     993          10 :                 nTypeId = NC_SHORT;
     994          10 :                 break;
     995           5 :             case GDT_UInt32:
     996           5 :                 nTypeId = NC_UINT;
     997           5 :                 break;
     998           2 :             case GDT_Int32:
     999           2 :                 nTypeId = NC_INT;
    1000           2 :                 break;
    1001           1 :             case GDT_UInt64:
    1002           1 :                 nTypeId = NC_UINT64;
    1003           1 :                 break;
    1004           1 :             case GDT_Int64:
    1005           1 :                 nTypeId = NC_INT64;
    1006           1 :                 break;
    1007           4 :             case GDT_Float32:
    1008           4 :                 nTypeId = NC_FLOAT;
    1009           4 :                 break;
    1010          71 :             case GDT_Float64:
    1011          71 :                 nTypeId = NC_DOUBLE;
    1012          71 :                 break;
    1013           7 :             case GDT_CInt16:
    1014             :             case GDT_CInt32:
    1015             :             case GDT_CFloat32:
    1016             :             case GDT_CFloat64:
    1017             :                 nTypeId =
    1018           7 :                     CreateOrGetComplexDataType(gid, oType.GetNumericDataType());
    1019           7 :                 break;
    1020           0 :             default:
    1021           0 :                 break;
    1022             :         }
    1023             :     }
    1024         140 :     else if (typeClass == GEDTC_STRING)
    1025             :     {
    1026         137 :         nTypeId = NC_STRING;
    1027             :     }
    1028           3 :     else if (typeClass == GEDTC_COMPOUND)
    1029             :     {
    1030           3 :         nTypeId = CreateOrGetCompoundDataType(gid, oType);
    1031             :     }
    1032         289 :     return nTypeId;
    1033             : }
    1034             : 
    1035             : /************************************************************************/
    1036             : /*                            CreateMDArray()                           */
    1037             : /************************************************************************/
    1038             : 
    1039         126 : std::shared_ptr<GDALMDArray> netCDFGroup::CreateMDArray(
    1040             :     const std::string &osName,
    1041             :     const std::vector<std::shared_ptr<GDALDimension>> &aoDimensions,
    1042             :     const GDALExtendedDataType &oType, CSLConstList papszOptions)
    1043             : {
    1044         126 :     if (osName.empty())
    1045             :     {
    1046           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1047             :                  "Empty array name not supported");
    1048           0 :         return nullptr;
    1049             :     }
    1050         252 :     CPLMutexHolderD(&hNCMutex);
    1051         126 :     m_poShared->SetDefineMode(true);
    1052         126 :     int nVarId = -1;
    1053         252 :     std::vector<int> anDimIds;
    1054         252 :     std::vector<std::shared_ptr<GDALDimension>> dims;
    1055         320 :     for (const auto &dim : aoDimensions)
    1056             :     {
    1057         194 :         int nDimId = -1;
    1058         194 :         auto netCDFDim = std::dynamic_pointer_cast<netCDFDimension>(dim);
    1059         194 :         if (netCDFDim)
    1060             :         {
    1061         191 :             nDimId = netCDFDim->GetId();
    1062             :         }
    1063             :         else
    1064             :         {
    1065           3 :             if (nc_inq_dimid(m_gid, dim->GetName().c_str(), &nDimId) ==
    1066             :                 NC_NOERR)
    1067             :             {
    1068           4 :                 netCDFDim = netCDFDimension::Create(
    1069           2 :                     m_poShared,
    1070           4 :                     std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
    1071           2 :                     m_gid, nDimId, 0, dim->GetType());
    1072           2 :                 if (netCDFDim->GetSize() != dim->GetSize())
    1073             :                 {
    1074           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1075             :                              "Dimension %s already exists, "
    1076             :                              "but with a size of " CPL_FRMT_GUIB,
    1077           1 :                              dim->GetName().c_str(),
    1078           1 :                              static_cast<GUIntBig>(netCDFDim->GetSize()));
    1079             :                 }
    1080             :             }
    1081             :             else
    1082             :             {
    1083             :                 netCDFDim =
    1084           2 :                     std::dynamic_pointer_cast<netCDFDimension>(CreateDimension(
    1085             :                         dim->GetName(), dim->GetType(), dim->GetDirection(),
    1086           1 :                         dim->GetSize(), nullptr));
    1087           1 :                 if (!netCDFDim)
    1088           0 :                     return nullptr;
    1089           1 :                 nDimId = netCDFDim->GetId();
    1090             :             }
    1091             :         }
    1092         194 :         anDimIds.push_back(nDimId);
    1093         194 :         dims.emplace_back(netCDFDim);
    1094             :     }
    1095         126 :     int nTypeId = CreateOrGetType(m_gid, oType);
    1096         126 :     if (nTypeId == NC_NAT)
    1097             :     {
    1098           0 :         CPLError(CE_Failure, CPLE_NotSupported, "Unhandled data type");
    1099           0 :         return nullptr;
    1100             :     }
    1101         126 :     const char *pszType = CSLFetchNameValueDef(papszOptions, "NC_TYPE", "");
    1102         183 :     if ((EQUAL(pszType, "") || EQUAL(pszType, "NC_CHAR")) && dims.size() == 1 &&
    1103         309 :         oType.GetClass() == GEDTC_STRING && oType.GetMaxStringLength() > 0)
    1104             :     {
    1105           2 :         nTypeId = NC_CHAR;
    1106             :         auto dimLength =
    1107           4 :             std::dynamic_pointer_cast<netCDFDimension>(CreateDimension(
    1108           4 :                 aoDimensions[0]->GetName() + "_length", std::string(),
    1109           4 :                 std::string(), oType.GetMaxStringLength(), nullptr));
    1110           2 :         if (!dimLength)
    1111           0 :             return nullptr;
    1112           2 :         anDimIds.push_back(dimLength->GetId());
    1113             :     }
    1114         124 :     else if (EQUAL(pszType, "NC_BYTE"))
    1115           1 :         nTypeId = NC_BYTE;
    1116         123 :     else if (EQUAL(pszType, "NC_INT64"))
    1117           1 :         nTypeId = NC_INT64;
    1118         122 :     else if (EQUAL(pszType, "NC_UINT64"))
    1119           1 :         nTypeId = NC_UINT64;
    1120         126 :     NCDF_ERR(nc_def_var(m_gid, osName.c_str(), nTypeId,
    1121             :                         static_cast<int>(anDimIds.size()),
    1122             :                         anDimIds.empty() ? nullptr : anDimIds.data(), &nVarId));
    1123         126 :     if (nVarId < 0)
    1124           3 :         return nullptr;
    1125             : 
    1126         123 :     const char *pszBlockSize = CSLFetchNameValue(papszOptions, "BLOCKSIZE");
    1127         133 :     if (pszBlockSize &&
    1128             :         /* ignore for now BLOCKSIZE for 1-dim string variables created as 2-dim
    1129             :          */
    1130          10 :         anDimIds.size() == aoDimensions.size())
    1131             :     {
    1132          10 :         auto aszTokens(CPLStringList(CSLTokenizeString2(pszBlockSize, ",", 0)));
    1133          10 :         if (static_cast<size_t>(aszTokens.size()) != aoDimensions.size())
    1134             :         {
    1135           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1136             :                      "Invalid number of values in BLOCKSIZE");
    1137           0 :             return nullptr;
    1138             :         }
    1139          10 :         if (!aoDimensions.empty())
    1140             :         {
    1141           9 :             std::vector<size_t> anChunkSize(aoDimensions.size());
    1142          27 :             for (size_t i = 0; i < anChunkSize.size(); ++i)
    1143             :             {
    1144          18 :                 anChunkSize[i] =
    1145          18 :                     static_cast<size_t>(CPLAtoGIntBig(aszTokens[i]));
    1146             :             }
    1147             :             int ret =
    1148           9 :                 nc_def_var_chunking(m_gid, nVarId, NC_CHUNKED, &anChunkSize[0]);
    1149           9 :             NCDF_ERR(ret);
    1150           9 :             if (ret != NC_NOERR)
    1151           0 :                 return nullptr;
    1152             :         }
    1153             :     }
    1154             : 
    1155         123 :     const char *pszCompress = CSLFetchNameValue(papszOptions, "COMPRESS");
    1156         123 :     if (pszCompress && EQUAL(pszCompress, "DEFLATE"))
    1157             :     {
    1158          30 :         int nZLevel = NCDF_DEFLATE_LEVEL;
    1159          30 :         const char *pszZLevel = CSLFetchNameValue(papszOptions, "ZLEVEL");
    1160          30 :         if (pszZLevel != nullptr)
    1161             :         {
    1162           2 :             nZLevel = atoi(pszZLevel);
    1163           2 :             if (!(nZLevel >= 1 && nZLevel <= 9))
    1164             :             {
    1165           0 :                 CPLError(CE_Warning, CPLE_IllegalArg,
    1166             :                          "ZLEVEL=%s value not recognised, ignoring.",
    1167             :                          pszZLevel);
    1168           0 :                 nZLevel = NCDF_DEFLATE_LEVEL;
    1169             :             }
    1170             :         }
    1171          30 :         int ret = nc_def_var_deflate(m_gid, nVarId, TRUE /* shuffle */,
    1172             :                                      TRUE /* deflate on */, nZLevel);
    1173          30 :         NCDF_ERR(ret);
    1174          30 :         if (ret != NC_NOERR)
    1175           0 :             return nullptr;
    1176             :     }
    1177             : 
    1178         123 :     const char *pszFilter = CSLFetchNameValue(papszOptions, "FILTER");
    1179         123 :     if (pszFilter)
    1180             :     {
    1181             : #ifdef NC_EFILTER
    1182             :         const auto aosTokens(
    1183           0 :             CPLStringList(CSLTokenizeString2(pszFilter, ",", 0)));
    1184           0 :         if (!aosTokens.empty())
    1185             :         {
    1186             :             const unsigned nFilterId =
    1187           0 :                 static_cast<unsigned>(CPLAtoGIntBig(aosTokens[0]));
    1188           0 :             std::vector<unsigned> anParams;
    1189           0 :             for (int i = 1; i < aosTokens.size(); ++i)
    1190             :             {
    1191           0 :                 anParams.push_back(
    1192           0 :                     static_cast<unsigned>(CPLAtoGIntBig(aosTokens[i])));
    1193             :             }
    1194           0 :             int ret = nc_def_var_filter(m_gid, nVarId, nFilterId,
    1195           0 :                                         anParams.size(), anParams.data());
    1196           0 :             NCDF_ERR(ret);
    1197           0 :             if (ret != NC_NOERR)
    1198           0 :                 return nullptr;
    1199             :         }
    1200             : #else
    1201             :         CPLError(CE_Failure, CPLE_NotSupported,
    1202             :                  "netCDF 4.6 or later needed for FILTER option");
    1203             :         return nullptr;
    1204             : #endif
    1205             :     }
    1206             : 
    1207             :     const bool bChecksum =
    1208         123 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "CHECKSUM", "FALSE"));
    1209         123 :     if (bChecksum)
    1210             :     {
    1211           1 :         int ret = nc_def_var_fletcher32(m_gid, nVarId, TRUE);
    1212           1 :         NCDF_ERR(ret);
    1213           1 :         if (ret != NC_NOERR)
    1214           0 :             return nullptr;
    1215             :     }
    1216             : 
    1217         123 :     return netCDFVariable::Create(
    1218         246 :         m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
    1219         123 :         m_gid, nVarId, dims, papszOptions, true);
    1220             : }
    1221             : 
    1222             : /************************************************************************/
    1223             : /*                          CreateAttribute()                           */
    1224             : /************************************************************************/
    1225             : 
    1226          76 : std::shared_ptr<GDALAttribute> netCDFGroup::CreateAttribute(
    1227             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
    1228             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
    1229             : {
    1230         152 :     return netCDFAttribute::Create(
    1231         228 :         m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
    1232         152 :         m_gid, NC_GLOBAL, osName, anDimensions, oDataType, papszOptions);
    1233             : }
    1234             : 
    1235             : /************************************************************************/
    1236             : /*                         DeleteAttribute()                            */
    1237             : /************************************************************************/
    1238             : 
    1239           2 : bool netCDFGroup::DeleteAttribute(const std::string &osName,
    1240             :                                   CSLConstList /*papszOptions*/)
    1241             : {
    1242           4 :     CPLMutexHolderD(&hNCMutex);
    1243           2 :     m_poShared->SetDefineMode(true);
    1244             : 
    1245           2 :     int ret = nc_del_att(m_gid, NC_GLOBAL, osName.c_str());
    1246           2 :     NCDF_ERR(ret);
    1247           2 :     if (ret != NC_NOERR)
    1248           1 :         return false;
    1249             : 
    1250           1 :     auto it = m_oMapAttributes.find(osName);
    1251           1 :     if (it != m_oMapAttributes.end())
    1252             :     {
    1253           1 :         it->second->Deleted();
    1254           1 :         m_oMapAttributes.erase(it);
    1255             :     }
    1256             : 
    1257           1 :     return true;
    1258             : }
    1259             : 
    1260             : /************************************************************************/
    1261             : /*                            GetGroupNames()                           */
    1262             : /************************************************************************/
    1263             : 
    1264             : std::vector<std::string>
    1265          22 : netCDFGroup::GetGroupNames(CSLConstList papszOptions) const
    1266             : {
    1267          44 :     CPLMutexHolderD(&hNCMutex);
    1268          22 :     int nSubGroups = 0;
    1269          22 :     NCDF_ERR(nc_inq_grps(m_gid, &nSubGroups, nullptr));
    1270          22 :     if (nSubGroups == 0)
    1271             :     {
    1272          16 :         if (EQUAL(CSLFetchNameValueDef(papszOptions, "GROUP_BY", ""),
    1273             :                   "SAME_DIMENSION"))
    1274             :         {
    1275           4 :             std::vector<std::string> names;
    1276           4 :             std::set<std::string> oSetDimNames;
    1277          30 :             for (const auto &osArrayName : GetMDArrayNames(nullptr))
    1278             :             {
    1279          56 :                 const auto poArray = OpenMDArray(osArrayName, nullptr);
    1280          28 :                 const auto &apoDims = poArray->GetDimensions();
    1281          28 :                 if (apoDims.size() == 1)
    1282             :                 {
    1283          28 :                     const auto &osDimName = apoDims[0]->GetName();
    1284          28 :                     if (oSetDimNames.find(osDimName) == oSetDimNames.end())
    1285             :                     {
    1286           6 :                         oSetDimNames.insert(osDimName);
    1287           6 :                         names.emplace_back(osDimName);
    1288             :                     }
    1289             :                 }
    1290             :             }
    1291           2 :             return names;
    1292             :         }
    1293          14 :         return {};
    1294             :     }
    1295          12 :     std::vector<int> anSubGroupdsIds(nSubGroups);
    1296           6 :     NCDF_ERR(nc_inq_grps(m_gid, nullptr, &anSubGroupdsIds[0]));
    1297          12 :     std::vector<std::string> names;
    1298           6 :     names.reserve(nSubGroups);
    1299          15 :     for (const auto &subgid : anSubGroupdsIds)
    1300             :     {
    1301           9 :         char szName[NC_MAX_NAME + 1] = {};
    1302           9 :         NCDF_ERR(nc_inq_grpname(subgid, szName));
    1303           9 :         if (GetFullName() == "/" && EQUAL(szName, "METADATA"))
    1304             :         {
    1305           4 :             const auto poMetadata = OpenGroup(szName);
    1306           2 :             if (poMetadata && poMetadata->OpenGroup("ISO_METADATA"))
    1307           2 :                 continue;
    1308             :         }
    1309           7 :         names.emplace_back(szName);
    1310             :     }
    1311           6 :     return names;
    1312             : }
    1313             : 
    1314             : /************************************************************************/
    1315             : /*                             OpenGroup()                              */
    1316             : /************************************************************************/
    1317             : 
    1318             : std::shared_ptr<GDALGroup>
    1319          74 : netCDFGroup::OpenGroup(const std::string &osName,
    1320             :                        CSLConstList papszOptions) const
    1321             : {
    1322         148 :     CPLMutexHolderD(&hNCMutex);
    1323          74 :     int nSubGroups = 0;
    1324             :     // This is weird but nc_inq_grp_ncid() succeeds on a single group file.
    1325          74 :     NCDF_ERR(nc_inq_grps(m_gid, &nSubGroups, nullptr));
    1326          74 :     if (nSubGroups == 0)
    1327             :     {
    1328          20 :         if (EQUAL(CSLFetchNameValueDef(papszOptions, "GROUP_BY", ""),
    1329             :                   "SAME_DIMENSION"))
    1330             :         {
    1331           1 :             const auto oCandidateGroupNames = GetGroupNames(papszOptions);
    1332           1 :             for (const auto &osCandidateGroupName : oCandidateGroupNames)
    1333             :             {
    1334           1 :                 if (osCandidateGroupName == osName)
    1335             :                 {
    1336           1 :                     auto poThisGroup = netCDFGroup::Create(m_poShared, m_gid);
    1337           2 :                     return netCDFVirtualGroupBySameDimension::Create(
    1338           1 :                         poThisGroup, osName);
    1339             :                 }
    1340             :             }
    1341             :         }
    1342          19 :         return nullptr;
    1343             :     }
    1344          54 :     int nSubGroupId = 0;
    1345          83 :     if (nc_inq_grp_ncid(m_gid, osName.c_str(), &nSubGroupId) != NC_NOERR ||
    1346          29 :         nSubGroupId <= 0)
    1347          25 :         return nullptr;
    1348          29 :     return netCDFGroup::Create(
    1349          58 :         m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
    1350          29 :         nSubGroupId);
    1351             : }
    1352             : 
    1353             : /************************************************************************/
    1354             : /*                         GetMDArrayNames()                            */
    1355             : /************************************************************************/
    1356             : 
    1357             : std::vector<std::string>
    1358         310 : netCDFGroup::GetMDArrayNames(CSLConstList papszOptions) const
    1359             : {
    1360         620 :     CPLMutexHolderD(&hNCMutex);
    1361         310 :     int nVars = 0;
    1362         310 :     NCDF_ERR(nc_inq_nvars(m_gid, &nVars));
    1363         310 :     if (nVars == 0)
    1364          11 :         return {};
    1365         598 :     std::vector<int> anVarIds(nVars);
    1366         299 :     NCDF_ERR(nc_inq_varids(m_gid, nullptr, &anVarIds[0]));
    1367         598 :     std::vector<std::string> names;
    1368         299 :     names.reserve(nVars);
    1369             :     const bool bAll =
    1370         299 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
    1371             :     const bool bZeroDim =
    1372         597 :         bAll ||
    1373         298 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ZERO_DIM", "NO"));
    1374             :     const bool bCoordinates =
    1375         299 :         bAll || CPLTestBool(CSLFetchNameValueDef(papszOptions,
    1376         299 :                                                  "SHOW_COORDINATES", "YES"));
    1377             :     const bool bBounds =
    1378         597 :         bAll ||
    1379         298 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_BOUNDS", "YES"));
    1380             :     const bool bIndexing =
    1381         597 :         bAll ||
    1382         298 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_INDEXING", "YES"));
    1383             :     const bool bTime =
    1384         597 :         bAll ||
    1385         298 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_TIME", "YES"));
    1386         598 :     std::set<std::string> ignoreList;
    1387         299 :     if (!bCoordinates || !bBounds)
    1388             :     {
    1389          12 :         for (const auto &varid : anVarIds)
    1390             :         {
    1391           9 :             char **papszTokens = nullptr;
    1392           9 :             if (!bCoordinates)
    1393             :             {
    1394           5 :                 char *pszTemp = nullptr;
    1395           5 :                 if (NCDFGetAttr(m_gid, varid, "coordinates", &pszTemp) ==
    1396             :                     CE_None)
    1397           1 :                     papszTokens = NCDFTokenizeCoordinatesAttribute(pszTemp);
    1398           5 :                 CPLFree(pszTemp);
    1399             :             }
    1400           9 :             if (!bBounds)
    1401             :             {
    1402           4 :                 char *pszTemp = nullptr;
    1403           4 :                 if (NCDFGetAttr(m_gid, varid, "bounds", &pszTemp) == CE_None &&
    1404           4 :                     pszTemp != nullptr && !EQUAL(pszTemp, ""))
    1405           2 :                     papszTokens = CSLAddString(papszTokens, pszTemp);
    1406           4 :                 CPLFree(pszTemp);
    1407             :             }
    1408          15 :             for (char **iter = papszTokens; iter && iter[0]; ++iter)
    1409           6 :                 ignoreList.insert(*iter);
    1410           9 :             CSLDestroy(papszTokens);
    1411             :         }
    1412             :     }
    1413             : 
    1414         299 :     const bool bGroupBySameDimension = EQUAL(
    1415             :         CSLFetchNameValueDef(papszOptions, "GROUP_BY", ""), "SAME_DIMENSION");
    1416        1517 :     for (const auto &varid : anVarIds)
    1417             :     {
    1418        1218 :         int nVarDims = 0;
    1419        1218 :         NCDF_ERR(nc_inq_varndims(m_gid, varid, &nVarDims));
    1420        1218 :         if (nVarDims == 0 && !bZeroDim)
    1421             :         {
    1422         145 :             continue;
    1423             :         }
    1424        1091 :         if (nVarDims == 1 && bGroupBySameDimension)
    1425             :         {
    1426          14 :             continue;
    1427             :         }
    1428             : 
    1429        1077 :         char szName[NC_MAX_NAME + 1] = {};
    1430        1077 :         NCDF_ERR(nc_inq_varname(m_gid, varid, szName));
    1431        1077 :         if (!bIndexing && nVarDims == 1)
    1432             :         {
    1433           1 :             int nDimId = 0;
    1434           1 :             NCDF_ERR(nc_inq_vardimid(m_gid, varid, &nDimId));
    1435           1 :             char szDimName[NC_MAX_NAME + 1] = {};
    1436           1 :             NCDF_ERR(nc_inq_dimname(m_gid, nDimId, szDimName));
    1437           1 :             if (strcmp(szDimName, szName) == 0)
    1438             :             {
    1439           1 :                 continue;
    1440             :             }
    1441             :         }
    1442             : 
    1443        1076 :         if (!bTime)
    1444             :         {
    1445          14 :             char *pszTemp = nullptr;
    1446          14 :             bool bSkip = false;
    1447          14 :             if (NCDFGetAttr(m_gid, varid, "standard_name", &pszTemp) == CE_None)
    1448             :             {
    1449          10 :                 bSkip = pszTemp && strcmp(pszTemp, "time") == 0;
    1450             :             }
    1451          14 :             CPLFree(pszTemp);
    1452          14 :             if (bSkip)
    1453             :             {
    1454           3 :                 continue;
    1455             :             }
    1456             :         }
    1457             : 
    1458        1073 :         if (ignoreList.find(szName) == ignoreList.end())
    1459             :         {
    1460        1067 :             names.emplace_back(szName);
    1461             :         }
    1462             :     }
    1463         299 :     return names;
    1464             : }
    1465             : 
    1466             : /************************************************************************/
    1467             : /*                              Rename()                                */
    1468             : /************************************************************************/
    1469             : 
    1470           5 : bool netCDFGroup::Rename(const std::string &osNewName)
    1471             : {
    1472           5 :     if (m_poShared->IsReadOnly())
    1473             :     {
    1474           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    1475             :                  "Rename() not supported on read-only file");
    1476           1 :         return false;
    1477             :     }
    1478           4 :     if (osNewName.empty())
    1479             :     {
    1480           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
    1481           1 :         return false;
    1482             :     }
    1483           3 :     if (m_osName == "/")
    1484             :     {
    1485           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Cannot rename root group");
    1486           1 :         return false;
    1487             :     }
    1488             : 
    1489           4 :     CPLMutexHolderD(&hNCMutex);
    1490           2 :     m_poShared->SetDefineMode(true);
    1491             : 
    1492           2 :     int ret = nc_rename_grp(m_gid, osNewName.c_str());
    1493           2 :     NCDF_ERR(ret);
    1494           2 :     if (ret != NC_NOERR)
    1495           1 :         return false;
    1496             : 
    1497           1 :     BaseRename(osNewName);
    1498             : 
    1499           1 :     return true;
    1500             : }
    1501             : 
    1502             : /************************************************************************/
    1503             : /*                       NotifyChildrenOfRenaming()                     */
    1504             : /************************************************************************/
    1505             : 
    1506           2 : void netCDFGroup::NotifyChildrenOfRenaming()
    1507             : {
    1508           3 :     for (const auto poSubGroup : m_oSetGroups)
    1509           1 :         poSubGroup->ParentRenamed(m_osFullName);
    1510             : 
    1511           3 :     for (const auto poDim : m_oSetDimensions)
    1512           1 :         poDim->ParentRenamed(m_osFullName);
    1513             : 
    1514           4 :     for (const auto poArray : m_oSetArrays)
    1515           2 :         poArray->ParentRenamed(m_osFullName);
    1516             : 
    1517           4 :     for (const auto &iter : m_oMapAttributes)
    1518           2 :         iter.second->ParentRenamed(m_osFullName);
    1519           2 : }
    1520             : 
    1521             : /************************************************************************/
    1522             : /*                           OpenMDArray()                              */
    1523             : /************************************************************************/
    1524             : 
    1525             : std::shared_ptr<GDALMDArray>
    1526        1209 : netCDFGroup::OpenMDArray(const std::string &osName,
    1527             :                          CSLConstList papszOptions) const
    1528             : {
    1529        2418 :     CPLMutexHolderD(&hNCMutex);
    1530        1209 :     int nVarId = 0;
    1531        1209 :     if (nc_inq_varid(m_gid, osName.c_str(), &nVarId) != NC_NOERR)
    1532          25 :         return nullptr;
    1533             :     auto poVar = netCDFVariable::Create(
    1534        2368 :         m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
    1535        1184 :         m_gid, nVarId, std::vector<std::shared_ptr<GDALDimension>>(), nullptr,
    1536        3552 :         false);
    1537        1184 :     if (poVar)
    1538             :     {
    1539        1184 :         poVar->SetUseDefaultFillAsNoData(CPLTestBool(CSLFetchNameValueDef(
    1540             :             papszOptions, "USE_DEFAULT_FILL_AS_NODATA", "NO")));
    1541             :     }
    1542        1184 :     return poVar;
    1543             : }
    1544             : 
    1545             : /************************************************************************/
    1546             : /*                         GetDimensions()                              */
    1547             : /************************************************************************/
    1548             : 
    1549             : std::vector<std::shared_ptr<GDALDimension>>
    1550          32 : netCDFGroup::GetDimensions(CSLConstList) const
    1551             : {
    1552          64 :     CPLMutexHolderD(&hNCMutex);
    1553          32 :     int nbDims = 0;
    1554          32 :     NCDF_ERR(nc_inq_ndims(m_gid, &nbDims));
    1555          32 :     if (nbDims == 0)
    1556           1 :         return {};
    1557          62 :     std::vector<int> dimids(nbDims);
    1558          31 :     NCDF_ERR(nc_inq_dimids(m_gid, &nbDims, &dimids[0], FALSE));
    1559          62 :     std::vector<std::shared_ptr<GDALDimension>> res;
    1560          96 :     for (int i = 0; i < nbDims; i++)
    1561             :     {
    1562         130 :         auto poCachedDim = m_poShared->GetCachedDimension(dimids[i]);
    1563          65 :         if (poCachedDim == nullptr)
    1564             :         {
    1565          58 :             poCachedDim = netCDFDimension::Create(
    1566          58 :                 m_poShared,
    1567         116 :                 std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()), m_gid,
    1568         174 :                 dimids[i], 0, std::string());
    1569          58 :             m_poShared->CacheDimension(dimids[i], poCachedDim);
    1570             :         }
    1571          65 :         res.emplace_back(poCachedDim);
    1572             :     }
    1573          31 :     return res;
    1574             : }
    1575             : 
    1576             : /************************************************************************/
    1577             : /*                         GetAttribute()                               */
    1578             : /************************************************************************/
    1579             : 
    1580             : static const char *const apszJSONMDKeys[] = {
    1581             :     "ISO_METADATA",  "ESA_METADATA",        "EOP_METADATA",
    1582             :     "QA_STATISTICS", "GRANULE_DESCRIPTION", "ALGORITHM_SETTINGS"};
    1583             : 
    1584             : std::shared_ptr<GDALAttribute>
    1585          10 : netCDFGroup::GetAttribute(const std::string &osName) const
    1586             : {
    1587          20 :     CPLMutexHolderD(&hNCMutex);
    1588          10 :     int nAttId = -1;
    1589          10 :     if (nc_inq_attid(m_gid, NC_GLOBAL, osName.c_str(), &nAttId) != NC_NOERR)
    1590             :     {
    1591           2 :         if (GetFullName() == "/")
    1592             :         {
    1593           8 :             for (const char *key : apszJSONMDKeys)
    1594             :             {
    1595           7 :                 if (osName == key)
    1596             :                 {
    1597           2 :                     auto poMetadata = OpenGroup("METADATA");
    1598           1 :                     if (poMetadata)
    1599             :                     {
    1600             :                         auto poSubMetadata =
    1601             :                             std::dynamic_pointer_cast<netCDFGroup>(
    1602           2 :                                 poMetadata->OpenGroup(key));
    1603           1 :                         if (poSubMetadata)
    1604             :                         {
    1605             :                             const auto osJson =
    1606           1 :                                 NCDFReadMetadataAsJson(poSubMetadata->m_gid);
    1607           2 :                             return std::make_shared<GDALAttributeString>(
    1608           3 :                                 GetFullName(), key, osJson, GEDTST_JSON);
    1609             :                         }
    1610             :                     }
    1611           0 :                     break;
    1612             :                 }
    1613             :             }
    1614             :         }
    1615           1 :         return nullptr;
    1616             :     }
    1617          16 :     return netCDFAttribute::Create(
    1618          24 :         m_poShared, std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()),
    1619          16 :         m_gid, NC_GLOBAL, osName);
    1620             : }
    1621             : 
    1622             : /************************************************************************/
    1623             : /*                         GetAttributes()                              */
    1624             : /************************************************************************/
    1625             : 
    1626             : std::vector<std::shared_ptr<GDALAttribute>>
    1627          37 : netCDFGroup::GetAttributes(CSLConstList) const
    1628             : {
    1629          74 :     CPLMutexHolderD(&hNCMutex);
    1630          37 :     std::vector<std::shared_ptr<GDALAttribute>> res;
    1631          37 :     int nbAttr = 0;
    1632          37 :     NCDF_ERR(nc_inq_varnatts(m_gid, NC_GLOBAL, &nbAttr));
    1633          37 :     res.reserve(nbAttr);
    1634         147 :     for (int i = 0; i < nbAttr; i++)
    1635             :     {
    1636             :         char szAttrName[NC_MAX_NAME + 1];
    1637         110 :         szAttrName[0] = 0;
    1638         110 :         NCDF_ERR(nc_inq_attname(m_gid, NC_GLOBAL, i, szAttrName));
    1639         110 :         if (!EQUAL(szAttrName, "_NCProperties"))
    1640             :         {
    1641         440 :             res.emplace_back(netCDFAttribute::Create(
    1642         110 :                 m_poShared,
    1643         220 :                 std::dynamic_pointer_cast<netCDFGroup>(m_pSelf.lock()), m_gid,
    1644         110 :                 NC_GLOBAL, szAttrName));
    1645             :         }
    1646             :     }
    1647             : 
    1648          37 :     if (GetFullName() == "/")
    1649             :     {
    1650          96 :         auto poMetadata = OpenGroup("METADATA");
    1651          32 :         if (poMetadata)
    1652             :         {
    1653          14 :             for (const char *key : apszJSONMDKeys)
    1654             :             {
    1655             :                 auto poSubMetadata = std::dynamic_pointer_cast<netCDFGroup>(
    1656          36 :                     poMetadata->OpenGroup(key));
    1657          12 :                 if (poSubMetadata)
    1658             :                 {
    1659             :                     const auto osJson =
    1660           2 :                         NCDFReadMetadataAsJson(poSubMetadata->m_gid);
    1661           4 :                     res.emplace_back(std::make_shared<GDALAttributeString>(
    1662           6 :                         GetFullName(), key, osJson, GEDTST_JSON));
    1663             :                 }
    1664             :             }
    1665             :         }
    1666             :     }
    1667             : 
    1668          74 :     return res;
    1669             : }
    1670             : 
    1671             : /************************************************************************/
    1672             : /*                         GetStructuralInfo()                          */
    1673             : /************************************************************************/
    1674             : 
    1675           9 : CSLConstList netCDFGroup::GetStructuralInfo() const
    1676             : {
    1677           9 :     return m_aosStructuralInfo.List();
    1678             : }
    1679             : 
    1680             : /************************************************************************/
    1681             : /*                          ClearStatistics()                           */
    1682             : /************************************************************************/
    1683             : 
    1684           1 : void netCDFGroup::ClearStatistics()
    1685             : {
    1686           1 :     m_poShared->GetPAM()->ClearStatistics();
    1687           1 : }
    1688             : 
    1689             : /************************************************************************/
    1690             : /*                   netCDFVirtualGroupBySameDimension()                */
    1691             : /************************************************************************/
    1692             : 
    1693           1 : netCDFVirtualGroupBySameDimension::netCDFVirtualGroupBySameDimension(
    1694           1 :     const std::shared_ptr<netCDFGroup> &poGroup, const std::string &osDimName)
    1695           1 :     : GDALGroup(poGroup->GetName(), osDimName), m_poGroup(poGroup),
    1696           2 :       m_osDimName(osDimName)
    1697             : {
    1698           1 : }
    1699             : 
    1700             : /************************************************************************/
    1701             : /*                              Create()                                */
    1702             : /************************************************************************/
    1703             : 
    1704             : /* static */ std::shared_ptr<netCDFVirtualGroupBySameDimension>
    1705           1 : netCDFVirtualGroupBySameDimension::Create(
    1706             :     const std::shared_ptr<netCDFGroup> &poGroup, const std::string &osDimName)
    1707             : {
    1708             :     auto poNewGroup = std::shared_ptr<netCDFVirtualGroupBySameDimension>(
    1709           1 :         new netCDFVirtualGroupBySameDimension(poGroup, osDimName));
    1710           1 :     poNewGroup->SetSelf(poNewGroup);
    1711           1 :     return poNewGroup;
    1712             : }
    1713             : 
    1714             : /************************************************************************/
    1715             : /*                         GetMDArrayNames()                            */
    1716             : /************************************************************************/
    1717             : 
    1718             : std::vector<std::string>
    1719           1 : netCDFVirtualGroupBySameDimension::GetMDArrayNames(CSLConstList) const
    1720             : {
    1721           2 :     const auto srcNames = m_poGroup->GetMDArrayNames(nullptr);
    1722           1 :     std::vector<std::string> names;
    1723          15 :     for (const auto &srcName : srcNames)
    1724             :     {
    1725          28 :         auto poArray = m_poGroup->OpenMDArray(srcName, nullptr);
    1726          14 :         if (poArray)
    1727             :         {
    1728          14 :             const auto &apoArrayDims = poArray->GetDimensions();
    1729          28 :             if (apoArrayDims.size() == 1 &&
    1730          14 :                 apoArrayDims[0]->GetName() == m_osDimName)
    1731             :             {
    1732           7 :                 names.emplace_back(srcName);
    1733             :             }
    1734             :         }
    1735             :     }
    1736           2 :     return names;
    1737             : }
    1738             : 
    1739             : /************************************************************************/
    1740             : /*                           OpenMDArray()                              */
    1741             : /************************************************************************/
    1742             : 
    1743             : std::shared_ptr<GDALMDArray>
    1744           7 : netCDFVirtualGroupBySameDimension::OpenMDArray(const std::string &osName,
    1745             :                                                CSLConstList papszOptions) const
    1746             : {
    1747           7 :     return m_poGroup->OpenMDArray(osName, papszOptions);
    1748             : }
    1749             : 
    1750             : /************************************************************************/
    1751             : /*                           netCDFDimension()                          */
    1752             : /************************************************************************/
    1753             : 
    1754        1673 : netCDFDimension::netCDFDimension(
    1755             :     const std::shared_ptr<netCDFSharedResources> &poShared, int cfid, int dimid,
    1756        1673 :     size_t nForcedSize, const std::string &osType)
    1757        3346 :     : GDALDimension(NCDFGetGroupFullName(cfid), retrieveName(cfid, dimid),
    1758             :                     osType,         // type
    1759        3346 :                     std::string(),  // direction
    1760        1540 :                     nForcedSize ? nForcedSize : retrieveSize(cfid, dimid)),
    1761        5019 :       m_poShared(poShared), m_gid(cfid), m_dimid(dimid)
    1762             : {
    1763        1673 :     if (m_osType.empty() && nForcedSize == 0)
    1764             :     {
    1765             :         auto var =
    1766        3080 :             std::dynamic_pointer_cast<netCDFVariable>(GetIndexingVariable());
    1767        1540 :         if (var)
    1768             :         {
    1769         224 :             const auto gid = var->GetGroupId();
    1770         224 :             const auto varid = var->GetVarId();
    1771         224 :             const auto varname = var->GetName().c_str();
    1772         435 :             if (NCDFIsVarLongitude(gid, varid, varname) ||
    1773         211 :                 NCDFIsVarProjectionX(gid, varid, varname))
    1774             :             {
    1775          38 :                 m_osType = GDAL_DIM_TYPE_HORIZONTAL_X;
    1776         114 :                 auto attrPositive = var->GetAttribute(CF_UNITS);
    1777          38 :                 if (attrPositive)
    1778             :                 {
    1779          35 :                     const auto val = attrPositive->ReadAsString();
    1780          35 :                     if (val)
    1781             :                     {
    1782          35 :                         if (EQUAL(val, CF_DEGREES_EAST))
    1783             :                         {
    1784          10 :                             m_osDirection = "EAST";
    1785             :                         }
    1786             :                     }
    1787             :                 }
    1788             :             }
    1789         357 :             else if (NCDFIsVarLatitude(gid, varid, varname) ||
    1790         171 :                      NCDFIsVarProjectionY(gid, varid, varname))
    1791             :             {
    1792          39 :                 m_osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
    1793         117 :                 auto attrPositive = var->GetAttribute(CF_UNITS);
    1794          39 :                 if (attrPositive)
    1795             :                 {
    1796          36 :                     const auto val = attrPositive->ReadAsString();
    1797          36 :                     if (val)
    1798             :                     {
    1799          36 :                         if (EQUAL(val, CF_DEGREES_NORTH))
    1800             :                         {
    1801          12 :                             m_osDirection = "NORTH";
    1802             :                         }
    1803             :                     }
    1804             :                 }
    1805             :             }
    1806         147 :             else if (NCDFIsVarVerticalCoord(gid, varid, varname))
    1807             :             {
    1808           9 :                 m_osType = GDAL_DIM_TYPE_VERTICAL;
    1809          27 :                 auto attrPositive = var->GetAttribute("positive");
    1810           9 :                 if (attrPositive)
    1811             :                 {
    1812           0 :                     const auto val = attrPositive->ReadAsString();
    1813           0 :                     if (val)
    1814             :                     {
    1815           0 :                         if (EQUAL(val, "up"))
    1816             :                         {
    1817           0 :                             m_osDirection = "UP";
    1818             :                         }
    1819           0 :                         else if (EQUAL(val, "down"))
    1820             :                         {
    1821           0 :                             m_osDirection = "DOWN";
    1822             :                         }
    1823             :                     }
    1824             :                 }
    1825             :             }
    1826         138 :             else if (NCDFIsVarTimeCoord(gid, varid, varname))
    1827             :             {
    1828          49 :                 m_osType = GDAL_DIM_TYPE_TEMPORAL;
    1829             :             }
    1830             :         }
    1831             :     }
    1832        1673 : }
    1833             : 
    1834             : /************************************************************************/
    1835             : /*                          ~netCDFDimension()                          */
    1836             : /************************************************************************/
    1837             : 
    1838        1673 : netCDFDimension::~netCDFDimension()
    1839             : {
    1840        3346 :     auto poParent = m_poParent.lock();
    1841        1673 :     if (poParent)
    1842         228 :         poParent->UnRegisterDimension(this);
    1843        1673 : }
    1844             : 
    1845             : /************************************************************************/
    1846             : /*                             Create()                                 */
    1847             : /************************************************************************/
    1848             : 
    1849             : /* static */
    1850             : std::shared_ptr<netCDFDimension>
    1851        1673 : netCDFDimension::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
    1852             :                         const std::shared_ptr<netCDFGroup> &poParent, int cfid,
    1853             :                         int dimid, size_t nForcedSize,
    1854             :                         const std::string &osType)
    1855             : {
    1856             :     auto poDim(std::make_shared<netCDFDimension>(poShared, cfid, dimid,
    1857        1673 :                                                  nForcedSize, osType));
    1858        1673 :     poDim->m_poParent = poParent;
    1859        1673 :     if (poParent)
    1860         494 :         poParent->RegisterDimension(poDim.get());
    1861        1673 :     return poDim;
    1862             : }
    1863             : 
    1864             : /************************************************************************/
    1865             : /*                         GetIndexingVariable()                        */
    1866             : /************************************************************************/
    1867             : 
    1868             : namespace
    1869             : {
    1870             : struct SetIsInGetIndexingVariable
    1871             : {
    1872             :     netCDFSharedResources *m_poShared;
    1873             : 
    1874         533 :     explicit SetIsInGetIndexingVariable(
    1875             :         netCDFSharedResources *poSharedResources)
    1876         533 :         : m_poShared(poSharedResources)
    1877             :     {
    1878         533 :         m_poShared->SetIsInGetIndexingVariable(true);
    1879         533 :     }
    1880             : 
    1881         533 :     ~SetIsInGetIndexingVariable()
    1882         533 :     {
    1883         533 :         m_poShared->SetIsInGetIndexingVariable(false);
    1884         533 :     }
    1885             : };
    1886             : }  // namespace
    1887             : 
    1888        1681 : std::shared_ptr<GDALMDArray> netCDFDimension::GetIndexingVariable() const
    1889             : {
    1890        1681 :     if (m_poShared->GetIsInIndexingVariable())
    1891        1148 :         return nullptr;
    1892             : 
    1893        1066 :     SetIsInGetIndexingVariable setterIsInGetIndexingVariable(m_poShared.get());
    1894             : 
    1895        1066 :     CPLMutexHolderD(&hNCMutex);
    1896             : 
    1897             :     // First try to find a variable in this group with the same name as the
    1898             :     // dimension
    1899         533 :     int nVarId = 0;
    1900         533 :     if (nc_inq_varid(m_gid, GetName().c_str(), &nVarId) == NC_NOERR)
    1901             :     {
    1902         261 :         int nDims = 0;
    1903         261 :         NCDF_ERR(nc_inq_varndims(m_gid, nVarId, &nDims));
    1904         261 :         int nVarType = NC_NAT;
    1905         261 :         NCDF_ERR(nc_inq_vartype(m_gid, nVarId, &nVarType));
    1906         261 :         if (nDims == 1 || (nDims == 2 && nVarType == NC_CHAR))
    1907             :         {
    1908         261 :             int anDimIds[2] = {};
    1909         261 :             NCDF_ERR(nc_inq_vardimid(m_gid, nVarId, anDimIds));
    1910         261 :             if (anDimIds[0] == m_dimid)
    1911             :             {
    1912         261 :                 if (nDims == 2)
    1913             :                 {
    1914             :                     // Check that there is no variable with the same of the
    1915             :                     // second dimension.
    1916           2 :                     char szExtraDim[NC_MAX_NAME + 1] = {};
    1917           2 :                     NCDF_ERR(nc_inq_dimname(m_gid, anDimIds[1], szExtraDim));
    1918             :                     int nUnused;
    1919           2 :                     if (nc_inq_varid(m_gid, szExtraDim, &nUnused) == NC_NOERR)
    1920           0 :                         return nullptr;
    1921             :                 }
    1922             : 
    1923         261 :                 return netCDFVariable::Create(
    1924         522 :                     m_poShared, m_poParent.lock(), m_gid, nVarId,
    1925         522 :                     std::vector<std::shared_ptr<GDALDimension>>(), nullptr,
    1926         261 :                     false);
    1927             :             }
    1928             :         }
    1929             :     }
    1930             : 
    1931             :     // Otherwise explore the variables in this group to find one that has a
    1932             :     // "coordinates" attribute that references this dimension. If so, let's
    1933             :     // return the variable pointed by the value of "coordinates" as the indexing
    1934             :     // variable. This assumes that there is no other variable that would use
    1935             :     // another variable for the matching dimension of its "coordinates".
    1936         544 :     netCDFGroup oGroup(m_poShared, m_gid);
    1937         544 :     const auto arrayNames = oGroup.GetMDArrayNames(nullptr);
    1938         272 :     std::shared_ptr<GDALMDArray> candidateIndexingVariable;
    1939        1119 :     for (const auto &arrayName : arrayNames)
    1940             :     {
    1941         867 :         const auto poArray = oGroup.OpenMDArray(arrayName, nullptr);
    1942             :         const auto poArrayNC =
    1943         867 :             std::dynamic_pointer_cast<netCDFVariable>(poArray);
    1944         867 :         if (!poArrayNC)
    1945           0 :             continue;
    1946             : 
    1947         867 :         const auto &apoArrayDims = poArray->GetDimensions();
    1948         867 :         if (apoArrayDims.size() == 1)
    1949             :         {
    1950         153 :             const auto &poArrayDim = apoArrayDims[0];
    1951             :             const auto poArrayDimNC =
    1952         153 :                 std::dynamic_pointer_cast<netCDFDimension>(poArrayDim);
    1953         306 :             if (poArrayDimNC && poArrayDimNC->m_gid == m_gid &&
    1954         153 :                 poArrayDimNC->m_dimid == m_dimid)
    1955             :             {
    1956             :                 // If the array doesn't have a coordinates variable, but is a 1D
    1957             :                 // array indexed by our dimension, then use it as the indexing
    1958             :                 // variable, provided it is the only such variable.
    1959          52 :                 if (!candidateIndexingVariable)
    1960             :                 {
    1961          44 :                     candidateIndexingVariable = poArray;
    1962             :                 }
    1963             :                 else
    1964             :                 {
    1965           8 :                     return nullptr;
    1966             :                 }
    1967          44 :                 continue;
    1968             :             }
    1969             :         }
    1970             : 
    1971        1630 :         const auto poCoordinates = poArray->GetAttribute("coordinates");
    1972         815 :         if (!(poCoordinates &&
    1973         815 :               poCoordinates->GetDataType().GetClass() == GEDTC_STRING))
    1974             :         {
    1975         803 :             continue;
    1976             :         }
    1977             : 
    1978             :         // Check that the arrays has as many dimensions as its coordinates
    1979             :         // attribute
    1980             :         const CPLStringList aosCoordinates(
    1981          12 :             NCDFTokenizeCoordinatesAttribute(poCoordinates->ReadAsString()));
    1982          12 :         if (apoArrayDims.size() != static_cast<size_t>(aosCoordinates.size()))
    1983           0 :             continue;
    1984             : 
    1985          26 :         for (size_t i = 0; i < apoArrayDims.size(); ++i)
    1986             :         {
    1987          26 :             const auto &poArrayDim = apoArrayDims[i];
    1988             :             const auto poArrayDimNC =
    1989          26 :                 std::dynamic_pointer_cast<netCDFDimension>(poArrayDim);
    1990             : 
    1991             :             // Check if the array is indexed by the current dimension
    1992          52 :             if (!(poArrayDimNC && poArrayDimNC->m_gid == m_gid &&
    1993          26 :                   poArrayDimNC->m_dimid == m_dimid))
    1994             :             {
    1995          14 :                 continue;
    1996             :             }
    1997             : 
    1998             :             // Caution: some datasets have their coordinates variables in the
    1999             :             // same order than dimensions (i.e. from slowest varying to
    2000             :             // fastest varying), while others have the coordinates variables
    2001             :             // in the opposite order.
    2002             :             // Assume same order by default, but if we find the first variable
    2003             :             // to be of longitude/X type, then assume the opposite order.
    2004          12 :             bool coordinatesInSameOrderThanDimensions = true;
    2005          12 :             if (aosCoordinates.size() > 1)
    2006             :             {
    2007          12 :                 int bFirstGroupId = -1;
    2008          12 :                 int nFirstVarId = -1;
    2009          12 :                 if (NCDFResolveVar(poArrayNC->GetGroupId(), aosCoordinates[0],
    2010          24 :                                    &bFirstGroupId, &nVarId, false) == CE_None &&
    2011          12 :                     (NCDFIsVarLongitude(bFirstGroupId, nFirstVarId,
    2012           4 :                                         aosCoordinates[0]) ||
    2013           4 :                      NCDFIsVarProjectionX(bFirstGroupId, nFirstVarId,
    2014             :                                           aosCoordinates[0])))
    2015             :                 {
    2016           8 :                     coordinatesInSameOrderThanDimensions = false;
    2017             :                 }
    2018             :             }
    2019             : 
    2020          12 :             int nIndexingVarGroupId = -1;
    2021          12 :             int nIndexingVarId = -1;
    2022             :             const size_t nIdxCoordinate = coordinatesInSameOrderThanDimensions
    2023          12 :                                               ? i
    2024           8 :                                               : aosCoordinates.size() - 1 - i;
    2025          12 :             if (NCDFResolveVar(
    2026             :                     poArrayNC->GetGroupId(), aosCoordinates[nIdxCoordinate],
    2027          12 :                     &nIndexingVarGroupId, &nIndexingVarId, false) == CE_None)
    2028             :             {
    2029          12 :                 return netCDFVariable::Create(
    2030          24 :                     m_poShared, m_poParent.lock(), nIndexingVarGroupId,
    2031             :                     nIndexingVarId,
    2032          24 :                     std::vector<std::shared_ptr<GDALDimension>>(), nullptr,
    2033          12 :                     false);
    2034             :             }
    2035             :         }
    2036             :     }
    2037             : 
    2038         252 :     return candidateIndexingVariable;
    2039             : }
    2040             : 
    2041             : /************************************************************************/
    2042             : /*                              Rename()                                */
    2043             : /************************************************************************/
    2044             : 
    2045           4 : bool netCDFDimension::Rename(const std::string &osNewName)
    2046             : {
    2047           4 :     if (m_poShared->IsReadOnly())
    2048             :     {
    2049           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2050             :                  "Rename() not supported on read-only file");
    2051           1 :         return false;
    2052             :     }
    2053           3 :     if (osNewName.empty())
    2054             :     {
    2055           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
    2056           1 :         return false;
    2057             :     }
    2058           4 :     CPLMutexHolderD(&hNCMutex);
    2059           2 :     m_poShared->SetDefineMode(true);
    2060             : 
    2061           2 :     int ret = nc_rename_dim(m_gid, m_dimid, osNewName.c_str());
    2062           2 :     NCDF_ERR(ret);
    2063           2 :     if (ret != NC_NOERR)
    2064           1 :         return false;
    2065             : 
    2066           1 :     BaseRename(osNewName);
    2067             : 
    2068           1 :     return true;
    2069             : }
    2070             : 
    2071             : /************************************************************************/
    2072             : /*                          netCDFVariable()                            */
    2073             : /************************************************************************/
    2074             : 
    2075        1586 : netCDFVariable::netCDFVariable(
    2076             :     const std::shared_ptr<netCDFSharedResources> &poShared, int gid, int varid,
    2077             :     const std::vector<std::shared_ptr<GDALDimension>> &dims,
    2078        1586 :     CSLConstList papszOptions)
    2079        3172 :     : GDALAbstractMDArray(NCDFGetGroupFullName(gid), retrieveName(gid, varid)),
    2080        3172 :       GDALPamMDArray(NCDFGetGroupFullName(gid), retrieveName(gid, varid),
    2081             :                      poShared->GetPAM()),
    2082        7930 :       m_poShared(poShared), m_gid(gid), m_varid(varid), m_dims(dims)
    2083             : {
    2084        1586 :     NCDF_ERR(nc_inq_varndims(m_gid, m_varid, &m_nDims));
    2085        1586 :     NCDF_ERR(nc_inq_vartype(m_gid, m_varid, &m_nVarType));
    2086        1586 :     if (m_nDims == 2 && m_nVarType == NC_CHAR)
    2087             :     {
    2088          35 :         int anDimIds[2] = {};
    2089          35 :         NCDF_ERR(nc_inq_vardimid(m_gid, m_varid, &anDimIds[0]));
    2090             : 
    2091             :         // Check that there is no variable with the same of the
    2092             :         // second dimension.
    2093          35 :         char szExtraDim[NC_MAX_NAME + 1] = {};
    2094          35 :         NCDF_ERR(nc_inq_dimname(m_gid, anDimIds[1], szExtraDim));
    2095             :         int nUnused;
    2096          35 :         if (nc_inq_varid(m_gid, szExtraDim, &nUnused) != NC_NOERR)
    2097             :         {
    2098          13 :             NCDF_ERR(nc_inq_dimlen(m_gid, anDimIds[1], &m_nTextLength));
    2099             :         }
    2100             :     }
    2101             : 
    2102        1586 :     int nShuffle = 0;
    2103        1586 :     int nDeflate = 0;
    2104        1586 :     int nDeflateLevel = 0;
    2105        1586 :     if (nc_inq_var_deflate(m_gid, m_varid, &nShuffle, &nDeflate,
    2106        1586 :                            &nDeflateLevel) == NC_NOERR)
    2107             :     {
    2108        1586 :         if (nDeflate)
    2109             :         {
    2110         106 :             m_aosStructuralInfo.SetNameValue("COMPRESS", "DEFLATE");
    2111             :         }
    2112             :     }
    2113        3172 :     auto unit = netCDFVariable::GetAttribute(CF_UNITS);
    2114        1586 :     if (unit && unit->GetDataType().GetClass() == GEDTC_STRING)
    2115             :     {
    2116         351 :         const char *pszVal = unit->ReadAsString();
    2117         351 :         if (pszVal)
    2118         351 :             m_osUnit = pszVal;
    2119             :     }
    2120        1586 :     m_bWriteGDALTags = CPLTestBool(
    2121             :         CSLFetchNameValueDef(papszOptions, "WRITE_GDAL_TAGS", "YES"));
    2122        1586 : }
    2123             : 
    2124             : /************************************************************************/
    2125             : /*                          ~netCDFVariable()                           */
    2126             : /************************************************************************/
    2127             : 
    2128        3172 : netCDFVariable::~netCDFVariable()
    2129             : {
    2130        3172 :     auto poParent = m_poParent.lock();
    2131        1586 :     if (poParent)
    2132         320 :         poParent->UnRegisterArray(this);
    2133             : 
    2134        1586 :     if (!m_poShared->IsReadOnly() && !m_dims.empty())
    2135             :     {
    2136         254 :         bool bNeedToWriteDummy = false;
    2137         705 :         for (auto &poDim : m_dims)
    2138             :         {
    2139         454 :             auto netCDFDim = std::dynamic_pointer_cast<netCDFDimension>(poDim);
    2140         454 :             CPLAssert(netCDFDim);
    2141         454 :             if (netCDFDim->GetSize() > netCDFDim->GetActualSize())
    2142             :             {
    2143           3 :                 bNeedToWriteDummy = true;
    2144           3 :                 break;
    2145             :             }
    2146             :         }
    2147         254 :         if (bNeedToWriteDummy)
    2148             :         {
    2149           3 :             CPLDebug("netCDF", "Extending array %s to new dimension sizes",
    2150           3 :                      GetName().c_str());
    2151           3 :             m_bGetRawNoDataValueHasRun = false;
    2152           3 :             m_bUseDefaultFillAsNoData = true;
    2153           3 :             const void *pNoData = GetRawNoDataValue();
    2154           6 :             std::vector<GByte> abyDummy(GetDataType().GetSize());
    2155           3 :             if (pNoData == nullptr)
    2156           0 :                 pNoData = abyDummy.data();
    2157           3 :             const auto nDimCount = m_dims.size();
    2158           6 :             std::vector<GUInt64> arrayStartIdx(nDimCount);
    2159           6 :             std::vector<size_t> count(nDimCount, 1);
    2160           6 :             std::vector<GInt64> arrayStep(nDimCount, 0);
    2161           6 :             std::vector<GPtrDiff_t> bufferStride(nDimCount, 0);
    2162           9 :             for (size_t i = 0; i < nDimCount; ++i)
    2163             :             {
    2164           6 :                 arrayStartIdx[i] = m_dims[i]->GetSize() - 1;
    2165             :             }
    2166           6 :             Write(arrayStartIdx.data(), count.data(), arrayStep.data(),
    2167           3 :                   bufferStride.data(), GetDataType(), pNoData);
    2168             :         }
    2169             :     }
    2170        3172 : }
    2171             : 
    2172             : /************************************************************************/
    2173             : /*                             GetDimensions()                          */
    2174             : /************************************************************************/
    2175             : 
    2176             : const std::vector<std::shared_ptr<GDALDimension>> &
    2177        3824 : netCDFVariable::GetDimensions() const
    2178             : {
    2179        3824 :     if (m_nDims == 0 || !m_dims.empty())
    2180        2678 :         return m_dims;
    2181        2292 :     CPLMutexHolderD(&hNCMutex);
    2182        1146 :     std::vector<int> anDimIds(m_nDims);
    2183        1146 :     NCDF_ERR(nc_inq_vardimid(m_gid, m_varid, &anDimIds[0]));
    2184        1146 :     if (m_nDims == 2 && m_nVarType == NC_CHAR && m_nTextLength > 0)
    2185           9 :         anDimIds.resize(1);
    2186        1146 :     m_dims.reserve(m_nDims);
    2187        3304 :     for (const auto &dimid : anDimIds)
    2188             :     {
    2189        4316 :         auto poCachedDim = m_poShared->GetCachedDimension(dimid);
    2190        2158 :         if (poCachedDim == nullptr)
    2191             :         {
    2192             :             const int groupDim =
    2193        1478 :                 m_poShared->GetBelongingGroupOfDim(m_gid, dimid);
    2194             :             poCachedDim =
    2195        2956 :                 netCDFDimension::Create(m_poShared, m_poParent.lock(), groupDim,
    2196        4434 :                                         dimid, 0, std::string());
    2197        1478 :             m_poShared->CacheDimension(dimid, poCachedDim);
    2198             :         }
    2199        2158 :         m_dims.emplace_back(poCachedDim);
    2200             :     }
    2201        1146 :     return m_dims;
    2202             : }
    2203             : 
    2204             : /************************************************************************/
    2205             : /*                         GetStructuralInfo()                          */
    2206             : /************************************************************************/
    2207             : 
    2208          14 : CSLConstList netCDFVariable::GetStructuralInfo() const
    2209             : {
    2210          14 :     return m_aosStructuralInfo.List();
    2211             : }
    2212             : 
    2213             : /************************************************************************/
    2214             : /*                          GetComplexDataType()                        */
    2215             : /************************************************************************/
    2216             : 
    2217          23 : static GDALDataType GetComplexDataType(int gid, int nVarType)
    2218             : {
    2219             :     // First enquire and check that the number of fields is 2
    2220          23 :     size_t nfields = 0;
    2221          23 :     size_t compoundsize = 0;
    2222          23 :     char szName[NC_MAX_NAME + 1] = {};
    2223          23 :     if (nc_inq_compound(gid, nVarType, szName, &compoundsize, &nfields) !=
    2224             :         NC_NOERR)
    2225             :     {
    2226           0 :         return GDT_Unknown;
    2227             :     }
    2228             : 
    2229          23 :     if (nfields != 2 || !STARTS_WITH_CI(szName, "complex"))
    2230             :     {
    2231           8 :         return GDT_Unknown;
    2232             :     }
    2233             : 
    2234             :     // Now check that that two types are the same in the struct.
    2235             :     nc_type field_type1, field_type2;
    2236             :     int field_dims1, field_dims2;
    2237          15 :     if (nc_inq_compound_field(gid, nVarType, 0, nullptr, nullptr, &field_type1,
    2238          15 :                               &field_dims1, nullptr) != NC_NOERR)
    2239             :     {
    2240           0 :         return GDT_Unknown;
    2241             :     }
    2242             : 
    2243          15 :     if (nc_inq_compound_field(gid, nVarType, 0, nullptr, nullptr, &field_type2,
    2244          15 :                               &field_dims2, nullptr) != NC_NOERR)
    2245             :     {
    2246           0 :         return GDT_Unknown;
    2247             :     }
    2248             : 
    2249          15 :     if ((field_type1 != field_type2) || (field_dims1 != field_dims2) ||
    2250          15 :         (field_dims1 != 0))
    2251             :     {
    2252           0 :         return GDT_Unknown;
    2253             :     }
    2254             : 
    2255          15 :     if (field_type1 == NC_SHORT)
    2256             :     {
    2257           3 :         return GDT_CInt16;
    2258             :     }
    2259          12 :     else if (field_type1 == NC_INT)
    2260             :     {
    2261           2 :         return GDT_CInt32;
    2262             :     }
    2263          10 :     else if (field_type1 == NC_FLOAT)
    2264             :     {
    2265           5 :         return GDT_CFloat32;
    2266             :     }
    2267           5 :     else if (field_type1 == NC_DOUBLE)
    2268             :     {
    2269           5 :         return GDT_CFloat64;
    2270             :     }
    2271             : 
    2272           0 :     return GDT_Unknown;
    2273             : }
    2274             : 
    2275             : /************************************************************************/
    2276             : /*                       GetCompoundDataType()                          */
    2277             : /************************************************************************/
    2278             : 
    2279             : static bool BuildDataType(int gid, int varid, int nVarType,
    2280             :                           std::unique_ptr<GDALExtendedDataType> &dt,
    2281             :                           bool &bPerfectDataTypeMatch);
    2282             : 
    2283           8 : static bool GetCompoundDataType(int gid, int nVarType,
    2284             :                                 std::unique_ptr<GDALExtendedDataType> &dt,
    2285             :                                 bool &bPerfectDataTypeMatch)
    2286             : {
    2287           8 :     size_t nfields = 0;
    2288           8 :     size_t compoundsize = 0;
    2289           8 :     char szName[NC_MAX_NAME + 1] = {};
    2290           8 :     if (nc_inq_compound(gid, nVarType, szName, &compoundsize, &nfields) !=
    2291             :         NC_NOERR)
    2292             :     {
    2293           0 :         return false;
    2294             :     }
    2295           8 :     bPerfectDataTypeMatch = true;
    2296          16 :     std::vector<std::unique_ptr<GDALEDTComponent>> comps;
    2297          25 :     for (size_t i = 0; i < nfields; i++)
    2298             :     {
    2299          17 :         nc_type field_type = 0;
    2300          17 :         int field_dims = 0;
    2301          17 :         size_t field_offset = 0;
    2302          17 :         char field_name[NC_MAX_NAME + 1] = {};
    2303          17 :         if (nc_inq_compound_field(gid, nVarType, static_cast<int>(i),
    2304             :                                   field_name, &field_offset, &field_type,
    2305          17 :                                   &field_dims, nullptr) != NC_NOERR)
    2306             :         {
    2307           0 :             return false;
    2308             :         }
    2309          17 :         if (field_dims != 0)
    2310             :         {
    2311             :             // We don't support that
    2312           0 :             return false;
    2313             :         }
    2314           0 :         std::unique_ptr<GDALExtendedDataType> subDt;
    2315          17 :         bool bSubPerfectDataTypeMatch = false;
    2316          17 :         if (!BuildDataType(gid, -1, field_type, subDt,
    2317             :                            bSubPerfectDataTypeMatch))
    2318             :         {
    2319           0 :             return false;
    2320             :         }
    2321          17 :         if (!bSubPerfectDataTypeMatch)
    2322             :         {
    2323           0 :             CPLError(
    2324             :                 CE_Failure, CPLE_NotSupported,
    2325             :                 "Non native GDAL type found in a component of a compound type");
    2326           0 :             return false;
    2327             :         }
    2328             :         auto comp = std::unique_ptr<GDALEDTComponent>(new GDALEDTComponent(
    2329          51 :             std::string(field_name), field_offset, *subDt));
    2330          17 :         comps.emplace_back(std::move(comp));
    2331             :     }
    2332          16 :     dt.reset(new GDALExtendedDataType(
    2333           8 :         GDALExtendedDataType::Create(szName, compoundsize, std::move(comps))));
    2334             : 
    2335           8 :     return dt->GetClass() == GEDTC_COMPOUND;
    2336             : }
    2337             : 
    2338             : /************************************************************************/
    2339             : /*                            BuildDataType()                           */
    2340             : /************************************************************************/
    2341             : 
    2342         411 : static bool BuildDataType(int gid, int varid, int nVarType,
    2343             :                           std::unique_ptr<GDALExtendedDataType> &dt,
    2344             :                           bool &bPerfectDataTypeMatch)
    2345             : {
    2346         411 :     GDALDataType eDataType = GDT_Unknown;
    2347         411 :     bPerfectDataTypeMatch = false;
    2348         411 :     if (NCDFIsUserDefinedType(gid, nVarType))
    2349             :     {
    2350          25 :         nc_type nBaseType = NC_NAT;
    2351          25 :         int eClass = 0;
    2352          25 :         nc_inq_user_type(gid, nVarType, nullptr, nullptr, &nBaseType, nullptr,
    2353             :                          &eClass);
    2354          25 :         if (eClass == NC_COMPOUND)
    2355             :         {
    2356          23 :             eDataType = GetComplexDataType(gid, nVarType);
    2357          23 :             if (eDataType != GDT_Unknown)
    2358             :             {
    2359          15 :                 bPerfectDataTypeMatch = true;
    2360          15 :                 dt.reset(new GDALExtendedDataType(
    2361          15 :                     GDALExtendedDataType::Create(eDataType)));
    2362          23 :                 return true;
    2363             :             }
    2364           8 :             else if (GetCompoundDataType(gid, nVarType, dt,
    2365             :                                          bPerfectDataTypeMatch))
    2366             :             {
    2367           8 :                 return true;
    2368             :             }
    2369             :             else
    2370             :             {
    2371           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2372             :                          "Unsupported netCDF compound data type encountered.");
    2373           0 :                 return false;
    2374             :             }
    2375             :         }
    2376           2 :         else if (eClass == NC_ENUM)
    2377             :         {
    2378           2 :             nVarType = nBaseType;
    2379             :         }
    2380           0 :         else if (eClass == NC_VLEN)
    2381             :         {
    2382           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2383             :                      "VLen data type not supported");
    2384           0 :             return false;
    2385             :         }
    2386           0 :         else if (eClass == NC_OPAQUE)
    2387             :         {
    2388           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2389             :                      "Opaque data type not supported");
    2390           0 :             return false;
    2391             :         }
    2392             :         else
    2393             :         {
    2394           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2395             :                      "Unsupported  netCDF data type encountered.");
    2396           0 :             return false;
    2397             :         }
    2398             :     }
    2399             : 
    2400         388 :     if (nVarType == NC_STRING)
    2401             :     {
    2402           7 :         bPerfectDataTypeMatch = true;
    2403           7 :         dt.reset(
    2404           7 :             new GDALExtendedDataType(GDALExtendedDataType::CreateString()));
    2405           7 :         return true;
    2406             :     }
    2407             :     else
    2408             :     {
    2409         381 :         if (nVarType == NC_BYTE)
    2410             :         {
    2411          57 :             char *pszTemp = nullptr;
    2412          57 :             bool bSignedData = true;
    2413         112 :             if (varid >= 0 &&
    2414          55 :                 NCDFGetAttr(gid, varid, "_Unsigned", &pszTemp) == CE_None)
    2415             :             {
    2416          45 :                 if (EQUAL(pszTemp, "true"))
    2417          44 :                     bSignedData = false;
    2418           1 :                 else if (EQUAL(pszTemp, "false"))
    2419           1 :                     bSignedData = true;
    2420          45 :                 CPLFree(pszTemp);
    2421             :             }
    2422          57 :             if (!bSignedData)
    2423             :             {
    2424          44 :                 eDataType = GDT_Byte;
    2425          44 :                 bPerfectDataTypeMatch = true;
    2426             :             }
    2427             :             else
    2428             :             {
    2429          13 :                 eDataType = GDT_Int8;
    2430          13 :                 bPerfectDataTypeMatch = true;
    2431             :             }
    2432             :         }
    2433         324 :         else if (nVarType == NC_CHAR)
    2434             :         {
    2435             :             // Not sure of this
    2436           1 :             bPerfectDataTypeMatch = true;
    2437           1 :             eDataType = GDT_Byte;
    2438             :         }
    2439         323 :         else if (nVarType == NC_SHORT)
    2440             :         {
    2441          50 :             bPerfectDataTypeMatch = true;
    2442          50 :             char *pszTemp = nullptr;
    2443          50 :             bool bSignedData = true;
    2444          93 :             if (varid >= 0 &&
    2445          43 :                 NCDFGetAttr(gid, varid, "_Unsigned", &pszTemp) == CE_None)
    2446             :             {
    2447          30 :                 if (EQUAL(pszTemp, "true"))
    2448          30 :                     bSignedData = false;
    2449           0 :                 else if (EQUAL(pszTemp, "false"))
    2450           0 :                     bSignedData = true;
    2451          30 :                 CPLFree(pszTemp);
    2452             :             }
    2453          50 :             if (!bSignedData)
    2454             :             {
    2455          30 :                 eDataType = GDT_UInt16;
    2456             :             }
    2457             :             else
    2458             :             {
    2459          20 :                 eDataType = GDT_Int16;
    2460             :             }
    2461             :         }
    2462         273 :         else if (nVarType == NC_INT)
    2463             :         {
    2464          46 :             bPerfectDataTypeMatch = true;
    2465          46 :             eDataType = GDT_Int32;
    2466             :         }
    2467         227 :         else if (nVarType == NC_FLOAT)
    2468             :         {
    2469          44 :             bPerfectDataTypeMatch = true;
    2470          44 :             eDataType = GDT_Float32;
    2471             :         }
    2472         183 :         else if (nVarType == NC_DOUBLE)
    2473             :         {
    2474          91 :             bPerfectDataTypeMatch = true;
    2475          91 :             eDataType = GDT_Float64;
    2476             :         }
    2477          92 :         else if (nVarType == NC_UBYTE)
    2478             :         {
    2479          62 :             bPerfectDataTypeMatch = true;
    2480          62 :             eDataType = GDT_Byte;
    2481             :         }
    2482          30 :         else if (nVarType == NC_USHORT)
    2483             :         {
    2484           9 :             bPerfectDataTypeMatch = true;
    2485           9 :             eDataType = GDT_UInt16;
    2486             :         }
    2487          21 :         else if (nVarType == NC_UINT)
    2488             :         {
    2489           7 :             bPerfectDataTypeMatch = true;
    2490           7 :             eDataType = GDT_UInt32;
    2491             :         }
    2492          14 :         else if (nVarType == NC_INT64)
    2493             :         {
    2494           8 :             bPerfectDataTypeMatch = true;
    2495           8 :             eDataType = GDT_Int64;
    2496             :         }
    2497           6 :         else if (nVarType == NC_UINT64)
    2498             :         {
    2499           6 :             bPerfectDataTypeMatch = true;
    2500           6 :             eDataType = GDT_UInt64;
    2501             :         }
    2502             :         else
    2503             :         {
    2504           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2505             :                      "Unsupported netCDF data type encountered.");
    2506           0 :             return false;
    2507             :         }
    2508             :     }
    2509         381 :     dt.reset(new GDALExtendedDataType(GDALExtendedDataType::Create(eDataType)));
    2510         381 :     return true;
    2511             : }
    2512             : 
    2513             : /************************************************************************/
    2514             : /*                             GetDataType()                            */
    2515             : /************************************************************************/
    2516             : 
    2517        2094 : const GDALExtendedDataType &netCDFVariable::GetDataType() const
    2518             : {
    2519        2094 :     if (m_dt)
    2520        1811 :         return *m_dt;
    2521         566 :     CPLMutexHolderD(&hNCMutex);
    2522             : 
    2523         283 :     if (m_nDims == 2 && m_nVarType == NC_CHAR && m_nTextLength > 0)
    2524             :     {
    2525           7 :         m_bPerfectDataTypeMatch = true;
    2526           7 :         m_dt.reset(new GDALExtendedDataType(
    2527           7 :             GDALExtendedDataType::CreateString(m_nTextLength)));
    2528             :     }
    2529             :     else
    2530             :     {
    2531         276 :         m_dt.reset(new GDALExtendedDataType(
    2532         276 :             GDALExtendedDataType::Create(GDT_Unknown)));
    2533             : 
    2534         276 :         BuildDataType(m_gid, m_varid, m_nVarType, m_dt,
    2535         276 :                       m_bPerfectDataTypeMatch);
    2536             :     }
    2537         283 :     return *m_dt;
    2538             : }
    2539             : 
    2540             : /************************************************************************/
    2541             : /*                              SetUnit()                               */
    2542             : /************************************************************************/
    2543             : 
    2544           1 : bool netCDFVariable::SetUnit(const std::string &osUnit)
    2545             : {
    2546           1 :     if (osUnit.empty())
    2547             :     {
    2548           0 :         nc_del_att(m_gid, m_varid, CF_UNITS);
    2549           0 :         return true;
    2550             :     }
    2551           3 :     auto poUnits(GetAttribute(CF_UNITS));
    2552           1 :     if (!poUnits)
    2553             :     {
    2554           2 :         poUnits = CreateAttribute(
    2555           3 :             CF_UNITS, {}, GDALExtendedDataType::CreateString(), nullptr);
    2556           1 :         if (!poUnits)
    2557           0 :             return false;
    2558             :     }
    2559           1 :     return poUnits->Write(osUnit.c_str());
    2560             : }
    2561             : 
    2562             : /************************************************************************/
    2563             : /*                            GetSpatialRef()                           */
    2564             : /************************************************************************/
    2565             : 
    2566         111 : std::shared_ptr<OGRSpatialReference> netCDFVariable::GetSpatialRef() const
    2567             : {
    2568         111 :     if (m_bSRSRead)
    2569          50 :         return m_poSRS;
    2570             : 
    2571          61 :     m_bSRSRead = true;
    2572         122 :     netCDFDataset poDS;
    2573          61 :     poDS.ReadAttributes(m_gid, m_varid);
    2574          61 :     int iDimX = 0;
    2575          61 :     int iDimY = 0;
    2576          61 :     int iCount = 1;
    2577         179 :     for (const auto &poDim : GetDimensions())
    2578             :     {
    2579         118 :         if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
    2580          20 :             iDimX = iCount;
    2581          98 :         else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
    2582          28 :             iDimY = iCount;
    2583         118 :         poDS.papszDimName.AddString(poDim->GetName().c_str());
    2584         118 :         iCount++;
    2585             :     }
    2586          61 :     if ((iDimX == 0 || iDimY == 0) && GetDimensionCount() >= 2)
    2587             :     {
    2588          26 :         iDimX = static_cast<int>(GetDimensionCount());
    2589          26 :         iDimY = iDimX - 1;
    2590             :     }
    2591          61 :     poDS.SetProjectionFromVar(m_gid, m_varid, true);
    2592          61 :     auto poSRS = poDS.GetSpatialRef();
    2593          61 :     if (poSRS)
    2594             :     {
    2595          21 :         m_poSRS.reset(poSRS->Clone());
    2596          21 :         if (iDimX > 0 && iDimY > 0)
    2597             :         {
    2598          21 :             if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
    2599          42 :                 std::vector<int>{2, 1})
    2600           5 :                 m_poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
    2601             :             else
    2602          16 :                 m_poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
    2603             :         }
    2604             :     }
    2605             : 
    2606          61 :     return m_poSRS;
    2607             : }
    2608             : 
    2609             : /************************************************************************/
    2610             : /*                            SetSpatialRef()                           */
    2611             : /************************************************************************/
    2612             : 
    2613          72 : static void WriteDimAttr(std::shared_ptr<GDALMDArray> &poVar,
    2614             :                          const char *pszAttrName, const char *pszAttrValue)
    2615             : {
    2616         216 :     auto poAttr = poVar->GetAttribute(pszAttrName);
    2617          72 :     if (poAttr)
    2618             :     {
    2619          24 :         const char *pszVal = poAttr->ReadAsString();
    2620          24 :         if (pszVal && !EQUAL(pszVal, pszAttrValue))
    2621             :         {
    2622           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2623             :                      "Variable %s has a %s which is %s and not %s",
    2624           0 :                      poVar->GetName().c_str(), pszAttrName, pszVal,
    2625             :                      pszAttrValue);
    2626             :         }
    2627             :     }
    2628             :     else
    2629             :     {
    2630         144 :         poAttr = poVar->CreateAttribute(
    2631         144 :             pszAttrName, {}, GDALExtendedDataType::CreateString(), nullptr);
    2632          48 :         if (poAttr)
    2633          48 :             poAttr->Write(pszAttrValue);
    2634             :     }
    2635          72 : }
    2636             : 
    2637          28 : static void WriteDimAttrs(const std::shared_ptr<GDALDimension> &dim,
    2638             :                           const char *pszStandardName, const char *pszLongName,
    2639             :                           const char *pszUnits)
    2640             : {
    2641          56 :     auto poVar = dim->GetIndexingVariable();
    2642          28 :     if (poVar)
    2643             :     {
    2644          24 :         WriteDimAttr(poVar, CF_STD_NAME, pszStandardName);
    2645          24 :         WriteDimAttr(poVar, CF_LNG_NAME, pszLongName);
    2646          24 :         WriteDimAttr(poVar, CF_UNITS, pszUnits);
    2647             :     }
    2648             :     else
    2649             :     {
    2650           4 :         CPLError(CE_Warning, CPLE_AppDefined,
    2651             :                  "Dimension %s lacks a indexing variable",
    2652           4 :                  dim->GetName().c_str());
    2653             :     }
    2654          28 : }
    2655             : 
    2656          18 : bool netCDFVariable::SetSpatialRef(const OGRSpatialReference *poSRS)
    2657             : {
    2658          18 :     m_bSRSRead = false;
    2659          18 :     m_poSRS.reset();
    2660             : 
    2661          36 :     CPLMutexHolderD(&hNCMutex);
    2662          18 :     m_poShared->SetDefineMode(true);
    2663             : 
    2664          18 :     if (poSRS == nullptr)
    2665             :     {
    2666           2 :         nc_del_att(m_gid, m_varid, CF_GRD_MAPPING);
    2667           2 :         return true;
    2668             :     }
    2669             : 
    2670          16 :     char *pszCFProjection = nullptr;
    2671             :     int nSRSVarId =
    2672          16 :         NCDFWriteSRSVariable(m_gid, poSRS, &pszCFProjection, m_bWriteGDALTags);
    2673          16 :     if (nSRSVarId < 0 || pszCFProjection == nullptr)
    2674           0 :         return false;
    2675             : 
    2676          16 :     NCDF_ERR(nc_put_att_text(m_gid, m_varid, CF_GRD_MAPPING,
    2677             :                              strlen(pszCFProjection), pszCFProjection));
    2678          16 :     CPLFree(pszCFProjection);
    2679             : 
    2680          16 :     auto apoDims = GetDimensions();
    2681          16 :     if (poSRS->IsProjected())
    2682             :     {
    2683          11 :         bool bWriteX = false;
    2684          11 :         bool bWriteY = false;
    2685          22 :         const std::string osUnits = NCDFGetProjectedCFUnit(poSRS);
    2686          33 :         for (const auto &poDim : apoDims)
    2687             :         {
    2688          22 :             const char *pszStandardName = nullptr;
    2689          22 :             const char *pszLongName = nullptr;
    2690          39 :             if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X ||
    2691          17 :                 EQUAL(poDim->GetName().c_str(), CF_PROJ_X_VAR_NAME))
    2692             :             {
    2693           8 :                 pszStandardName = CF_PROJ_X_COORD;
    2694           8 :                 pszLongName = CF_PROJ_X_COORD_LONG_NAME;
    2695           8 :                 bWriteX = true;
    2696             :             }
    2697          23 :             else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y ||
    2698           9 :                      EQUAL(poDim->GetName().c_str(), CF_PROJ_Y_VAR_NAME))
    2699             :             {
    2700           8 :                 pszStandardName = CF_PROJ_Y_COORD;
    2701           8 :                 pszLongName = CF_PROJ_Y_COORD_LONG_NAME;
    2702           8 :                 bWriteY = true;
    2703             :             }
    2704          22 :             if (pszStandardName && pszLongName)
    2705             :             {
    2706          16 :                 WriteDimAttrs(poDim, pszStandardName, pszLongName,
    2707             :                               osUnits.c_str());
    2708             :             }
    2709             :         }
    2710           6 :         if (!bWriteX && !bWriteY && apoDims.size() >= 2 &&
    2711           6 :             apoDims[apoDims.size() - 2]->GetType().empty() &&
    2712           6 :             apoDims[apoDims.size() - 1]->GetType().empty() &&
    2713          28 :             apoDims[apoDims.size() - 2]->GetIndexingVariable() &&
    2714          12 :             apoDims[apoDims.size() - 1]->GetIndexingVariable())
    2715             :         {
    2716           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    2717             :                      "Dimensions of variable %s have no type declared. "
    2718             :                      "Assuming the last one is X, and the preceding one Y",
    2719           1 :                      GetName().c_str());
    2720           1 :             WriteDimAttrs(apoDims[apoDims.size() - 1], CF_PROJ_X_COORD,
    2721             :                           CF_PROJ_X_COORD_LONG_NAME, osUnits.c_str());
    2722           1 :             WriteDimAttrs(apoDims[apoDims.size() - 2], CF_PROJ_Y_COORD,
    2723             :                           CF_PROJ_Y_COORD_LONG_NAME, osUnits.c_str());
    2724             :         }
    2725             :     }
    2726           5 :     else if (poSRS->IsGeographic())
    2727             :     {
    2728           5 :         bool bWriteX = false;
    2729           5 :         bool bWriteY = false;
    2730          15 :         for (const auto &poDim : apoDims)
    2731             :         {
    2732          10 :             const char *pszStandardName = nullptr;
    2733          10 :             const char *pszLongName = nullptr;
    2734          10 :             const char *pszUnits = "";
    2735          16 :             if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X ||
    2736           6 :                 EQUAL(poDim->GetName().c_str(), CF_LONGITUDE_VAR_NAME))
    2737             :             {
    2738           4 :                 pszStandardName = CF_LONGITUDE_STD_NAME;
    2739           4 :                 pszLongName = CF_LONGITUDE_LNG_NAME;
    2740           4 :                 pszUnits = CF_DEGREES_EAST;
    2741           4 :                 bWriteX = true;
    2742             :             }
    2743           8 :             else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y ||
    2744           2 :                      EQUAL(poDim->GetName().c_str(), CF_LATITUDE_VAR_NAME))
    2745             :             {
    2746           4 :                 pszStandardName = CF_LATITUDE_STD_NAME;
    2747           4 :                 pszLongName = CF_LATITUDE_LNG_NAME;
    2748           4 :                 pszUnits = CF_DEGREES_NORTH;
    2749           4 :                 bWriteY = true;
    2750             :             }
    2751          10 :             if (pszStandardName && pszLongName)
    2752             :             {
    2753           8 :                 WriteDimAttrs(poDim, pszStandardName, pszLongName, pszUnits);
    2754             :             }
    2755             :         }
    2756           2 :         if (!bWriteX && !bWriteY && apoDims.size() >= 2 &&
    2757           2 :             apoDims[apoDims.size() - 2]->GetType().empty() &&
    2758           2 :             apoDims[apoDims.size() - 1]->GetType().empty() &&
    2759          12 :             apoDims[apoDims.size() - 2]->GetIndexingVariable() &&
    2760           6 :             apoDims[apoDims.size() - 1]->GetIndexingVariable())
    2761             :         {
    2762           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    2763             :                      "Dimensions of variable %s have no type declared. "
    2764             :                      "Assuming the last one is longitude, "
    2765             :                      "and the preceding one latitude",
    2766           1 :                      GetName().c_str());
    2767           1 :             WriteDimAttrs(apoDims[apoDims.size() - 1], CF_LONGITUDE_STD_NAME,
    2768             :                           CF_LONGITUDE_LNG_NAME, CF_DEGREES_EAST);
    2769           1 :             WriteDimAttrs(apoDims[apoDims.size() - 2], CF_LATITUDE_STD_NAME,
    2770             :                           CF_LATITUDE_LNG_NAME, CF_DEGREES_NORTH);
    2771             :         }
    2772             :     }
    2773             : 
    2774          16 :     return true;
    2775             : }
    2776             : 
    2777             : /************************************************************************/
    2778             : /*                           SetStatistics()                            */
    2779             : /************************************************************************/
    2780             : 
    2781           2 : bool netCDFVariable::SetStatistics(bool bApproxStats, double dfMin,
    2782             :                                    double dfMax, double dfMean, double dfStdDev,
    2783             :                                    GUInt64 nValidCount,
    2784             :                                    CSLConstList papszOptions)
    2785             : {
    2786           3 :     if (!bApproxStats && !m_poShared->IsReadOnly() &&
    2787           1 :         CPLTestBool(
    2788             :             CSLFetchNameValueDef(papszOptions, "UPDATE_METADATA", "NO")))
    2789             :     {
    2790           3 :         auto poAttr = GetAttribute("actual_range");
    2791           1 :         if (!poAttr)
    2792             :         {
    2793             :             poAttr =
    2794           1 :                 CreateAttribute("actual_range", {2}, GetDataType(), nullptr);
    2795             :         }
    2796           1 :         if (poAttr)
    2797             :         {
    2798           2 :             std::vector<GUInt64> startIdx = {0};
    2799           2 :             std::vector<size_t> count = {2};
    2800           1 :             std::vector<double> values = {dfMin, dfMax};
    2801           2 :             poAttr->Write(startIdx.data(), count.data(), nullptr, nullptr,
    2802           2 :                           GDALExtendedDataType::Create(GDT_Float64),
    2803           1 :                           values.data(), nullptr, 0);
    2804             :         }
    2805             :     }
    2806           2 :     return GDALPamMDArray::SetStatistics(bApproxStats, dfMin, dfMax, dfMean,
    2807           2 :                                          dfStdDev, nValidCount, papszOptions);
    2808             : }
    2809             : 
    2810             : /************************************************************************/
    2811             : /*                             GetNCTypeSize()                          */
    2812             : /************************************************************************/
    2813             : 
    2814         656 : static size_t GetNCTypeSize(const GDALExtendedDataType &dt,
    2815             :                             bool bPerfectDataTypeMatch, int nAttType)
    2816             : {
    2817         656 :     auto nElementSize = dt.GetSize();
    2818         656 :     if (!bPerfectDataTypeMatch)
    2819             :     {
    2820          10 :         if (nAttType == NC_BYTE)
    2821             :         {
    2822           4 :             CPLAssert(dt.GetNumericDataType() == GDT_Int16);
    2823           4 :             nElementSize = sizeof(signed char);
    2824             :         }
    2825           6 :         else if (nAttType == NC_INT64)
    2826             :         {
    2827           4 :             CPLAssert(dt.GetNumericDataType() == GDT_Float64);
    2828           4 :             nElementSize = sizeof(GInt64);
    2829             :         }
    2830           2 :         else if (nAttType == NC_UINT64)
    2831             :         {
    2832           2 :             CPLAssert(dt.GetNumericDataType() == GDT_Float64);
    2833           2 :             nElementSize = sizeof(GUInt64);
    2834             :         }
    2835             :         else
    2836             :         {
    2837           0 :             CPLAssert(false);
    2838             :         }
    2839             :     }
    2840         656 :     return nElementSize;
    2841             : }
    2842             : 
    2843             : /************************************************************************/
    2844             : /*                   ConvertNCStringsToCPLStrings()                     */
    2845             : /************************************************************************/
    2846             : 
    2847          67 : static void ConvertNCStringsToCPLStrings(GByte *pBuffer,
    2848             :                                          const GDALExtendedDataType &dt)
    2849             : {
    2850          67 :     switch (dt.GetClass())
    2851             :     {
    2852           0 :         case GEDTC_STRING:
    2853             :         {
    2854             :             char *pszStr;
    2855             :             // cppcheck-suppress pointerSize
    2856           0 :             memcpy(&pszStr, pBuffer, sizeof(char *));
    2857           0 :             if (pszStr)
    2858             :             {
    2859           0 :                 char *pszNewStr = VSIStrdup(pszStr);
    2860           0 :                 nc_free_string(1, &pszStr);
    2861             :                 // cppcheck-suppress pointerSize
    2862           0 :                 memcpy(pBuffer, &pszNewStr, sizeof(char *));
    2863             :             }
    2864           0 :             break;
    2865             :         }
    2866             : 
    2867          63 :         case GEDTC_NUMERIC:
    2868             :         {
    2869          63 :             break;
    2870             :         }
    2871             : 
    2872           4 :         case GEDTC_COMPOUND:
    2873             :         {
    2874           4 :             const auto &comps = dt.GetComponents();
    2875          12 :             for (const auto &comp : comps)
    2876             :             {
    2877           8 :                 ConvertNCStringsToCPLStrings(pBuffer + comp->GetOffset(),
    2878             :                                              comp->GetType());
    2879             :             }
    2880           4 :             break;
    2881             :         }
    2882             :     }
    2883          67 : }
    2884             : 
    2885             : /************************************************************************/
    2886             : /*                            FreeNCStrings()                           */
    2887             : /************************************************************************/
    2888             : 
    2889         127 : static void FreeNCStrings(GByte *pBuffer, const GDALExtendedDataType &dt)
    2890             : {
    2891         127 :     switch (dt.GetClass())
    2892             :     {
    2893           0 :         case GEDTC_STRING:
    2894             :         {
    2895             :             char *pszStr;
    2896             :             // cppcheck-suppress pointerSize
    2897           0 :             memcpy(&pszStr, pBuffer, sizeof(char *));
    2898           0 :             if (pszStr)
    2899             :             {
    2900           0 :                 nc_free_string(1, &pszStr);
    2901             :             }
    2902           0 :             break;
    2903             :         }
    2904             : 
    2905         127 :         case GEDTC_NUMERIC:
    2906             :         {
    2907         127 :             break;
    2908             :         }
    2909             : 
    2910           0 :         case GEDTC_COMPOUND:
    2911             :         {
    2912           0 :             const auto &comps = dt.GetComponents();
    2913           0 :             for (const auto &comp : comps)
    2914             :             {
    2915           0 :                 FreeNCStrings(pBuffer + comp->GetOffset(), comp->GetType());
    2916             :             }
    2917           0 :             break;
    2918             :         }
    2919             :     }
    2920         127 : }
    2921             : 
    2922             : /************************************************************************/
    2923             : /*                          IReadWriteGeneric()                         */
    2924             : /************************************************************************/
    2925             : 
    2926             : namespace
    2927             : {
    2928             : template <typename T> struct GetGByteType
    2929             : {
    2930             : };
    2931             : 
    2932             : template <> struct GetGByteType<void *>
    2933             : {
    2934             :     typedef GByte *type;
    2935             : };
    2936             : 
    2937             : template <> struct GetGByteType<const void *>
    2938             : {
    2939             :     typedef const GByte *type;
    2940             : };
    2941             : }  // namespace
    2942             : 
    2943             : template <typename BufferType, typename NCGetPutVar1FuncType,
    2944             :           typename ReadOrWriteOneElementType>
    2945          15 : bool netCDFVariable::IReadWriteGeneric(
    2946             :     const size_t *arrayStartIdx, const size_t *count, const GInt64 *arrayStep,
    2947             :     const GPtrDiff_t *bufferStride, const GDALExtendedDataType &bufferDataType,
    2948             :     BufferType buffer, NCGetPutVar1FuncType NCGetPutVar1Func,
    2949             :     ReadOrWriteOneElementType ReadOrWriteOneElement) const
    2950             : {
    2951          15 :     CPLAssert(m_nDims > 0);
    2952          30 :     std::vector<size_t> array_idx(m_nDims);
    2953          30 :     std::vector<size_t> stack_count_iters(m_nDims - 1);
    2954             :     typedef typename GetGByteType<BufferType>::type GBytePtrType;
    2955          30 :     std::vector<GBytePtrType> stack_ptr(m_nDims);
    2956          30 :     std::vector<GPtrDiff_t> ptr_inc;
    2957          15 :     ptr_inc.reserve(m_nDims);
    2958          15 :     const auto &eArrayEDT = GetDataType();
    2959          15 :     const bool bSameDT = m_bPerfectDataTypeMatch && eArrayEDT == bufferDataType;
    2960          15 :     const auto nBufferDTSize = bufferDataType.GetSize();
    2961          42 :     for (int i = 0; i < m_nDims; i++)
    2962             :     {
    2963          27 :         ptr_inc.push_back(bufferStride[i] * nBufferDTSize);
    2964             :     }
    2965          15 :     const auto nDimsMinus1 = m_nDims - 1;
    2966          15 :     stack_ptr[0] = static_cast<GBytePtrType>(buffer);
    2967             : 
    2968        4579 :     auto lambdaLastDim = [&](GBytePtrType ptr)
    2969             :     {
    2970          60 :         array_idx[nDimsMinus1] = arrayStartIdx[nDimsMinus1];
    2971          60 :         size_t nIters = count[nDimsMinus1];
    2972         520 :         while (true)
    2973             :         {
    2974         580 :             if (bSameDT)
    2975             :             {
    2976             :                 int ret =
    2977           8 :                     NCGetPutVar1Func(m_gid, m_varid, array_idx.data(), ptr);
    2978           8 :                 NCDF_ERR(ret);
    2979           8 :                 if (ret != NC_NOERR)
    2980           0 :                     return false;
    2981             :             }
    2982             :             else
    2983             :             {
    2984        1144 :                 if (!(this->*ReadOrWriteOneElement)(eArrayEDT, bufferDataType,
    2985         572 :                                                     array_idx.data(), ptr))
    2986           0 :                     return false;
    2987             :             }
    2988         580 :             if ((--nIters) == 0)
    2989          60 :                 break;
    2990         520 :             ptr += ptr_inc[nDimsMinus1];
    2991             :             // CPLUnsanitizedAdd needed as arrayStep[] might be negative, and
    2992             :             // thus automatic conversion from negative to big unsigned might
    2993             :             // occur
    2994         520 :             array_idx[nDimsMinus1] = CPLUnsanitizedAdd<size_t>(
    2995             :                 array_idx[nDimsMinus1],
    2996         520 :                 static_cast<GPtrDiff_t>(arrayStep[nDimsMinus1]));
    2997             :         }
    2998          60 :         return true;
    2999             :     };
    3000             : 
    3001          15 :     if (m_nDims == 1)
    3002             :     {
    3003           6 :         return lambdaLastDim(stack_ptr[0]);
    3004             :     }
    3005           9 :     else if (m_nDims == 2)
    3006             :     {
    3007           7 :         auto nIters = count[0];
    3008           7 :         array_idx[0] = arrayStartIdx[0];
    3009             :         while (true)
    3010             :         {
    3011          36 :             if (!lambdaLastDim(stack_ptr[0]))
    3012           0 :                 return false;
    3013          36 :             if ((--nIters) == 0)
    3014           7 :                 break;
    3015          29 :             stack_ptr[0] += ptr_inc[0];
    3016             :             // CPLUnsanitizedAdd needed as arrayStep[] might be negative, and
    3017             :             // thus automatic conversion from negative to big unsigned might
    3018             :             // occur
    3019          29 :             array_idx[0] = CPLUnsanitizedAdd<size_t>(
    3020             :                 array_idx[0], static_cast<GPtrDiff_t>(arrayStep[0]));
    3021             :         }
    3022             :     }
    3023           2 :     else if (m_nDims == 3)
    3024             :     {
    3025           1 :         stack_count_iters[0] = count[0];
    3026           1 :         array_idx[0] = arrayStartIdx[0];
    3027           2 :         while (true)
    3028             :         {
    3029           3 :             auto nIters = count[1];
    3030           3 :             array_idx[1] = arrayStartIdx[1];
    3031           3 :             stack_ptr[1] = stack_ptr[0];
    3032             :             while (true)
    3033             :             {
    3034           6 :                 if (!lambdaLastDim(stack_ptr[1]))
    3035           0 :                     return false;
    3036           6 :                 if ((--nIters) == 0)
    3037           3 :                     break;
    3038           3 :                 stack_ptr[1] += ptr_inc[1];
    3039             :                 // CPLUnsanitizedAdd needed as arrayStep[] might be negative,
    3040             :                 // and thus automatic conversion from negative to big unsigned
    3041             :                 // might occur
    3042           3 :                 array_idx[1] = CPLUnsanitizedAdd<size_t>(
    3043           3 :                     array_idx[1], static_cast<GPtrDiff_t>(arrayStep[1]));
    3044             :             }
    3045           3 :             if ((--stack_count_iters[0]) == 0)
    3046           1 :                 break;
    3047           2 :             stack_ptr[0] += ptr_inc[0];
    3048             :             // CPLUnsanitizedAdd needed as arrayStep[] might be negative, and
    3049             :             // thus automatic conversion from negative to big unsigned might
    3050             :             // occur
    3051           2 :             array_idx[0] = CPLUnsanitizedAdd<size_t>(
    3052             :                 array_idx[0], static_cast<GPtrDiff_t>(arrayStep[0]));
    3053             :         }
    3054             :     }
    3055             :     else
    3056             :     {
    3057             :         // Implementation valid for nDims >= 3
    3058             : 
    3059           1 :         int dimIdx = 0;
    3060             :         // Non-recursive implementation. Hence the gotos
    3061             :         // It might be possible to rewrite this without gotos, but I find they
    3062             :         // make it clearer to understand the recursive nature of the code
    3063           9 :     lbl_start:
    3064           9 :         if (dimIdx == nDimsMinus1 - 1)
    3065             :         {
    3066           6 :             array_idx[dimIdx] = arrayStartIdx[dimIdx];
    3067           6 :             auto nIters = count[dimIdx];
    3068             :             while (true)
    3069             :             {
    3070          12 :                 if (!(lambdaLastDim(stack_ptr[dimIdx])))
    3071           0 :                     return false;
    3072          12 :                 if ((--nIters) == 0)
    3073           6 :                     break;
    3074           6 :                 stack_ptr[dimIdx] += ptr_inc[dimIdx];
    3075             :                 // CPLUnsanitizedAdd needed as arrayStep[] might be negative,
    3076             :                 // and thus automatic conversion from negative to big unsigned
    3077             :                 // might occur
    3078           6 :                 array_idx[dimIdx] = CPLUnsanitizedAdd<size_t>(
    3079             :                     array_idx[dimIdx],
    3080           6 :                     static_cast<GPtrDiff_t>(arrayStep[dimIdx]));
    3081             :             }
    3082             :             // If there was a test if( dimIdx > 0 ), that would be valid for
    3083             :             // nDims == 2
    3084           6 :             goto lbl_return_to_caller;
    3085             :         }
    3086             :         else
    3087             :         {
    3088           3 :             array_idx[dimIdx] = arrayStartIdx[dimIdx];
    3089           3 :             stack_count_iters[dimIdx] = count[dimIdx];
    3090             :             while (true)
    3091             :             {
    3092             :                 // Simulate a recursive call to the next dimension
    3093             :                 // Implicitly save back count and ptr
    3094           8 :                 dimIdx++;
    3095           8 :                 stack_ptr[dimIdx] = stack_ptr[dimIdx - 1];
    3096           8 :                 goto lbl_start;
    3097           8 :             lbl_return_to_caller:
    3098           8 :                 dimIdx--;
    3099           8 :                 if ((--stack_count_iters[dimIdx]) == 0)
    3100           3 :                     break;
    3101           5 :                 stack_ptr[dimIdx] += ptr_inc[dimIdx];
    3102             :                 // CPLUnsanitizedAdd needed as arrayStep[] might be negative,
    3103             :                 // and thus automatic conversion from negative to big unsigned
    3104             :                 // might occur
    3105           5 :                 array_idx[dimIdx] = CPLUnsanitizedAdd<size_t>(
    3106             :                     array_idx[dimIdx],
    3107           5 :                     static_cast<GPtrDiff_t>(arrayStep[dimIdx]));
    3108             :             }
    3109           3 :             if (dimIdx > 0)
    3110           2 :                 goto lbl_return_to_caller;
    3111             :         }
    3112             :     }
    3113             : 
    3114           9 :     return true;
    3115             : }
    3116             : 
    3117             : /************************************************************************/
    3118             : /*                          CheckNumericDataType()                      */
    3119             : /************************************************************************/
    3120             : 
    3121         901 : static bool CheckNumericDataType(const GDALExtendedDataType &dt)
    3122             : {
    3123         901 :     const auto klass = dt.GetClass();
    3124         901 :     if (klass == GEDTC_NUMERIC)
    3125         891 :         return dt.GetNumericDataType() != GDT_Unknown;
    3126          10 :     if (klass == GEDTC_STRING)
    3127           0 :         return false;
    3128          10 :     CPLAssert(klass == GEDTC_COMPOUND);
    3129          10 :     const auto &comps = dt.GetComponents();
    3130          31 :     for (const auto &comp : comps)
    3131             :     {
    3132          21 :         if (!CheckNumericDataType(comp->GetType()))
    3133           0 :             return false;
    3134             :     }
    3135          10 :     return true;
    3136             : }
    3137             : 
    3138             : /************************************************************************/
    3139             : /*                            IReadWrite()                              */
    3140             : /************************************************************************/
    3141             : 
    3142             : template <typename BufferType, typename NCGetPutVar1FuncType,
    3143             :           typename NCGetPutVaraFuncType, typename NCGetPutVarmFuncType,
    3144             :           typename ReadOrWriteOneElementType>
    3145         443 : bool netCDFVariable::IReadWrite(
    3146             :     const bool bIsRead, const GUInt64 *arrayStartIdx, const size_t *count,
    3147             :     const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    3148             :     const GDALExtendedDataType &bufferDataType, BufferType buffer,
    3149             :     NCGetPutVar1FuncType NCGetPutVar1Func,
    3150             :     NCGetPutVaraFuncType NCGetPutVaraFunc,
    3151             :     NCGetPutVarmFuncType NCGetPutVarmFunc,
    3152             :     ReadOrWriteOneElementType ReadOrWriteOneElement) const
    3153             : {
    3154         886 :     CPLMutexHolderD(&hNCMutex);
    3155         443 :     m_poShared->SetDefineMode(false);
    3156             : 
    3157         443 :     const auto &eDT = GetDataType();
    3158         886 :     std::vector<size_t> startp;
    3159         443 :     startp.reserve(m_nDims);
    3160         443 :     bool bUseSlowPath =
    3161         443 :         !m_bPerfectDataTypeMatch &&
    3162           0 :         !(bIsRead && bufferDataType.GetClass() == GEDTC_NUMERIC &&
    3163           0 :           eDT.GetClass() == GEDTC_NUMERIC &&
    3164           0 :           bufferDataType.GetSize() >= eDT.GetSize());
    3165        1304 :     for (int i = 0; i < m_nDims; i++)
    3166             :     {
    3167             : #if SIZEOF_VOIDP == 4
    3168             :         if (arrayStartIdx[i] > std::numeric_limits<size_t>::max())
    3169             :             return false;
    3170             : #endif
    3171         861 :         startp.push_back(static_cast<size_t>(arrayStartIdx[i]));
    3172             : 
    3173             : #if SIZEOF_VOIDP == 4
    3174             :         if (arrayStep[i] < std::numeric_limits<ptrdiff_t>::min() ||
    3175             :             arrayStep[i] > std::numeric_limits<ptrdiff_t>::max())
    3176             :         {
    3177             :             return false;
    3178             :         }
    3179             : #endif
    3180             : 
    3181         861 :         if (count[i] != 1 && arrayStep[i] <= 0)
    3182           3 :             bUseSlowPath = true;  // netCDF rejects negative or NULL strides
    3183             : 
    3184         861 :         if (bufferStride[i] < 0)
    3185           0 :             bUseSlowPath =
    3186             :                 true;  // and it seems to silently cast to size_t imapp
    3187             :     }
    3188             : 
    3189         446 :     if (eDT.GetClass() == GEDTC_STRING &&
    3190         446 :         bufferDataType.GetClass() == GEDTC_STRING && m_nVarType == NC_STRING)
    3191             :     {
    3192           3 :         if (m_nDims == 0)
    3193             :         {
    3194           0 :             return (this->*ReadOrWriteOneElement)(eDT, bufferDataType, nullptr,
    3195           0 :                                                   buffer);
    3196             :         }
    3197             : 
    3198           3 :         return IReadWriteGeneric(startp.data(), count, arrayStep, bufferStride,
    3199             :                                  bufferDataType, buffer, NCGetPutVar1Func,
    3200           3 :                                  ReadOrWriteOneElement);
    3201             :     }
    3202             : 
    3203         440 :     if (!CheckNumericDataType(eDT))
    3204           0 :         return false;
    3205         440 :     if (!CheckNumericDataType(bufferDataType))
    3206           0 :         return false;
    3207             : 
    3208         440 :     if (m_nDims == 0)
    3209             :     {
    3210          14 :         return (this->*ReadOrWriteOneElement)(eDT, bufferDataType, nullptr,
    3211          14 :                                               buffer);
    3212             :     }
    3213             : 
    3214         849 :     if (!bUseSlowPath &&
    3215         836 :         ((GDALDataTypeIsComplex(bufferDataType.GetNumericDataType()) ||
    3216         413 :           bufferDataType.GetClass() == GEDTC_COMPOUND) &&
    3217          15 :          bufferDataType == eDT))
    3218             :     {
    3219             :         // nc_get_varm() not supported for non-atomic types.
    3220          14 :         ptrdiff_t nExpectedBufferStride = 1;
    3221          40 :         for (int i = m_nDims; i != 0;)
    3222             :         {
    3223          26 :             --i;
    3224          26 :             if (count[i] != 1 &&
    3225          17 :                 (arrayStep[i] != 1 || bufferStride[i] != nExpectedBufferStride))
    3226             :             {
    3227           0 :                 bUseSlowPath = true;
    3228           0 :                 break;
    3229             :             }
    3230          26 :             nExpectedBufferStride *= count[i];
    3231             :         }
    3232          14 :         if (!bUseSlowPath)
    3233             :         {
    3234             :             int ret =
    3235          14 :                 NCGetPutVaraFunc(m_gid, m_varid, startp.data(), count, buffer);
    3236          14 :             NCDF_ERR(ret);
    3237          14 :             return ret == NC_NOERR;
    3238             :         }
    3239             :     }
    3240             : 
    3241         817 :     if (bUseSlowPath || bufferDataType.GetClass() == GEDTC_COMPOUND ||
    3242         408 :         eDT.GetClass() == GEDTC_COMPOUND ||
    3243         408 :         (!bIsRead &&
    3244        1176 :          bufferDataType.GetNumericDataType() != eDT.GetNumericDataType()) ||
    3245         355 :         (bIsRead && bufferDataType.GetSize() < eDT.GetSize()))
    3246             :     {
    3247           8 :         return IReadWriteGeneric(startp.data(), count, arrayStep, bufferStride,
    3248             :                                  bufferDataType, buffer, NCGetPutVar1Func,
    3249           8 :                                  ReadOrWriteOneElement);
    3250             :     }
    3251             : 
    3252         404 :     bUseSlowPath = false;
    3253         404 :     ptrdiff_t nExpectedBufferStride = 1;
    3254        1202 :     for (int i = m_nDims; i != 0;)
    3255             :     {
    3256         809 :         --i;
    3257         809 :         if (count[i] != 1 &&
    3258         471 :             (arrayStep[i] != 1 || bufferStride[i] != nExpectedBufferStride))
    3259             :         {
    3260          11 :             bUseSlowPath = true;
    3261          11 :             break;
    3262             :         }
    3263         798 :         nExpectedBufferStride *= count[i];
    3264             :     }
    3265         404 :     if (!bUseSlowPath)
    3266             :     {
    3267             :         // nc_get_varm() is terribly inefficient, so use nc_get_vara()
    3268             :         // when possible.
    3269             :         int ret =
    3270         393 :             NCGetPutVaraFunc(m_gid, m_varid, startp.data(), count, buffer);
    3271         393 :         if (ret != NC_NOERR)
    3272             :         {
    3273           1 :             NCDF_ERR(ret);
    3274           1 :             return false;
    3275             :         }
    3276         733 :         if (bIsRead &&
    3277         341 :             (!m_bPerfectDataTypeMatch ||
    3278         341 :              bufferDataType.GetNumericDataType() != eDT.GetNumericDataType()))
    3279             :         {
    3280             :             // If the buffer data type is "larger" or of the same size as the
    3281             :             // native data type, we can do a in-place conversion
    3282          16 :             GByte *pabyBuffer =
    3283             :                 static_cast<GByte *>(const_cast<void *>(buffer));
    3284          16 :             CPLAssert(bufferDataType.GetSize() >= eDT.GetSize());
    3285          16 :             const auto nDTSize = eDT.GetSize();
    3286          16 :             const auto nBufferDTSize = bufferDataType.GetSize();
    3287          16 :             if (!m_bPerfectDataTypeMatch &&
    3288           0 :                 (m_nVarType == NC_CHAR || m_nVarType == NC_BYTE))
    3289             :             {
    3290             :                 // native NC type translates into GDAL data type of larger size
    3291           0 :                 for (ptrdiff_t i = nExpectedBufferStride - 1; i >= 0; --i)
    3292             :                 {
    3293             :                     GByte abySrc[sizeof(
    3294             :                         double)];  // 2 is enough here, but sizeof(double) make
    3295             :                                    // MSVC happy
    3296           0 :                     abySrc[0] = *(pabyBuffer + i);
    3297           0 :                     ConvertNCToGDAL(&abySrc[0]);
    3298           0 :                     GDALExtendedDataType::CopyValue(
    3299           0 :                         &abySrc[0], eDT, pabyBuffer + i * nBufferDTSize,
    3300             :                         bufferDataType);
    3301           0 :                 }
    3302             :             }
    3303          16 :             else if (!m_bPerfectDataTypeMatch)
    3304             :             {
    3305             :                 // native NC type translates into GDAL data type of same size
    3306           0 :                 CPLAssert(m_nVarType == NC_INT64 || m_nVarType == NC_UINT64);
    3307           0 :                 for (ptrdiff_t i = nExpectedBufferStride - 1; i >= 0; --i)
    3308             :                 {
    3309           0 :                     ConvertNCToGDAL(pabyBuffer + i * nDTSize);
    3310           0 :                     GDALExtendedDataType::CopyValue(
    3311           0 :                         pabyBuffer + i * nDTSize, eDT,
    3312           0 :                         pabyBuffer + i * nBufferDTSize, bufferDataType);
    3313             :                 }
    3314             :             }
    3315             :             else
    3316             :             {
    3317        2196 :                 for (ptrdiff_t i = nExpectedBufferStride - 1; i >= 0; --i)
    3318             :                 {
    3319        2180 :                     GDALExtendedDataType::CopyValue(
    3320        2180 :                         pabyBuffer + i * nDTSize, eDT,
    3321        2180 :                         pabyBuffer + i * nBufferDTSize, bufferDataType);
    3322             :                 }
    3323             :             }
    3324             :         }
    3325         392 :         return true;
    3326             :     }
    3327             :     else
    3328             :     {
    3329          11 :         if (bufferDataType.GetNumericDataType() != eDT.GetNumericDataType())
    3330             :         {
    3331           4 :             return IReadWriteGeneric(startp.data(), count, arrayStep,
    3332             :                                      bufferStride, bufferDataType, buffer,
    3333           4 :                                      NCGetPutVar1Func, ReadOrWriteOneElement);
    3334             :         }
    3335          14 :         std::vector<ptrdiff_t> stridep;
    3336           7 :         stridep.reserve(m_nDims);
    3337           7 :         std::vector<ptrdiff_t> imapp;
    3338           7 :         imapp.reserve(m_nDims);
    3339          25 :         for (int i = 0; i < m_nDims; i++)
    3340             :         {
    3341          18 :             stridep.push_back(
    3342          18 :                 static_cast<ptrdiff_t>(count[i] == 1 ? 1 : arrayStep[i]));
    3343          18 :             imapp.push_back(static_cast<ptrdiff_t>(bufferStride[i]));
    3344             :         }
    3345             : 
    3346           7 :         if (!m_poShared->GetImappIsInElements())
    3347             :         {
    3348             :             const size_t nMul =
    3349           0 :                 GetNCTypeSize(eDT, m_bPerfectDataTypeMatch, m_nVarType);
    3350           0 :             for (int i = 0; i < m_nDims; ++i)
    3351             :             {
    3352           0 :                 imapp[i] = static_cast<ptrdiff_t>(imapp[i] * nMul);
    3353             :             }
    3354             :         }
    3355           7 :         int ret = NCGetPutVarmFunc(m_gid, m_varid, startp.data(), count,
    3356           7 :                                    stridep.data(), imapp.data(), buffer);
    3357           7 :         NCDF_ERR(ret);
    3358           7 :         return ret == NC_NOERR;
    3359             :     }
    3360             : }
    3361             : 
    3362             : /************************************************************************/
    3363             : /*                          ConvertNCToGDAL()                           */
    3364             : /************************************************************************/
    3365             : 
    3366         579 : void netCDFVariable::ConvertNCToGDAL(GByte *buffer) const
    3367             : {
    3368         579 :     if (!m_bPerfectDataTypeMatch)
    3369             :     {
    3370           0 :         if (m_nVarType == NC_CHAR || m_nVarType == NC_BYTE)
    3371             :         {
    3372           0 :             short s = reinterpret_cast<signed char *>(buffer)[0];
    3373           0 :             memcpy(buffer, &s, sizeof(s));
    3374             :         }
    3375           0 :         else if (m_nVarType == NC_INT64)
    3376             :         {
    3377           0 :             double v =
    3378           0 :                 static_cast<double>(reinterpret_cast<GInt64 *>(buffer)[0]);
    3379           0 :             memcpy(buffer, &v, sizeof(v));
    3380             :         }
    3381           0 :         else if (m_nVarType == NC_UINT64)
    3382             :         {
    3383           0 :             double v =
    3384           0 :                 static_cast<double>(reinterpret_cast<GUInt64 *>(buffer)[0]);
    3385           0 :             memcpy(buffer, &v, sizeof(v));
    3386             :         }
    3387             :     }
    3388         579 : }
    3389             : 
    3390             : /************************************************************************/
    3391             : /*                           ReadOneElement()                           */
    3392             : /************************************************************************/
    3393             : 
    3394         579 : bool netCDFVariable::ReadOneElement(const GDALExtendedDataType &src_datatype,
    3395             :                                     const GDALExtendedDataType &bufferDataType,
    3396             :                                     const size_t *array_idx,
    3397             :                                     void *pDstBuffer) const
    3398             : {
    3399         579 :     if (src_datatype.GetClass() == GEDTC_STRING)
    3400             :     {
    3401           0 :         char *pszStr = nullptr;
    3402           0 :         int ret = nc_get_var1_string(m_gid, m_varid, array_idx, &pszStr);
    3403           0 :         NCDF_ERR(ret);
    3404           0 :         if (ret != NC_NOERR)
    3405           0 :             return false;
    3406           0 :         nc_free_string(1, &pszStr);
    3407           0 :         GDALExtendedDataType::CopyValue(&pszStr, src_datatype, pDstBuffer,
    3408             :                                         bufferDataType);
    3409           0 :         return true;
    3410             :     }
    3411             : 
    3412             :     std::vector<GByte> abySrc(std::max(
    3413           0 :         src_datatype.GetSize(),
    3414        1158 :         GetNCTypeSize(src_datatype, m_bPerfectDataTypeMatch, m_nVarType)));
    3415             : 
    3416         579 :     int ret = nc_get_var1(m_gid, m_varid, array_idx, &abySrc[0]);
    3417         579 :     NCDF_ERR(ret);
    3418         579 :     if (ret != NC_NOERR)
    3419           0 :         return false;
    3420             : 
    3421         579 :     ConvertNCToGDAL(&abySrc[0]);
    3422             : 
    3423         579 :     GDALExtendedDataType::CopyValue(&abySrc[0], src_datatype, pDstBuffer,
    3424             :                                     bufferDataType);
    3425         579 :     return true;
    3426             : }
    3427             : 
    3428             : /************************************************************************/
    3429             : /*                                   IRead()                            */
    3430             : /************************************************************************/
    3431             : 
    3432         472 : bool netCDFVariable::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    3433             :                            const GInt64 *arrayStep,
    3434             :                            const GPtrDiff_t *bufferStride,
    3435             :                            const GDALExtendedDataType &bufferDataType,
    3436             :                            void *pDstBuffer) const
    3437             : {
    3438         472 :     if (m_nDims == 2 && m_nVarType == NC_CHAR && GetDimensions().size() == 1)
    3439             :     {
    3440           8 :         CPLMutexHolderD(&hNCMutex);
    3441           4 :         m_poShared->SetDefineMode(false);
    3442             : 
    3443           4 :         if (bufferDataType.GetClass() != GEDTC_STRING)
    3444           0 :             return false;
    3445           4 :         GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    3446           4 :         size_t array_idx[2] = {static_cast<size_t>(arrayStartIdx[0]), 0};
    3447           4 :         size_t array_count[2] = {1, m_nTextLength};
    3448           8 :         std::string osTmp(m_nTextLength, 0);
    3449           4 :         const char *pszTmp = osTmp.c_str();
    3450          12 :         for (size_t i = 0; i < count[0]; i++)
    3451             :         {
    3452             :             int ret =
    3453           8 :                 nc_get_vara(m_gid, m_varid, array_idx, array_count, &osTmp[0]);
    3454           8 :             NCDF_ERR(ret);
    3455           8 :             if (ret != NC_NOERR)
    3456           0 :                 return false;
    3457             :             // coverity[use_after_free]
    3458           8 :             GDALExtendedDataType::CopyValue(&pszTmp, GetDataType(),
    3459             :                                             pabyDstBuffer, GetDataType());
    3460           8 :             array_idx[0] = static_cast<size_t>(array_idx[0] + arrayStep[0]);
    3461           8 :             pabyDstBuffer += bufferStride[0] * sizeof(char *);
    3462             :         }
    3463           4 :         return true;
    3464             :     }
    3465             : 
    3466         468 :     if (m_poCachedArray)
    3467             :     {
    3468           5 :         const auto nDims = GetDimensionCount();
    3469           5 :         std::vector<GUInt64> modifiedArrayStartIdx(nDims);
    3470           5 :         bool canUseCache = true;
    3471          13 :         for (size_t i = 0; i < nDims; i++)
    3472             :         {
    3473          17 :             if (arrayStartIdx[i] >= m_cachedArrayStartIdx[i] &&
    3474           8 :                 arrayStartIdx[i] + (count[i] - 1) * arrayStep[i] <=
    3475           8 :                     m_cachedArrayStartIdx[i] + m_cachedCount[i] - 1)
    3476             :             {
    3477           8 :                 modifiedArrayStartIdx[i] =
    3478           8 :                     arrayStartIdx[i] - m_cachedArrayStartIdx[i];
    3479             :             }
    3480             :             else
    3481             :             {
    3482           1 :                 canUseCache = false;
    3483           1 :                 break;
    3484             :             }
    3485             :         }
    3486           5 :         if (canUseCache)
    3487             :         {
    3488           4 :             return m_poCachedArray->Read(modifiedArrayStartIdx.data(), count,
    3489             :                                          arrayStep, bufferStride,
    3490           4 :                                          bufferDataType, pDstBuffer);
    3491             :         }
    3492             :     }
    3493             : 
    3494         464 :     if (IsTransposedRequest(count, bufferStride))
    3495             :     {
    3496          84 :         return ReadForTransposedRequest(arrayStartIdx, count, arrayStep,
    3497             :                                         bufferStride, bufferDataType,
    3498          84 :                                         pDstBuffer);
    3499             :     }
    3500             : 
    3501         380 :     return IReadWrite(true, arrayStartIdx, count, arrayStep, bufferStride,
    3502             :                       bufferDataType, pDstBuffer, nc_get_var1, nc_get_vara,
    3503         380 :                       nc_get_varm, &netCDFVariable::ReadOneElement);
    3504             : }
    3505             : 
    3506             : /************************************************************************/
    3507             : /*                             IAdviseRead()                            */
    3508             : /************************************************************************/
    3509             : 
    3510           4 : bool netCDFVariable::IAdviseRead(const GUInt64 *arrayStartIdx,
    3511             :                                  const size_t *count,
    3512             :                                  CSLConstList /* papszOptions */) const
    3513             : {
    3514           4 :     const auto nDims = GetDimensionCount();
    3515           4 :     if (nDims == 0)
    3516           0 :         return true;
    3517           4 :     const auto &eDT = GetDataType();
    3518           4 :     if (eDT.GetClass() != GEDTC_NUMERIC)
    3519           0 :         return false;
    3520             : 
    3521           4 :     auto poMemDriver = static_cast<GDALDriver *>(GDALGetDriverByName("MEM"));
    3522           4 :     if (poMemDriver == nullptr)
    3523           0 :         return false;
    3524             : 
    3525           4 :     m_poCachedArray.reset();
    3526             : 
    3527           4 :     size_t nElts = 1;
    3528          12 :     for (size_t i = 0; i < nDims; i++)
    3529           8 :         nElts *= count[i];
    3530             : 
    3531           4 :     void *pData = VSI_MALLOC2_VERBOSE(nElts, eDT.GetSize());
    3532           4 :     if (pData == nullptr)
    3533           0 :         return false;
    3534             : 
    3535           4 :     if (!Read(arrayStartIdx, count, nullptr, nullptr, eDT, pData))
    3536             :     {
    3537           0 :         VSIFree(pData);
    3538           0 :         return false;
    3539             :     }
    3540             : 
    3541           4 :     auto poDS = poMemDriver->CreateMultiDimensional("", nullptr, nullptr);
    3542           8 :     auto poGroup = poDS->GetRootGroup();
    3543           4 :     delete poDS;
    3544             : 
    3545           4 :     std::vector<std::shared_ptr<GDALDimension>> apoMemDims;
    3546           4 :     const auto &poDims = GetDimensions();
    3547          12 :     for (size_t i = 0; i < nDims; i++)
    3548             :     {
    3549             :         apoMemDims.emplace_back(
    3550          24 :             poGroup->CreateDimension(poDims[i]->GetName(), std::string(),
    3551          24 :                                      std::string(), count[i], nullptr));
    3552             :     }
    3553             :     m_poCachedArray =
    3554           4 :         poGroup->CreateMDArray(GetName(), apoMemDims, eDT, nullptr);
    3555           4 :     m_poCachedArray->Write(std::vector<GUInt64>(nDims).data(), count, nullptr,
    3556             :                            nullptr, eDT, pData);
    3557           4 :     m_cachedArrayStartIdx.resize(nDims);
    3558           4 :     memcpy(&m_cachedArrayStartIdx[0], arrayStartIdx, nDims * sizeof(GUInt64));
    3559           4 :     m_cachedCount.resize(nDims);
    3560           4 :     memcpy(&m_cachedCount[0], count, nDims * sizeof(size_t));
    3561           4 :     VSIFree(pData);
    3562           4 :     return true;
    3563             : }
    3564             : 
    3565             : /************************************************************************/
    3566             : /*                          ConvertGDALToNC()                           */
    3567             : /************************************************************************/
    3568             : 
    3569          21 : void netCDFVariable::ConvertGDALToNC(GByte *buffer) const
    3570             : {
    3571          21 :     if (!m_bPerfectDataTypeMatch)
    3572             :     {
    3573           0 :         if (m_nVarType == NC_CHAR || m_nVarType == NC_BYTE)
    3574             :         {
    3575           0 :             const auto c =
    3576           0 :                 static_cast<signed char>(reinterpret_cast<short *>(buffer)[0]);
    3577           0 :             memcpy(buffer, &c, sizeof(c));
    3578             :         }
    3579           0 :         else if (m_nVarType == NC_INT64)
    3580             :         {
    3581           0 :             const auto v =
    3582           0 :                 static_cast<GInt64>(reinterpret_cast<double *>(buffer)[0]);
    3583           0 :             memcpy(buffer, &v, sizeof(v));
    3584             :         }
    3585           0 :         else if (m_nVarType == NC_UINT64)
    3586             :         {
    3587           0 :             const auto v =
    3588           0 :                 static_cast<GUInt64>(reinterpret_cast<double *>(buffer)[0]);
    3589           0 :             memcpy(buffer, &v, sizeof(v));
    3590             :         }
    3591             :     }
    3592          21 : }
    3593             : 
    3594             : /************************************************************************/
    3595             : /*                          WriteOneElement()                           */
    3596             : /************************************************************************/
    3597             : 
    3598           7 : bool netCDFVariable::WriteOneElement(const GDALExtendedDataType &dst_datatype,
    3599             :                                      const GDALExtendedDataType &bufferDataType,
    3600             :                                      const size_t *array_idx,
    3601             :                                      const void *pSrcBuffer) const
    3602             : {
    3603           7 :     if (dst_datatype.GetClass() == GEDTC_STRING)
    3604             :     {
    3605           0 :         const char *pszStr = (static_cast<const char *const *>(pSrcBuffer))[0];
    3606           0 :         int ret = nc_put_var1_string(m_gid, m_varid, array_idx, &pszStr);
    3607           0 :         NCDF_ERR(ret);
    3608           0 :         return ret == NC_NOERR;
    3609             :     }
    3610             : 
    3611           7 :     std::vector<GByte> abyTmp(dst_datatype.GetSize());
    3612           7 :     GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &abyTmp[0],
    3613             :                                     dst_datatype);
    3614             : 
    3615           7 :     ConvertGDALToNC(&abyTmp[0]);
    3616             : 
    3617           7 :     int ret = nc_put_var1(m_gid, m_varid, array_idx, &abyTmp[0]);
    3618           7 :     NCDF_ERR(ret);
    3619           7 :     return ret == NC_NOERR;
    3620             : }
    3621             : 
    3622             : /************************************************************************/
    3623             : /*                                IWrite()                              */
    3624             : /************************************************************************/
    3625             : 
    3626          66 : bool netCDFVariable::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    3627             :                             const GInt64 *arrayStep,
    3628             :                             const GPtrDiff_t *bufferStride,
    3629             :                             const GDALExtendedDataType &bufferDataType,
    3630             :                             const void *pSrcBuffer)
    3631             : {
    3632          66 :     m_bHasWrittenData = true;
    3633             : 
    3634          66 :     m_poCachedArray.reset();
    3635             : 
    3636          66 :     if (m_nDims == 2 && m_nVarType == NC_CHAR && GetDimensions().size() == 1)
    3637             :     {
    3638           6 :         CPLMutexHolderD(&hNCMutex);
    3639           3 :         m_poShared->SetDefineMode(false);
    3640             : 
    3641           3 :         if (bufferDataType.GetClass() != GEDTC_STRING)
    3642           0 :             return false;
    3643           3 :         const char *const *ppszSrcBuffer =
    3644             :             static_cast<const char *const *>(pSrcBuffer);
    3645           3 :         size_t array_idx[2] = {static_cast<size_t>(arrayStartIdx[0]), 0};
    3646           3 :         size_t array_count[2] = {1, m_nTextLength};
    3647           6 :         std::string osTmp(m_nTextLength, 0);
    3648           8 :         for (size_t i = 0; i < count[0]; i++)
    3649             :         {
    3650           5 :             const char *pszStr = *ppszSrcBuffer;
    3651           5 :             memset(&osTmp[0], 0, m_nTextLength);
    3652           5 :             if (pszStr)
    3653             :             {
    3654           5 :                 size_t nLen = strlen(pszStr);
    3655           5 :                 memcpy(&osTmp[0], pszStr, std::min(m_nTextLength, nLen));
    3656             :             }
    3657             :             int ret =
    3658           5 :                 nc_put_vara(m_gid, m_varid, array_idx, array_count, &osTmp[0]);
    3659           5 :             NCDF_ERR(ret);
    3660           5 :             if (ret != NC_NOERR)
    3661           0 :                 return false;
    3662           5 :             array_idx[0] = static_cast<size_t>(array_idx[0] + arrayStep[0]);
    3663           5 :             ppszSrcBuffer += bufferStride[0];
    3664             :         }
    3665           3 :         return true;
    3666             :     }
    3667             : 
    3668          63 :     return IReadWrite(false, arrayStartIdx, count, arrayStep, bufferStride,
    3669             :                       bufferDataType, pSrcBuffer, nc_put_var1, nc_put_vara,
    3670          63 :                       nc_put_varm, &netCDFVariable::WriteOneElement);
    3671             : }
    3672             : 
    3673             : /************************************************************************/
    3674             : /*                          GetRawNoDataValue()                         */
    3675             : /************************************************************************/
    3676             : 
    3677         166 : const void *netCDFVariable::GetRawNoDataValue() const
    3678             : {
    3679         166 :     const auto &dt = GetDataType();
    3680         166 :     if (dt.GetClass() != GEDTC_NUMERIC)
    3681           2 :         return nullptr;
    3682             : 
    3683         164 :     if (m_bGetRawNoDataValueHasRun)
    3684             :     {
    3685          81 :         return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
    3686             :     }
    3687             : 
    3688          83 :     m_bGetRawNoDataValueHasRun = true;
    3689             : 
    3690          83 :     const char *pszAttrName = _FillValue;
    3691         249 :     auto poAttr = GetAttribute(pszAttrName);
    3692          83 :     if (!poAttr)
    3693             :     {
    3694          54 :         pszAttrName = "missing_value";
    3695          54 :         poAttr = GetAttribute(pszAttrName);
    3696             :     }
    3697          83 :     if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_NUMERIC)
    3698             :     {
    3699          32 :         auto oRawResult = poAttr->ReadAsRaw();
    3700          32 :         if (oRawResult.data())
    3701             :         {
    3702             :             // Round-trip attribute value to target data type and back
    3703             :             // to attribute data type to ensure there is no loss
    3704             :             // Normally _FillValue data type should be the same
    3705             :             // as the array one, but this is not always the case.
    3706             :             // For example NASA GEDI L2B products have Float64
    3707             :             // _FillValue for Float32 variables.
    3708          32 :             m_abyNoData.resize(dt.GetSize());
    3709          32 :             GDALExtendedDataType::CopyValue(oRawResult.data(),
    3710          32 :                                             poAttr->GetDataType(),
    3711          32 :                                             m_abyNoData.data(), dt);
    3712          64 :             std::vector<GByte> abyTmp(poAttr->GetDataType().GetSize());
    3713          32 :             GDALExtendedDataType::CopyValue(
    3714          32 :                 m_abyNoData.data(), dt, abyTmp.data(), poAttr->GetDataType());
    3715          64 :             std::vector<GByte> abyOri;
    3716          32 :             abyOri.assign(oRawResult.data(),
    3717          32 :                           oRawResult.data() + oRawResult.size());
    3718          32 :             if (abyOri == abyTmp)
    3719          31 :                 return m_abyNoData.data();
    3720           1 :             m_abyNoData.clear();
    3721           1 :             char *pszVal = nullptr;
    3722           1 :             GDALExtendedDataType::CopyValue(
    3723           1 :                 oRawResult.data(), poAttr->GetDataType(), &pszVal,
    3724           2 :                 GDALExtendedDataType::CreateString());
    3725           1 :             CPLError(CE_Warning, CPLE_AppDefined,
    3726             :                      "%s attribute value (%s) is not in the range of the "
    3727             :                      "variable data type",
    3728           1 :                      pszAttrName, pszVal ? pszVal : "(null)");
    3729           1 :             CPLFree(pszVal);
    3730           1 :             return nullptr;
    3731             :         }
    3732             :     }
    3733          51 :     else if (poAttr && poAttr->GetDataType().GetClass() == GEDTC_STRING)
    3734             :     {
    3735           3 :         const char *pszVal = poAttr->ReadAsString();
    3736           3 :         if (pszVal)
    3737             :         {
    3738             :             // Round-trip attribute value to target data type and back
    3739             :             // to attribute data type to ensure there is no loss
    3740           3 :             m_abyNoData.resize(dt.GetSize());
    3741           3 :             GDALExtendedDataType::CopyValue(&pszVal, poAttr->GetDataType(),
    3742           3 :                                             m_abyNoData.data(), dt);
    3743           3 :             char *pszTmpVal = nullptr;
    3744           3 :             GDALExtendedDataType::CopyValue(m_abyNoData.data(), dt, &pszTmpVal,
    3745           3 :                                             poAttr->GetDataType());
    3746           3 :             if (pszTmpVal)
    3747             :             {
    3748           3 :                 const bool bSame = strcmp(pszVal, pszTmpVal) == 0;
    3749           3 :                 CPLFree(pszTmpVal);
    3750           3 :                 if (bSame)
    3751           3 :                     return m_abyNoData.data();
    3752           2 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3753             :                          "%s attribute value ('%s') is not in the range of the "
    3754             :                          "variable data type",
    3755             :                          pszAttrName, pszVal);
    3756           2 :                 m_abyNoData.clear();
    3757           2 :                 return nullptr;
    3758             :             }
    3759             :         }
    3760             :     }
    3761             : 
    3762          60 :     if (m_bUseDefaultFillAsNoData && m_abyNoData.empty() &&
    3763          12 :         (m_nVarType == NC_SHORT || m_nVarType == NC_USHORT ||
    3764           8 :          m_nVarType == NC_INT || m_nVarType == NC_UINT ||
    3765           6 :          m_nVarType == NC_FLOAT || m_nVarType == NC_DOUBLE))
    3766             :     {
    3767           9 :         bool bGotNoData = false;
    3768             :         double dfNoData =
    3769           9 :             NCDFGetDefaultNoDataValue(m_gid, m_varid, m_nVarType, bGotNoData);
    3770           9 :         m_abyNoData.resize(dt.GetSize());
    3771           9 :         GDALCopyWords(&dfNoData, GDT_Float64, 0, &m_abyNoData[0],
    3772             :                       dt.GetNumericDataType(), 0, 1);
    3773             :     }
    3774          42 :     else if (m_bUseDefaultFillAsNoData && m_abyNoData.empty() &&
    3775           3 :              m_nVarType == NC_INT64)
    3776             :     {
    3777           1 :         bool bGotNoData = false;
    3778             :         const auto nNoData =
    3779           1 :             NCDFGetDefaultNoDataValueAsInt64(m_gid, m_varid, bGotNoData);
    3780           1 :         m_abyNoData.resize(dt.GetSize());
    3781           1 :         memcpy(&m_abyNoData[0], &nNoData, sizeof(nNoData));
    3782             :     }
    3783          40 :     else if (m_bUseDefaultFillAsNoData && m_abyNoData.empty() &&
    3784           2 :              m_nVarType == NC_UINT64)
    3785             :     {
    3786           1 :         bool bGotNoData = false;
    3787             :         const auto nNoData =
    3788           1 :             NCDFGetDefaultNoDataValueAsUInt64(m_gid, m_varid, bGotNoData);
    3789           1 :         m_abyNoData.resize(dt.GetSize());
    3790           1 :         memcpy(&m_abyNoData[0], &nNoData, sizeof(nNoData));
    3791             :     }
    3792             : 
    3793          48 :     return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
    3794             : }
    3795             : 
    3796             : /************************************************************************/
    3797             : /*                          SetRawNoDataValue()                         */
    3798             : /************************************************************************/
    3799             : 
    3800          17 : bool netCDFVariable::SetRawNoDataValue(const void *pNoData)
    3801             : {
    3802          17 :     GetDataType();
    3803          17 :     if (m_nVarType == NC_STRING)
    3804           0 :         return false;
    3805             : 
    3806          17 :     m_bGetRawNoDataValueHasRun = false;
    3807          34 :     CPLMutexHolderD(&hNCMutex);
    3808          17 :     m_poShared->SetDefineMode(true);
    3809             :     int ret;
    3810          17 :     if (pNoData == nullptr)
    3811             :     {
    3812           3 :         m_abyNoData.clear();
    3813           3 :         nc_type atttype = NC_NAT;
    3814           3 :         size_t attlen = 0;
    3815           3 :         if (nc_inq_att(m_gid, m_varid, _FillValue, &atttype, &attlen) ==
    3816             :             NC_NOERR)
    3817           2 :             ret = nc_del_att(m_gid, m_varid, _FillValue);
    3818             :         else
    3819           1 :             ret = NC_NOERR;
    3820           3 :         if (nc_inq_att(m_gid, m_varid, "missing_value", &atttype, &attlen) ==
    3821             :             NC_NOERR)
    3822             :         {
    3823           0 :             int ret2 = nc_del_att(m_gid, m_varid, "missing_value");
    3824           0 :             if (ret2 != NC_NOERR)
    3825           0 :                 ret = ret2;
    3826             :         }
    3827             :     }
    3828             :     else
    3829             :     {
    3830          14 :         const auto nSize = GetDataType().GetSize();
    3831          14 :         m_abyNoData.resize(nSize);
    3832          14 :         memcpy(&m_abyNoData[0], pNoData, nSize);
    3833             : 
    3834          14 :         std::vector<GByte> abyTmp(nSize);
    3835          14 :         memcpy(&abyTmp[0], pNoData, nSize);
    3836          14 :         ConvertGDALToNC(&abyTmp[0]);
    3837             : 
    3838          14 :         if (!m_bHasWrittenData)
    3839             :         {
    3840          12 :             ret = nc_def_var_fill(m_gid, m_varid, NC_FILL, &abyTmp[0]);
    3841          12 :             NCDF_ERR(ret);
    3842             :         }
    3843             : 
    3844          14 :         nc_type atttype = NC_NAT;
    3845          14 :         size_t attlen = 0;
    3846          14 :         if (nc_inq_att(m_gid, m_varid, "missing_value", &atttype, &attlen) ==
    3847             :             NC_NOERR)
    3848             :         {
    3849           2 :             if (nc_inq_att(m_gid, m_varid, _FillValue, &atttype, &attlen) ==
    3850             :                 NC_NOERR)
    3851             :             {
    3852           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    3853             :                          "Cannot change nodata when missing_value and "
    3854             :                          "_FillValue both exist");
    3855           1 :                 return false;
    3856             :             }
    3857           1 :             ret = nc_put_att(m_gid, m_varid, "missing_value", m_nVarType, 1,
    3858           1 :                              &abyTmp[0]);
    3859             :         }
    3860             :         else
    3861             :         {
    3862          12 :             ret = nc_put_att(m_gid, m_varid, _FillValue, m_nVarType, 1,
    3863          12 :                              &abyTmp[0]);
    3864             :         }
    3865             :     }
    3866          16 :     NCDF_ERR(ret);
    3867          16 :     if (ret == NC_NOERR)
    3868          16 :         m_bGetRawNoDataValueHasRun = true;
    3869          16 :     return ret == NC_NOERR;
    3870             : }
    3871             : 
    3872             : /************************************************************************/
    3873             : /*                               SetScale()                             */
    3874             : /************************************************************************/
    3875             : 
    3876           3 : bool netCDFVariable::SetScale(double dfScale, GDALDataType eStorageType)
    3877             : {
    3878           9 :     auto poAttr = GetAttribute(CF_SCALE_FACTOR);
    3879           3 :     if (!poAttr)
    3880             :     {
    3881           4 :         poAttr = CreateAttribute(
    3882             :             CF_SCALE_FACTOR, {},
    3883           4 :             GDALExtendedDataType::Create(
    3884             :                 eStorageType == GDT_Unknown ? GDT_Float64 : eStorageType),
    3885           2 :             nullptr);
    3886             :     }
    3887           3 :     if (!poAttr)
    3888           0 :         return false;
    3889           3 :     return poAttr->Write(dfScale);
    3890             : }
    3891             : 
    3892             : /************************************************************************/
    3893             : /*                              SetOffset()                             */
    3894             : /************************************************************************/
    3895             : 
    3896           3 : bool netCDFVariable::SetOffset(double dfOffset, GDALDataType eStorageType)
    3897             : {
    3898           9 :     auto poAttr = GetAttribute(CF_ADD_OFFSET);
    3899           3 :     if (!poAttr)
    3900             :     {
    3901           4 :         poAttr = CreateAttribute(
    3902             :             CF_ADD_OFFSET, {},
    3903           4 :             GDALExtendedDataType::Create(
    3904             :                 eStorageType == GDT_Unknown ? GDT_Float64 : eStorageType),
    3905           2 :             nullptr);
    3906             :     }
    3907           3 :     if (!poAttr)
    3908           0 :         return false;
    3909           3 :     return poAttr->Write(dfOffset);
    3910             : }
    3911             : 
    3912             : /************************************************************************/
    3913             : /*                               GetScale()                             */
    3914             : /************************************************************************/
    3915             : 
    3916          59 : double netCDFVariable::GetScale(bool *pbHasScale,
    3917             :                                 GDALDataType *peStorageType) const
    3918             : {
    3919         177 :     auto poAttr = GetAttribute(CF_SCALE_FACTOR);
    3920          59 :     if (!poAttr || poAttr->GetDataType().GetClass() != GEDTC_NUMERIC)
    3921             :     {
    3922          54 :         if (pbHasScale)
    3923          53 :             *pbHasScale = false;
    3924          54 :         return 1.0;
    3925             :     }
    3926           5 :     if (pbHasScale)
    3927           5 :         *pbHasScale = true;
    3928           5 :     if (peStorageType)
    3929           2 :         *peStorageType = poAttr->GetDataType().GetNumericDataType();
    3930           5 :     return poAttr->ReadAsDouble();
    3931             : }
    3932             : 
    3933             : /************************************************************************/
    3934             : /*                               GetOffset()                            */
    3935             : /************************************************************************/
    3936             : 
    3937          59 : double netCDFVariable::GetOffset(bool *pbHasOffset,
    3938             :                                  GDALDataType *peStorageType) const
    3939             : {
    3940         177 :     auto poAttr = GetAttribute(CF_ADD_OFFSET);
    3941          59 :     if (!poAttr || poAttr->GetDataType().GetClass() != GEDTC_NUMERIC)
    3942             :     {
    3943          54 :         if (pbHasOffset)
    3944          53 :             *pbHasOffset = false;
    3945          54 :         return 0.0;
    3946             :     }
    3947           5 :     if (pbHasOffset)
    3948           5 :         *pbHasOffset = true;
    3949           5 :     if (peStorageType)
    3950           2 :         *peStorageType = poAttr->GetDataType().GetNumericDataType();
    3951           5 :     return poAttr->ReadAsDouble();
    3952             : }
    3953             : 
    3954             : /************************************************************************/
    3955             : /*                           GetBlockSize()                             */
    3956             : /************************************************************************/
    3957             : 
    3958          97 : std::vector<GUInt64> netCDFVariable::GetBlockSize() const
    3959             : {
    3960          97 :     const auto nDimCount = GetDimensionCount();
    3961          97 :     std::vector<GUInt64> res(nDimCount);
    3962          97 :     if (res.empty())
    3963           0 :         return res;
    3964          97 :     int nStorageType = 0;
    3965             :     // We add 1 to the dimension count, for 2D char variables that we
    3966             :     // expose as a 1D variable.
    3967         194 :     std::vector<size_t> anTemp(1 + nDimCount);
    3968         194 :     CPLMutexHolderD(&hNCMutex);
    3969          97 :     nc_inq_var_chunking(m_gid, m_varid, &nStorageType, &anTemp[0]);
    3970          97 :     if (nStorageType == NC_CHUNKED)
    3971             :     {
    3972          33 :         for (size_t i = 0; i < res.size(); ++i)
    3973          21 :             res[i] = anTemp[i];
    3974             :     }
    3975          97 :     return res;
    3976             : }
    3977             : 
    3978             : /************************************************************************/
    3979             : /*                         GetAttribute()                               */
    3980             : /************************************************************************/
    3981             : 
    3982             : std::shared_ptr<GDALAttribute>
    3983        2960 : netCDFVariable::GetAttribute(const std::string &osName) const
    3984             : {
    3985        5920 :     CPLMutexHolderD(&hNCMutex);
    3986        2960 :     int nAttId = -1;
    3987        2960 :     if (nc_inq_attid(m_gid, m_varid, osName.c_str(), &nAttId) != NC_NOERR)
    3988        2391 :         return nullptr;
    3989        1138 :     return netCDFAttribute::Create(
    3990        1707 :         m_poShared, std::dynamic_pointer_cast<netCDFVariable>(m_pSelf.lock()),
    3991        1138 :         m_gid, m_varid, osName);
    3992             : }
    3993             : 
    3994             : /************************************************************************/
    3995             : /*                         GetAttributes()                              */
    3996             : /************************************************************************/
    3997             : 
    3998             : std::vector<std::shared_ptr<GDALAttribute>>
    3999          86 : netCDFVariable::GetAttributes(CSLConstList papszOptions) const
    4000             : {
    4001         172 :     CPLMutexHolderD(&hNCMutex);
    4002          86 :     std::vector<std::shared_ptr<GDALAttribute>> res;
    4003          86 :     int nbAttr = 0;
    4004          86 :     NCDF_ERR(nc_inq_varnatts(m_gid, m_varid, &nbAttr));
    4005          86 :     res.reserve(nbAttr);
    4006             :     const bool bShowAll =
    4007          86 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
    4008         367 :     for (int i = 0; i < nbAttr; i++)
    4009             :     {
    4010             :         char szAttrName[NC_MAX_NAME + 1];
    4011         281 :         szAttrName[0] = 0;
    4012         281 :         NCDF_ERR(nc_inq_attname(m_gid, m_varid, i, szAttrName));
    4013         281 :         if (bShowAll || (!EQUAL(szAttrName, _FillValue) &&
    4014         251 :                          !EQUAL(szAttrName, "missing_value") &&
    4015         251 :                          !EQUAL(szAttrName, CF_UNITS) &&
    4016         216 :                          !EQUAL(szAttrName, CF_SCALE_FACTOR) &&
    4017         216 :                          !EQUAL(szAttrName, CF_ADD_OFFSET) &&
    4018         216 :                          !EQUAL(szAttrName, CF_GRD_MAPPING) &&
    4019         185 :                          !(EQUAL(szAttrName, "_Unsigned") &&
    4020          29 :                            (m_nVarType == NC_BYTE || m_nVarType == NC_SHORT))))
    4021             :         {
    4022         624 :             res.emplace_back(netCDFAttribute::Create(
    4023         156 :                 m_poShared,
    4024         312 :                 std::dynamic_pointer_cast<netCDFVariable>(m_pSelf.lock()),
    4025         312 :                 m_gid, m_varid, szAttrName));
    4026             :         }
    4027             :     }
    4028         172 :     return res;
    4029             : }
    4030             : 
    4031             : /************************************************************************/
    4032             : /*                          CreateAttribute()                           */
    4033             : /************************************************************************/
    4034             : 
    4035          88 : std::shared_ptr<GDALAttribute> netCDFVariable::CreateAttribute(
    4036             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
    4037             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
    4038             : {
    4039         176 :     return netCDFAttribute::Create(
    4040         264 :         m_poShared, std::dynamic_pointer_cast<netCDFVariable>(m_pSelf.lock()),
    4041         176 :         m_gid, m_varid, osName, anDimensions, oDataType, papszOptions);
    4042             : }
    4043             : 
    4044             : /************************************************************************/
    4045             : /*                         DeleteAttribute()                            */
    4046             : /************************************************************************/
    4047             : 
    4048           2 : bool netCDFVariable::DeleteAttribute(const std::string &osName,
    4049             :                                      CSLConstList /*papszOptions*/)
    4050             : {
    4051           4 :     CPLMutexHolderD(&hNCMutex);
    4052           2 :     m_poShared->SetDefineMode(true);
    4053             : 
    4054           2 :     int ret = nc_del_att(m_gid, m_varid, osName.c_str());
    4055           2 :     NCDF_ERR(ret);
    4056           2 :     if (ret != NC_NOERR)
    4057           1 :         return false;
    4058             : 
    4059           1 :     auto it = m_oMapAttributes.find(osName);
    4060           1 :     if (it != m_oMapAttributes.end())
    4061             :     {
    4062           1 :         it->second->Deleted();
    4063           1 :         m_oMapAttributes.erase(it);
    4064             :     }
    4065             : 
    4066           1 :     return true;
    4067             : }
    4068             : 
    4069             : /************************************************************************/
    4070             : /*                      GetCoordinateVariables()                        */
    4071             : /************************************************************************/
    4072             : 
    4073             : std::vector<std::shared_ptr<GDALMDArray>>
    4074          12 : netCDFVariable::GetCoordinateVariables() const
    4075             : {
    4076          24 :     std::vector<std::shared_ptr<GDALMDArray>> ret;
    4077             : 
    4078          36 :     const auto poCoordinates = GetAttribute("coordinates");
    4079           3 :     if (poCoordinates &&
    4080          15 :         poCoordinates->GetDataType().GetClass() == GEDTC_STRING &&
    4081           3 :         poCoordinates->GetDimensionCount() == 0)
    4082             :     {
    4083           3 :         const char *pszCoordinates = poCoordinates->ReadAsString();
    4084           3 :         if (pszCoordinates)
    4085             :         {
    4086             :             const CPLStringList aosNames(
    4087           6 :                 NCDFTokenizeCoordinatesAttribute(pszCoordinates));
    4088           6 :             CPLMutexHolderD(&hNCMutex);
    4089           9 :             for (int i = 0; i < aosNames.size(); i++)
    4090             :             {
    4091           6 :                 int nVarId = 0;
    4092           6 :                 if (nc_inq_varid(m_gid, aosNames[i], &nVarId) == NC_NOERR)
    4093             :                 {
    4094           6 :                     ret.emplace_back(netCDFVariable::Create(
    4095          12 :                         m_poShared, m_poParent.lock(), m_gid, nVarId,
    4096          12 :                         std::vector<std::shared_ptr<GDALDimension>>(), nullptr,
    4097           6 :                         false));
    4098             :                 }
    4099             :                 else
    4100             :                 {
    4101           0 :                     CPLError(
    4102             :                         CE_Warning, CPLE_AppDefined,
    4103             :                         "Cannot find variable corresponding to coordinate %s",
    4104             :                         aosNames[i]);
    4105             :                 }
    4106             :             }
    4107             :         }
    4108             :     }
    4109             : 
    4110             :     // Special case for NASA EMIT datasets
    4111          24 :     auto apoDims = GetDimensions();
    4112          21 :     if ((apoDims.size() == 3 && apoDims[0]->GetName() == "downtrack" &&
    4113           6 :          apoDims[1]->GetName() == "crosstrack" &&
    4114          33 :          apoDims[2]->GetName() == "bands") ||
    4115          18 :         (apoDims.size() == 2 && apoDims[0]->GetName() == "downtrack" &&
    4116           3 :          apoDims[1]->GetName() == "crosstrack"))
    4117             :     {
    4118           6 :         auto poRootGroup = netCDFGroup::Create(m_poShared, nullptr, m_gid);
    4119           6 :         if (poRootGroup)
    4120             :         {
    4121          12 :             auto poLocationGroup = poRootGroup->OpenGroup("location");
    4122           6 :             if (poLocationGroup)
    4123             :             {
    4124          12 :                 auto poLon = poLocationGroup->OpenMDArray("lon");
    4125          12 :                 auto poLat = poLocationGroup->OpenMDArray("lat");
    4126           6 :                 if (poLon && poLat)
    4127             :                 {
    4128          18 :                     return {std::move(poLon), std::move(poLat)};
    4129             :                 }
    4130             :             }
    4131             :         }
    4132             :     }
    4133             : 
    4134           6 :     return ret;
    4135             : }
    4136             : 
    4137             : /************************************************************************/
    4138             : /*                            Resize()                                  */
    4139             : /************************************************************************/
    4140             : 
    4141           9 : bool netCDFVariable::Resize(const std::vector<GUInt64> &anNewDimSizes,
    4142             :                             CSLConstList /* papszOptions */)
    4143             : {
    4144           9 :     if (!IsWritable())
    4145             :     {
    4146           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4147             :                  "Resize() not supported on read-only file");
    4148           1 :         return false;
    4149             :     }
    4150             : 
    4151           8 :     const auto nDimCount = GetDimensionCount();
    4152           8 :     if (anNewDimSizes.size() != nDimCount)
    4153             :     {
    4154           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    4155             :                  "Not expected number of values in anNewDimSizes.");
    4156           0 :         return false;
    4157             :     }
    4158             : 
    4159           8 :     auto &dims = GetDimensions();
    4160          16 :     std::vector<size_t> anGrownDimIdx;
    4161          16 :     std::map<GDALDimension *, GUInt64> oMapDimToSize;
    4162          18 :     for (size_t i = 0; i < nDimCount; ++i)
    4163             :     {
    4164          14 :         auto oIter = oMapDimToSize.find(dims[i].get());
    4165          14 :         if (oIter != oMapDimToSize.end() && oIter->second != anNewDimSizes[i])
    4166             :         {
    4167           2 :             CPLError(CE_Failure, CPLE_AppDefined,
    4168             :                      "Cannot resize a dimension referenced several times "
    4169             :                      "to different sizes");
    4170           4 :             return false;
    4171             :         }
    4172          12 :         if (anNewDimSizes[i] != dims[i]->GetSize())
    4173             :         {
    4174           9 :             if (anNewDimSizes[i] < dims[i]->GetSize())
    4175             :             {
    4176           2 :                 CPLError(CE_Failure, CPLE_NotSupported,
    4177             :                          "Resize() does not support shrinking the array.");
    4178           2 :                 return false;
    4179             :             }
    4180             : 
    4181           7 :             oMapDimToSize[dims[i].get()] = anNewDimSizes[i];
    4182           7 :             anGrownDimIdx.push_back(i);
    4183             :         }
    4184             :         else
    4185             :         {
    4186           3 :             oMapDimToSize[dims[i].get()] = dims[i]->GetSize();
    4187             :         }
    4188             :     }
    4189             : 
    4190           4 :     if (!anGrownDimIdx.empty())
    4191             :     {
    4192           4 :         CPLMutexHolderD(&hNCMutex);
    4193             :         // Query which netCDF dimensions have unlimited size
    4194           4 :         int nUnlimitedDimIds = 0;
    4195           4 :         nc_inq_unlimdims(m_gid, &nUnlimitedDimIds, nullptr);
    4196           4 :         std::vector<int> anUnlimitedDimIds(nUnlimitedDimIds);
    4197           4 :         nc_inq_unlimdims(m_gid, &nUnlimitedDimIds, anUnlimitedDimIds.data());
    4198           4 :         std::set<int> oSetUnlimitedDimId;
    4199           8 :         for (int idx : anUnlimitedDimIds)
    4200           4 :             oSetUnlimitedDimId.insert(idx);
    4201             : 
    4202             :         // Check that dimensions that need to grow are of unlimited size
    4203           8 :         for (size_t dimIdx : anGrownDimIdx)
    4204             :         {
    4205             :             auto netCDFDim =
    4206           5 :                 std::dynamic_pointer_cast<netCDFDimension>(dims[dimIdx]);
    4207           5 :             if (!netCDFDim)
    4208             :             {
    4209           0 :                 CPLAssert(false);
    4210             :             }
    4211           5 :             else if (oSetUnlimitedDimId.find(netCDFDim->GetId()) ==
    4212          10 :                      oSetUnlimitedDimId.end())
    4213             :             {
    4214           1 :                 CPLError(CE_Failure, CPLE_NotSupported,
    4215             :                          "Resize() cannot grow dimension %d (%s) "
    4216             :                          "as it is not created as UNLIMITED.",
    4217             :                          static_cast<int>(dimIdx),
    4218           1 :                          netCDFDim->GetName().c_str());
    4219           1 :                 return false;
    4220             :             }
    4221             :         }
    4222           9 :         for (size_t i = 0; i < nDimCount; ++i)
    4223             :         {
    4224           6 :             if (anNewDimSizes[i] > dims[i]->GetSize())
    4225             :             {
    4226             :                 auto netCDFDim =
    4227           6 :                     std::dynamic_pointer_cast<netCDFDimension>(dims[i]);
    4228           3 :                 if (!netCDFDim)
    4229             :                 {
    4230           0 :                     CPLAssert(false);
    4231             :                 }
    4232             :                 else
    4233             :                 {
    4234           3 :                     netCDFDim->SetSize(anNewDimSizes[i]);
    4235             :                 }
    4236             :             }
    4237             :         }
    4238             :     }
    4239           3 :     return true;
    4240             : }
    4241             : 
    4242             : /************************************************************************/
    4243             : /*                              Rename()                                */
    4244             : /************************************************************************/
    4245             : 
    4246           4 : bool netCDFVariable::Rename(const std::string &osNewName)
    4247             : {
    4248           4 :     if (m_poShared->IsReadOnly())
    4249             :     {
    4250           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4251             :                  "Rename() not supported on read-only file");
    4252           1 :         return false;
    4253             :     }
    4254           3 :     if (osNewName.empty())
    4255             :     {
    4256           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
    4257           1 :         return false;
    4258             :     }
    4259             : 
    4260           4 :     CPLMutexHolderD(&hNCMutex);
    4261           2 :     m_poShared->SetDefineMode(true);
    4262             : 
    4263           2 :     int ret = nc_rename_var(m_gid, m_varid, osNewName.c_str());
    4264           2 :     NCDF_ERR(ret);
    4265           2 :     if (ret != NC_NOERR)
    4266           1 :         return false;
    4267             : 
    4268           1 :     BaseRename(osNewName);
    4269             : 
    4270           1 :     return true;
    4271             : }
    4272             : 
    4273             : /************************************************************************/
    4274             : /*                       NotifyChildrenOfRenaming()                     */
    4275             : /************************************************************************/
    4276             : 
    4277           3 : void netCDFVariable::NotifyChildrenOfRenaming()
    4278             : {
    4279           6 :     for (const auto &iter : m_oMapAttributes)
    4280           3 :         iter.second->ParentRenamed(m_osFullName);
    4281           3 : }
    4282             : 
    4283             : /************************************************************************/
    4284             : /*                    retrieveAttributeParentName()                     */
    4285             : /************************************************************************/
    4286             : 
    4287        2008 : static CPLString retrieveAttributeParentName(int gid, int varid)
    4288             : {
    4289        4016 :     auto groupName(NCDFGetGroupFullName(gid));
    4290        2008 :     if (varid == NC_GLOBAL)
    4291             :     {
    4292         384 :         if (groupName == "/")
    4293         358 :             return "/_GLOBAL_";
    4294          52 :         return groupName + "/_GLOBAL_";
    4295             :     }
    4296             : 
    4297        3248 :     return groupName + "/" + netCDFVariable::retrieveName(gid, varid);
    4298             : }
    4299             : 
    4300             : /************************************************************************/
    4301             : /*                          netCDFAttribute()                           */
    4302             : /************************************************************************/
    4303             : 
    4304         843 : netCDFAttribute::netCDFAttribute(
    4305             :     const std::shared_ptr<netCDFSharedResources> &poShared, int gid, int varid,
    4306         843 :     const std::string &name)
    4307         843 :     : GDALAbstractMDArray(retrieveAttributeParentName(gid, varid), name),
    4308         843 :       GDALAttribute(retrieveAttributeParentName(gid, varid), name),
    4309        2529 :       m_poShared(poShared), m_gid(gid), m_varid(varid)
    4310             : {
    4311        1686 :     CPLMutexHolderD(&hNCMutex);
    4312         843 :     size_t nLen = 0;
    4313         843 :     NCDF_ERR(nc_inq_atttype(m_gid, m_varid, GetName().c_str(), &m_nAttType));
    4314         843 :     NCDF_ERR(nc_inq_attlen(m_gid, m_varid, GetName().c_str(), &nLen));
    4315         843 :     if (m_nAttType == NC_CHAR)
    4316             :     {
    4317         653 :         m_nTextLength = nLen;
    4318             :     }
    4319         190 :     else if (nLen > 1)
    4320             :     {
    4321         160 :         m_dims.emplace_back(std::make_shared<GDALDimension>(
    4322         240 :             std::string(), "length", std::string(), std::string(), nLen));
    4323             :     }
    4324         843 : }
    4325             : 
    4326             : /************************************************************************/
    4327             : /*                          netCDFAttribute()                           */
    4328             : /************************************************************************/
    4329             : 
    4330         161 : netCDFAttribute::netCDFAttribute(
    4331             :     const std::shared_ptr<netCDFSharedResources> &poShared, int gid, int varid,
    4332             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
    4333         161 :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
    4334         161 :     : GDALAbstractMDArray(retrieveAttributeParentName(gid, varid), osName),
    4335         161 :       GDALAttribute(retrieveAttributeParentName(gid, varid), osName),
    4336         483 :       m_poShared(poShared), m_gid(gid), m_varid(varid)
    4337             : {
    4338         322 :     CPLMutexHolderD(&hNCMutex);
    4339         161 :     m_bPerfectDataTypeMatch = true;
    4340         161 :     m_nAttType = CreateOrGetType(gid, oDataType);
    4341         161 :     m_dt.reset(new GDALExtendedDataType(oDataType));
    4342         161 :     if (!anDimensions.empty())
    4343             :     {
    4344          20 :         m_dims.emplace_back(std::make_shared<GDALDimension>(
    4345          20 :             std::string(), "length", std::string(), std::string(),
    4346          20 :             anDimensions[0]));
    4347             :     }
    4348             : 
    4349         161 :     const char *pszType = CSLFetchNameValueDef(papszOptions, "NC_TYPE", "");
    4350         293 :     if (oDataType.GetClass() == GEDTC_STRING && anDimensions.empty() &&
    4351         132 :         (EQUAL(pszType, "") || EQUAL(pszType, "NC_CHAR")))
    4352             :     {
    4353         131 :         m_nAttType = NC_CHAR;
    4354             :     }
    4355          40 :     else if (oDataType.GetNumericDataType() == GDT_Byte &&
    4356          10 :              EQUAL(CSLFetchNameValueDef(papszOptions, "NC_TYPE", ""),
    4357             :                    "NC_BYTE"))
    4358             :     {
    4359           1 :         m_nAttType = NC_BYTE;
    4360             :     }
    4361          31 :     else if (oDataType.GetNumericDataType() == GDT_Int16 &&
    4362           2 :              EQUAL(CSLFetchNameValueDef(papszOptions, "NC_TYPE", ""),
    4363             :                    "NC_BYTE"))
    4364             :     {
    4365           2 :         m_bPerfectDataTypeMatch = false;
    4366           2 :         m_nAttType = NC_BYTE;
    4367             :     }
    4368          27 :     else if (oDataType.GetNumericDataType() == GDT_Float64)
    4369             :     {
    4370           9 :         if (EQUAL(pszType, "NC_INT64"))
    4371             :         {
    4372           2 :             m_bPerfectDataTypeMatch = false;
    4373           2 :             m_nAttType = NC_INT64;
    4374             :         }
    4375           7 :         else if (EQUAL(pszType, "NC_UINT64"))
    4376             :         {
    4377           1 :             m_bPerfectDataTypeMatch = false;
    4378           1 :             m_nAttType = NC_UINT64;
    4379             :         }
    4380             :     }
    4381         161 : }
    4382             : 
    4383             : /************************************************************************/
    4384             : /*                         ~netCDFAttribute()                           */
    4385             : /************************************************************************/
    4386             : 
    4387        2008 : netCDFAttribute::~netCDFAttribute()
    4388             : {
    4389        1004 :     if (m_bValid)
    4390             :     {
    4391        1591 :         if (auto poParent = m_poParent.lock())
    4392         589 :             poParent->UnRegisterAttribute(this);
    4393             :     }
    4394        2008 : }
    4395             : 
    4396             : /************************************************************************/
    4397             : /*                              Create()                                */
    4398             : /************************************************************************/
    4399             : 
    4400             : std::shared_ptr<netCDFAttribute>
    4401         843 : netCDFAttribute::Create(const std::shared_ptr<netCDFSharedResources> &poShared,
    4402             :                         const std::shared_ptr<netCDFAttributeHolder> &poParent,
    4403             :                         int gid, int varid, const std::string &name)
    4404             : {
    4405             :     auto attr(std::shared_ptr<netCDFAttribute>(
    4406         843 :         new netCDFAttribute(poShared, gid, varid, name)));
    4407         843 :     attr->SetSelf(attr);
    4408         843 :     attr->m_poParent = poParent;
    4409         843 :     if (poParent)
    4410         492 :         poParent->RegisterAttribute(attr.get());
    4411         843 :     return attr;
    4412             : }
    4413             : 
    4414         164 : std::shared_ptr<netCDFAttribute> netCDFAttribute::Create(
    4415             :     const std::shared_ptr<netCDFSharedResources> &poShared,
    4416             :     const std::shared_ptr<netCDFAttributeHolder> &poParent, int gid, int varid,
    4417             :     const std::string &osName, const std::vector<GUInt64> &anDimensions,
    4418             :     const GDALExtendedDataType &oDataType, CSLConstList papszOptions)
    4419             : {
    4420         164 :     if (poShared->IsReadOnly())
    4421             :     {
    4422           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    4423             :                  "CreateAttribute() not supported on read-only file");
    4424           2 :         return nullptr;
    4425             :     }
    4426         162 :     if (anDimensions.size() > 1)
    4427             :     {
    4428           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    4429             :                  "Only 0 or 1-dimensional attribute are supported");
    4430           1 :         return nullptr;
    4431             :     }
    4432             : 
    4433         161 :     const char *apszOptions[2] = {nullptr, nullptr};
    4434         176 :     if (!poShared->IsNC4() && oDataType.GetClass() == GEDTC_NUMERIC &&
    4435         176 :         oDataType.GetNumericDataType() == GDT_Byte && !papszOptions)
    4436             :     {
    4437             :         // GDT_Byte would map to a NC_UBYTE datatype, which is not available in
    4438             :         // NC3 datasets
    4439           1 :         apszOptions[0] = "NC_TYPE=NC_BYTE";
    4440           1 :         papszOptions = apszOptions;
    4441             :     }
    4442             : 
    4443             :     auto attr(std::shared_ptr<netCDFAttribute>(new netCDFAttribute(
    4444         322 :         poShared, gid, varid, osName, anDimensions, oDataType, papszOptions)));
    4445         161 :     if (attr->m_nAttType == NC_NAT)
    4446           0 :         return nullptr;
    4447         161 :     attr->SetSelf(attr);
    4448         161 :     attr->m_poParent = poParent;
    4449         161 :     if (poParent)
    4450         161 :         poParent->RegisterAttribute(attr.get());
    4451         161 :     return attr;
    4452             : }
    4453             : 
    4454             : /************************************************************************/
    4455             : /*                             GetDataType()                            */
    4456             : /************************************************************************/
    4457             : 
    4458        3320 : const GDALExtendedDataType &netCDFAttribute::GetDataType() const
    4459             : {
    4460        3320 :     if (m_dt)
    4461        2620 :         return *m_dt;
    4462        1400 :     CPLMutexHolderD(&hNCMutex);
    4463             : 
    4464         700 :     if (m_nAttType == NC_CHAR)
    4465             :     {
    4466         582 :         m_dt.reset(
    4467         582 :             new GDALExtendedDataType(GDALExtendedDataType::CreateString()));
    4468             :     }
    4469             :     else
    4470             :     {
    4471         118 :         m_dt.reset(new GDALExtendedDataType(
    4472         118 :             GDALExtendedDataType::Create(GDT_Unknown)));
    4473         118 :         BuildDataType(m_gid, m_varid, m_nAttType, m_dt,
    4474         118 :                       m_bPerfectDataTypeMatch);
    4475             :     }
    4476             : 
    4477         700 :     return *m_dt;
    4478             : }
    4479             : 
    4480             : /************************************************************************/
    4481             : /*                                   IRead()                            */
    4482             : /************************************************************************/
    4483             : 
    4484         718 : bool netCDFAttribute::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    4485             :                             const GInt64 *arrayStep,
    4486             :                             const GPtrDiff_t *bufferStride,
    4487             :                             const GDALExtendedDataType &bufferDataType,
    4488             :                             void *pDstBuffer) const
    4489             : {
    4490         718 :     if (!CheckValidAndErrorOutIfNot())
    4491           0 :         return false;
    4492        1436 :     CPLMutexHolderD(&hNCMutex);
    4493             : 
    4494         718 :     if (m_nAttType == NC_STRING)
    4495             :     {
    4496           8 :         CPLAssert(GetDataType().GetClass() == GEDTC_STRING);
    4497             :         std::vector<char *> apszStrings(
    4498          16 :             static_cast<size_t>(GetTotalElementsCount()));
    4499           8 :         int ret = nc_get_att_string(m_gid, m_varid, GetName().c_str(),
    4500           8 :                                     &apszStrings[0]);
    4501           8 :         NCDF_ERR(ret);
    4502           8 :         if (ret != NC_NOERR)
    4503           0 :             return false;
    4504           8 :         if (m_dims.empty())
    4505             :         {
    4506           3 :             const char *pszStr = apszStrings[0];
    4507           3 :             GDALExtendedDataType::CopyValue(&pszStr, GetDataType(), pDstBuffer,
    4508             :                                             bufferDataType);
    4509             :         }
    4510             :         else
    4511             :         {
    4512           5 :             GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4513          14 :             for (size_t i = 0; i < count[0]; i++)
    4514             :             {
    4515           9 :                 auto srcIdx =
    4516           9 :                     static_cast<size_t>(arrayStartIdx[0] + arrayStep[0] * i);
    4517           9 :                 const char *pszStr = apszStrings[srcIdx];
    4518           9 :                 GDALExtendedDataType::CopyValue(&pszStr, GetDataType(),
    4519             :                                                 pabyDstBuffer, bufferDataType);
    4520           9 :                 pabyDstBuffer += sizeof(char *) * bufferStride[0];
    4521             :             }
    4522             :         }
    4523           8 :         nc_free_string(apszStrings.size(), &apszStrings[0]);
    4524           8 :         return true;
    4525             :     }
    4526             : 
    4527         710 :     if (m_nAttType == NC_CHAR)
    4528             :     {
    4529         588 :         CPLAssert(GetDataType().GetClass() == GEDTC_STRING);
    4530         588 :         CPLAssert(m_dims.empty());
    4531         588 :         if (bufferDataType != GetDataType())
    4532             :         {
    4533           0 :             std::string osStr;
    4534           0 :             osStr.resize(m_nTextLength);
    4535             :             int ret =
    4536           0 :                 nc_get_att_text(m_gid, m_varid, GetName().c_str(), &osStr[0]);
    4537           0 :             NCDF_ERR(ret);
    4538           0 :             if (ret != NC_NOERR)
    4539           0 :                 return false;
    4540           0 :             const char *pszStr = osStr.c_str();
    4541           0 :             GDALExtendedDataType::CopyValue(&pszStr, GetDataType(), pDstBuffer,
    4542             :                                             bufferDataType);
    4543             :         }
    4544             :         else
    4545             :         {
    4546         588 :             char *pszStr = static_cast<char *>(CPLCalloc(1, m_nTextLength + 1));
    4547             :             int ret =
    4548         588 :                 nc_get_att_text(m_gid, m_varid, GetName().c_str(), pszStr);
    4549         588 :             NCDF_ERR(ret);
    4550         588 :             if (ret != NC_NOERR)
    4551             :             {
    4552           1 :                 CPLFree(pszStr);
    4553           1 :                 return false;
    4554             :             }
    4555         587 :             *static_cast<char **>(pDstBuffer) = pszStr;
    4556             :         }
    4557         587 :         return true;
    4558             :     }
    4559             : 
    4560         122 :     const auto &dt(GetDataType());
    4561         240 :     if (dt.GetClass() == GEDTC_NUMERIC &&
    4562         118 :         dt.GetNumericDataType() == GDT_Unknown)
    4563             :     {
    4564           0 :         return false;
    4565             :     }
    4566             : 
    4567         122 :     CPLAssert(dt.GetClass() != GEDTC_STRING);
    4568         218 :     const bool bFastPath = ((m_dims.size() == 1 && arrayStartIdx[0] == 0 &&
    4569          48 :                              count[0] == m_dims[0]->GetSize() &&
    4570         122 :                              arrayStep[0] == 1 && bufferStride[0] == 1) ||
    4571          75 :                             m_dims.empty()) &&
    4572         303 :                            m_bPerfectDataTypeMatch && bufferDataType == dt &&
    4573          59 :                            dt.GetSize() > 0;
    4574         122 :     if (bFastPath)
    4575             :     {
    4576          59 :         int ret = nc_get_att(m_gid, m_varid, GetName().c_str(), pDstBuffer);
    4577          59 :         NCDF_ERR(ret);
    4578          59 :         if (ret == NC_NOERR)
    4579             :         {
    4580          59 :             ConvertNCStringsToCPLStrings(static_cast<GByte *>(pDstBuffer), dt);
    4581             :         }
    4582          59 :         return ret == NC_NOERR;
    4583             :     }
    4584             : 
    4585             :     const auto nElementSize =
    4586          63 :         GetNCTypeSize(dt, m_bPerfectDataTypeMatch, m_nAttType);
    4587          63 :     if (nElementSize == 0)
    4588           0 :         return false;
    4589          63 :     const auto nOutputDTSize = bufferDataType.GetSize();
    4590          63 :     std::vector<GByte> abyBuffer(static_cast<size_t>(GetTotalElementsCount()) *
    4591         189 :                                  nElementSize);
    4592          63 :     int ret = nc_get_att(m_gid, m_varid, GetName().c_str(), &abyBuffer[0]);
    4593          63 :     NCDF_ERR(ret);
    4594          63 :     if (ret != NC_NOERR)
    4595           0 :         return false;
    4596             : 
    4597             :     GByte *pabySrcBuffer =
    4598          63 :         m_dims.empty()
    4599          63 :             ? abyBuffer.data()
    4600          37 :             : abyBuffer.data() +
    4601          37 :                   static_cast<size_t>(arrayStartIdx[0]) * nElementSize;
    4602          63 :     GByte *pabyDstBuffer = static_cast<GByte *>(pDstBuffer);
    4603         190 :     for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
    4604             :     {
    4605             :         GByte abyTmpBuffer[sizeof(double)];
    4606         127 :         const GByte *pabySrcElement = pabySrcBuffer;
    4607         127 :         if (!m_bPerfectDataTypeMatch)
    4608             :         {
    4609           7 :             if (m_nAttType == NC_BYTE)
    4610             :             {
    4611           3 :                 short s =
    4612           3 :                     reinterpret_cast<const signed char *>(pabySrcBuffer)[0];
    4613           3 :                 memcpy(abyTmpBuffer, &s, sizeof(s));
    4614           3 :                 pabySrcElement = abyTmpBuffer;
    4615             :             }
    4616           4 :             else if (m_nAttType == NC_INT64)
    4617             :             {
    4618           3 :                 double v = static_cast<double>(
    4619           3 :                     reinterpret_cast<const GInt64 *>(pabySrcBuffer)[0]);
    4620           3 :                 memcpy(abyTmpBuffer, &v, sizeof(v));
    4621           3 :                 pabySrcElement = abyTmpBuffer;
    4622             :             }
    4623           1 :             else if (m_nAttType == NC_UINT64)
    4624             :             {
    4625           1 :                 double v = static_cast<double>(
    4626           1 :                     reinterpret_cast<const GUInt64 *>(pabySrcBuffer)[0]);
    4627           1 :                 memcpy(abyTmpBuffer, &v, sizeof(v));
    4628           1 :                 pabySrcElement = abyTmpBuffer;
    4629             :             }
    4630             :             else
    4631             :             {
    4632           0 :                 CPLAssert(false);
    4633             :             }
    4634             :         }
    4635         127 :         GDALExtendedDataType::CopyValue(pabySrcElement, dt, pabyDstBuffer,
    4636             :                                         bufferDataType);
    4637         127 :         FreeNCStrings(pabySrcBuffer, dt);
    4638         127 :         if (!m_dims.empty())
    4639             :         {
    4640         101 :             pabySrcBuffer +=
    4641         101 :                 static_cast<std::ptrdiff_t>(arrayStep[0] * nElementSize);
    4642         101 :             pabyDstBuffer += nOutputDTSize * bufferStride[0];
    4643             :         }
    4644             :     }
    4645             : 
    4646          63 :     return true;
    4647             : }
    4648             : 
    4649             : /************************************************************************/
    4650             : /*                                IWrite()                              */
    4651             : /************************************************************************/
    4652             : 
    4653         166 : bool netCDFAttribute::IWrite(const GUInt64 *arrayStartIdx, const size_t *count,
    4654             :                              const GInt64 *arrayStep,
    4655             :                              const GPtrDiff_t *bufferStride,
    4656             :                              const GDALExtendedDataType &bufferDataType,
    4657             :                              const void *pSrcBuffer)
    4658             : {
    4659         166 :     if (!CheckValidAndErrorOutIfNot())
    4660           0 :         return false;
    4661         332 :     CPLMutexHolderD(&hNCMutex);
    4662             : 
    4663         177 :     if (m_dims.size() == 1 &&
    4664          11 :         (arrayStartIdx[0] != 0 || count[0] != m_dims[0]->GetSize() ||
    4665          11 :          arrayStep[0] != 1))
    4666             :     {
    4667           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    4668             :                  "Only contiguous writing of attribute values supported");
    4669           0 :         return false;
    4670             :     }
    4671             : 
    4672         166 :     m_poShared->SetDefineMode(true);
    4673             : 
    4674         166 :     if (m_nAttType == NC_STRING)
    4675             :     {
    4676           4 :         CPLAssert(GetDataType().GetClass() == GEDTC_STRING);
    4677           4 :         if (m_dims.empty())
    4678             :         {
    4679           2 :             char *pszStr = nullptr;
    4680             :             const char *pszStrConst;
    4681           2 :             if (bufferDataType != GetDataType())
    4682             :             {
    4683           1 :                 GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType,
    4684             :                                                 &pszStr, GetDataType());
    4685           1 :                 pszStrConst = pszStr;
    4686             :             }
    4687             :             else
    4688             :             {
    4689           1 :                 memcpy(&pszStrConst, pSrcBuffer, sizeof(const char *));
    4690             :             }
    4691           2 :             int ret = nc_put_att_string(m_gid, m_varid, GetName().c_str(), 1,
    4692             :                                         &pszStrConst);
    4693           2 :             CPLFree(pszStr);
    4694           2 :             NCDF_ERR(ret);
    4695           2 :             if (ret != NC_NOERR)
    4696           0 :                 return false;
    4697           2 :             return true;
    4698             :         }
    4699             : 
    4700             :         int ret;
    4701           2 :         if (bufferDataType != GetDataType())
    4702             :         {
    4703           2 :             std::vector<char *> apszStrings(count[0]);
    4704             :             const char **ppszStr;
    4705           1 :             memcpy(&ppszStr, &pSrcBuffer, sizeof(const char **));
    4706           3 :             for (size_t i = 0; i < count[0]; i++)
    4707             :             {
    4708           4 :                 GDALExtendedDataType::CopyValue(&ppszStr[i], bufferDataType,
    4709           2 :                                                 &apszStrings[i], GetDataType());
    4710             :             }
    4711           1 :             ret = nc_put_att_string(m_gid, m_varid, GetName().c_str(), count[0],
    4712           1 :                                     const_cast<const char **>(&apszStrings[0]));
    4713           3 :             for (size_t i = 0; i < count[0]; i++)
    4714             :             {
    4715           2 :                 CPLFree(apszStrings[i]);
    4716             :             }
    4717             :         }
    4718             :         else
    4719             :         {
    4720             :             const char **ppszStr;
    4721           1 :             memcpy(&ppszStr, &pSrcBuffer, sizeof(const char **));
    4722           1 :             ret = nc_put_att_string(m_gid, m_varid, GetName().c_str(), count[0],
    4723             :                                     ppszStr);
    4724             :         }
    4725           2 :         NCDF_ERR(ret);
    4726           2 :         if (ret != NC_NOERR)
    4727           0 :             return false;
    4728           2 :         return true;
    4729             :     }
    4730             : 
    4731         162 :     if (m_nAttType == NC_CHAR)
    4732             :     {
    4733         137 :         CPLAssert(GetDataType().GetClass() == GEDTC_STRING);
    4734         137 :         CPLAssert(m_dims.empty());
    4735         137 :         char *pszStr = nullptr;
    4736             :         const char *pszStrConst;
    4737         137 :         if (bufferDataType != GetDataType())
    4738             :         {
    4739           1 :             GDALExtendedDataType::CopyValue(pSrcBuffer, bufferDataType, &pszStr,
    4740             :                                             GetDataType());
    4741           1 :             pszStrConst = pszStr;
    4742             :         }
    4743             :         else
    4744             :         {
    4745         136 :             memcpy(&pszStrConst, pSrcBuffer, sizeof(const char *));
    4746             :         }
    4747         137 :         m_nTextLength = pszStrConst ? strlen(pszStrConst) : 0;
    4748         137 :         int ret = nc_put_att_text(m_gid, m_varid, GetName().c_str(),
    4749             :                                   m_nTextLength, pszStrConst);
    4750         137 :         CPLFree(pszStr);
    4751         137 :         NCDF_ERR(ret);
    4752         137 :         if (ret != NC_NOERR)
    4753           1 :             return false;
    4754         136 :         return true;
    4755             :     }
    4756             : 
    4757          25 :     const auto &dt(GetDataType());
    4758          49 :     if (dt.GetClass() == GEDTC_NUMERIC &&
    4759          24 :         dt.GetNumericDataType() == GDT_Unknown)
    4760             :     {
    4761           0 :         return false;
    4762             :     }
    4763             : 
    4764          25 :     CPLAssert(dt.GetClass() != GEDTC_STRING);
    4765             :     const bool bFastPath =
    4766          50 :         ((m_dims.size() == 1 && bufferStride[0] == 1) || m_dims.empty()) &&
    4767          50 :         m_bPerfectDataTypeMatch && bufferDataType == dt && dt.GetSize() > 0;
    4768          25 :     if (bFastPath)
    4769             :     {
    4770          11 :         int ret = nc_put_att(m_gid, m_varid, GetName().c_str(), m_nAttType,
    4771          11 :                              m_dims.empty() ? 1 : count[0], pSrcBuffer);
    4772          11 :         NCDF_ERR(ret);
    4773          11 :         return ret == NC_NOERR;
    4774             :     }
    4775             : 
    4776             :     const auto nElementSize =
    4777          14 :         GetNCTypeSize(dt, m_bPerfectDataTypeMatch, m_nAttType);
    4778          14 :     if (nElementSize == 0)
    4779           0 :         return false;
    4780          14 :     const auto nInputDTSize = bufferDataType.GetSize();
    4781          14 :     std::vector<GByte> abyBuffer(static_cast<size_t>(GetTotalElementsCount()) *
    4782          28 :                                  nElementSize);
    4783             : 
    4784          14 :     const GByte *pabySrcBuffer = static_cast<const GByte *>(pSrcBuffer);
    4785          14 :     auto pabyDstBuffer = &abyBuffer[0];
    4786          31 :     for (size_t i = 0; i < (m_dims.empty() ? 1 : count[0]); i++)
    4787             :     {
    4788          17 :         if (!m_bPerfectDataTypeMatch)
    4789             :         {
    4790           7 :             if (m_nAttType == NC_BYTE)
    4791             :             {
    4792             :                 short s;
    4793           3 :                 GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
    4794             :                                                 &s, dt);
    4795           3 :                 signed char c = static_cast<signed char>(s);
    4796           3 :                 memcpy(pabyDstBuffer, &c, sizeof(c));
    4797             :             }
    4798           4 :             else if (m_nAttType == NC_INT64)
    4799             :             {
    4800             :                 double d;
    4801           3 :                 GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
    4802             :                                                 &d, dt);
    4803           3 :                 GInt64 v = static_cast<GInt64>(d);
    4804           3 :                 memcpy(pabyDstBuffer, &v, sizeof(v));
    4805             :             }
    4806           1 :             else if (m_nAttType == NC_UINT64)
    4807             :             {
    4808             :                 double d;
    4809           1 :                 GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
    4810             :                                                 &d, dt);
    4811           1 :                 GUInt64 v = static_cast<GUInt64>(d);
    4812           1 :                 memcpy(pabyDstBuffer, &v, sizeof(v));
    4813             :             }
    4814             :             else
    4815             :             {
    4816           0 :                 CPLAssert(false);
    4817             :             }
    4818             :         }
    4819             :         else
    4820             :         {
    4821          10 :             GDALExtendedDataType::CopyValue(pabySrcBuffer, bufferDataType,
    4822             :                                             pabyDstBuffer, dt);
    4823             :         }
    4824             : 
    4825          17 :         if (!m_dims.empty())
    4826             :         {
    4827           7 :             pabySrcBuffer += nInputDTSize * bufferStride[0];
    4828           7 :             pabyDstBuffer += nElementSize;
    4829             :         }
    4830             :     }
    4831             : 
    4832          14 :     int ret = nc_put_att(m_gid, m_varid, GetName().c_str(), m_nAttType,
    4833          14 :                          m_dims.empty() ? 1 : count[0], &abyBuffer[0]);
    4834          14 :     NCDF_ERR(ret);
    4835          14 :     return ret == NC_NOERR;
    4836             : }
    4837             : 
    4838             : /************************************************************************/
    4839             : /*                              Rename()                                */
    4840             : /************************************************************************/
    4841             : 
    4842           8 : bool netCDFAttribute::Rename(const std::string &osNewName)
    4843             : {
    4844           8 :     if (!CheckValidAndErrorOutIfNot())
    4845           2 :         return false;
    4846           6 :     if (m_poShared->IsReadOnly())
    4847             :     {
    4848           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    4849             :                  "Rename() not supported on read-only file");
    4850           1 :         return false;
    4851             :     }
    4852           5 :     if (osNewName.empty())
    4853             :     {
    4854           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Empty name not supported");
    4855           1 :         return false;
    4856             :     }
    4857           8 :     CPLMutexHolderD(&hNCMutex);
    4858           4 :     m_poShared->SetDefineMode(true);
    4859             : 
    4860             :     int ret =
    4861           4 :         nc_rename_att(m_gid, m_varid, m_osName.c_str(), osNewName.c_str());
    4862           4 :     NCDF_ERR(ret);
    4863           4 :     if (ret != NC_NOERR)
    4864           2 :         return false;
    4865             : 
    4866           2 :     BaseRename(osNewName);
    4867             : 
    4868           2 :     return true;
    4869             : }
    4870             : 
    4871             : /************************************************************************/
    4872             : /*                           OpenMultiDim()                             */
    4873             : /************************************************************************/
    4874             : 
    4875         169 : GDALDataset *netCDFDataset::OpenMultiDim(GDALOpenInfo *poOpenInfo)
    4876             : {
    4877             : 
    4878         338 :     CPLMutexHolderD(&hNCMutex);
    4879             : 
    4880         169 :     CPLReleaseMutex(hNCMutex);  // Release mutex otherwise we'll deadlock with
    4881             :                                 // GDALDataset own mutex.
    4882         169 :     netCDFDataset *poDS = new netCDFDataset();
    4883         169 :     CPLAcquireMutex(hNCMutex, 1000.0);
    4884             : 
    4885         338 :     std::string osFilename;
    4886             : 
    4887             :     // For example to open DAP datasets
    4888         169 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "NETCDF:"))
    4889             :     {
    4890           0 :         osFilename = poOpenInfo->pszFilename + strlen("NETCDF:");
    4891           0 :         if (!osFilename.empty() && osFilename[0] == '"' &&
    4892           0 :             osFilename.back() == '"')
    4893             :         {
    4894           0 :             osFilename = osFilename.substr(1, osFilename.size() - 2);
    4895             :         }
    4896             :     }
    4897             :     else
    4898             :     {
    4899         169 :         osFilename = poOpenInfo->pszFilename;
    4900         169 :         poDS->eFormat =
    4901         169 :             netCDFIdentifyFormat(poOpenInfo, /* bCheckExt = */ true);
    4902             :     }
    4903             : 
    4904         169 :     poDS->SetDescription(poOpenInfo->pszFilename);
    4905         169 :     poDS->papszOpenOptions = CSLDuplicate(poOpenInfo->papszOpenOptions);
    4906             : 
    4907             : #ifdef ENABLE_NCDUMP
    4908         169 :     bool bFileToDestroyAtClosing = false;
    4909         169 :     const char *pszHeader =
    4910             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
    4911         169 :     if (poOpenInfo->fpL != nullptr && STARTS_WITH(pszHeader, "netcdf ") &&
    4912           1 :         strstr(pszHeader, "dimensions:") && strstr(pszHeader, "variables:"))
    4913             :     {
    4914             :         // By default create a temporary file that will be destroyed,
    4915             :         // unless NETCDF_TMP_FILE is defined. Can be useful to see which
    4916             :         // netCDF file has been generated from a potential fuzzed input.
    4917           1 :         osFilename = CPLGetConfigOption("NETCDF_TMP_FILE", "");
    4918           1 :         if (osFilename.empty())
    4919             :         {
    4920           1 :             bFileToDestroyAtClosing = true;
    4921           1 :             osFilename = CPLGenerateTempFilename("netcdf_tmp");
    4922             :         }
    4923           1 :         if (!netCDFDatasetCreateTempFile(NCDF_FORMAT_NC4, osFilename.c_str(),
    4924             :                                          poOpenInfo->fpL))
    4925             :         {
    4926           0 :             CPLReleaseMutex(hNCMutex);  // Release mutex otherwise we'll
    4927             :                                         // deadlock with GDALDataset own mutex.
    4928           0 :             delete poDS;
    4929           0 :             CPLAcquireMutex(hNCMutex, 1000.0);
    4930           0 :             return nullptr;
    4931             :         }
    4932           1 :         poDS->eFormat = NCDF_FORMAT_NC4;
    4933             :     }
    4934             : #endif
    4935             : 
    4936             :     // Try opening the dataset.
    4937             : #if defined(NCDF_DEBUG) && defined(ENABLE_UFFD)
    4938             :     CPLDebug("GDAL_netCDF", "calling nc_open_mem(%s)", osFilename.c_str());
    4939             : #elif defined(NCDF_DEBUG) && !defined(ENABLE_UFFD)
    4940             :     CPLDebug("GDAL_netCDF", "calling nc_open(%s)", osFilename.c_str());
    4941             : #endif
    4942         169 :     int cdfid = -1;
    4943         169 :     const int nMode =
    4944         169 :         (poOpenInfo->nOpenFlags & GDAL_OF_UPDATE) != 0 ? NC_WRITE : NC_NOWRITE;
    4945         338 :     CPLString osFilenameForNCOpen(osFilename);
    4946             : #ifdef _WIN32
    4947             :     if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
    4948             :     {
    4949             :         char *pszTemp = CPLRecode(osFilenameForNCOpen, CPL_ENC_UTF8, "CP_ACP");
    4950             :         osFilenameForNCOpen = pszTemp;
    4951             :         CPLFree(pszTemp);
    4952             :     }
    4953             : #endif
    4954         169 :     int status2 = -1;
    4955             : 
    4956         338 :     auto poSharedResources(std::make_shared<netCDFSharedResources>(osFilename));
    4957             : #ifdef ENABLE_NCDUMP
    4958         169 :     poSharedResources->m_bFileToDestroyAtClosing = bFileToDestroyAtClosing;
    4959             : #endif
    4960             : 
    4961         173 :     if (STARTS_WITH(osFilenameForNCOpen, "/vsimem/") &&
    4962           4 :         poOpenInfo->eAccess == GA_ReadOnly)
    4963             :     {
    4964           4 :         vsi_l_offset nLength = 0;
    4965           4 :         poDS->fpVSIMEM = VSIFOpenL(osFilenameForNCOpen, "rb");
    4966           4 :         if (poDS->fpVSIMEM)
    4967             :         {
    4968             :             // We assume that the file will not be modified. If it is, then
    4969             :             // pabyBuffer might become invalid.
    4970             :             GByte *pabyBuffer =
    4971           4 :                 VSIGetMemFileBuffer(osFilenameForNCOpen, &nLength, false);
    4972           4 :             if (pabyBuffer)
    4973             :             {
    4974           4 :                 status2 = nc_open_mem(CPLGetFilename(osFilenameForNCOpen),
    4975             :                                       nMode, static_cast<size_t>(nLength),
    4976             :                                       pabyBuffer, &cdfid);
    4977             :             }
    4978             :         }
    4979             :     }
    4980             :     else
    4981             :     {
    4982             : #ifdef ENABLE_UFFD
    4983         165 :         bool bVsiFile = !strncmp(osFilenameForNCOpen, "/vsi", strlen("/vsi"));
    4984         165 :         bool bReadOnly = (poOpenInfo->eAccess == GA_ReadOnly);
    4985         165 :         void *pVma = nullptr;
    4986         165 :         uint64_t nVmaSize = 0;
    4987         165 :         cpl_uffd_context *pCtx = nullptr;
    4988             : 
    4989         165 :         if (bVsiFile && bReadOnly && CPLIsUserFaultMappingSupported())
    4990           1 :             pCtx = CPLCreateUserFaultMapping(osFilenameForNCOpen, &pVma,
    4991             :                                              &nVmaSize);
    4992         165 :         if (pCtx != nullptr && pVma != nullptr && nVmaSize > 0)
    4993             :         {
    4994             :             // netCDF code, at least for netCDF 4.7.0, is confused by filenames
    4995             :             // like /vsicurl/http[s]://example.com/foo.nc, so just pass the
    4996             :             // final part
    4997           1 :             status2 = nc_open_mem(CPLGetFilename(osFilenameForNCOpen), nMode,
    4998             :                                   static_cast<size_t>(nVmaSize), pVma, &cdfid);
    4999             :         }
    5000             :         else
    5001         164 :             status2 = GDAL_nc_open(osFilenameForNCOpen, nMode, &cdfid);
    5002         165 :         poSharedResources->m_pUffdCtx = pCtx;
    5003             : #else
    5004             :         status2 = GDAL_nc_open(osFilenameForNCOpen, nMode, &cdfid);
    5005             : #endif
    5006             :     }
    5007         169 :     if (status2 != NC_NOERR)
    5008             :     {
    5009             : #ifdef NCDF_DEBUG
    5010             :         CPLDebug("GDAL_netCDF", "error opening");
    5011             : #endif
    5012           3 :         CPLReleaseMutex(hNCMutex);  // Release mutex otherwise we'll deadlock
    5013             :                                     // with GDALDataset own mutex.
    5014           3 :         delete poDS;
    5015           3 :         CPLAcquireMutex(hNCMutex, 1000.0);
    5016           3 :         return nullptr;
    5017             :     }
    5018             : #ifdef NCDF_DEBUG
    5019             :     CPLDebug("GDAL_netCDF", "got cdfid=%d", cdfid);
    5020             : #endif
    5021             : 
    5022             : #if defined(ENABLE_NCDUMP) && !defined(_WIN32)
    5023             :     // Try to destroy the temporary file right now on Unix
    5024         166 :     if (poSharedResources->m_bFileToDestroyAtClosing)
    5025             :     {
    5026           1 :         if (VSIUnlink(poSharedResources->m_osFilename) == 0)
    5027             :         {
    5028           1 :             poSharedResources->m_bFileToDestroyAtClosing = false;
    5029             :         }
    5030             :     }
    5031             : #endif
    5032         166 :     poSharedResources->m_bReadOnly = nMode == NC_NOWRITE;
    5033         166 :     poSharedResources->m_bIsNC4 =
    5034         166 :         poDS->eFormat == NCDF_FORMAT_NC4 || poDS->eFormat == NCDF_FORMAT_NC4C;
    5035         166 :     poSharedResources->m_cdfid = cdfid;
    5036         166 :     poSharedResources->m_fpVSIMEM = poDS->fpVSIMEM;
    5037         166 :     poDS->fpVSIMEM = nullptr;
    5038             : 
    5039             :     // Is this a real netCDF file?
    5040             :     int ndims;
    5041             :     int ngatts;
    5042             :     int nvars;
    5043             :     int unlimdimid;
    5044         166 :     int status = nc_inq(cdfid, &ndims, &nvars, &ngatts, &unlimdimid);
    5045         166 :     if (status != NC_NOERR)
    5046             :     {
    5047           0 :         CPLReleaseMutex(hNCMutex);  // Release mutex otherwise we'll deadlock
    5048             :                                     // with GDALDataset own mutex.
    5049           0 :         delete poDS;
    5050           0 :         CPLAcquireMutex(hNCMutex, 1000.0);
    5051           0 :         return nullptr;
    5052             :     }
    5053             : 
    5054         166 :     poDS->m_poRootGroup = netCDFGroup::Create(poSharedResources, cdfid);
    5055             : 
    5056         166 :     poDS->TryLoadXML();
    5057             : 
    5058         166 :     return poDS;
    5059             : }
    5060             : 
    5061             : /************************************************************************/
    5062             : /*                          GetRootGroup()                              */
    5063             : /************************************************************************/
    5064             : 
    5065         288 : std::shared_ptr<GDALGroup> netCDFDataset::GetRootGroup() const
    5066             : {
    5067         288 :     return m_poRootGroup;
    5068             : }
    5069             : 
    5070             : /************************************************************************/
    5071             : /*                      CreateMultiDimensional()                        */
    5072             : /************************************************************************/
    5073             : 
    5074             : GDALDataset *
    5075          57 : netCDFDataset::CreateMultiDimensional(const char *pszFilename,
    5076             :                                       CSLConstList /* papszRootGroupOptions */,
    5077             :                                       CSLConstList papszOptions)
    5078             : {
    5079         114 :     CPLMutexHolderD(&hNCMutex);
    5080             : 
    5081          57 :     CPLReleaseMutex(hNCMutex);  // Release mutex otherwise we'll deadlock with
    5082             :                                 // GDALDataset own mutex.
    5083          57 :     netCDFDataset *poDS = new netCDFDataset();
    5084          57 :     CPLAcquireMutex(hNCMutex, 1000.0);
    5085          57 :     poDS->eAccess = GA_Update;
    5086          57 :     poDS->osFilename = pszFilename;
    5087             : 
    5088             :     // process options.
    5089          57 :     poDS->papszCreationOptions = CSLDuplicate(papszOptions);
    5090          57 :     if (CSLFetchNameValue(papszOptions, "FORMAT") == nullptr)
    5091             :     {
    5092          56 :         poDS->papszCreationOptions =
    5093          56 :             CSLSetNameValue(poDS->papszCreationOptions, "FORMAT", "NC4");
    5094             :     }
    5095          57 :     poDS->ProcessCreationOptions();
    5096             : 
    5097             :     // Create the dataset.
    5098         114 :     CPLString osFilenameForNCCreate(pszFilename);
    5099             : #ifdef _WIN32
    5100             :     if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
    5101             :     {
    5102             :         char *pszTemp =
    5103             :             CPLRecode(osFilenameForNCCreate, CPL_ENC_UTF8, "CP_ACP");
    5104             :         osFilenameForNCCreate = pszTemp;
    5105             :         CPLFree(pszTemp);
    5106             :     }
    5107             : #endif
    5108          57 :     int cdfid = 0;
    5109          57 :     int status = nc_create(osFilenameForNCCreate, poDS->nCreateMode, &cdfid);
    5110          57 :     if (status != NC_NOERR)
    5111             :     {
    5112           2 :         CPLError(CE_Failure, CPLE_OpenFailed,
    5113             :                  "Unable to create netCDF file %s (Error code %d): %s .",
    5114             :                  pszFilename, status, nc_strerror(status));
    5115           2 :         CPLReleaseMutex(hNCMutex);  // Release mutex otherwise we'll deadlock
    5116             :                                     // with GDALDataset own mutex.
    5117           2 :         delete poDS;
    5118           2 :         CPLAcquireMutex(hNCMutex, 1000.0);
    5119           2 :         return nullptr;
    5120             :     }
    5121             : 
    5122             :     auto poSharedResources(
    5123          55 :         std::make_shared<netCDFSharedResources>(pszFilename));
    5124          55 :     poSharedResources->m_cdfid = cdfid;
    5125          55 :     poSharedResources->m_bReadOnly = false;
    5126          55 :     poSharedResources->m_bDefineMode = true;
    5127          55 :     poSharedResources->m_bIsNC4 =
    5128          55 :         poDS->eFormat == NCDF_FORMAT_NC4 || poDS->eFormat == NCDF_FORMAT_NC4C;
    5129             :     poDS->m_poRootGroup =
    5130          55 :         netCDFGroup::Create(poSharedResources, nullptr, cdfid);
    5131          55 :     const char *pszConventions = CSLFetchNameValueDef(
    5132             :         papszOptions, "CONVENTIONS", NCDF_CONVENTIONS_CF_V1_6);
    5133          55 :     if (!EQUAL(pszConventions, ""))
    5134             :     {
    5135          55 :         auto poAttr = poDS->m_poRootGroup->CreateAttribute(
    5136         165 :             NCDF_CONVENTIONS, {}, GDALExtendedDataType::CreateString());
    5137          55 :         if (poAttr)
    5138          55 :             poAttr->Write(pszConventions);
    5139             :     }
    5140             : 
    5141          55 :     return poDS;
    5142             : }

Generated by: LCOV version 1.14