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