Coverage Report

Created: 2025-06-09 07:42

/src/gdal/frmts/netcdf/netcdfdataset.h
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  netCDF read/write Driver
4
 * Purpose:  GDAL bindings over netCDF library.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2004, Frank Warmerdam
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#ifndef NETCDFDATASET_H_INCLUDED_
14
#define NETCDFDATASET_H_INCLUDED_
15
16
#include <array>
17
#include <ctime>
18
#include <cfloat>
19
#include <cstdlib>
20
#include <functional>
21
#include <map>
22
#include <memory>
23
#include <utility>
24
#include <vector>
25
26
#include "cpl_mem_cache.h"
27
#include "cpl_string.h"
28
#include "gdal_frmts.h"
29
#include "gdal_pam.h"
30
#include "gdal_priv.h"
31
#include "netcdf.h"
32
#include "netcdfformatenum.h"
33
#include "netcdfsg.h"
34
#include "netcdfsgwriterutil.h"
35
#include "ogr_spatialref.h"
36
#include "ogrsf_frmts.h"
37
#include "netcdfuffd.h"
38
#include "netcdf_cf_constants.h"
39
40
#if CPL_IS_LSB
41
0
#define PLATFORM_HEADER 1
42
#else
43
#define PLATFORM_HEADER 0
44
#endif
45
46
/************************************************************************/
47
/* ==================================================================== */
48
/*                           defines                                    */
49
/* ==================================================================== */
50
/************************************************************************/
51
52
/* -------------------------------------------------------------------- */
53
/*      Creation and Configuration Options                              */
54
/* -------------------------------------------------------------------- */
55
56
/* Creation options
57
58
   FORMAT=NC/NC2/NC4/NC4C (COMPRESS=DEFLATE sets FORMAT=NC4C)
59
   COMPRESS=NONE/DEFLATE (default: NONE)
60
   ZLEVEL=[1-9] (default: 1)
61
   WRITE_BOTTOMUP=YES/NO (default: YES)
62
   WRITE_GDAL_TAGS=YES/NO (default: YES)
63
   WRITE_LONLAT=YES/NO/IF_NEEDED (default: YES for geographic, NO for projected)
64
   TYPE_LONLAT=float/double (default: double for geographic, float for
65
   projected) PIXELTYPE=DEFAULT/SIGNEDBYTE (use SIGNEDBYTE to get a signed Byte
66
   Band)
67
*/
68
69
/* Config Options
70
71
   GDAL_NETCDF_BOTTOMUP=YES/NO overrides bottom-up value on import
72
   GDAL_NETCDF_VERIFY_DIMS=[YES/STRICT] : Try to guess which dimensions
73
   represent the latitude and longitude only by their attributes (STRICT) or
74
   also by guessing the name (YES), default is YES.
75
   GDAL_NETCDF_IGNORE_XY_AXIS_NAME_CHECKS=[YES/NO] Whether X/Y dimensions should
76
   be always considered as geospatial axis, even if the lack conventional
77
   attributes confirming it. Default is NO. GDAL_NETCDF_ASSUME_LONGLAT=[YES/NO]
78
   Whether when all else has failed for determining a CRS, a meaningful
79
   geotransform has been found, and is within the bounds -180,360 -90,90, if YES
80
   assume OGC:CRS84. Default is NO.
81
82
   // TODO: this unusued and a few others occur in the source that are not
83
   documented, flush out unused opts and document the rest mdsumner@gmail.com
84
   GDAL_NETCDF_CONVERT_LAT_180=YES/NO convert longitude values from ]180,360] to
85
   [-180,180]
86
*/
87
88
/* -------------------------------------------------------------------- */
89
/*      Driver-specific defines                                         */
90
/* -------------------------------------------------------------------- */
91
92
/* NETCDF driver defs */
93
static const size_t NCDF_MAX_STR_LEN = 8192;
94
399
#define NCDF_CONVENTIONS "Conventions"
95
2
#define NCDF_CONVENTIONS_CF_V1_5 "CF-1.5"
96
2
#define GDAL_DEFAULT_NCDF_CONVENTIONS NCDF_CONVENTIONS_CF_V1_5
97
0
#define NCDF_CONVENTIONS_CF_V1_6 "CF-1.6"
98
0
#define NCDF_CONVENTIONS_CF_V1_8 "CF-1.8"
99
0
#define NCDF_GEOTRANSFORM "GeoTransform"
100
0
#define NCDF_DIMNAME_X "x"
101
0
#define NCDF_DIMNAME_Y "y"
102
0
#define NCDF_DIMNAME_LON "lon"
103
0
#define NCDF_DIMNAME_LAT "lat"
104
0
#define NCDF_LONLAT "lon lat"
105
0
#define NCDF_DIMNAME_RLON "rlon"  // rotated longitude
106
0
#define NCDF_DIMNAME_RLAT "rlat"  // rotated latitude
107
108
/* compression parameters */
109
typedef enum
110
{
111
    NCDF_COMPRESS_NONE = 0,
112
    /* TODO */
113
    /* http://www.unidata.ucar.edu/software/netcdf/docs/BestPractices.html#Packed%20Data%20Values
114
     */
115
    NCDF_COMPRESS_PACKED = 1,
116
    NCDF_COMPRESS_DEFLATE = 2,
117
    NCDF_COMPRESS_SZIP = 3 /* no support for writing */
118
} NetCDFCompressEnum;
119
120
static const int NCDF_DEFLATE_LEVEL = 1; /* best time/size ratio */
121
122
/* helper for libnetcdf errors */
123
#define NCDF_ERR(status)                                                       \
124
18.8k
    do                                                                         \
