Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: netCDF driver
5 : * Author: Even Rouault, <even.rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2023, Even Rouault, <even.rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogrsf_frmts.h"
14 : #include "gdal_priv.h"
15 :
16 : #include "gdal_frmts.h"
17 : #include "gdalplugindriverproxy.h"
18 : #include "gdalsubdatasetinfo.h"
19 :
20 : #include "netcdfdrivercore.h"
21 :
22 : #include <algorithm>
23 : #include <cctype>
24 : #include <string_view>
25 :
26 : #ifdef HAS_NETCDF_H
27 : #include "netcdfdataset.h"
28 : #ifdef NETCDF_HAS_NC2
29 : #define NETCDF_CORE_HAS_NC2 1
30 : #endif
31 : #else
32 : // We don't have an easy way to guess that without accessing netcdf.h, so
33 : // assume it is present
34 : #ifndef NETCDF_CORE_HAS_NC2
35 : #define NETCDF_CORE_HAS_NC2 1
36 : #endif
37 : #endif
38 :
39 : /************************************************************************/
40 : /* netCDFIdentifyFormat() */
41 : /************************************************************************/
42 :
43 81353 : NetCDFFormatEnum netCDFIdentifyFormat(GDALOpenInfo *poOpenInfo, bool bCheckExt)
44 : {
45 : // Does this appear to be a netcdf file? If so, which format?
46 : // http://www.unidata.ucar.edu/software/netcdf/docs/faq.html#fv1_5
47 :
48 81353 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "NETCDF:"))
49 0 : return NCDF_FORMAT_UNKNOWN;
50 81353 : if (poOpenInfo->nHeaderBytes < 4)
51 62965 : return NCDF_FORMAT_NONE;
52 18388 : const char *pszHeader =
53 : reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
54 :
55 : #ifdef ENABLE_NCDUMP
56 18388 : if (poOpenInfo->fpL != nullptr && STARTS_WITH(pszHeader, "netcdf ") &&
57 9 : strstr(pszHeader, "dimensions:") && strstr(pszHeader, "variables:"))
58 : {
59 9 : if (strstr(pszHeader, "// NC4C"))
60 0 : return NCDF_FORMAT_NC4C;
61 9 : else if (strstr(pszHeader, "// NC4"))
62 0 : return NCDF_FORMAT_NC4;
63 : else
64 9 : return NCDF_FORMAT_NC;
65 : }
66 : #endif // ENABLE_NCDUMP
67 :
68 : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
69 : // We don't necessarily want to catch bugs in libnetcdf ...
70 : if (CPLGetConfigOption("DISABLE_OPEN_REAL_NETCDF_FILES", nullptr))
71 : {
72 : return NCDF_FORMAT_NONE;
73 : }
74 : #endif
75 :
76 18379 : if (STARTS_WITH_CI(pszHeader, "CDF\001"))
77 : {
78 1141 : return NCDF_FORMAT_NC;
79 : }
80 :
81 17238 : if (STARTS_WITH_CI(pszHeader, "CDF\002"))
82 : {
83 14 : return NCDF_FORMAT_NC2;
84 : }
85 :
86 17224 : constexpr char HDF5_SIG[] = "\211HDF\r\n\032\n";
87 17224 : constexpr int HDF5_SIG_LEN = int(std::char_traits<char>::length(HDF5_SIG));
88 : static_assert(HDF5_SIG_LEN == 8);
89 : // First non-zero offset at which the HDF5 signature can be found.
90 17224 : constexpr int HDF5_SIG_OFFSET = 512;
91 17224 : if (STARTS_WITH_CI(pszHeader, HDF5_SIG) ||
92 16101 : (poOpenInfo->nHeaderBytes > HDF5_SIG_OFFSET + HDF5_SIG_LEN &&
93 11120 : memcmp(pszHeader + HDF5_SIG_OFFSET, HDF5_SIG, HDF5_SIG_LEN) == 0))
94 : {
95 : // Requires netCDF-4/HDF5 support in libnetcdf (not just libnetcdf-v4).
96 :
97 : // If only the netCDF driver is allowed, immediately recognize the file
98 1123 : if (poOpenInfo->IsSingleAllowedDriver("netCDF"))
99 : {
100 19 : return NCDF_FORMAT_NC4;
101 : }
102 :
103 : // If there is a HDF5 GDAL driver available, delegate to it.
104 : // Otherwise open it with this driver.
105 : // If the user really wants to open with this driver, use NETCDF:file.h5
106 : // format.
107 :
108 : // Check for HDF5 driver availability.
109 1104 : if (bCheckExt)
110 : {
111 : // Check by default.
112 1083 : const char *pszExtension = poOpenInfo->osExtension.c_str();
113 1083 : if (!(EQUAL(pszExtension, "nc") || EQUAL(pszExtension, "cdf") ||
114 513 : EQUAL(pszExtension, "nc2") || EQUAL(pszExtension, "nc4") ||
115 513 : EQUAL(pszExtension, "nc3") || EQUAL(pszExtension, "grd") ||
116 513 : EQUAL(pszExtension, "gmac")))
117 : {
118 363 : if (GDALGetDriverByName("HDF5") != nullptr)
119 : {
120 363 : return NCDF_FORMAT_HDF5;
121 : }
122 : }
123 : }
124 :
125 741 : return NCDF_FORMAT_NC4;
126 : }
127 16101 : else if (STARTS_WITH_CI(pszHeader, "\016\003\023\001"))
128 : {
129 : #ifdef NETCDF_HAS_HDF4
130 : // There is HDF4 support in libnetcdf and only the netCDF driver is
131 : // allowed ==> immediately recognize the file.
132 : if (poOpenInfo->IsSingleAllowedDriver("netCDF"))
133 : {
134 : return NCDF_FORMAT_HDF4;
135 : }
136 : #endif
137 :
138 : // If there is a HDF4 GDAL driver available, delegate to it.
139 : // Otherwise open it with this driver.
140 : // If the user really wants to open with this driver, use NETCDF:file.hdf
141 : // format.
142 :
143 : // Check for HDF4 driver availability.
144 325 : if (bCheckExt && GDALGetDriverByName("HDF4") != nullptr)
145 : {
146 : // Check by default.
147 : // Always treat as HDF4 file.
148 325 : return NCDF_FORMAT_HDF4;
149 : }
150 :
151 : #ifdef NETCDF_HAS_HDF4
152 : // There is HDF4 support in libnetcdf.
153 : return NCDF_FORMAT_HDF4;
154 : #else
155 0 : return NCDF_FORMAT_NONE;
156 : #endif
157 : }
158 :
159 : // The HDF5 signature of netCDF 4 files can be at offsets 512, 1024, 2048,
160 : // etc.
161 15776 : const char *pszExtension = poOpenInfo->osExtension.c_str();
162 31440 : if (poOpenInfo->fpL != nullptr &&
163 15664 : (!bCheckExt || EQUAL(pszExtension, "nc") ||
164 15662 : EQUAL(pszExtension, "cdf") || EQUAL(pszExtension, "nc4") ||
165 15662 : poOpenInfo->IsSingleAllowedDriver("netCDF")))
166 : {
167 4 : vsi_l_offset nOffset = HDF5_SIG_OFFSET;
168 8 : for (int i = 0; i < 64; i++)
169 : {
170 : GByte abyBuf[HDF5_SIG_LEN];
171 16 : if (VSIFSeekL(poOpenInfo->fpL, nOffset, SEEK_SET) != 0 ||
172 8 : VSIFReadL(abyBuf, 1, HDF5_SIG_LEN, poOpenInfo->fpL) !=
173 : HDF5_SIG_LEN)
174 : {
175 2 : break;
176 : }
177 6 : if (memcmp(abyBuf, HDF5_SIG, HDF5_SIG_LEN) == 0)
178 : {
179 2 : return NCDF_FORMAT_NC4;
180 : }
181 4 : nOffset *= 2;
182 : }
183 : }
184 :
185 15774 : return NCDF_FORMAT_NONE;
186 : }
187 :
188 : /************************************************************************/
189 : /* Identify() */
190 : /************************************************************************/
191 :
192 80231 : static int netCDFDatasetIdentify(GDALOpenInfo *poOpenInfo)
193 :
194 : {
195 80231 : if (STARTS_WITH_CI(poOpenInfo->pszFilename, "NETCDF:"))
196 : {
197 68 : return TRUE;
198 : }
199 : const NetCDFFormatEnum eTmpFormat =
200 80163 : netCDFIdentifyFormat(poOpenInfo,
201 : /* bCheckExt = */ true);
202 80163 : if (NCDF_FORMAT_NC == eTmpFormat || NCDF_FORMAT_NC2 == eTmpFormat ||
203 79425 : NCDF_FORMAT_NC4 == eTmpFormat || NCDF_FORMAT_NC4C == eTmpFormat)
204 738 : return TRUE;
205 79750 : if (eTmpFormat == NCDF_FORMAT_HDF4 &&
206 325 : poOpenInfo->IsSingleAllowedDriver("netCDF"))
207 : {
208 0 : return TRUE;
209 : }
210 :
211 79425 : return FALSE;
212 : }
213 :
214 : /************************************************************************/
215 : /* NCDFDriverGetSubdatasetInfo() */
216 : /************************************************************************/
217 :
218 : struct NCDFDriverSubdatasetInfo final : public GDALSubdatasetInfo
219 : {
220 : public:
221 129 : explicit NCDFDriverSubdatasetInfo(const std::string &fileName)
222 129 : : GDALSubdatasetInfo(fileName)
223 : {
224 129 : }
225 :
226 : // GDALSubdatasetInfo interface
227 : private:
228 : void parseFileName() override;
229 : };
230 :
231 129 : void NCDFDriverSubdatasetInfo::parseFileName()
232 : {
233 :
234 129 : if (!STARTS_WITH_CI(m_fileName.c_str(), "NETCDF:"))
235 : {
236 5 : return;
237 : }
238 :
239 : CPLStringList aosParts{CSLTokenizeString2(
240 129 : m_fileName.c_str(), ":", CSLT_HONOURSTRINGS | CSLT_PRESERVEQUOTES)};
241 129 : const int iPartsCount{CSLCount(aosParts)};
242 :
243 129 : m_driverPrefixComponent = aosParts[0];
244 129 : if (iPartsCount == 1)
245 : {
246 2 : return;
247 : }
248 :
249 127 : if (iPartsCount == 2)
250 : {
251 3 : m_pathComponent = aosParts[1];
252 3 : return;
253 : }
254 :
255 124 : int subdatasetIndex{2};
256 :
257 248 : std::string part1{aosParts[1]};
258 124 : if (!part1.empty() && part1[0] == '"')
259 : {
260 103 : part1 = part1.substr(1);
261 : }
262 :
263 : const bool hasDriveLetter{
264 124 : (strlen(aosParts[2]) > 1 &&
265 132 : (aosParts[2][0] == '\\' || aosParts[2][0] == '/')) &&
266 256 : part1.length() == 1 &&
267 3 : std::isalpha(static_cast<unsigned char>(part1.at(0)))};
268 :
269 248 : const bool hasProtocol{part1 == "/vsicurl/http" ||
270 247 : part1 == "/vsicurl/https" ||
271 246 : part1 == "/vsicurl_streaming/http" ||
272 246 : part1 == "/vsicurl_streaming/https" ||
273 371 : part1 == "http" || part1 == "https"};
274 :
275 124 : m_pathComponent = aosParts[1];
276 124 : if (hasDriveLetter || hasProtocol)
277 : {
278 6 : m_pathComponent.append(":");
279 6 : m_pathComponent.append(aosParts[2]);
280 6 : subdatasetIndex++;
281 :
282 : // Check for a port number and add it to the path component.
283 6 : if (hasProtocol && aosParts.size() > 3)
284 : {
285 2 : const auto nChars = strlen(aosParts[3]);
286 2 : size_t i = 0;
287 9 : while (i < nChars && std::isdigit(aosParts[3][i]))
288 : {
289 7 : i++;
290 : }
291 2 : if (i > 0 && aosParts[3][i] == '/')
292 : {
293 2 : m_pathComponent.append(":");
294 2 : m_pathComponent.append(aosParts[3]);
295 2 : subdatasetIndex++;
296 : }
297 : }
298 : }
299 :
300 : // Check for bogus paths
301 124 : if (subdatasetIndex < iPartsCount)
302 : {
303 121 : m_subdatasetComponent = aosParts[subdatasetIndex];
304 :
305 : // Append any remaining part
306 121 : for (int i = subdatasetIndex + 1; i < iPartsCount; ++i)
307 : {
308 0 : m_subdatasetComponent.append(":");
309 0 : m_subdatasetComponent.append(aosParts[i]);
310 : }
311 : }
312 :
313 : // Remove quotes from subdataset component
314 124 : if (!m_subdatasetComponent.empty() && m_subdatasetComponent[0] == '"')
315 : {
316 2 : m_subdatasetComponent = m_subdatasetComponent.substr(1);
317 : }
318 245 : if (!m_subdatasetComponent.empty() &&
319 121 : m_subdatasetComponent.rfind('"') == m_subdatasetComponent.length() - 1)
320 : {
321 4 : m_subdatasetComponent.pop_back();
322 : }
323 : }
324 :
325 2890 : static GDALSubdatasetInfo *NCDFDriverGetSubdatasetInfo(const char *pszFileName)
326 : {
327 2890 : if (STARTS_WITH_CI(pszFileName, "NETCDF:"))
328 : {
329 : std::unique_ptr<GDALSubdatasetInfo> info =
330 129 : std::make_unique<NCDFDriverSubdatasetInfo>(pszFileName);
331 : // Subdataset component can be empty, path cannot.
332 129 : if (!info->GetPathComponent().empty())
333 : {
334 127 : return info.release();
335 : }
336 : }
337 2763 : return nullptr;
338 : }
339 :
340 : /************************************************************************/
341 : /* netCDFDriverSetCommonMetadata() */
342 : /************************************************************************/
343 :
344 1896 : void netCDFDriverSetCommonMetadata(GDALDriver *poDriver)
345 : {
346 : // Set the driver details.
347 1896 : poDriver->SetDescription(DRIVER_NAME);
348 1896 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
349 1896 : poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
350 1896 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
351 1896 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
352 1896 : poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
353 1896 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Network Common Data Format");
354 1896 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/netcdf.html");
355 1896 : poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "nc");
356 :
357 1896 : poDriver->SetMetadataItem(
358 : GDAL_DMD_OPENOPTIONLIST,
359 : "<OpenOptionList>"
360 : " <Option name='LIST_ALL_ARRAYS' type='boolean' "
361 : "description='Whether to list all arrays, and not only those whose "
362 : "dimension count is 2 or more' default='NO'/>"
363 : " <Option name='HONOUR_VALID_RANGE' type='boolean' scope='raster' "
364 : "description='Whether to set to nodata pixel values outside of the "
365 : "validity range' default='YES'/>"
366 : " <Option name='IGNORE_XY_AXIS_NAME_CHECKS' type='boolean' "
367 : "scope='raster' "
368 : "description='Whether X/Y dimensions should be always considered as "
369 : "geospatial axis, even if the lack conventional attributes confirming "
370 : "it.'"
371 : " default='NO'/>"
372 : " <Option name='VARIABLES_AS_BANDS' type='boolean' scope='raster' "
373 : "description='Whether 2D variables that share the same indexing "
374 : "dimensions "
375 : "should be exposed as several bands of a same dataset instead of "
376 : "several "
377 : "subdatasets.' default='NO'/>"
378 : " <Option name='ASSUME_LONGLAT' type='boolean' scope='raster' "
379 : "description='Whether when all else has failed for determining a CRS, "
380 : "a "
381 : "meaningful geotransform has been found, and is within the "
382 : "bounds -180,360 -90,90, assume OGC:CRS84.' default='NO'/>"
383 : " <Option name='PRESERVE_AXIS_UNIT_IN_CRS' type='boolean' "
384 : "scope='raster' description='Whether unusual linear axis unit (km) "
385 : "should be kept as such, instead of being normalized to metre' "
386 : "default='NO'/>"
387 1896 : "</OpenOptionList>");
388 1896 : poDriver->SetMetadataItem(
389 : GDAL_DMD_CREATIONDATATYPES,
390 : "Byte Int8 UInt16 Int16 UInt32 Int32 Int64 UInt64 "
391 : "Float32 Float64 "
392 1896 : "CInt16 CInt32 CFloat32 CFloat64");
393 1896 : poDriver->SetMetadataItem(
394 : GDAL_DMD_CREATIONOPTIONLIST,
395 : "<CreationOptionList>"
396 : " <Option name='FORMAT' type='string-select' default='NC'>"
397 : " <Value>NC</Value>"
398 : #if NETCDF_CORE_HAS_NC2
399 : " <Value>NC2</Value>"
400 : #endif
401 : " <Value>NC4</Value>"
402 : " <Value>NC4C</Value>"
403 : " </Option>"
404 : " <Option name='COMPRESS' type='string-select' scope='raster' "
405 : "default='NONE'>"
406 : " <Value>NONE</Value>"
407 : " <Value>DEFLATE</Value>"
408 : " </Option>"
409 : " <Option name='ZLEVEL' type='int' scope='raster' "
410 : "description='DEFLATE compression level 1-9' default='1'/>"
411 : " <Option name='WRITE_BOTTOMUP' type='boolean' scope='raster' "
412 : "default='YES'>"
413 : " </Option>"
414 : " <Option name='WRITE_GDAL_TAGS' type='boolean' default='YES'>"
415 : " </Option>"
416 : " <Option name='WRITE_LONLAT' type='string-select' scope='raster'>"
417 : " <Value>YES</Value>"
418 : " <Value>NO</Value>"
419 : " <Value>IF_NEEDED</Value>"
420 : " </Option>"
421 : " <Option name='TYPE_LONLAT' type='string-select' scope='raster'>"
422 : " <Value>float</Value>"
423 : " <Value>double</Value>"
424 : " </Option>"
425 : " <Option name='PIXELTYPE' type='string-select' scope='raster' "
426 : "description='(deprecated, use Int8 datatype) only used in Create()'>"
427 : " <Value>DEFAULT</Value>"
428 : " <Value>SIGNEDBYTE</Value>"
429 : " </Option>"
430 : " <Option name='CHUNKING' type='boolean' scope='raster' "
431 : "default='YES' description='define chunking when creating netcdf4 "
432 : "file'/>"
433 : " <Option name='MULTIPLE_LAYERS' type='string-select' scope='vector' "
434 : "description='Behaviour regarding multiple vector layer creation' "
435 : "default='NO'>"
436 : " <Value>NO</Value>"
437 : " <Value>SEPARATE_FILES</Value>"
438 : " <Value>SEPARATE_GROUPS</Value>"
439 : " </Option>"
440 : " <Option name='GEOMETRY_ENCODING' type='string' scope='vector' "
441 : "default='CF_1.8' description='Specifies the type of geometry encoding "
442 : "when creating a netCDF dataset'>"
443 : " <Value>WKT</Value>"
444 : " <Value>CF_1.8</Value>"
445 : " </Option>"
446 : " <Option name='CONFIG_FILE' type='string' scope='vector' "
447 : "description='Path to a XML configuration file (or content inlined)'/>"
448 : " <Option name='WRITE_GDAL_VERSION' type='boolean' default='YES'/>"
449 : " <Option name='WRITE_GDAL_HISTORY' type='boolean' default='YES'/>"
450 : " <Option name='BAND_NAMES' type='string' scope='raster' />"
451 1896 : "</CreationOptionList>");
452 1896 : poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
453 :
454 1896 : poDriver->SetMetadataItem(
455 : GDAL_DS_LAYER_CREATIONOPTIONLIST,
456 : "<LayerCreationOptionList>"
457 : " <Option name='RECORD_DIM_NAME' type='string' description='Name of "
458 : "the unlimited dimension' default='record'/>"
459 : " <Option name='STRING_DEFAULT_WIDTH' type='int' description='"
460 : "For non-NC4 format, "
461 : "default width of strings. Default is 10 in autogrow mode, 80 "
462 : "otherwise.'/>"
463 : " <Option name='WKT_DEFAULT_WIDTH' type='int' description='"
464 : "For non-NC4 format, "
465 : "default width of WKT strings. Default is 1000 in autogrow mode, 10000 "
466 : "otherwise.'/>"
467 : " <Option name='AUTOGROW_STRINGS' type='boolean' "
468 : "description='Whether to auto-grow non-bounded string fields of "
469 : "bidimensional char variable' default='YES'/>"
470 : " <Option name='USE_STRING_IN_NC4' type='boolean' "
471 : "description='Whether to use NetCDF string type for strings in NC4 "
472 : "format. If NO, bidimensional char variable are used' default='YES'/>"
473 : #if 0
474 : " <Option name='NCDUMP_COMPAT' type='boolean' description='When USE_STRING_IN_NC4=YEs, whether to use empty string instead of null string to avoid crashes with ncdump' default='NO'/>"
475 : #endif
476 : " <Option name='FEATURE_TYPE' type='string-select' description='CF "
477 : "FeatureType' default='AUTO'>"
478 : " <Value>AUTO</Value>"
479 : " <Value>POINT</Value>"
480 : " <Value>PROFILE</Value>"
481 : " </Option>"
482 : " <Option name='BUFFER_SIZE' type='int' default='' "
483 : "description='Specifies the soft limit of buffer translation in bytes. "
484 : "Minimum size is 4096. Does not apply to datasets with CF version less "
485 : "than 1.8.'/>"
486 : " <Option name='GROUPLESS_WRITE_BACK' type='boolean' default='NO' "
487 : "description='Enables or disables array building write back for "
488 : "CF-1.8.'/>"
489 : " <Option name='PROFILE_DIM_NAME' type='string' description='Name of "
490 : "the profile dimension and variable' default='profile'/>"
491 : " <Option name='PROFILE_DIM_INIT_SIZE' type='string' "
492 : "description='Initial size of profile dimension (default 100), or "
493 : "UNLIMITED for NC4 files'/>"
494 : " <Option name='PROFILE_VARIABLES' type='string' description='Comma "
495 : "separated list of field names that must be indexed by the profile "
496 : "dimension'/>"
497 1896 : "</LayerCreationOptionList>");
498 :
499 : // Make driver config and capabilities available.
500 : #if NETCDF_CORE_HAS_NC2
501 1896 : poDriver->SetMetadataItem("NETCDF_HAS_NC2", "YES");
502 : #endif
503 1896 : poDriver->SetMetadataItem("NETCDF_HAS_NC4", "YES");
504 : #ifdef NETCDF_HAS_HDF4
505 : poDriver->SetMetadataItem("NETCDF_HAS_HDF4", "YES");
506 : #endif
507 :
508 1896 : poDriver->SetMetadataItem("NETCDF_HAS_NETCDF_MEM", "YES");
509 :
510 : #ifdef ENABLE_NCDUMP
511 1896 : poDriver->SetMetadataItem("ENABLE_NCDUMP", "YES");
512 : #endif
513 :
514 1896 : poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES");
515 :
516 1896 : poDriver->SetMetadataItem(
517 : GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
518 : "<MultiDimDatasetCreationOptionList>"
519 : " <Option name='FORMAT' type='string-select' default='NC4'>"
520 : " <Value>NC</Value>"
521 : #if NETCDF_CORE_HAS_NC2
522 : " <Value>NC2</Value>"
523 : #endif
524 : " <Value>NC4</Value>"
525 : " <Value>NC4C</Value>"
526 : " </Option>"
527 : " <Option name='CONVENTIONS' type='string' default='CF-1.6' "
528 : "description='Value of the Conventions attribute'/>"
529 1896 : "</MultiDimDatasetCreationOptionList>");
530 :
531 1896 : poDriver->SetMetadataItem(
532 : GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
533 : "<MultiDimDimensionCreationOptionList>"
534 : " <Option name='UNLIMITED' type='boolean' description='Whether the "
535 : "dimension should be unlimited' default='false'/>"
536 1896 : "</MultiDimDimensionCreationOptionList>");
537 :
538 1896 : poDriver->SetMetadataItem(
539 : GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
540 : "<MultiDimArrayCreationOptionList>"
541 : " <Option name='BLOCKSIZE' type='int' description='Block size in "
542 : "pixels'/>"
543 : " <Option name='COMPRESS' type='string-select' default='NONE'>"
544 : " <Value>NONE</Value>"
545 : " <Value>DEFLATE</Value>"
546 : " </Option>"
547 : " <Option name='ZLEVEL' type='int' description='DEFLATE compression "
548 : "level 1-9' default='1'/>"
549 : " <Option name='NC_TYPE' type='string-select' default='netCDF data "
550 : "type'>"
551 : " <Value>AUTO</Value>"
552 : " <Value>NC_BYTE</Value>"
553 : " <Value>NC_INT64</Value>"
554 : " <Value>NC_UINT64</Value>"
555 : " </Option>"
556 1896 : "</MultiDimArrayCreationOptionList>");
557 :
558 1896 : poDriver->SetMetadataItem(
559 : GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
560 : "<MultiDimArrayOpenOptionList>"
561 : " <Option name='USE_DEFAULT_FILL_AS_NODATA' type='boolean' "
562 : "description='Whether the default fill value should be used as nodata "
563 : "when there is no _FillValue or missing_value attribute' default='NO'/>"
564 : " <Option name='RAW_DATA_CHUNK_CACHE_SIZE' type='integer' "
565 : "description='The total size of the libnetcdf raw data chunk cache, "
566 : "in bytes. Only for netCDF4/HDF5 files'/>"
567 : " <Option name='CHUNK_SLOTS' type='integer' "
568 : "description='The number of chunk slots in the libnetcdf raw data "
569 : "chunk cache. "
570 : "Only for netCDF4/HDF5 files'/>"
571 : " <Option name='PREEMPTION' type='float' min='0' max='1' "
572 : "description='Indicates how much chunks from libnetcdf chunk cache "
573 : "that have been fully read are favored for preemption. "
574 : "Only for netCDF4/HDF5 files'/>"
575 1896 : "</MultiDimArrayOpenOptionList>");
576 :
577 1896 : poDriver->SetMetadataItem(GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
578 : "<MultiDimAttributeCreationOptionList>"
579 : " <Option name='NC_TYPE' type='string-select' "
580 : "default='netCDF data type'>"
581 : " <Value>AUTO</Value>"
582 : " <Value>NC_BYTE</Value>"
583 : " <Value>NC_CHAR</Value>"
584 : " <Value>NC_INT64</Value>"
585 : " <Value>NC_UINT64</Value>"
586 : " </Option>"
587 1896 : "</MultiDimAttributeCreationOptionList>");
588 :
589 1896 : poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
590 1896 : "Integer Integer64 Real String Date DateTime");
591 1896 : poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
592 1896 : "Comment AlternativeName");
593 :
594 1896 : poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
595 :
596 1896 : poDriver->pfnIdentify = netCDFDatasetIdentify;
597 1896 : poDriver->pfnGetSubdatasetInfoFunc = NCDFDriverGetSubdatasetInfo;
598 1896 : poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
599 1896 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
600 1896 : poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
601 1896 : poDriver->SetMetadataItem(GDAL_DCAP_CREATE_MULTIDIMENSIONAL, "YES");
602 :
603 1896 : poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
604 1896 : poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
605 : "GeoTransform SRS " // if not already set...
606 1896 : "DatasetMetadata BandMetadata RasterValues");
607 1896 : }
608 :
609 : /************************************************************************/
610 : /* DeclareDeferredNetCDFPlugin() */
611 : /************************************************************************/
612 :
613 : #ifdef PLUGIN_FILENAME
614 2138 : void DeclareDeferredNetCDFPlugin()
615 : {
616 2138 : if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
617 : {
618 263 : return;
619 : }
620 1875 : auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
621 : #ifdef PLUGIN_INSTALLATION_MESSAGE
622 : poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
623 : PLUGIN_INSTALLATION_MESSAGE);
624 : #endif
625 1875 : netCDFDriverSetCommonMetadata(poDriver);
626 1875 : GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
627 : }
628 : #endif
|