Coverage Report

Created: 2025-06-09 07:07

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