125
18.8k
    {                                                                          \
126
18.8k
        int NCDF_ERR_status_ = (status);                                       \
127
18.8k
        if (NCDF_ERR_status_ != NC_NOERR)                                      \
128
18.8k
        {                                                                      \
129
14.9k
            CPLError(CE_Failure, CPLE_AppDefined,                              \
130
14.9k
                     "netcdf error #%d : %s .\nat (%s,%s,%d)\n",               \
131
14.9k
                     NCDF_ERR_status_, nc_strerror(NCDF_ERR_status_),          \
132
14.9k
                     __FILE__, __FUNCTION__, __LINE__);                        \
133
14.9k
        }                                                                      \
134
18.8k
    } while (0)
135
136
#define NCDF_ERR_RET(status)                                                   \
137
227k
    do                                                                         \
138
227k
    {                                                                          \
139
227k
        int NCDF_ERR_RET_status_ = (status);                                   \
140
227k
        if (NCDF_ERR_RET_status_ != NC_NOERR)                                  \
141
227k
        {                                                                      \
142
11.1k
            NCDF_ERR(NCDF_ERR_RET_status_);                                    \
143
11.1k
            return CE_Failure;                                                 \
144
11.1k
        }                                                                      \
145
227k
    } while (0)
146
147
#define ERR_RET(eErr)                                                          \
148
239k
    do                                                                         \
149
239k
    {                                                                          \
150
239k
        CPLErr ERR_RET_eErr_ = (eErr);                                         \
151
239k
        if (ERR_RET_eErr_ != CE_None)                                          \
152
239k
            return ERR_RET_eErr_;                                              \
153
239k
    } while (0)
154
155
/* Check for NC2 support in case it was not enabled at compile time. */
156
/* NC4 has to be detected at compile as it requires a special build of netcdf-4.
157
 */
158
#ifndef NETCDF_HAS_NC2
159
#ifdef NC_64BIT_OFFSET
160
#define NETCDF_HAS_NC2 1
161
#endif
162
#endif
163
164
/* Some additional metadata */
165
0
#define OGR_SG_ORIGINAL_LAYERNAME "ogr_layer_name"
166
167
/*
168
 * Starting `c26f7ea`, netcdf-c exposes the `NC_FillValue`[1] macro instead of
169
 * `_FillValue` to avoid collisions with C++ standard library[2]. However, the
170
 * previous macro, `_FillValue`, was fully removed causing netcdf-c consumers,
171
 * including GDAL, fail to build.
172
 *
173
 * It's unlikely that this naming change will be backported to the previous
174
 * netcdf-c releases, so we have to account for both macros variants. We do so
175
 * by introducing our own macro, `NCDF_FillValue`, and using that in places
176
 * where `_FillValue` was previously used. If `NC_FillValue` is defined by
177
 * `netcdf.h`, `NCDF_FillValue` expands to it and, if it's not, to `_FillValue`.
178
 *
179
 * References:
180
 * 1. https://github.com/Unidata/netcdf-c/commit/c26f7eabf4a1cd25353f22734f439505fe636a45
181
 * 2. https://github.com/Unidata/netcdf-c/issues/2858
182
 */
183
#if defined(NC_FillValue)
184
#define NCDF_FillValue NC_FillValue
185
#elif defined(_FillValue)
186
30
#define NCDF_FillValue _FillValue
187
#endif
188
189
/* -------------------------------------------------------------------- */
190
/*         CF-1 Coordinate Type Naming (Chapter 4.  Coordinate Types )  */
191
/* -------------------------------------------------------------------- */
192
static const char *const papszCFLongitudeVarNames[] = {CF_LONGITUDE_VAR_NAME,
193
                                                       "longitude", nullptr};
