LCOV - code coverage report
Current view: top level - frmts/netcdf - netcdfdrivercore.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 170 175 97.1 %
Date: 2026-02-10 10:01:39 Functions: 9 9 100.0 %

          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       79166 : 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       79166 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "NETCDF:"))
      49           0 :         return NCDF_FORMAT_UNKNOWN;
      50       79166 :     if (poOpenInfo->nHeaderBytes < 4)
      51       61983 :         return NCDF_FORMAT_NONE;
      52       17183 :     const char *pszHeader =
      53             :         reinterpret_cast<const char *>(poOpenInfo->pabyHeader);
      54             : 
      55             : #ifdef ENABLE_NCDUMP
      56       17183 :     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       17174 :     if (STARTS_WITH_CI(pszHeader, "CDF\001"))
      77             :     {
      78         963 :         return NCDF_FORMAT_NC;
      79             :     }
      80             : 
      81       16211 :     if (STARTS_WITH_CI(pszHeader, "CDF\002"))
      82             :     {
      83          14 :         return NCDF_FORMAT_NC2;
      84             :     }
      85             : 
      86       16197 :     constexpr char HDF5_SIG[] = "\211HDF\r\n\032\n";
      87       16197 :     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       16197 :     constexpr int HDF5_SIG_OFFSET = 512;
      91       16197 :     if (STARTS_WITH_CI(pszHeader, HDF5_SIG) ||
      92       15151 :         (poOpenInfo->nHeaderBytes > HDF5_SIG_OFFSET + HDF5_SIG_LEN &&
      93       10221 :          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        1046 :         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        1027 :         if (bCheckExt)
     110             :         {
     111             :             // Check by default.
     112        1010 :             const char *pszExtension = poOpenInfo->osExtension.c_str();
     113        1010 :             if (!(EQUAL(pszExtension, "nc") || EQUAL(pszExtension, "cdf") ||
     114         459 :                   EQUAL(pszExtension, "nc2") || EQUAL(pszExtension, "nc4") ||
     115         459 :                   EQUAL(pszExtension, "nc3") || EQUAL(pszExtension, "grd") ||
     116         459 :                   EQUAL(pszExtension, "gmac")))
     117             :             {
     118         357 :                 if (GDALGetDriverByName("HDF5") != nullptr)
     119             :                 {
     120         357 :                     return NCDF_FORMAT_HDF5;
     121             :                 }
     122             :             }
     123             :         }
     124             : 
     125         670 :         return NCDF_FORMAT_NC4;
     126             :     }
     127       15151 :     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         313 :         if (bCheckExt && GDALGetDriverByName("HDF4") != nullptr)
     145             :         {
     146             :             // Check by default.
     147             :             // Always treat as HDF4 file.
     148         313 :             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       14838 :     const char *pszExtension = poOpenInfo->osExtension.c_str();
     162       29585 :     if (poOpenInfo->fpL != nullptr &&
     163       14747 :         (!bCheckExt || EQUAL(pszExtension, "nc") ||
     164       14745 :          EQUAL(pszExtension, "cdf") || EQUAL(pszExtension, "nc4") ||
     165       14745 :          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       14836 :     return NCDF_FORMAT_NONE;
     186             : }
     187             : 
     188             : /************************************************************************/
     189             : /*                              Identify()                              */
     190             : /************************************************************************/
     191             : 
     192       78199 : static int netCDFDatasetIdentify(GDALOpenInfo *poOpenInfo)
     193             : 
     194             : {
     195       78199 :     if (STARTS_WITH_CI(poOpenInfo->pszFilename, "NETCDF:"))
     196             :     {
     197          60 :         return TRUE;
     198             :     }
     199             :     const NetCDFFormatEnum eTmpFormat =
     200       78139 :         netCDFIdentifyFormat(poOpenInfo,
     201             :                              /* bCheckExt = */ true);
     202       78139 :     if (NCDF_FORMAT_NC == eTmpFormat || NCDF_FORMAT_NC2 == eTmpFormat ||
     203       77487 :         NCDF_FORMAT_NC4 == eTmpFormat || NCDF_FORMAT_NC4C == eTmpFormat)
     204         652 :         return TRUE;
     205       77800 :     if (eTmpFormat == NCDF_FORMAT_HDF4 &&
     206         313 :         poOpenInfo->IsSingleAllowedDriver("netCDF"))
     207             :     {
     208           0 :         return TRUE;
     209             :     }
     210             : 
     211       77487 :     return FALSE;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                    NCDFDriverGetSubdatasetInfo()                     */
     216             : /************************************************************************/
     217             : 
     218             : struct NCDFDriverSubdatasetInfo final : public GDALSubdatasetInfo
     219             : {
     220             :   public:
     221         118 :     explicit NCDFDriverSubdatasetInfo(const std::string &fileName)
     222         118 :         : GDALSubdatasetInfo(fileName)
     223             :     {
     224         118 :     }
     225             : 
     226             :     // GDALSubdatasetInfo interface
     227             :   private:
     228             :     void parseFileName() override;
     229             : };
     230             : 
     231         118 : void NCDFDriverSubdatasetInfo::parseFileName()
     232             : {
     233             : 
     234         118 :     if (!STARTS_WITH_CI(m_fileName.c_str(), "NETCDF:"))
     235             :     {
     236           5 :         return;
     237             :     }
     238             : 
     239         118 :     CPLStringList aosParts{CSLTokenizeString2(m_fileName.c_str(), ":", 0)};
     240         118 :     const int iPartsCount{CSLCount(aosParts)};
     241             : 
     242         118 :     m_driverPrefixComponent = aosParts[0];
     243         118 :     if (iPartsCount == 1)
     244             :     {
     245           2 :         return;
     246             :     }
     247             : 
     248         116 :     if (iPartsCount == 2)
     249             :     {
     250           3 :         m_pathComponent = aosParts[1];
     251           3 :         return;
     252             :     }
     253             : 
     254         113 :     int subdatasetIndex{2};
     255             : 
     256         226 :     std::string part1{aosParts[1]};
     257         113 :     if (!part1.empty() && part1[0] == '"')
     258             :     {
     259          92 :         part1 = part1.substr(1);
     260             :     }
     261             : 
     262             :     const bool hasDriveLetter{
     263         113 :         (strlen(aosParts[2]) > 1 &&
     264         119 :          (aosParts[2][0] == '\\' || aosParts[2][0] == '/')) &&
     265         232 :         part1.length() == 1 &&
     266           7 :         std::isalpha(static_cast<unsigned char>(part1.at(0)))};
     267             : 
     268         226 :     const bool hasProtocol{part1 == "/vsicurl/http" ||
     269         224 :                            part1 == "/vsicurl/https" ||
     270         222 :                            part1 == "/vsicurl_streaming/http" ||
     271         222 :                            part1 == "/vsicurl_streaming/https" ||
     272         337 :                            part1 == "http" || part1 == "https"};
     273             : 
     274         113 :     m_pathComponent = aosParts[1];
     275         113 :     if (hasDriveLetter || hasProtocol)
     276             :     {
     277          11 :         m_pathComponent.append(":");
     278          11 :         m_pathComponent.append(aosParts[2]);
     279          11 :         subdatasetIndex++;
     280             : 
     281             :         // Check for a port number and add it to the path component.
     282          11 :         if (hasProtocol && aosParts.size() > 3)
     283             :         {
     284           3 :             const auto nChars = strlen(aosParts[3]);
     285           3 :             size_t i = 0;
     286          10 :             while (i < nChars && std::isdigit(aosParts[3][i]))
     287             :             {
     288           7 :                 i++;
     289             :             }
     290           3 :             if (i > 0 && aosParts[3][i] == '/')
     291             :             {
     292           2 :                 m_pathComponent.append(":");
     293           2 :                 m_pathComponent.append(aosParts[3]);
     294           2 :                 subdatasetIndex++;
     295             :             }
     296             :         }
     297             :     }
     298             : 
     299             :     // Check for bogus paths
     300         113 :     if (subdatasetIndex < iPartsCount)
     301             :     {
     302         110 :         m_subdatasetComponent = aosParts[subdatasetIndex];
     303             : 
     304             :         // Append any remaining part
     305         112 :         for (int i = subdatasetIndex + 1; i < iPartsCount; ++i)
     306             :         {
     307           2 :             m_subdatasetComponent.append(":");
     308           2 :             m_subdatasetComponent.append(aosParts[i]);
     309             :         }
     310             :     }
     311             : 
     312             :     // Remove quotes from subdataset component
     313         113 :     if (!m_subdatasetComponent.empty() && m_subdatasetComponent[0] == '"')
     314             :     {
     315           2 :         m_subdatasetComponent = m_subdatasetComponent.substr(1);
     316             :     }
     317         223 :     if (!m_subdatasetComponent.empty() &&
     318         110 :         m_subdatasetComponent.rfind('"') == m_subdatasetComponent.length() - 1)
     319             :     {
     320           4 :         m_subdatasetComponent.pop_back();
     321             :     }
     322             : }
     323             : 
     324        2870 : static GDALSubdatasetInfo *NCDFDriverGetSubdatasetInfo(const char *pszFileName)
     325             : {
     326        2870 :     if (STARTS_WITH_CI(pszFileName, "NETCDF:"))
     327             :     {
     328             :         std::unique_ptr<GDALSubdatasetInfo> info =
     329         118 :             std::make_unique<NCDFDriverSubdatasetInfo>(pszFileName);
     330             :         // Subdataset component can be empty, path cannot.
     331         118 :         if (!info->GetPathComponent().empty())
     332             :         {
     333         116 :             return info.release();
     334             :         }
     335             :     }
     336        2754 :     return nullptr;
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                   netCDFDriverSetCommonMetadata()                    */
     341             : /************************************************************************/
     342             : 
     343        1795 : void netCDFDriverSetCommonMetadata(GDALDriver *poDriver)
     344             : {
     345             :     // Set the driver details.
     346        1795 :     poDriver->SetDescription(DRIVER_NAME);
     347        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     348        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_VECTOR, "YES");
     349        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_LAYER, "YES");
     350        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_FIELD, "YES");
     351        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_Z_GEOMETRIES, "YES");
     352        1795 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Network Common Data Format");
     353        1795 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/netcdf.html");
     354        1795 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "nc");
     355             : 
     356        1795 :     poDriver->SetMetadataItem(
     357             :         GDAL_DMD_OPENOPTIONLIST,
     358             :         "<OpenOptionList>"
     359             :         "   <Option name='LIST_ALL_ARRAYS' type='boolean' "
     360             :         "description='Whether to list all arrays, and not only those whose "
     361             :         "dimension count is 2 or more' default='NO'/>"
     362             :         "   <Option name='HONOUR_VALID_RANGE' type='boolean' scope='raster' "
     363             :         "description='Whether to set to nodata pixel values outside of the "
     364             :         "validity range' default='YES'/>"
     365             :         "   <Option name='IGNORE_XY_AXIS_NAME_CHECKS' type='boolean' "
     366             :         "scope='raster' "
     367             :         "description='Whether X/Y dimensions should be always considered as "
     368             :         "geospatial axis, even if the lack conventional attributes confirming "
     369             :         "it.'"
     370             :         " default='NO'/>"
     371             :         "   <Option name='VARIABLES_AS_BANDS' type='boolean' scope='raster' "
     372             :         "description='Whether 2D variables that share the same indexing "
     373             :         "dimensions "
     374             :         "should be exposed as several bands of a same dataset instead of "
     375             :         "several "
     376             :         "subdatasets.' default='NO'/>"
     377             :         "   <Option name='ASSUME_LONGLAT' type='boolean' scope='raster' "
     378             :         "description='Whether when all else has failed for determining a CRS, "
     379             :         "a "
     380             :         "meaningful geotransform has been found, and is within the  "
     381             :         "bounds -180,360 -90,90, assume OGC:CRS84.' default='NO'/>"
     382             :         "   <Option name='PRESERVE_AXIS_UNIT_IN_CRS' type='boolean' "
     383             :         "scope='raster' description='Whether unusual linear axis unit (km) "
     384             :         "should be kept as such, instead of being normalized to metre' "
     385             :         "default='NO'/>"
     386        1795 :         "</OpenOptionList>");
     387        1795 :     poDriver->SetMetadataItem(
     388             :         GDAL_DMD_CREATIONDATATYPES,
     389             :         "Byte Int8 UInt16 Int16 UInt32 Int32 Int64 UInt64 "
     390             :         "Float32 Float64 "
     391        1795 :         "CInt16 CInt32 CFloat32 CFloat64");
     392        1795 :     poDriver->SetMetadataItem(
     393             :         GDAL_DMD_CREATIONOPTIONLIST,
     394             :         "<CreationOptionList>"
     395             :         "   <Option name='FORMAT' type='string-select' default='NC'>"
     396             :         "     <Value>NC</Value>"
     397             : #if NETCDF_CORE_HAS_NC2
     398             :         "     <Value>NC2</Value>"
     399             : #endif
     400             :         "     <Value>NC4</Value>"
     401             :         "     <Value>NC4C</Value>"
     402             :         "   </Option>"
     403             :         "   <Option name='COMPRESS' type='string-select' scope='raster' "
     404             :         "default='NONE'>"
     405             :         "     <Value>NONE</Value>"
     406             :         "     <Value>DEFLATE</Value>"
     407             :         "   </Option>"
     408             :         "   <Option name='ZLEVEL' type='int' scope='raster' "
     409             :         "description='DEFLATE compression level 1-9' default='1'/>"
     410             :         "   <Option name='WRITE_BOTTOMUP' type='boolean' scope='raster' "
     411             :         "default='YES'>"
     412             :         "   </Option>"
     413             :         "   <Option name='WRITE_GDAL_TAGS' type='boolean' default='YES'>"
     414             :         "   </Option>"
     415             :         "   <Option name='WRITE_LONLAT' type='string-select' scope='raster'>"
     416             :         "     <Value>YES</Value>"
     417             :         "     <Value>NO</Value>"
     418             :         "     <Value>IF_NEEDED</Value>"
     419             :         "   </Option>"
     420             :         "   <Option name='TYPE_LONLAT' type='string-select' scope='raster'>"
     421             :         "     <Value>float</Value>"
     422             :         "     <Value>double</Value>"
     423             :         "   </Option>"
     424             :         "   <Option name='PIXELTYPE' type='string-select' scope='raster' "
     425             :         "description='(deprecated, use Int8 datatype) only used in Create()'>"
     426             :         "       <Value>DEFAULT</Value>"
     427             :         "       <Value>SIGNEDBYTE</Value>"
     428             :         "   </Option>"
     429             :         "   <Option name='CHUNKING' type='boolean' scope='raster' "
     430             :         "default='YES' description='define chunking when creating netcdf4 "
     431             :         "file'/>"
     432             :         "   <Option name='MULTIPLE_LAYERS' type='string-select' scope='vector' "
     433             :         "description='Behaviour regarding multiple vector layer creation' "
     434             :         "default='NO'>"
     435             :         "       <Value>NO</Value>"
     436             :         "       <Value>SEPARATE_FILES</Value>"
     437             :         "       <Value>SEPARATE_GROUPS</Value>"
     438             :         "   </Option>"
     439             :         "   <Option name='GEOMETRY_ENCODING' type='string' scope='vector' "
     440             :         "default='CF_1.8' description='Specifies the type of geometry encoding "
     441             :         "when creating a netCDF dataset'>"
     442             :         "       <Value>WKT</Value>"
     443             :         "       <Value>CF_1.8</Value>"
     444             :         "   </Option>"
     445             :         "   <Option name='CONFIG_FILE' type='string' scope='vector' "
     446             :         "description='Path to a XML configuration file (or content inlined)'/>"
     447             :         "   <Option name='WRITE_GDAL_VERSION' type='boolean' default='YES'/>"
     448             :         "   <Option name='WRITE_GDAL_HISTORY' type='boolean' default='YES'/>"
     449             :         "   <Option name='BAND_NAMES' type='string' scope='raster' />"
     450        1795 :         "</CreationOptionList>");
     451        1795 :     poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
     452             : 
     453        1795 :     poDriver->SetMetadataItem(
     454             :         GDAL_DS_LAYER_CREATIONOPTIONLIST,
     455             :         "<LayerCreationOptionList>"
     456             :         "   <Option name='RECORD_DIM_NAME' type='string' description='Name of "
     457             :         "the unlimited dimension' default='record'/>"
     458             :         "   <Option name='STRING_DEFAULT_WIDTH' type='int' description='"
     459             :         "For non-NC4 format, "
     460             :         "default width of strings. Default is 10 in autogrow mode, 80 "
     461             :         "otherwise.'/>"
     462             :         "   <Option name='WKT_DEFAULT_WIDTH' type='int' description='"
     463             :         "For non-NC4 format, "
     464             :         "default width of WKT strings. Default is 1000 in autogrow mode, 10000 "
     465             :         "otherwise.'/>"
     466             :         "   <Option name='AUTOGROW_STRINGS' type='boolean' "
     467             :         "description='Whether to auto-grow non-bounded string fields of "
     468             :         "bidimensional char variable' default='YES'/>"
     469             :         "   <Option name='USE_STRING_IN_NC4' type='boolean' "
     470             :         "description='Whether to use NetCDF string type for strings in NC4 "
     471             :         "format. If NO, bidimensional char variable are used' default='YES'/>"
     472             : #if 0
     473             : "   <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'/>"
     474             : #endif
     475             :         "   <Option name='FEATURE_TYPE' type='string-select' description='CF "
     476             :         "FeatureType' default='AUTO'>"
     477             :         "       <Value>AUTO</Value>"
     478             :         "       <Value>POINT</Value>"
     479             :         "       <Value>PROFILE</Value>"
     480             :         "   </Option>"
     481             :         "   <Option name='BUFFER_SIZE' type='int' default='' "
     482             :         "description='Specifies the soft limit of buffer translation in bytes. "
     483             :         "Minimum size is 4096. Does not apply to datasets with CF version less "
     484             :         "than 1.8.'/>"
     485             :         "   <Option name='GROUPLESS_WRITE_BACK' type='boolean' default='NO' "
     486             :         "description='Enables or disables array building write back for "
     487             :         "CF-1.8.'/>"
     488             :         "   <Option name='PROFILE_DIM_NAME' type='string' description='Name of "
     489             :         "the profile dimension and variable' default='profile'/>"
     490             :         "   <Option name='PROFILE_DIM_INIT_SIZE' type='string' "
     491             :         "description='Initial size of profile dimension (default 100), or "
     492             :         "UNLIMITED for NC4 files'/>"
     493             :         "   <Option name='PROFILE_VARIABLES' type='string' description='Comma "
     494             :         "separated list of field names that must be indexed by the profile "
     495             :         "dimension'/>"
     496        1795 :         "</LayerCreationOptionList>");
     497             : 
     498             :     // Make driver config and capabilities available.
     499             : #if NETCDF_CORE_HAS_NC2
     500        1795 :     poDriver->SetMetadataItem("NETCDF_HAS_NC2", "YES");
     501             : #endif
     502        1795 :     poDriver->SetMetadataItem("NETCDF_HAS_NC4", "YES");
     503             : #ifdef NETCDF_HAS_HDF4
     504             :     poDriver->SetMetadataItem("NETCDF_HAS_HDF4", "YES");
     505             : #endif
     506             : 
     507        1795 :     poDriver->SetMetadataItem("NETCDF_HAS_NETCDF_MEM", "YES");
     508             : 
     509             : #ifdef ENABLE_NCDUMP
     510        1795 :     poDriver->SetMetadataItem("ENABLE_NCDUMP", "YES");
     511             : #endif
     512             : 
     513        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_MULTIDIM_RASTER, "YES");
     514             : 
     515        1795 :     poDriver->SetMetadataItem(
     516             :         GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
     517             :         "<MultiDimDatasetCreationOptionList>"
     518             :         "   <Option name='FORMAT' type='string-select' default='NC4'>"
     519             :         "     <Value>NC</Value>"
     520             : #if NETCDF_CORE_HAS_NC2
     521             :         "     <Value>NC2</Value>"
     522             : #endif
     523             :         "     <Value>NC4</Value>"
     524             :         "     <Value>NC4C</Value>"
     525             :         "   </Option>"
     526             :         "   <Option name='CONVENTIONS' type='string' default='CF-1.6' "
     527             :         "description='Value of the Conventions attribute'/>"
     528        1795 :         "</MultiDimDatasetCreationOptionList>");
     529             : 
     530        1795 :     poDriver->SetMetadataItem(
     531             :         GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
     532             :         "<MultiDimDimensionCreationOptionList>"
     533             :         "   <Option name='UNLIMITED' type='boolean' description='Whether the "
     534             :         "dimension should be unlimited' default='false'/>"
     535        1795 :         "</MultiDimDimensionCreationOptionList>");
     536             : 
     537        1795 :     poDriver->SetMetadataItem(
     538             :         GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
     539             :         "<MultiDimArrayCreationOptionList>"
     540             :         "   <Option name='BLOCKSIZE' type='int' description='Block size in "
     541             :         "pixels'/>"
     542             :         "   <Option name='COMPRESS' type='string-select' default='NONE'>"
     543             :         "     <Value>NONE</Value>"
     544             :         "     <Value>DEFLATE</Value>"
     545             :         "   </Option>"
     546             :         "   <Option name='ZLEVEL' type='int' description='DEFLATE compression "
     547             :         "level 1-9' default='1'/>"
     548             :         "   <Option name='NC_TYPE' type='string-select' default='netCDF data "
     549             :         "type'>"
     550             :         "     <Value>AUTO</Value>"
     551             :         "     <Value>NC_BYTE</Value>"
     552             :         "     <Value>NC_INT64</Value>"
     553             :         "     <Value>NC_UINT64</Value>"
     554             :         "   </Option>"
     555        1795 :         "</MultiDimArrayCreationOptionList>");
     556             : 
     557        1795 :     poDriver->SetMetadataItem(
     558             :         GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
     559             :         "<MultiDimArrayOpenOptionList>"
     560             :         "   <Option name='USE_DEFAULT_FILL_AS_NODATA' type='boolean' "
     561             :         "description='Whether the default fill value should be used as nodata "
     562             :         "when there is no _FillValue or missing_value attribute' default='NO'/>"
     563             :         "   <Option name='RAW_DATA_CHUNK_CACHE_SIZE' type='integer' "
     564             :         "description='The total size of the libnetcdf raw data chunk cache, "
     565             :         "in bytes. Only for netCDF4/HDF5 files'/>"
     566             :         "   <Option name='CHUNK_SLOTS' type='integer' "
     567             :         "description='The number of chunk slots in the libnetcdf raw data "
     568             :         "chunk cache. "
     569             :         "Only for netCDF4/HDF5 files'/>"
     570             :         "   <Option name='PREEMPTION' type='float' min='0' max='1' "
     571             :         "description='Indicates how much chunks from libnetcdf chunk cache "
     572             :         "that have been fully read are favored for preemption. "
     573             :         "Only for netCDF4/HDF5 files'/>"
     574        1795 :         "</MultiDimArrayOpenOptionList>");
     575             : 
     576        1795 :     poDriver->SetMetadataItem(GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
     577             :                               "<MultiDimAttributeCreationOptionList>"
     578             :                               "   <Option name='NC_TYPE' type='string-select' "
     579             :                               "default='netCDF data type'>"
     580             :                               "     <Value>AUTO</Value>"
     581             :                               "     <Value>NC_BYTE</Value>"
     582             :                               "     <Value>NC_CHAR</Value>"
     583             :                               "     <Value>NC_INT64</Value>"
     584             :                               "     <Value>NC_UINT64</Value>"
     585             :                               "   </Option>"
     586        1795 :                               "</MultiDimAttributeCreationOptionList>");
     587             : 
     588        1795 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONFIELDDATATYPES,
     589        1795 :                               "Integer Integer64 Real String Date DateTime");
     590        1795 :     poDriver->SetMetadataItem(GDAL_DMD_CREATION_FIELD_DEFN_FLAGS,
     591        1795 :                               "Comment AlternativeName");
     592             : 
     593        1795 :     poDriver->SetMetadataItem(GDAL_DMD_SUPPORTED_SQL_DIALECTS, "OGRSQL SQLITE");
     594             : 
     595        1795 :     poDriver->pfnIdentify = netCDFDatasetIdentify;
     596        1795 :     poDriver->pfnGetSubdatasetInfoFunc = NCDFDriverGetSubdatasetInfo;
     597        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_OPEN, "YES");
     598        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE, "YES");
     599        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATECOPY, "YES");
     600        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_CREATE_MULTIDIMENSIONAL, "YES");
     601             : 
     602        1795 :     poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
     603        1795 :     poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS,
     604             :                               "GeoTransform SRS "  // if not already set...
     605        1795 :                               "DatasetMetadata BandMetadata RasterValues");
     606        1795 : }
     607             : 
     608             : /************************************************************************/
     609             : /*                    DeclareDeferredNetCDFPlugin()                     */
     610             : /************************************************************************/
     611             : 
     612             : #ifdef PLUGIN_FILENAME
     613        2059 : void DeclareDeferredNetCDFPlugin()
     614             : {
     615        2059 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
     616             :     {
     617         283 :         return;
     618             :     }
     619        1776 :     auto poDriver = new GDALPluginDriverProxy(PLUGIN_FILENAME);
     620             : #ifdef PLUGIN_INSTALLATION_MESSAGE
     621             :     poDriver->SetMetadataItem(GDAL_DMD_PLUGIN_INSTALLATION_MESSAGE,
     622             :                               PLUGIN_INSTALLATION_MESSAGE);
     623             : #endif
     624        1776 :     netCDFDriverSetCommonMetadata(poDriver);
     625        1776 :     GetGDALDriverManager()->DeclareDeferredPluginDriver(poDriver);
     626             : }
     627             : #endif

Generated by: LCOV version 1.14