LCOV - code coverage report
Current view: top level - frmts/hdf5 - s111dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 255 311 82.0 %
Date: 2025-12-09 00:43:28 Functions: 10 11 90.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  Read S111 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             : /*                             S111Dataset                              */
      30             : /************************************************************************/
      31             : 
      32          14 : class S111Dataset final : public S100BaseDataset
      33             : {
      34             :   public:
      35           7 :     explicit S111Dataset(const std::string &osFilename)
      36           7 :         : S100BaseDataset(osFilename)
      37             :     {
      38           7 :     }
      39             : 
      40             :     ~S111Dataset() override;
      41             : 
      42             :     static GDALDataset *Open(GDALOpenInfo *);
      43             : };
      44             : 
      45             : S111Dataset::~S111Dataset() = default;
      46             : 
      47             : /************************************************************************/
      48             : /*                            S111RasterBand                            */
      49             : /************************************************************************/
      50             : 
      51             : class S111RasterBand final : public GDALProxyRasterBand
      52             : {
      53             :     friend class S111Dataset;
      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(S111RasterBand)
      60             : 
      61             :   public:
      62           8 :     explicit S111RasterBand(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           4 :     const char *GetUnitType() override
      74             :     {
      75           4 :         return m_osUnitType.c_str();
      76             :     }
      77             : 
      78           1 :     GDALRasterAttributeTable *GetDefaultRAT() override
      79             :     {
      80           1 :         return m_poRAT.get();
      81             :     }
      82             : 
      83           0 :     char **GetMetadata(const char *pszDomain) override
      84             :     {
      85             :         // Short-circuit GDALProxyRasterBand...
      86           0 :         return GDALRasterBand::GetMetadata(pszDomain);
      87             :     }
      88             : };
      89             : 
      90             : GDALRasterBand *
      91          12 : S111RasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
      92             : {
      93          12 :     return m_poUnderlyingBand;
      94             : }
      95             : 
      96             : /************************************************************************/
      97             : /*                                Open()                                */
      98             : /************************************************************************/
      99             : 
     100           8 : GDALDataset *S111Dataset::Open(GDALOpenInfo *poOpenInfo)
     101             : 
     102             : {
     103             :     // Confirm that this appears to be a S111 file.
     104           8 :     if (!S111DatasetIdentify(poOpenInfo))
     105           0 :         return nullptr;
     106             : 
     107             :     HDF5_GLOBAL_LOCK();
     108             : 
     109           8 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
     110             :     {
     111           1 :         return HDF5Dataset::OpenMultiDim(poOpenInfo);
     112             :     }
     113             : 
     114             :     // Confirm the requested access is supported.
     115           7 :     if (poOpenInfo->eAccess == GA_Update)
     116             :     {
     117           0 :         ReportUpdateNotSupportedByDriver("S111");
     118           0 :         return nullptr;
     119             :     }
     120             : 
     121          14 :     std::string osFilename(poOpenInfo->pszFilename);
     122          14 :     std::string osFeatureInstance = "SurfaceCurrent.01";
     123          14 :     std::string osGroup;
     124           7 :     if (STARTS_WITH(poOpenInfo->pszFilename, "S111:"))
     125             :     {
     126             :         const CPLStringList aosTokens(
     127           5 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":",
     128           5 :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
     129             : 
     130           5 :         if (aosTokens.size() == 2)
     131             :         {
     132           0 :             osFilename = aosTokens[1];
     133             :         }
     134           5 :         else if (aosTokens.size() == 3)
     135             :         {
     136           3 :             osFilename = aosTokens[1];
     137           3 :             osGroup = aosTokens[2];
     138             :         }
     139           2 :         else if (aosTokens.size() == 4)
     140             :         {
     141           2 :             osFilename = aosTokens[1];
     142           2 :             osFeatureInstance = aosTokens[2];
     143           2 :             osGroup = aosTokens[3];
     144             :         }
     145             :         else
     146             :         {
     147           0 :             return nullptr;
     148             :         }
     149             :     }
     150             : 
     151          14 :     auto poDS = std::make_unique<S111Dataset>(osFilename);
     152           7 :     if (!poDS->Init())
     153           0 :         return nullptr;
     154             : 
     155           7 :     const auto &poRootGroup = poDS->m_poRootGroup;
     156             : 
     157          21 :     auto poVerticalCS = poRootGroup->GetAttribute("verticalCS");
     158           7 :     if (poVerticalCS && poVerticalCS->GetDataType().GetClass() == GEDTC_NUMERIC)
     159             :     {
     160           7 :         const int nVerticalCS = poVerticalCS->ReadAsInt();
     161           7 :         if (nVerticalCS == 6498)
     162           7 :             poDS->GDALDataset::SetMetadataItem(
     163             :                 "VERTICAL_CS_MEANING", "depth, meters, orientation down");
     164           0 :         else if (nVerticalCS == 6499)
     165           0 :             poDS->GDALDataset::SetMetadataItem(
     166             :                 "VERTICAL_CS_MEANING", "height, meters, orientation up");
     167             : 
     168          14 :         poDS->GDALDataset::SetMetadataItem("verticalCS",
     169          14 :                                            std::to_string(nVerticalCS).c_str());
     170             :     }
     171             : 
     172          21 :     auto poSurfaceCurrent = poRootGroup->OpenGroup("SurfaceCurrent");
     173           7 :     if (!poSurfaceCurrent)
     174             :     {
     175           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     176             :                  "Cannot find /SurfaceCurrent group");
     177           0 :         return nullptr;
     178             :     }
     179             : 
     180             :     auto poDataCodingFormat =
     181          21 :         poSurfaceCurrent->GetAttribute("dataCodingFormat");
     182          14 :     if (!poDataCodingFormat ||
     183           7 :         poDataCodingFormat->GetDataType().GetClass() != GEDTC_NUMERIC)
     184             :     {
     185           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     186             :                  "Cannot find /SurfaceCurrent/dataCodingFormat attribute");
     187           0 :         return nullptr;
     188             :     }
     189           7 :     const int nDataCodingFormat = poDataCodingFormat->ReadAsInt();
     190           7 :     if (nDataCodingFormat != 2)
     191             :     {
     192           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     193             :                  "dataCodingFormat=%d is not supported by the S111 driver",
     194             :                  nDataCodingFormat);
     195           0 :         return nullptr;
     196             :     }
     197             : 
     198             :     // Read additional metadata
     199          21 :     for (const char *pszAttrName :
     200             :          {"methodCurrentsProduct", "minDatasetCurrentSpeed",
     201          28 :           "maxDatasetCurrentSpeed"})
     202             :     {
     203          63 :         auto poAttr = poSurfaceCurrent->GetAttribute(pszAttrName);
     204          21 :         if (poAttr)
     205             :         {
     206          14 :             const char *pszVal = poAttr->ReadAsString();
     207          14 :             if (pszVal)
     208             :             {
     209          14 :                 poDS->GDALDataset::SetMetadataItem(pszAttrName, pszVal);
     210             :             }
     211             :         }
     212             :     }
     213             : 
     214           7 :     int nNumInstances = 1;
     215           7 :     if (osGroup.empty())
     216             :     {
     217           6 :         auto poNumInstances = poSurfaceCurrent->GetAttribute("numInstances");
     218           3 :         if (poNumInstances &&
     219           3 :             poNumInstances->GetDataType().GetClass() == GEDTC_NUMERIC)
     220             :         {
     221           1 :             nNumInstances = poNumInstances->ReadAsInt();
     222             :         }
     223             :     }
     224           7 :     if (nNumInstances != 1)
     225             :     {
     226           2 :         CPLStringList aosSubDSList;
     227           1 :         int iSubDS = 0;
     228           2 :         for (const std::string &featureInstanceName :
     229           5 :              poSurfaceCurrent->GetGroupNames())
     230             :         {
     231             :             auto poFeatureInstance =
     232           4 :                 poSurfaceCurrent->OpenGroup(featureInstanceName);
     233           2 :             if (poFeatureInstance)
     234             :             {
     235           4 :                 GDALMajorObject mo;
     236             :                 // Read first vertical datum from root group and let the
     237             :                 // coverage override it.
     238           2 :                 S100ReadVerticalDatum(&mo, poRootGroup.get());
     239           2 :                 S100ReadVerticalDatum(&mo, poFeatureInstance.get());
     240             : 
     241           4 :                 const auto aosGroupNames = poFeatureInstance->GetGroupNames();
     242           4 :                 for (const auto &osSubGroup : aosGroupNames)
     243             :                 {
     244           2 :                     if (auto poSubGroup =
     245           4 :                             poFeatureInstance->OpenGroup(osSubGroup))
     246             :                     {
     247           2 :                         ++iSubDS;
     248             :                         aosSubDSList.SetNameValue(
     249             :                             CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
     250             :                             CPLSPrintf("S111:\"%s\":%s:%s", osFilename.c_str(),
     251             :                                        featureInstanceName.c_str(),
     252           2 :                                        osSubGroup.c_str()));
     253             : 
     254           4 :                         std::string verticalDatum;
     255             :                         const char *pszValue =
     256           2 :                             mo.GetMetadataItem(S100_VERTICAL_DATUM_MEANING);
     257           2 :                         if (pszValue)
     258             :                         {
     259           2 :                             verticalDatum = ", vertical datum ";
     260           2 :                             verticalDatum += pszValue;
     261             :                             pszValue =
     262           2 :                                 mo.GetMetadataItem(S100_VERTICAL_DATUM_ABBREV);
     263           2 :                             if (pszValue)
     264             :                             {
     265           2 :                                 verticalDatum += " (";
     266           2 :                                 verticalDatum += pszValue;
     267           2 :                                 verticalDatum += ')';
     268             :                             }
     269             :                         }
     270             :                         else
     271             :                         {
     272             :                             pszValue =
     273           0 :                                 mo.GetMetadataItem(S100_VERTICAL_DATUM_NAME);
     274           0 :                             if (pszValue)
     275             :                             {
     276           0 :                                 verticalDatum = ", vertical datum ";
     277           0 :                                 verticalDatum += pszValue;
     278             :                             }
     279             :                         }
     280             : 
     281           4 :                         std::string osSubDSDesc;
     282             :                         const auto poTimePoint =
     283           6 :                             poSubGroup->GetAttribute("timePoint");
     284           2 :                         if (poTimePoint)
     285             :                         {
     286           2 :                             const char *pszVal = poTimePoint->ReadAsString();
     287           2 :                             if (pszVal)
     288             :                             {
     289           2 :                                 osSubDSDesc = "Values for feature instance ";
     290           2 :                                 osSubDSDesc += featureInstanceName;
     291           2 :                                 osSubDSDesc += verticalDatum;
     292           2 :                                 osSubDSDesc += " at timestamp ";
     293           2 :                                 osSubDSDesc += pszVal;
     294             :                             }
     295             :                         }
     296           2 :                         if (osSubDSDesc.empty())
     297             :                         {
     298           0 :                             osSubDSDesc = "Values for feature instance ";
     299           0 :                             osSubDSDesc += featureInstanceName;
     300           0 :                             osSubDSDesc += verticalDatum;
     301           0 :                             osSubDSDesc += " and group ";
     302           0 :                             osSubDSDesc += osSubGroup;
     303             :                         }
     304             : 
     305             :                         aosSubDSList.SetNameValue(
     306             :                             CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
     307           2 :                             osSubDSDesc.c_str());
     308             :                     }
     309             :                 }
     310             :             }
     311             :         }
     312             : 
     313           1 :         poDS->GDALDataset::SetMetadata(aosSubDSList.List(), "SUBDATASETS");
     314             : 
     315             :         // Setup/check for pam .aux.xml.
     316           1 :         poDS->SetDescription(osFilename.c_str());
     317           1 :         poDS->TryLoadXML();
     318             : 
     319             :         // Setup overviews.
     320           1 :         poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     321             : 
     322           1 :         return poDS.release();
     323             :     }
     324             : 
     325          12 :     auto poFeatureInstance = poSurfaceCurrent->OpenGroup(osFeatureInstance);
     326           6 :     if (!poFeatureInstance)
     327             :     {
     328           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     329             :                  "Cannot find /SurfaceCurrent/%s group",
     330             :                  osFeatureInstance.c_str());
     331           0 :         return nullptr;
     332             :     }
     333             : 
     334             :     // Read additional metadata
     335          24 :     for (const char *pszAttrName :
     336             :          {"timeRecordInterval", "dateTimeOfFirstRecord", "dateTimeOfLastRecord",
     337          30 :           "numberOfTimes"})
     338             :     {
     339          72 :         auto poAttr = poFeatureInstance->GetAttribute(pszAttrName);
     340          24 :         if (poAttr)
     341             :         {
     342          24 :             const char *pszVal = poAttr->ReadAsString();
     343          24 :             if (pszVal)
     344             :             {
     345          24 :                 poDS->GDALDataset::SetMetadataItem(pszAttrName, pszVal);
     346             :             }
     347             :         }
     348             :     }
     349             : 
     350           6 :     if (auto poDataDynamicity =
     351          18 :             poFeatureInstance->GetAttribute("dataDynamicity"))
     352             :     {
     353           2 :         if (poDataDynamicity->GetDataType().GetClass() == GEDTC_NUMERIC)
     354             :         {
     355           2 :             const int nVal = poDataDynamicity->ReadAsInt();
     356             :             const std::map<int, const char *> oDataDynamicityMap = {
     357             :                 {1, "Observation"},
     358             :                 {2, "Astronomical prediction"},
     359             :                 {3, "Analysis or hybrid method"},
     360             :                 {4, "Hydrodynamic model hindcast"},
     361             :                 {5, "Hydrodynamic model forecast"},
     362             :                 {6, "Observed minus predicted"},
     363             :                 {7, "Observed minus analysis"},
     364             :                 {8, "Observed minus hindcast"},
     365             :                 {9, "Observed minus forecast"},
     366             :                 {10, "Forecast minus predicted"},
     367           4 :             };
     368           2 :             const auto oIter = oDataDynamicityMap.find(nVal);
     369           2 :             if (oIter != oDataDynamicityMap.end())
     370           2 :                 poDS->GDALDataset::SetMetadataItem("DATA_DYNAMICITY_MEANING",
     371           2 :                                                    oIter->second);
     372             :         }
     373             :     }
     374             : 
     375             :     // Read optional uncertainty array
     376          13 :     if (auto poUncertainty = poFeatureInstance->OpenMDArray("uncertainty"))
     377             :     {
     378           1 :         auto &apoDims = poUncertainty->GetDimensions();
     379           2 :         if (poUncertainty->GetDataType().GetClass() == GEDTC_COMPOUND &&
     380           2 :             apoDims.size() == 1 && apoDims[0]->GetSize() == 2)
     381             :         {
     382             :             const auto &oComponents =
     383           1 :                 poUncertainty->GetDataType().GetComponents();
     384           2 :             if (oComponents.size() == 2 &&
     385           2 :                 oComponents[0]->GetName() == "name" &&
     386           2 :                 oComponents[0]->GetType().GetClass() == GEDTC_STRING &&
     387           3 :                 oComponents[1]->GetName() == "value" &&
     388           1 :                 oComponents[1]->GetType().GetNumericDataType() == GDT_Float64)
     389             :             {
     390           3 :                 auto poName = poUncertainty->GetView("[\"name\"]");
     391           3 :                 auto poValue = poUncertainty->GetView("[\"value\"]");
     392           1 :                 if (poName && poValue)
     393             :                 {
     394           1 :                     char *apszStr[2] = {nullptr, nullptr};
     395           1 :                     double adfVals[2] = {0, 0};
     396           1 :                     GUInt64 arrayStartIdx[] = {0};
     397           1 :                     size_t count[] = {2};
     398           1 :                     GInt64 arrayStep[] = {1};
     399           1 :                     GPtrDiff_t bufferStride[] = {1};
     400           2 :                     if (poName->Read(arrayStartIdx, count, arrayStep,
     401           1 :                                      bufferStride, oComponents[0]->GetType(),
     402           2 :                                      apszStr) &&
     403           2 :                         poValue->Read(arrayStartIdx, count, arrayStep,
     404           1 :                                       bufferStride, oComponents[1]->GetType(),
     405             :                                       adfVals))
     406             :                     {
     407           3 :                         for (int i = 0; i < 2; ++i)
     408             :                         {
     409           4 :                             std::string osName = apszStr[i];
     410           2 :                             if (osName[0] >= 'a' && osName[0] <= 'z')
     411           2 :                                 osName[0] = osName[0] - 'a' + 'A';
     412           2 :                             osName = "uncertainty" + osName;
     413           2 :                             poDS->GDALDataset::SetMetadataItem(
     414             :                                 osName.c_str(), CPLSPrintf("%f", adfVals[i]));
     415             :                         }
     416             :                     }
     417           1 :                     VSIFree(apszStr[0]);
     418           1 :                     VSIFree(apszStr[1]);
     419             :                 }
     420             :             }
     421             :         }
     422             :     }
     423             : 
     424          12 :     if (auto poStartSequence = poFeatureInstance->GetAttribute("startSequence"))
     425             :     {
     426           6 :         const char *pszStartSequence = poStartSequence->ReadAsString();
     427           6 :         if (pszStartSequence && !EQUAL(pszStartSequence, "0,0"))
     428             :         {
     429           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     430             :                      "startSequence (=%s) != 0,0 is not supported",
     431             :                      pszStartSequence);
     432           0 :             return nullptr;
     433             :         }
     434             :     }
     435             : 
     436           6 :     if (!S100GetNumPointsLongitudinalLatitudinal(
     437           6 :             poFeatureInstance.get(), poDS->nRasterXSize, poDS->nRasterYSize))
     438             :     {
     439           0 :         return nullptr;
     440             :     }
     441             : 
     442             :     // Potentially override vertical datum
     443           6 :     S100ReadVerticalDatum(poDS.get(), poFeatureInstance.get());
     444             : 
     445           6 :     const bool bNorthUp = CPLTestBool(
     446           6 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
     447             : 
     448             :     // Compute geotransform
     449          12 :     poDS->m_bHasGT =
     450           6 :         S100GetGeoTransform(poFeatureInstance.get(), poDS->m_gt, bNorthUp);
     451             : 
     452           6 :     if (osGroup.empty())
     453             :     {
     454           2 :         const auto aosGroupNames = poFeatureInstance->GetGroupNames();
     455           1 :         int iSubDS = 1;
     456           2 :         for (const auto &osSubGroup : aosGroupNames)
     457             :         {
     458           2 :             if (auto poSubGroup = poFeatureInstance->OpenGroup(osSubGroup))
     459             :             {
     460           1 :                 poDS->GDALDataset::SetMetadataItem(
     461             :                     CPLSPrintf("SUBDATASET_%d_NAME", iSubDS),
     462             :                     CPLSPrintf("S111:\"%s\":%s", osFilename.c_str(),
     463             :                                osSubGroup.c_str()),
     464             :                     "SUBDATASETS");
     465           2 :                 std::string osSubDSDesc = "Values for group ";
     466           1 :                 osSubDSDesc += osSubGroup;
     467           2 :                 const auto poTimePoint = poSubGroup->GetAttribute("timePoint");
     468           1 :                 if (poTimePoint)
     469             :                 {
     470           1 :                     const char *pszVal = poTimePoint->ReadAsString();
     471           1 :                     if (pszVal)
     472             :                     {
     473           1 :                         osSubDSDesc = "Values at timestamp ";
     474           1 :                         osSubDSDesc += pszVal;
     475             :                     }
     476             :                 }
     477           1 :                 poDS->GDALDataset::SetMetadataItem(
     478             :                     CPLSPrintf("SUBDATASET_%d_DESC", iSubDS),
     479             :                     osSubDSDesc.c_str(), "SUBDATASETS");
     480           1 :                 ++iSubDS;
     481             :             }
     482             :         }
     483             :     }
     484             :     else
     485             :     {
     486           5 :         auto poGroup = poFeatureInstance->OpenGroup(osGroup);
     487           5 :         if (!poGroup)
     488             :         {
     489           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     490             :                      "Cannot find /SurfaceCurrent/%s/%s group",
     491             :                      osFeatureInstance.c_str(), osGroup.c_str());
     492           1 :             return nullptr;
     493             :         }
     494             : 
     495           8 :         auto poValuesArray = poGroup->OpenMDArray("values");
     496           4 :         if (!poValuesArray)
     497             :         {
     498           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     499             :                      "Cannot find /SurfaceCurrent/%s/%s/values array",
     500             :                      osFeatureInstance.c_str(), osGroup.c_str());
     501           0 :             return nullptr;
     502             :         }
     503             : 
     504           4 :         if (poValuesArray->GetDimensionCount() != 2)
     505             :         {
     506           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     507             :                      "Wrong dimension count for %s",
     508           0 :                      poValuesArray->GetFullName().c_str());
     509           0 :             return nullptr;
     510             :         }
     511             : 
     512           4 :         const auto &oType = poValuesArray->GetDataType();
     513           4 :         if (oType.GetClass() != GEDTC_COMPOUND)
     514             :         {
     515           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong data type for %s",
     516           0 :                      poValuesArray->GetFullName().c_str());
     517           0 :             return nullptr;
     518             :         }
     519             : 
     520           4 :         const auto &oComponents = oType.GetComponents();
     521           8 :         if (!(oComponents.size() == 2 &&
     522           8 :               ((oComponents[0]->GetName() == "surfaceCurrentSpeed" &&
     523           4 :                 oComponents[0]->GetType().GetNumericDataType() == GDT_Float32 &&
     524           8 :                 oComponents[1]->GetName() == "surfaceCurrentDirection" &&
     525           4 :                 oComponents[1]->GetType().GetNumericDataType() ==
     526           0 :                     GDT_Float32) ||
     527             :                // S111US_20170829.0100_W078.N44_F2_loofs_type2.h5 has direction first...
     528           0 :                (oComponents[0]->GetName() == "surfaceCurrentDirection" &&
     529           0 :                 oComponents[0]->GetType().GetNumericDataType() == GDT_Float32 &&
     530           0 :                 oComponents[1]->GetName() == "surfaceCurrentSpeed" &&
     531           0 :                 oComponents[1]->GetType().GetNumericDataType() ==
     532             :                     GDT_Float32))))
     533             :         {
     534           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong data type for %s",
     535           0 :                      poValuesArray->GetFullName().c_str());
     536           0 :             return nullptr;
     537             :         }
     538             : 
     539           4 :         const auto &apoDims = poValuesArray->GetDimensions();
     540           4 :         if (apoDims[0]->GetSize() != static_cast<unsigned>(poDS->nRasterYSize))
     541             :         {
     542           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     543             :                      "numPointsLatitudinal(=%d) doesn't match first dimension "
     544             :                      "size of %s (=%d)",
     545           0 :                      poDS->nRasterYSize, poValuesArray->GetFullName().c_str(),
     546           0 :                      static_cast<int>(apoDims[0]->GetSize()));
     547           0 :             return nullptr;
     548             :         }
     549           4 :         if (apoDims[1]->GetSize() != static_cast<unsigned>(poDS->nRasterXSize))
     550             :         {
     551           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     552             :                      "numPointsLongitudinal(=%d) doesn't match second "
     553             :                      "dimension size of %s (=%d)",
     554           0 :                      poDS->nRasterXSize, poValuesArray->GetFullName().c_str(),
     555           0 :                      static_cast<int>(apoDims[1]->GetSize()));
     556           0 :             return nullptr;
     557             :         }
     558             : 
     559           4 :         if (bNorthUp)
     560           3 :             poValuesArray = poValuesArray->GetView("[::-1,...]");
     561             : 
     562             :         // Create surfaceCurrentSpeed band
     563             :         auto poSurfaceCurrentSpeed =
     564          12 :             poValuesArray->GetView("[\"surfaceCurrentSpeed\"]");
     565             :         auto poSurfaceCurrentSpeedDS = std::unique_ptr<GDALDataset>(
     566           8 :             poSurfaceCurrentSpeed->AsClassicDataset(1, 0));
     567             :         auto poSurfaceCurrentSpeedBand = std::make_unique<S111RasterBand>(
     568           8 :             std::move(poSurfaceCurrentSpeedDS));
     569           4 :         poSurfaceCurrentSpeedBand->SetDescription("surfaceCurrentSpeed");
     570           4 :         poSurfaceCurrentSpeedBand->m_osUnitType = "knots";
     571             : 
     572             :         // From S-111 v1.2 table 9.1 (Speed ranges) and 9.2 (Colour schemas)
     573           8 :         auto poRAT = std::make_unique<GDALDefaultRasterAttributeTable>();
     574           4 :         poRAT->CreateColumn("speed_band", GFT_Integer, GFU_Generic);
     575           4 :         poRAT->CreateColumn("min_speed", GFT_Real, GFU_Min);
     576           4 :         poRAT->CreateColumn("width_band", GFT_Real, GFU_Generic);
     577           4 :         poRAT->CreateColumn("color", GFT_String, GFU_Generic);
     578           4 :         poRAT->CreateColumn("red", GFT_Integer, GFU_RedMin);
     579           4 :         poRAT->CreateColumn("green", GFT_Integer, GFU_GreenMin);
     580           4 :         poRAT->CreateColumn("blue", GFT_Integer, GFU_BlueMin);
     581             : 
     582             :         const struct
     583             :         {
     584             :             int nSpeedBand;
     585             :             double dfMinSpeed;
     586             :             double dfWidthBand;
     587             :             const char *pszColor;
     588             :             int nRed;
     589             :             int nGreen;
     590             :             int nBlue;
     591           4 :         } aoRatValues[] = {
     592             :             {1, 0.0, 0.5, "purple", 118, 82, 226},
     593             :             {2, 0.5, 0.5, "dark blue", 72, 152, 211},
     594             :             {3, 1.0, 1.0, "light blue", 97, 203, 229},
     595             :             {4, 2.0, 1.0, "dark green", 109, 188, 69},
     596             :             {5, 3.0, 2.0, "light green", 180, 220, 0},
     597             :             {6, 5.0, 2.0, "yellow green", 205, 193, 0},
     598             :             {7, 7.0, 3.0, "orange", 248, 167, 24},
     599             :             {8, 10.0, 3.0, "pink", 247, 162, 157},
     600             :             {9, 13.0, 86.0, "red", 255, 30, 30},
     601             :         };
     602             : 
     603           4 :         int iRow = 0;
     604          40 :         for (const auto &oRecord : aoRatValues)
     605             :         {
     606          36 :             int iCol = 0;
     607          36 :             poRAT->SetValue(iRow, iCol++, oRecord.nSpeedBand);
     608          36 :             poRAT->SetValue(iRow, iCol++, oRecord.dfMinSpeed);
     609          36 :             poRAT->SetValue(iRow, iCol++, oRecord.dfWidthBand);
     610          36 :             poRAT->SetValue(iRow, iCol++, oRecord.pszColor);
     611          36 :             poRAT->SetValue(iRow, iCol++, oRecord.nRed);
     612          36 :             poRAT->SetValue(iRow, iCol++, oRecord.nGreen);
     613          36 :             poRAT->SetValue(iRow, iCol++, oRecord.nBlue);
     614          36 :             ++iRow;
     615             :         }
     616             : 
     617           4 :         poSurfaceCurrentSpeedBand->m_poRAT = std::move(poRAT);
     618             : 
     619           4 :         poDS->SetBand(1, poSurfaceCurrentSpeedBand.release());
     620             : 
     621             :         // Create surfaceCurrentDirection band
     622             :         auto poSurfaceCurrentDirection =
     623          12 :             poValuesArray->GetView("[\"surfaceCurrentDirection\"]");
     624             :         auto poSurfaceCurrentDirectionDS = std::unique_ptr<GDALDataset>(
     625           8 :             poSurfaceCurrentDirection->AsClassicDataset(1, 0));
     626             :         auto poSurfaceCurrentDirectionBand = std::make_unique<S111RasterBand>(
     627           8 :             std::move(poSurfaceCurrentDirectionDS));
     628           4 :         poSurfaceCurrentDirectionBand->SetDescription(
     629           4 :             "surfaceCurrentDirection");
     630           4 :         poSurfaceCurrentDirectionBand->m_osUnitType = "degree";
     631           4 :         poSurfaceCurrentDirectionBand->GDALRasterBand::SetMetadataItem(
     632             :             "ANGLE_CONVENTION", "From true north, clockwise");
     633           4 :         poDS->SetBand(2, poSurfaceCurrentDirectionBand.release());
     634             :     }
     635             : 
     636           5 :     poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
     637             : 
     638             :     // Setup/check for pam .aux.xml.
     639           5 :     poDS->SetDescription(osFilename.c_str());
     640           5 :     poDS->TryLoadXML();
     641             : 
     642             :     // Setup overviews.
     643           5 :     poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     644             : 
     645           5 :     return poDS.release();
     646             : }
     647             : 
     648             : /************************************************************************/
     649             : /*                      S111DatasetDriverUnload()                       */
     650             : /************************************************************************/
     651             : 
     652           6 : static void S111DatasetDriverUnload(GDALDriver *)
     653             : {
     654           6 :     HDF5UnloadFileDriver();
     655           6 : }
     656             : 
     657             : /************************************************************************/
     658             : /*                         GDALRegister_S111()                          */
     659             : /************************************************************************/
     660          11 : void GDALRegister_S111()
     661             : 
     662             : {
     663          11 :     if (!GDAL_CHECK_VERSION("S111"))
     664           0 :         return;
     665             : 
     666          11 :     if (GDALGetDriverByName(S111_DRIVER_NAME) != nullptr)
     667           0 :         return;
     668             : 
     669          11 :     GDALDriver *poDriver = new GDALDriver();
     670             : 
     671          11 :     S111DriverSetCommonMetadata(poDriver);
     672          11 :     poDriver->pfnOpen = S111Dataset::Open;
     673          11 :     poDriver->pfnUnloadDriver = S111DatasetDriverUnload;
     674             : 
     675          11 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     676             : }

Generated by: LCOV version 1.14