194
static const char *const papszCFLongitudeAttribNames[] = {
195
    CF_UNITS, CF_UNITS, CF_UNITS, CF_STD_NAME, CF_AXIS, CF_LNG_NAME, nullptr};
196
static const char *const papszCFLongitudeAttribValues[] = {
197
    CF_DEGREES_EAST,
198
    CF_DEGREE_EAST,
199
    CF_DEGREES_E,
200
    CF_LONGITUDE_STD_NAME,
201
    "X",
202
    CF_LONGITUDE_LNG_NAME,
203
    nullptr};
204
static const char *const papszCFLatitudeVarNames[] = {CF_LATITUDE_VAR_NAME,
205
                                                      "latitude", nullptr};
206
static const char *const papszCFLatitudeAttribNames[] = {
207
    CF_UNITS, CF_UNITS, CF_UNITS, CF_STD_NAME, CF_AXIS, CF_LNG_NAME, nullptr};
208
static const char *const papszCFLatitudeAttribValues[] = {CF_DEGREES_NORTH,
209
                                                          CF_DEGREE_NORTH,
210
                                                          CF_DEGREES_N,
211
                                                          CF_LATITUDE_STD_NAME,
212
                                                          "Y",
213
                                                          CF_LATITUDE_LNG_NAME,
214
                                                          nullptr};
215
216
static const char *const papszCFProjectionXVarNames[] = {CF_PROJ_X_VAR_NAME,
217
                                                         "xc", nullptr};
218
static const char *const papszCFProjectionXAttribNames[] = {CF_STD_NAME,
219
                                                            CF_AXIS, nullptr};
220
static const char *const papszCFProjectionXAttribValues[] = {CF_PROJ_X_COORD,
221
                                                             "X", nullptr};
222
static const char *const papszCFProjectionYVarNames[] = {CF_PROJ_Y_VAR_NAME,
223
                                                         "yc", nullptr};
224
static const char *const papszCFProjectionYAttribNames[] = {CF_STD_NAME,
225
                                                            CF_AXIS, nullptr};
226
static const char *const papszCFProjectionYAttribValues[] = {CF_PROJ_Y_COORD,
227
                                                             "Y", nullptr};
228
229
static const char *const papszCFVerticalAttribNames[] = {CF_AXIS, "positive",
230
                                                         "positive", nullptr};
231
static const char *const papszCFVerticalAttribValues[] = {"Z", "up", "down",
232
                                                          nullptr};
233
static const char *const papszCFVerticalUnitsValues[] = {
234
    /* units of pressure */
235
    "bar", "bars", "millibar", "millibars", "decibar", "decibars", "atmosphere",
236
    "atmospheres", "atm", "pascal", "pascals", "Pa", "hPa",
237
    /* units of length */
238
    "meter", "meters", "m", "kilometer", "kilometers", "km",
239
    /* dimensionless vertical coordinates */
240
    "level", "layer", "sigma_level", nullptr};
241
/* dimensionless vertical coordinates */
242
static const char *const papszCFVerticalStandardNameValues[] = {
243
    "atmosphere_ln_pressure_coordinate",
244
    "atmosphere_sigma_coordinate",
245
    "atmosphere_hybrid_sigma_pressure_coordinate",
246
    "atmosphere_hybrid_height_coordinate",
247
    "atmosphere_sleve_coordinate",
248
    "ocean_sigma_coordinate",
249
    "ocean_s_coordinate",
250
    "ocean_sigma_z_coordinate",
251
    "ocean_double_sigma_coordinate",
252
    "atmosphere_ln_pressure_coordinate",
253
    "atmosphere_sigma_coordinate",
254
    "atmosphere_hybrid_sigma_pressure_coordinate",
255
    "atmosphere_hybrid_height_coordinate",
256
    "atmosphere_sleve_coordinate",
257
    "ocean_sigma_coordinate",
258
    "ocean_s_coordinate",
259
    "ocean_sigma_z_coordinate",
260
    "ocean_double_sigma_coordinate",
261
    nullptr};
262
263
static const char *const papszCFTimeAttribNames[] = {CF_AXIS, CF_STD_NAME,
264
                                                     nullptr};
265
static const char *const papszCFTimeAttribValues[] = {"T", "time", nullptr};
266
static const char *const papszCFTimeUnitsValues[] = {
267
    "days since",   "day since", "d since",       "hours since",
268
    "hour since",   "h since",   "hr since",      "minutes since",
269
    "minute since", "min since", "seconds since", "second since",
270
    "sec since",    "s since",   nullptr};
