LCOV - code coverage report
Current view: top level - frmts/hdf5 - s104dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 205 262 78.2 %
Date: 2025-12-09 00:43:28 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  Read S104 datasets.
       5             :  * Author:   Even Rouault <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2023, Even Rouault <even dot rouault at spatialys dot com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : #include "hdf5dataset.h"
      15             : #include "hdf5drivercore.h"
      16             : #include "gh5_convenience.h"
      17             : #include "rat.h"
      18             : #include "s100.h"
      19             : 
      20             : #include "gdal_frmts.h"
      21             : #include "gdal_priv.h"
      22             : #include "gdal_proxy.h"
      23             : #include "gdal_rat.h"
      24             : 
      25             : #include <limits>
      26             : #include <map>
      27             : 
      28             : /************************************************************************/
      29             : /*                             S104Dataset                              */
      30             : /************************************************************************/
      31             : 
      32          42 : class S104Dataset final : public S100BaseDataset
      33             : {
      34             :   public:
      35          21 :     explicit S104Dataset(const std::string &osFilename)
      36          21 :         : S100BaseDataset(osFilename)
      37             :     {
      38          21 :     }
      39             : 
      40             :     ~S104Dataset() override;
      41             : 
      42             :     static GDALDataset *Open(GDALOpenInfo *);
      43             : };
      44             : 
      45             : S104Dataset::~S104Dataset() = default;
      46             : 
      47             : /************************************************************************/
      48             : /*                            S104RasterBand                            */
      49             : /************************************************************************/
      50             : 
      51             : class S104RasterBand final : public GDALProxyRasterBand
      52             : {
      53             :     friend class S104Dataset;
      54             :     std::unique_ptr<GDALDataset> m_poDS{};
      55             :     GDALRasterBand *m_poUnderlyingBand = nullptr;
      56             :     std::string m_osUnitType{};
      57             :     std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
      58             : 
      59             :     CPL_DISALLOW_COPY_ASSIGN(S104RasterBand)
      60             : 
      61             :   public:
      62           8 :     explicit S104RasterBand(std::unique_ptr<GDALDataset> &&poDSIn)
      63          16 :         : m_poDS(std::move(poDSIn)),
      64           8 :           m_poUnderlyingBand(m_poDS->GetRasterBand(1))
      65             :     {
      66           8 :         eDataType = m_poUnderlyingBand->GetRasterDataType();
      67           8 :         m_poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
      68           8 :     }
      69             : 
      70             :     GDALRasterBand *
      71             :     RefUnderlyingRasterBand(bool /*bForceOpen*/ = true) const override;
      72             : 
      73           2 :     const char *GetUnitType() override
      74             :     {
      75           2 :         return m_osUnitType.c_str();
      76             :     }
      77             : 
      78           1 :     GDALRasterAttributeTable *GetDefaultRAT() override
      79             :     {
      80           1 :         return m_poRAT.get();
      81             :     }
      82             : };
      83             : 
      84             : GDALRasterBand *
      85          12 : S104RasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
      86             : {
      87          12 :     return m_poUnderlyingBand;
      88             : }
      89             : 
      90             : /************************************************************************/
      91             : /*                                Open()                                */
      92             : /************************************************************************/
      93             : 
      94          22 : GDALDataset *S104Dataset::Open(GDALOpenInfo *poOpenInfo)
      95             : 
      96             : {
      97             :     // Confirm that this appears to be a S104 file.
      98          22 :     if (!S104DatasetIdentify(poOpenInfo))
      99           0 :         return nullptr;
     100             : 
     101             :     HDF5_GLOBAL_LOCK();
     102             : 
     103          22 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
     104             :     {
     105           1 :         return HDF5Dataset::OpenMultiDim(poOpenInfo);
     106             :     }
     107             : 
     108             :     // Confirm the requested access is supported.
     109          21 :     if (poOpenInfo->eAccess == GA_Update)
     110             :     {
     111           0 :         ReportUpdateNotSupportedByDriver("S104");
     112           0 :         return nullptr;
     113             :     }
     114             : 
     115          42 :     std::string osFilename(poOpenInfo->pszFilename);
     116          42 :     std::string osFeatureInstance = "WaterLevel.01";
     117          42 :     std::string osGroup;
     118          21 :     if (STARTS_WITH(poOpenInfo->pszFilename, "S104:"))
     119             :     {
     120             :         const CPLStringList aosTokens(
     121           5 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":",
     122           5 :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
     123             : 
     124           5 :         if (aosTokens.size() == 2)
     125             :         {
     126           0 :             osFilename = aosTokens[1];
     127             :         }
     128           5 :         else if (aosTokens.size() == 3)
     129             :         {
     130           3 :             osFilename = aosTokens[1];
     131           3 :             osGroup = aosTokens[2];
     132             :         }
     133           2 :         else if (aosTokens.size() == 4)
     134             :         {
     135           2 :             osFilename = aosTokens[1];
     136           2 :             osFeatureInstance = aosTokens[2];
     137           2 :             osGroup = aosTokens[3];
     138             :         }
     139             :         else
     140             :         {
     141           0 :             return nullptr;
     142             :         }
     143             :     }
     144             : 
     145          42 :     auto poDS = std::make_unique<S104Dataset>(osFilename);
     146          21 :     if (!poDS->Init())
     147           0 :         return nullptr;
     148             : 
     149          21 :     const auto &poRootGroup = poDS->m_poRootGroup;
     150             : 
     151          63 :     auto poVerticalCS = poRootGroup->GetAttribute("verticalCS");
     152          21 :     if (poVerticalCS && poVerticalCS->GetDataType().GetClass() == GEDTC_NUMERIC)
     153             :     {
     154          21 :         const int nVerticalCS = poVerticalCS->ReadAsInt();
     155          21 :         if (nVerticalCS == 6498)
     156          21 :             poDS->GDALDataset::SetMetadataItem(
     157             :                 "VERTICAL_CS_MEANING", "depth, meters, orientation down");
     158           0 :         else if (nVerticalCS == 6499)
     159           0 :             poDS->GDALDataset::SetMetadataItem(
     160             :                 "VERTICAL_CS_MEANING", "height, meters, orientation up");
     161             : 
     162          42 :         poDS->GDALDataset::SetMetadataItem("verticalCS",
     163          42 :                                            std::to_string(nVerticalCS).c_str());
     164             :     }
     165             : 
     166          63 :     auto poWaterLevel = poRootGroup->OpenGroup("WaterLevel");
     167          21 :     if (!poWaterLevel)
     168             :     {
     169           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find /WaterLevel group");
     170           0 :         return nullptr;
     171             :     }
     172             : 
     173          63 :     auto poDataCodingFormat = poWaterLevel->GetAttribute("dataCodingFormat");
     174          42 :     if (!poDataCodingFormat ||
     175          21 :         poDataCodingFormat->GetDataType().GetClass() != GEDTC_NUMERIC)
     176             :     {
     177           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     178             :                  "Cannot find /WaterLevel/dataCodingFormat attribute");
     179           0 :         return nullptr;
     180             :     }
     181          21 :     const int nDataCodingFormat = poDataCodingFormat->ReadAsInt();
     182          21 :     if (nDataCodingFormat != 2)
     183             :     {
     184           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     185             :                  "dataCodingFormat=%d is not supported by the S104 driver",
     186             :                  nDataCodingFormat);
     187           0 :         return nullptr;
     188             :     }
     189             : 
     190             :     // Read additional metadata
     191          63 :     for (const char *pszAttrName :
     192          84 :          {"methodWaterLevelProduct", "minDatasetHeight", "maxDatasetHeight"})
     193             :     {
     194         189 :         auto poAttr = poWaterLevel->GetAttribute(pszAttrName);
     195          63 :         if (poAttr)
     196             :         {
     197          42 :             const char *pszVal = poAttr->ReadAsString();
     198          42 :             if (pszVal)
     199             :             {
     200          42 :                 poDS->GDALDataset::SetMetadataItem(pszAttrName, pszVal);
     201             :             }
     202             :         }
     203             :     }
     204             : 
     205          21 :     int nNumInstances = 1;
     206          21 :     if (osGroup.empty())
     207             :     {
     208          48 :         auto poNumInstances = poWaterLevel->GetAttribute("numInstances");
     209          17 :         if (poNumInstances &&
     210          17 :             poNumInstances->GetDataType().GetClass() == GEDTC_NUMERIC)
     211             :         {
     212           1 :             nNumInstances = poNumInstances->ReadAsInt();
     213             :         }
     214             :     }
     215          21 :     if (nNumInstances != 1)
     216             :     {
     217           2 :         CPLStringList aosSubDSList;
     218           1 :         int iSubDS = 0;
     219           2 :         for (const std::string &featureInstanceName :
     220           5 :              poWaterLevel->GetGroupNames())
     221             :         {
     222             :             auto poFeatureInstance =
     223           4 :                 poWaterLevel->OpenGroup(featureInstanceName);
     224           2 :             if (poFeatureInstance)
     225             :             {
     226           4 :                 GDALMajorObject mo;
     227             :                 // Read first vertical datum from root group and let the
     228             :                 // coverage override it.
     229           2 :                 S100ReadVerticalDatum(&mo, poRootGroup.get());
     230           2 :                 S100ReadVerticalDatum(&mo, poFeatureInstance.get());
     231             : 
     232           4 :                 const auto aosGroupNames = poFeatureInstance->GetGroupNames();
     233           4 :                 for (const auto &osSubGroup : aosGroupNames)
     234             :                 {
     235           2 :                     if (auto poSubGroup =
     236           4 :                             poFeatureInstance->OpenGroup(osSubGroup))
     237             :                     {
     238           2 :                         ++iSubDS;
     239             :                         aosSubDSList.SetNameValue(
     240             :                             CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
     241             :                             CPLSPrintf("S104:\"%s\":%s:%s", osFilename.c_str(),
     242             :                                        featureInstanceName.c_str(),
     243           2 :                                        osSubGroup.c_str()));
     244             : 
     245           4 :                         std::string verticalDatum;
     246             :                         const char *pszValue =
     247           2 :                             mo.GetMetadataItem(S100_VERTICAL_DATUM_MEANING);
     248           2 :                         if (pszValue)
     249             :                         {
     250           2 :                             verticalDatum = ", vertical datum ";
     251           2 :                             verticalDatum += pszValue;
     252             :                             pszValue =
     253           2 :                                 mo.GetMetadataItem(S100_VERTICAL_DATUM_ABBREV);
     254           2 :                             if (pszValue)
     255             :                             {
     256           2 :                                 verticalDatum += " (";
     257           2 :                                 verticalDatum += pszValue;
     258           2 :                                 verticalDatum += ')';
     259             :                             }
     260             :                         }
     261             :                         else
     262             :                         {
     263             :                             pszValue =
     264           0 :                                 mo.GetMetadataItem(S100_VERTICAL_DATUM_NAME);
     265           0 :                             if (pszValue)
     266             :                             {
     267           0 :                                 verticalDatum = ", vertical datum ";
     268           0 :                                 verticalDatum += pszValue;
     269             :                             }
     270             :                         }
     271             : 
     272           4 :                         std::string osSubDSDesc;
     273             :                         const auto poTimePoint =
     274           6 :                             poSubGroup->GetAttribute("timePoint");
     275           2 :                         if (poTimePoint)
     276             :                         {
     277           2 :                             const char *pszVal = poTimePoint->ReadAsString();
     278           2 :                             if (pszVal)
     279             :                             {
     280           2 :                                 osSubDSDesc = "Values for feature instance ";
     281           2 :                                 osSubDSDesc += featureInstanceName;
     282           2 :                                 osSubDSDesc += verticalDatum;
     283           2 :                                 osSubDSDesc += " at timestamp ";
     284           2 :                                 osSubDSDesc += pszVal;
     285             :                             }
     286             :                         }
     287           2 :                         if (osSubDSDesc.empty())
     288             :                         {
     289           0 :                             osSubDSDesc = "Values for feature instance ";
     290           0 :                             osSubDSDesc += featureInstanceName;
     291           0 :                             osSubDSDesc += verticalDatum;
     292           0 :                             osSubDSDesc += " and group ";
     293           0 :                             osSubDSDesc += osSubGroup;
     294             :                         }
     295             : 
     296             :                         aosSubDSList.SetNameValue(
     297             :                             CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
     298           2 :                             osSubDSDesc.c_str());
     299             :                     }
     300             :                 }
     301             :             }
     302             :         }
     303             : 
     304           1 :         poDS->GDALDataset::SetMetadata(aosSubDSList.List(), "SUBDATASETS");
     305             : 
     306             :         // Setup/check for pam .aux.xml.
     307           1 :         poDS->SetDescription(osFilename.c_str());
     308           1 :         poDS->TryLoadXML();
     309             : 
     310             :         // Setup overviews.
     311           1 :         poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     312             : 
     313           1 :         return poDS.release();
     314             :     }
     315             : 
     316          40 :     auto poFeatureInstance = poWaterLevel->OpenGroup(osFeatureInstance);
     317          20 :     if (!poFeatureInstance)
     318             :     {
     319           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     320             :                  "Cannot find /WaterLevel/%s group", osFeatureInstance.c_str());
     321           0 :         return nullptr;
     322             :     }
     323             : 
     324             :     // Read additional metadata
     325         100 :     for (const char *pszAttrName :
     326             :          {"timeRecordInterval", "dateTimeOfFirstRecord", "dateTimeOfLastRecord",
     327         120 :           "numberOfTimes", "dataDynamicity"})
     328             :     {
     329         300 :         auto poAttr = poFeatureInstance->GetAttribute(pszAttrName);
     330         100 :         if (poAttr)
     331             :         {
     332          80 :             const char *pszVal = poAttr->ReadAsString();
     333          80 :             if (pszVal)
     334             :             {
     335          80 :                 poDS->GDALDataset::SetMetadataItem(pszAttrName, pszVal);
     336             :             }
     337             :         }
     338             :     }
     339             : 
     340          20 :     if (auto poDataDynamicity =
     341          60 :             poFeatureInstance->GetAttribute("dataDynamicity"))
     342             :     {
     343           0 :         if (poDataDynamicity->GetDataType().GetClass() == GEDTC_NUMERIC)
     344             :         {
     345           0 :             const int nVal = poDataDynamicity->ReadAsInt();
     346             :             const std::map<int, const char *> oDataDynamicityMap = {
     347             :                 {1, "Observation"},
     348             :                 {2, "Astronomical prediction"},
     349             :                 {3, "Analysis or hybrid method"},
     350             :                 {5, "Hydrodynamic model forecast"},
     351           0 :             };
     352           0 :             const auto oIter = oDataDynamicityMap.find(nVal);
     353           0 :             if (oIter != oDataDynamicityMap.end())
     354           0 :                 poDS->GDALDataset::SetMetadataItem("DATA_DYNAMICITY_MEANING",
     355           0 :                                                    oIter->second);
     356             :         }
     357             :     }
     358             : 
     359          40 :     if (auto poStartSequence = poFeatureInstance->GetAttribute("startSequence"))
     360             :     {
     361          20 :         const char *pszStartSequence = poStartSequence->ReadAsString();
     362          20 :         if (pszStartSequence && !EQUAL(pszStartSequence, "0,0"))
     363             :         {
     364           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     365             :                      "startSequence (=%s) != 0,0 is not supported",
     366             :                      pszStartSequence);
     367           0 :             return nullptr;
     368             :         }
     369             :     }
     370             : 
     371          20 :     if (!S100GetNumPointsLongitudinalLatitudinal(
     372          20 :             poFeatureInstance.get(), poDS->nRasterXSize, poDS->nRasterYSize))
     373             :     {
     374           0 :         return nullptr;
     375             :     }
     376             : 
     377             :     // Potentially override vertical datum
     378          20 :     S100ReadVerticalDatum(poDS.get(), poFeatureInstance.get());
     379             : 
     380          20 :     const bool bNorthUp = CPLTestBool(
     381          20 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
     382             : 
     383             :     // Compute geotransform
     384          40 :     poDS->m_bHasGT =
     385          20 :         S100GetGeoTransform(poFeatureInstance.get(), poDS->m_gt, bNorthUp);
     386             : 
     387          20 :     if (osGroup.empty())
     388             :     {
     389          30 :         const auto aosGroupNames = poFeatureInstance->GetGroupNames();
     390          15 :         int iSubDS = 1;
     391          30 :         for (const auto &osSubGroup : aosGroupNames)
     392             :         {
     393          30 :             if (auto poSubGroup = poFeatureInstance->OpenGroup(osSubGroup))
     394             :             {
     395          15 :                 poDS->GDALDataset::SetMetadataItem(
     396             :                     CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
     397             :                     CPLSPrintf("S104:\"%s\":%s", osFilename.c_str(),
     398             :                                osSubGroup.c_str()),
     399             :                     "SUBDATASETS");
     400          30 :                 std::string osSubDSDesc = "Values for group ";
     401          15 :                 osSubDSDesc += osSubGroup;
     402          30 :                 const auto poTimePoint = poSubGroup->GetAttribute("timePoint");
     403          15 :                 if (poTimePoint)
     404             :                 {
     405          15 :                     const char *pszVal = poTimePoint->ReadAsString();
     406          15 :                     if (pszVal)
     407             :                     {
     408          15 :                         osSubDSDesc = "Values at timestamp ";
     409          15 :                         osSubDSDesc += pszVal;
     410             :                     }
     411             :                 }
     412          15 :                 poDS->GDALDataset::SetMetadataItem(
     413             :                     CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
     414             :                     osSubDSDesc.c_str(), "SUBDATASETS");
     415          15 :                 ++iSubDS;
     416             :             }
     417             :         }
     418             :     }
     419             :     else
     420             :     {
     421           5 :         auto poGroup = poFeatureInstance->OpenGroup(osGroup);
     422           5 :         if (!poGroup)
     423             :         {
     424           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     425             :                      "Cannot find /WaterLevel/%s/%s group",
     426             :                      osFeatureInstance.c_str(), osGroup.c_str());
     427           1 :             return nullptr;
     428             :         }
     429             : 
     430           8 :         auto poValuesArray = poGroup->OpenMDArray("values");
     431           4 :         if (!poValuesArray)
     432             :         {
     433           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     434             :                      "Cannot find /WaterLevel/%s/%s/values array",
     435             :                      osFeatureInstance.c_str(), osGroup.c_str());
     436           0 :             return nullptr;
     437             :         }
     438             : 
     439           4 :         if (poValuesArray->GetDimensionCount() != 2)
     440             :         {
     441           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     442             :                      "Wrong dimension count for %s",
     443           0 :                      poValuesArray->GetFullName().c_str());
     444           0 :             return nullptr;
     445             :         }
     446             : 
     447           4 :         const auto &oType = poValuesArray->GetDataType();
     448           4 :         if (oType.GetClass() != GEDTC_COMPOUND)
     449             :         {
     450           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong data type for %s",
     451           0 :                      poValuesArray->GetFullName().c_str());
     452           0 :             return nullptr;
     453             :         }
     454             : 
     455           4 :         const auto &oComponents = oType.GetComponents();
     456           4 :         if (oComponents.size() != 2 ||
     457           8 :             oComponents[0]->GetName() != "waterLevelHeight" ||
     458           4 :             oComponents[0]->GetType().GetNumericDataType() != GDT_Float32 ||
     459          12 :             oComponents[1]->GetName() != "waterLevelTrend" ||
     460           4 :             (oComponents[1]->GetType().GetNumericDataType() != GDT_UInt8 &&
     461             :              // In theory should be Byte, but 104US00_ches_dcf2_20190606T12Z.h5 uses Int32
     462           0 :              oComponents[1]->GetType().GetNumericDataType() != GDT_Int32))
     463             :         {
     464           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong data type for %s",
     465           0 :                      poValuesArray->GetFullName().c_str());
     466           0 :             return nullptr;
     467             :         }
     468             : 
     469           4 :         const auto &apoDims = poValuesArray->GetDimensions();
     470           4 :         if (apoDims[0]->GetSize() != static_cast<unsigned>(poDS->nRasterYSize))
     471             :         {
     472           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     473             :                      "numPointsLatitudinal(=%d) doesn't match first dimension "
     474             :                      "size of %s (=%d)",
     475           0 :                      poDS->nRasterYSize, poValuesArray->GetFullName().c_str(),
     476           0 :                      static_cast<int>(apoDims[0]->GetSize()));
     477           0 :             return nullptr;
     478             :         }
     479           4 :         if (apoDims[1]->GetSize() != static_cast<unsigned>(poDS->nRasterXSize))
     480             :         {
     481           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     482             :                      "numPointsLongitudinal(=%d) doesn't match second "
     483             :                      "dimension size of %s (=%d)",
     484           0 :                      poDS->nRasterXSize, poValuesArray->GetFullName().c_str(),
     485           0 :                      static_cast<int>(apoDims[1]->GetSize()));
     486           0 :             return nullptr;
     487             :         }
     488             : 
     489           4 :         if (bNorthUp)
     490           3 :             poValuesArray = poValuesArray->GetView("[::-1,...]");
     491             : 
     492             :         // Create waterLevelHeight band
     493             :         auto poWaterLevelHeight =
     494          12 :             poValuesArray->GetView("[\"waterLevelHeight\"]");
     495             :         auto poWaterLevelHeightDS = std::unique_ptr<GDALDataset>(
     496           8 :             poWaterLevelHeight->AsClassicDataset(1, 0));
     497             :         auto poWaterLevelHeightBand =
     498           8 :             std::make_unique<S104RasterBand>(std::move(poWaterLevelHeightDS));
     499           4 :         poWaterLevelHeightBand->SetDescription("waterLevelHeight");
     500           4 :         poWaterLevelHeightBand->m_osUnitType = "metre";
     501           4 :         poDS->SetBand(1, poWaterLevelHeightBand.release());
     502             : 
     503             :         // Create waterLevelTrend band
     504             :         auto poWaterLevelTrend =
     505          12 :             poValuesArray->GetView("[\"waterLevelTrend\"]");
     506             :         auto poWaterLevelTrendDS = std::unique_ptr<GDALDataset>(
     507           8 :             poWaterLevelTrend->AsClassicDataset(1, 0));
     508             :         auto poWaterLevelTrendBand =
     509           8 :             std::make_unique<S104RasterBand>(std::move(poWaterLevelTrendDS));
     510           4 :         poWaterLevelTrendBand->SetDescription("waterLevelTrend");
     511             : 
     512             :         // From D-5.3 Water Level Trend of S-101 v1.1 spec
     513           8 :         auto poRAT = std::make_unique<GDALDefaultRasterAttributeTable>();
     514           4 :         poRAT->CreateColumn("code", GFT_Integer, GFU_MinMax);
     515           4 :         poRAT->CreateColumn("label", GFT_String, GFU_Generic);
     516           4 :         poRAT->CreateColumn("definition", GFT_String, GFU_Generic);
     517             : 
     518             :         const struct
     519             :         {
     520             :             int nCode;
     521             :             const char *pszLabel;
     522             :             const char *pszDefinition;
     523           4 :         } aoRatValues[] = {
     524             :             {0, "Nodata", "No data"},
     525             :             {1, "Decreasing", "Becoming smaller in magnitude"},
     526             :             {2, "Increasing", "Becoming larger in magnitude"},
     527             :             {3, "Steady", "Constant"},
     528             :         };
     529             : 
     530           4 :         int iRow = 0;
     531          20 :         for (const auto &oRecord : aoRatValues)
     532             :         {
     533          16 :             int iCol = 0;
     534          16 :             poRAT->SetValue(iRow, iCol++, oRecord.nCode);
     535          16 :             poRAT->SetValue(iRow, iCol++, oRecord.pszLabel);
     536          16 :             poRAT->SetValue(iRow, iCol++, oRecord.pszDefinition);
     537          16 :             ++iRow;
     538             :         }
     539             : 
     540           4 :         poWaterLevelTrendBand->m_poRAT = std::move(poRAT);
     541             : 
     542           4 :         poDS->SetBand(2, poWaterLevelTrendBand.release());
     543             :     }
     544             : 
     545          19 :     poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
     546             : 
     547             :     // Setup/check for pam .aux.xml.
     548          19 :     poDS->SetDescription(osFilename.c_str());
     549          19 :     poDS->TryLoadXML();
     550             : 
     551             :     // Setup overviews.
     552          19 :     poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     553             : 
     554          19 :     return poDS.release();
     555             : }
     556             : 
     557             : /************************************************************************/
     558             : /*                      S104DatasetDriverUnload()                       */
     559             : /************************************************************************/
     560             : 
     561           6 : static void S104DatasetDriverUnload(GDALDriver *)
     562             : {
     563           6 :     HDF5UnloadFileDriver();
     564           6 : }
     565             : 
     566             : /************************************************************************/
     567             : /*                         GDALRegister_S104()                          */
     568             : /************************************************************************/
     569          11 : void GDALRegister_S104()
     570             : 
     571             : {
     572          11 :     if (!GDAL_CHECK_VERSION("S104"))
     573           0 :         return;
     574             : 
     575          11 :     if (GDALGetDriverByName(S104_DRIVER_NAME) != nullptr)
     576           0 :         return;
     577             : 
     578          11 :     GDALDriver *poDriver = new GDALDriver();
     579             : 
     580          11 :     S104DriverSetCommonMetadata(poDriver);
     581          11 :     poDriver->pfnOpen = S104Dataset::Open;
     582          11 :     poDriver->pfnUnloadDriver = S104DatasetDriverUnload;
     583             : 
     584          11 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     585             : }

Generated by: LCOV version 1.14