/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  |