271
272
/************************************************************************/
273
/* ==================================================================== */
274
/*                        netCDFWriterConfig classes                    */
275
/* ==================================================================== */
276
/************************************************************************/
277
278
class netCDFWriterConfigAttribute
279
{
280
  public:
281
    CPLString m_osName;
282
    CPLString m_osType;
283
    CPLString m_osValue;
284
285
    bool Parse(CPLXMLNode *psNode);
286
};
287
288
class netCDFWriterConfigField
289
{
290
  public:
291
    CPLString m_osName;
292
    CPLString m_osNetCDFName;
293
    CPLString m_osMainDim;
294
    std::vector<netCDFWriterConfigAttribute> m_aoAttributes;
295
296
    bool Parse(CPLXMLNode *psNode);
297
};
298
299
class netCDFWriterConfigLayer
300
{
301
  public:
302
    CPLString m_osName;
303
    CPLString m_osNetCDFName;
304
    std::map<CPLString, CPLString> m_oLayerCreationOptions;
305
    std::vector<netCDFWriterConfigAttribute> m_aoAttributes;
306
    std::map<CPLString, netCDFWriterConfigField> m_oFields;
307
308
    bool Parse(CPLXMLNode *psNode);
309
};
310
311
class netCDFWriterConfiguration
312
{
313
  public:
314
    bool m_bIsValid;
315
    std::map<CPLString, CPLString> m_oDatasetCreationOptions;
316
    std::map<CPLString, CPLString> m_oLayerCreationOptions;
317
    std::vector<netCDFWriterConfigAttribute> m_aoAttributes;
318
    std::map<CPLString, netCDFWriterConfigField> m_oFields;
319
    std::map<CPLString, netCDFWriterConfigLayer> m_oLayers;
320
321
399
    netCDFWriterConfiguration() : m_bIsValid(false)
322
399
    {
323
399
    }
324
325
    bool Parse(const char *pszFilename);
326
    static bool SetNameValue(CPLXMLNode *psNode,
327
                             std::map<CPLString, CPLString> &oMap);
328
};
329
330
/************************************************************************/
331
/* ==================================================================== */
332
/*                           netCDFDataset                              */
333
/* ==================================================================== */
334
/************************************************************************/
335
336
class netCDFRasterBand;
337
class netCDFLayer;
338
339
class netCDFDataset final : public GDALPamDataset
340
{
341
    friend class netCDFRasterBand;  // TMP
342
    friend class netCDFLayer;
343
    friend class netCDFVariable;
344
    friend class nccfdriver::netCDFVID;
345
346
    typedef enum
347
    {
348
        SINGLE_LAYER,
349
        SEPARATE_FILES,
350
        SEPARATE_GROUPS
351
    } MultipleLayerBehavior;
352
353
    /* basic dataset vars */
354
    CPLString osFilename;
355
#ifdef ENABLE_NCDUMP
356
    bool bFileToDestroyAtClosing;
357
#endif
358
    int cdfid;
359
#ifdef ENABLE_UFFD
360
    cpl_uffd_context *pCtx = nullptr;
361
#endif
362
    VSILFILE *fpVSIMEM = nullptr;
363
    int nSubDatasets;
364
    char **papszSubDatasets;
365
    char **papszMetadata;
366
367
    // Used to report metadata found in Sentinel 5
368
    std::map<std::string, CPLStringList> m_oMapDomainToJSon{};
369
370
    CPLStringList papszDimName;
371
    bool bBottomUp;
372
    NetCDFFormatEnum eFormat;
373
    bool bIsGdalFile;   /* was this file created by GDAL? */
374
    bool bIsGdalCfFile; /* was this file created by the (new) CF-compliant
375
                           driver? */
376
    char *pszCFProjection;
377
    const char *pszCFCoordinates;
378
    double nCFVersion;
379
    bool bSGSupport;
380
    MultipleLayerBehavior eMultipleLayerBehavior;
381
    std::vector<netCDFDataset *> apoVectorDatasets;
382
    std::string logHeader;
383
    int logCount;
384
    nccfdriver::netCDFVID vcdf;
385
    nccfdriver::OGR_NCScribe GeometryScribe;
386
    nccfdriver::OGR_NCScribe FieldScribe;
387
    nccfdriver::WBufferManager bufManager;
388
389
    bool bWriteGDALVersion = true;
390
    bool bWriteGDALHistory = true;
391
392
    /* projection/GT */
393
    double m_adfGeoTransform[6];
394
    OGRSpatialReference m_oSRS{};
395
    int nXDimID;
396
    int nYDimID;
397
    bool bIsProjected;
398
    bool bIsGeographic;
399
    bool bSwitchedXY = false;
400
401
    /* state vars */
402
    bool bDefineMode;
403
    bool m_bHasProjection = false;
404
    bool m_bHasGeoTransform = false;
405
    bool m_bAddedProjectionVarsDefs = false;
406
    bool m_bAddedProjectionVarsData = false;
407
    bool bAddedGridMappingRef;
408
409
    /* create vars */
410
    char **papszCreationOptions;
411
    NetCDFCompressEnum eCompress;
412
    int nZLevel;
413
    bool bChunking;
414
    int nCreateMode;
415
    bool bSignedData;
416
417
    // IDs of the dimensions of the variables
418
    std::vector<int> m_anDimIds{};
419
420
    // Extra dimension info (size of those arrays is m_anDimIds.size() - 2)
421
    std::vector<int> m_anExtraDimVarIds{};
422
    std::vector<int> m_anExtraDimGroupIds{};
423
424
    std::vector<std::shared_ptr<OGRLayer>> papoLayers;
425
426
    netCDFWriterConfiguration oWriterConfig;
427
428
    struct ChunkKey
429
    {
430
        size_t xChunk;  // netCDF chunk number along X axis
431
        size_t yChunk;  // netCDF chunk number along Y axis
432
        int nBand;
433
434
        ChunkKey(size_t xChunkIn, size_t yChunkIn, int nBandIn)
435
0
            : xChunk(xChunkIn), yChunk(yChunkIn), nBand(nBandIn)
436
0
        {
437
0
        }
438
439
        bool operator==(const ChunkKey &other) const
440
0
        {
441
0
            return xChunk == other.xChunk && yChunk == other.yChunk &&
442
0
                   nBand == other.nBand;
443
0
        }
444
445
        bool operator!=(const ChunkKey &other) const
446
0
        {
447
0
            return !(operator==(other));
448
0
        }
449
    };
450
451
    struct KeyHasher
452
    {
453
        std::size_t operator()(const ChunkKey &k) const
454
0
        {
455
0
            return std::hash<size_t>{}(k.xChunk) ^
456
0
                   (std::hash<size_t>{}(k.yChunk) << 1) ^
457
0
                   (std::hash<size_t>{}(k.nBand) << 2);
458
0
        }
459
    };
460
461
    typedef lru11::Cache<
462
        ChunkKey, std::shared_ptr<std::vector<GByte>>, lru11::NullLock,
463
        std::unordered_map<
464
            ChunkKey,
465
            typename std::list<lru11::KeyValuePair<
466
                ChunkKey, std::shared_ptr<std::vector<GByte>>>>::iterator,
467
            KeyHasher>>
468
        ChunkCacheType;
469
470
    std::unique_ptr<ChunkCacheType> poChunkCache;
471
472
    static double rint(double);
473
474
    double FetchCopyParam(const char *pszGridMappingValue, const char *pszParam,
475
                          double dfDefault, bool *pbFound = nullptr);
476
477
    std::vector<std::string>
478
    FetchStandardParallels(const char *pszGridMappingValue);
479
480
    const char *FetchAttr(const char *pszVarFullName, const char *pszAttr);
481
    const char *FetchAttr(int nGroupId, int nVarId, const char *pszAttr);
482
483
    void ProcessCreationOptions();
484
    int DefVarDeflate(int nVarId, bool bChunkingArg = true);
485
    CPLErr AddProjectionVars(bool bDefsOnly, GDALProgressFunc pfnProgress,
486
                             void *pProgressData);
487
    bool AddGridMappingRef();
488
489
    bool GetDefineMode() const
490
0
    {
491
0
        return bDefineMode;
492
0
    }
493
494
    bool SetDefineMode(bool bNewDefineMode);
495
496
    CPLErr ReadAttributes(int, int);
497
498
    void CreateSubDatasetList(int nGroupId);
499
500
    void SetProjectionFromVar(int nGroupId, int nVarId, bool bReadSRSOnly,
501
                              const char *pszGivenGM, std::string *,
502
                              nccfdriver::SGeometry_Reader *,
503
                              std::vector<std::string> *paosRemovedMDItems);
504
    void SetProjectionFromVar(int nGroupId, int nVarId, bool bReadSRSOnly);
505
506
    bool ProcessNASAL2OceanGeoLocation(int nGroupId, int nVarId);
507
508
    bool ProcessNASAEMITGeoLocation(int nGroupId, int nVarId);
509
510
    int ProcessCFGeolocation(int nGroupId, int nVarId,
511
                             const std::string &osGeolocWKT,
512
                             std::string &osGeolocXNameOut,
513
                             std::string &osGeolocYNameOut);
514
    CPLErr Set1DGeolocation(int nGroupId, int nVarId, const char *szDimName);
515
    double *Get1DGeolocation(const char *szDimName, int &nVarLen);
516
517
    static bool CloneAttributes(int old_cdfid, int new_cdfid, int nSrcVarId,
518
                                int nDstVarId);
519
    static bool CloneVariableContent(int old_cdfid, int new_cdfid,
520
                                     int nSrcVarId, int nDstVarId);
521
    static bool CloneGrp(int nOldGrpId, int nNewGrpId, bool bIsNC4,
522
                         int nLayerId, int nDimIdToGrow, size_t nNewSize);
523
    bool GrowDim(int nLayerId, int nDimIdToGrow, size_t nNewSize);
524
525
    void ProcessSentinel3_SRAL_MWR();
526
527
    CPLErr
528
    FilterVars(int nCdfId, bool bKeepRasters, bool bKeepVectors,
529
               char **papszIgnoreVars, int *pnRasterVars, int *pnGroupId,
530
               int *pnVarId, int *pnIgnoredVars,
531
               // key is (dim1Id, dim2Id, nc_type varType)
532
               // value is (groupId, varId)
533
               std::map<std::array<int, 3>, std::vector<std::pair<int, int>>>
534
                   &oMap2DDimsToGroupAndVar);
535
    CPLErr CreateGrpVectorLayers(int nCdfId, const CPLString &osFeatureType,
536
                                 const std::vector<int> &anPotentialVectorVarID,
537
                                 const std::map<int, int> &oMapDimIdToCount,
538
                                 int nVarXId, int nVarYId, int nVarZId,
539
                                 int nProfileDimId, int nParentIndexVarID,
540
                                 bool bKeepRasters);
541
542
    bool DetectAndFillSGLayers(int ncid);
543
    CPLErr LoadSGVarIntoLayer(int ncid, int nc_basevarId);
544
545
    static GDALDataset *OpenMultiDim(GDALOpenInfo *);
546
    std::shared_ptr<GDALGroup> m_poRootGroup{};
547
548
    void SetGeoTransformNoUpdate(double *);
549
    void SetSpatialRefNoUpdate(const OGRSpatialReference *);
550
551
  protected:
552
    OGRLayer *ICreateLayer(const char *pszName,
553
                           const OGRGeomFieldDefn *poGeomFieldDefn,
554
                           CSLConstList papszOptions) override;
555
556
    CPLErr Close() override;
557
558
  public:
559
    netCDFDataset();
560
    virtual ~netCDFDataset();
561
    bool SGCommitPendingTransaction();
562
    void SGLogPendingTransaction();
563
    static std::string generateLogName();
564
565
    /* Projection/GT */
566
    CPLErr GetGeoTransform(double *) override;
567
    CPLErr SetGeoTransform(double *) override;
568
    const OGRSpatialReference *GetSpatialRef() const override;
569
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
570
571
    virtual char **GetMetadataDomainList() override;
572
    char **GetMetadata(const char *) override;
573
574
    virtual CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
575
                                   const char *pszDomain = "") override;
576
    virtual CPLErr SetMetadata(char **papszMD,
577
                               const char *pszDomain = "") override;
578
579
    virtual int TestCapability(const char *pszCap) override;
580
581
    virtual int GetLayerCount() override
582
867
    {
583
867
        return static_cast<int>(this->papoLayers.size());
584
867
    }
585
586
    virtual OGRLayer *GetLayer(int nIdx) override;
587
588
    std::shared_ptr<GDALGroup> GetRootGroup() const override;
589
590
    int GetCDFID() const
591
0
    {
592
0
        return cdfid;
593
0
    }
594
595
    inline bool HasInfiniteRecordDim()
596
0
    {
597
0
        return !bSGSupport;
598
0
    }
599
600
    /* static functions */
601
    static GDALDataset *Open(GDALOpenInfo *);
602
603
    static netCDFDataset *CreateLL(const char *pszFilename, int nXSize,
604
                                   int nYSize, int nBands, char **papszOptions);
605
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
606
                               int nBands, GDALDataType eType,
607
                               char **papszOptions);
608
    static GDALDataset *CreateCopy(const char *pszFilename,
609
                                   GDALDataset *poSrcDS, int bStrict,
610
                                   char **papszOptions,
611
                                   GDALProgressFunc pfnProgress,
612
                                   void *pProgressData);
613
614
    static GDALDataset *
615
    CreateMultiDimensional(const char *pszFilename,
616
                           CSLConstList papszRootGroupOptions,
617
                           CSLConstList papzOptions);
618
};
619
620
class netCDFLayer final : public OGRLayer
621
{
622
    typedef union
623
    {
624
        signed char chVal;
625
        unsigned char uchVal;
626
        short sVal;
627
        unsigned short usVal;
628
        int nVal;
629
        unsigned int unVal;
630
        GIntBig nVal64;
631
        GUIntBig unVal64;
632
        float fVal;
633
        double dfVal;
634
    } NCDFNoDataUnion;
635
636
    typedef struct
637
    {
638
        NCDFNoDataUnion uNoData;
639
        nc_type nType;
640
        int nVarId;
641
        int nDimCount;
642
        bool bHasWarnedAboutTruncation;
643
        int nMainDimId;
644
        int nSecDimId;
645
        bool bIsDays;
646
    } FieldDesc;
647
648
    netCDFDataset *m_poDS;
649
    int m_nLayerCDFId;
650
    OGRFeatureDefn *m_poFeatureDefn;
651
    CPLString m_osRecordDimName;
652
    int m_nRecordDimID;
653
    int m_nDefaultWidth;
654
    bool m_bAutoGrowStrings;
655
    int m_nDefaultMaxWidthDimId;
656
    int m_nXVarID;
657
    int m_nYVarID;
658
    int m_nZVarID;
659
    nc_type m_nXVarNCDFType;
660
    nc_type m_nYVarNCDFType;
661
    nc_type m_nZVarNCDFType;
662
    NCDFNoDataUnion m_uXVarNoData;
663
    NCDFNoDataUnion m_uYVarNoData;
664
    NCDFNoDataUnion m_uZVarNoData;
665
    CPLString m_osWKTVarName;
666
    int m_nWKTMaxWidth;
667
    int m_nWKTMaxWidthDimId;
668
    int m_nWKTVarID;
669
    nc_type m_nWKTNCDFType;
670
    CPLString m_osCoordinatesValue;
671
    std::vector<FieldDesc> m_aoFieldDesc;
672
    bool m_bLegacyCreateMode;
673
    int m_nCurFeatureId;
674
    CPLString m_osGridMapping;
675
    bool m_bWriteGDALTags;
676
    bool m_bUseStringInNC4;
677
    bool m_bNCDumpCompat;
678
679
    CPLString m_osProfileDimName;
680
    int m_nProfileDimID;
681
    CPLString m_osProfileVariables;
682
    int m_nProfileVarID;
683
    bool m_bProfileVarUnlimited;
684
    int m_nParentIndexVarID;
685
    std::shared_ptr<nccfdriver::SGeometry_Reader> m_simpleGeometryReader;
686
    std::unique_ptr<nccfdriver::netCDFVID>
687
        layerVID_alloc;  // Allocation wrapper for group specific netCDFVID
688
    nccfdriver::netCDFVID &layerVID;  // refers to the "correct" VID
689
    std::string m_sgCRSname;
690
    size_t m_SGeometryFeatInd;
691
692
    const netCDFWriterConfigLayer *m_poLayerConfig;
693
694
    nccfdriver::ncLayer_SG_Metadata m_layerSGDefn;
695
696
    OGRFeature *GetNextRawFeature();
697
    double Get1DVarAsDouble(int nVarId, nc_type nVarType, size_t nIndex,
698
                            const NCDFNoDataUnion &noDataVal, bool *pbIsNoData);
699
    CPLErr GetFillValue(int nVarID, char **ppszValue);
700
    CPLErr GetFillValue(int nVarID, double *pdfValue);
701
    void GetNoDataValueForFloat(int nVarId, NCDFNoDataUnion *puNoData);
702
    void GetNoDataValueForDouble(int nVarId, NCDFNoDataUnion *puNoData);
703
    void GetNoDataValue(int nVarId, nc_type nVarType,
704
                        NCDFNoDataUnion *puNoData);
705
    bool FillVarFromFeature(OGRFeature *poFeature, int nMainDimId,
706
                            size_t nIndex);
707
    OGRFeature *buildSGeometryFeature(size_t featureInd);
708
    void netCDFWriteAttributesFromConf(
709
        int cdfid, int varid,
710
        const std::vector<netCDFWriterConfigAttribute> &aoAttributes);
711
712
  protected:
713
    bool FillFeatureFromVar(OGRFeature *poFeature, int nMainDimId,
714
                            size_t nIndex);
715
716
  public:
717
    netCDFLayer(netCDFDataset *poDS, int nLayerCDFId, const char *pszName,
718
                OGRwkbGeometryType eGeomType, OGRSpatialReference *poSRS);
719
    virtual ~netCDFLayer();
720
721
    bool Create(char **papszOptions,
722
                const netCDFWriterConfigLayer *poLayerConfig);
723
    void SetRecordDimID(int nRecordDimID);
724
    void SetXYZVars(int nXVarId, int nYVarId, int nZVarId);
725
    void SetWKTGeometryField(const char *pszWKTVarName);
726
    void SetGridMapping(const char *pszGridMapping);
727
    void SetProfile(int nProfileDimID, int nParentIndexVarID);
728
729
    void EnableSGBypass()
730
0
    {
731
0
        this->m_bLegacyCreateMode = false;
732
0
    }
733
734
    bool AddField(int nVarId);
735
736
    int GetCDFID() const
737
0
    {
738
0
        return m_nLayerCDFId;
739
0
    }
740
741
    void SetCDFID(int nId)
742
0
    {
743
0
        m_nLayerCDFId = nId;
744
0
    }
745
746
    void SetSGeometryRepresentation(
747
        const std::shared_ptr<nccfdriver::SGeometry_Reader> &sg)
748
0
    {
749
0
        m_simpleGeometryReader = sg;
750
0
    }
751
752
    nccfdriver::ncLayer_SG_Metadata &getLayerSGMetadata()
753
0
    {
754
0
        return m_layerSGDefn;
755
0
    }
756
757
    virtual void ResetReading() override;
758
    virtual OGRFeature *GetNextFeature() override;
759
760
    virtual GIntBig GetFeatureCount(int bForce) override;
761
762
    virtual int TestCapability(const char *pszCap) override;
763
764
    virtual OGRFeatureDefn *GetLayerDefn() override;
765
766
    virtual OGRErr ICreateFeature(OGRFeature *poFeature) override;
767
    virtual OGRErr CreateField(const OGRFieldDefn *poFieldDefn,
768
                               int bApproxOK) override;
769
770
    GDALDataset *GetDataset() override;
771
};
772
773
std::string NCDFGetProjectedCFUnit(const OGRSpatialReference *poSRS);
774
void NCDFWriteLonLatVarsAttributes(nccfdriver::netCDFVID &vcdf, int nVarLonID,
775
                                   int nVarLatID);
776
void NCDFWriteRLonRLatVarsAttributes(nccfdriver::netCDFVID &vcdf,
777
                                     int nVarRLonID, int nVarRLatID);
778
void NCDFWriteXYVarsAttributes(nccfdriver::netCDFVID &vcdf, int nVarXID,
779
                               int nVarYID, const OGRSpatialReference *poSRS);
780
int NCDFWriteSRSVariable(int cdfid, const OGRSpatialReference *poSRS,
781
                         char **ppszCFProjection, bool bWriteGDALTags,
782
                         const std::string & = std::string());
783
784
double NCDFGetDefaultNoDataValue(int nCdfId, int nVarId, int nVarType,
785
                                 bool &bGotNoData);
786
787
int64_t NCDFGetDefaultNoDataValueAsInt64(int nCdfId, int nVarId,
788
                                         bool &bGotNoData);
789
uint64_t NCDFGetDefaultNoDataValueAsUInt64(int nCdfId, int nVarId,
790
                                           bool &bGotNoData);
791
792
CPLErr NCDFGetAttr(int nCdfId, int nVarId, const char *pszAttrName,
793
                   double *pdfValue);
794
CPLErr NCDFGetAttr(int nCdfId, int nVarId, const char *pszAttrName,
795
                   char **pszValue);
796
bool NCDFIsUnlimitedDim(bool bIsNC4, int cdfid, int nDimId);
797
bool NCDFIsUserDefinedType(int ncid, int type);
798
799
CPLString NCDFGetGroupFullName(int nGroupId);
800
801
CPLErr NCDFResolveVar(int nStartGroupId, const char *pszVar, int *pnGroupId,
802
                      int *pnVarId, bool bMandatory = false);
803
804
// Dimension check functions.
805
bool NCDFIsVarLongitude(int nCdfId, int nVarId, const char *pszVarName);
806
bool NCDFIsVarLatitude(int nCdfId, int nVarId, const char *pszVarName);
807
bool NCDFIsVarProjectionX(int nCdfId, int nVarId, const char *pszVarName);
808
bool NCDFIsVarProjectionY(int nCdfId, int nVarId, const char *pszVarName);
809
bool NCDFIsVarVerticalCoord(int nCdfId, int nVarId, const char *pszVarName);
810
bool NCDFIsVarTimeCoord(int nCdfId, int nVarId, const char *pszVarName);
811
812
std::string NCDFReadMetadataAsJson(int cdfid);
813
814
char **NCDFTokenizeCoordinatesAttribute(const char *pszCoordinates);
815
816
extern CPLMutex *hNCMutex;
817
818
#ifdef ENABLE_NCDUMP
819
bool netCDFDatasetCreateTempFile(NetCDFFormatEnum eFormat,
820
                                 const char *pszTmpFilename, VSILFILE *fpSrc);
821
#endif
822
823
int GDAL_nc_open(const char *pszFilename, int nMode, int *pID);
824
int GDAL_nc_close(int cdfid);
825
826
#endif