LCOV - code coverage report
Current view: top level - frmts/hdf5 - s102dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 224 270 83.0 %
Date: 2025-07-05 13:22:42 Functions: 16 16 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  Read S102 bathymetric 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_priv.h"
      21             : #include "gdal_proxy.h"
      22             : #include "gdal_rat.h"
      23             : 
      24             : #include <cmath>
      25             : #include <limits>
      26             : 
      27             : /************************************************************************/
      28             : /*                             S102Dataset                              */
      29             : /************************************************************************/
      30             : 
      31          44 : class S102Dataset final : public S100BaseDataset
      32             : {
      33             :     bool OpenQuality(GDALOpenInfo *poOpenInfo,
      34             :                      const std::shared_ptr<GDALGroup> &poRootGroup);
      35             : 
      36             :   public:
      37          22 :     explicit S102Dataset(const std::string &osFilename)
      38          22 :         : S100BaseDataset(osFilename)
      39             :     {
      40          22 :     }
      41             : 
      42             :     ~S102Dataset() override;
      43             : 
      44             :     static GDALDataset *Open(GDALOpenInfo *);
      45             : };
      46             : 
      47             : S102Dataset::~S102Dataset() = default;
      48             : 
      49             : /************************************************************************/
      50             : /*                            S102RasterBand                            */
      51             : /************************************************************************/
      52             : 
      53             : class S102RasterBand : public GDALProxyRasterBand
      54             : {
      55             :     friend class S102Dataset;
      56             :     std::unique_ptr<GDALDataset> m_poDS{};
      57             :     GDALRasterBand *m_poUnderlyingBand = nullptr;
      58             :     double m_dfMinimum = std::numeric_limits<double>::quiet_NaN();
      59             :     double m_dfMaximum = std::numeric_limits<double>::quiet_NaN();
      60             : 
      61             :     CPL_DISALLOW_COPY_ASSIGN(S102RasterBand)
      62             : 
      63             :   public:
      64          31 :     explicit S102RasterBand(std::unique_ptr<GDALDataset> &&poDSIn)
      65          62 :         : m_poDS(std::move(poDSIn)),
      66          31 :           m_poUnderlyingBand(m_poDS->GetRasterBand(1))
      67             :     {
      68          31 :         eDataType = m_poUnderlyingBand->GetRasterDataType();
      69          31 :         m_poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
      70          31 :     }
      71             : 
      72             :     GDALRasterBand *
      73             :     RefUnderlyingRasterBand(bool /*bForceOpen*/ = true) const override;
      74             : 
      75           8 :     double GetMinimum(int *pbSuccess = nullptr) override
      76             :     {
      77           8 :         if (pbSuccess)
      78           8 :             *pbSuccess = !std::isnan(m_dfMinimum);
      79           8 :         return m_dfMinimum;
      80             :     }
      81             : 
      82           8 :     double GetMaximum(int *pbSuccess = nullptr) override
      83             :     {
      84           8 :         if (pbSuccess)
      85           8 :             *pbSuccess = !std::isnan(m_dfMaximum);
      86           8 :         return m_dfMaximum;
      87             :     }
      88             : 
      89           4 :     const char *GetUnitType() override
      90             :     {
      91           4 :         return "metre";
      92             :     }
      93             : };
      94             : 
      95             : GDALRasterBand *
      96          23 : S102RasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
      97             : {
      98          23 :     return m_poUnderlyingBand;
      99             : }
     100             : 
     101             : /************************************************************************/
     102             : /*                   S102GeoreferencedMetadataRasterBand                */
     103             : /************************************************************************/
     104             : 
     105             : class S102GeoreferencedMetadataRasterBand : public GDALProxyRasterBand
     106             : {
     107             :     friend class S102Dataset;
     108             : 
     109             :     std::unique_ptr<GDALDataset> m_poDS{};
     110             :     GDALRasterBand *m_poUnderlyingBand = nullptr;
     111             :     std::unique_ptr<GDALRasterAttributeTable> m_poRAT{};
     112             : 
     113             :     CPL_DISALLOW_COPY_ASSIGN(S102GeoreferencedMetadataRasterBand)
     114             : 
     115             :   public:
     116           4 :     explicit S102GeoreferencedMetadataRasterBand(
     117             :         std::unique_ptr<GDALDataset> &&poDSIn,
     118             :         std::unique_ptr<GDALRasterAttributeTable> &&poRAT)
     119           8 :         : m_poDS(std::move(poDSIn)),
     120           4 :           m_poUnderlyingBand(m_poDS->GetRasterBand(1)),
     121           8 :           m_poRAT(std::move(poRAT))
     122             :     {
     123           4 :         eDataType = m_poUnderlyingBand->GetRasterDataType();
     124           4 :         m_poUnderlyingBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     125           4 :     }
     126             : 
     127             :     GDALRasterBand *
     128             :     RefUnderlyingRasterBand(bool /*bForceOpen*/ = true) const override;
     129             : 
     130           2 :     GDALRasterAttributeTable *GetDefaultRAT() override
     131             :     {
     132           2 :         return m_poRAT.get();
     133             :     }
     134             : };
     135             : 
     136           6 : GDALRasterBand *S102GeoreferencedMetadataRasterBand::RefUnderlyingRasterBand(
     137             :     bool /*bForceOpen*/) const
     138             : {
     139           6 :     return m_poUnderlyingBand;
     140             : }
     141             : 
     142             : /************************************************************************/
     143             : /*                                Open()                                */
     144             : /************************************************************************/
     145             : 
     146          26 : GDALDataset *S102Dataset::Open(GDALOpenInfo *poOpenInfo)
     147             : 
     148             : {
     149             :     // Confirm that this appears to be a S102 file.
     150          26 :     if (!S102DatasetIdentify(poOpenInfo))
     151           0 :         return nullptr;
     152             : 
     153             :     HDF5_GLOBAL_LOCK();
     154             : 
     155          26 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
     156             :     {
     157           2 :         return HDF5Dataset::OpenMultiDim(poOpenInfo);
     158             :     }
     159             : 
     160             :     // Confirm the requested access is supported.
     161          24 :     if (poOpenInfo->eAccess == GA_Update)
     162             :     {
     163           0 :         ReportUpdateNotSupportedByDriver("S102");
     164           0 :         return nullptr;
     165             :     }
     166             : 
     167          48 :     std::string osFilename(poOpenInfo->pszFilename);
     168          24 :     bool bIsSubdataset = false;
     169          24 :     bool bIsQuality = false;
     170          24 :     if (STARTS_WITH(poOpenInfo->pszFilename, "S102:"))
     171             :     {
     172             :         const CPLStringList aosTokens(
     173          11 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":",
     174          11 :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
     175             : 
     176          11 :         if (aosTokens.size() == 2)
     177             :         {
     178           1 :             osFilename = aosTokens[1];
     179             :         }
     180          10 :         else if (aosTokens.size() == 3)
     181             :         {
     182          10 :             bIsSubdataset = true;
     183          10 :             osFilename = aosTokens[1];
     184          10 :             if (EQUAL(aosTokens[2], "BathymetryCoverage"))
     185             :             {
     186             :                 // Default dataset
     187             :             }
     188          12 :             else if (EQUAL(aosTokens[2], "QualityOfSurvey") ||  // < v3
     189           4 :                      EQUAL(aosTokens[2], "QualityOfBathymetryCoverage"))  // v3
     190             :             {
     191           6 :                 bIsQuality = true;
     192             :             }
     193             :             else
     194             :             {
     195           2 :                 CPLError(CE_Failure, CPLE_NotSupported,
     196             :                          "Unsupported subdataset component: '%s'. Expected "
     197             :                          "'QualityOfSurvey'",
     198             :                          aosTokens[2]);
     199           2 :                 return nullptr;
     200             :             }
     201             :         }
     202             :         else
     203             :         {
     204           0 :             return nullptr;
     205             :         }
     206             :     }
     207             : 
     208          44 :     auto poDS = std::make_unique<S102Dataset>(osFilename);
     209          22 :     if (!poDS->Init())
     210           0 :         return nullptr;
     211             : 
     212          22 :     const auto &poRootGroup = poDS->m_poRootGroup;
     213             :     auto poBathymetryCoverage01 = poRootGroup->OpenGroupFromFullname(
     214          66 :         "/BathymetryCoverage/BathymetryCoverage.01");
     215          22 :     if (!poBathymetryCoverage01)
     216             :     {
     217           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     218             :                  "S102: Cannot find /BathymetryCoverage/BathymetryCoverage.01");
     219           0 :         return nullptr;
     220             :     }
     221             : 
     222          22 :     const bool bNorthUp = CPLTestBool(
     223          22 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
     224             : 
     225          22 :     if (bIsQuality)
     226             :     {
     227           6 :         if (!poDS->OpenQuality(poOpenInfo, poRootGroup))
     228           2 :             return nullptr;
     229             : 
     230             :         // Setup/check for pam .aux.xml.
     231           4 :         poDS->SetDescription(osFilename.c_str());
     232           4 :         poDS->TryLoadXML();
     233             : 
     234             :         // Setup overviews.
     235           4 :         poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     236             : 
     237           4 :         return poDS.release();
     238             :     }
     239             : 
     240             :     // Compute geotransform
     241          32 :     poDS->m_bHasGT =
     242          16 :         S100GetGeoTransform(poBathymetryCoverage01.get(), poDS->m_gt, bNorthUp);
     243             : 
     244          48 :     auto poGroup001 = poBathymetryCoverage01->OpenGroup("Group_001");
     245          16 :     if (!poGroup001)
     246             :     {
     247           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     248             :                  "S102: Cannot find "
     249             :                  "/BathymetryCoverage/BathymetryCoverage.01/Group_001");
     250           0 :         return nullptr;
     251             :     }
     252          48 :     auto poValuesArray = poGroup001->OpenMDArray("values");
     253          16 :     if (!poValuesArray || poValuesArray->GetDimensionCount() != 2)
     254             :     {
     255           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     256             :                  "S102: Cannot find "
     257             :                  "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values");
     258           0 :         return nullptr;
     259             :     }
     260          16 :     const auto &oType = poValuesArray->GetDataType();
     261          16 :     if (oType.GetClass() != GEDTC_COMPOUND)
     262             :     {
     263           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     264             :                  "S102: Wrong type for "
     265             :                  "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values");
     266           0 :         return nullptr;
     267             :     }
     268          16 :     const auto &oComponents = oType.GetComponents();
     269          16 :     if (oComponents.size() == 0 || oComponents[0]->GetName() != "depth")
     270             :     {
     271           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     272             :                  "S102: Wrong type for "
     273             :                  "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values");
     274           0 :         return nullptr;
     275             :     }
     276             : 
     277          16 :     if (bNorthUp)
     278          15 :         poValuesArray = poValuesArray->GetView("[::-1,...]");
     279             : 
     280          48 :     auto poDepth = poValuesArray->GetView("[\"depth\"]");
     281             : 
     282             :     // Mandatory in v2.2
     283          16 :     bool bCSIsElevation = false;
     284          48 :     auto poVerticalCS = poRootGroup->GetAttribute("verticalCS");
     285          16 :     if (poVerticalCS && poVerticalCS->GetDataType().GetClass() == GEDTC_NUMERIC)
     286             :     {
     287           6 :         const auto nVal = poVerticalCS->ReadAsInt();
     288           6 :         if (nVal == 6498)  // Depth metre
     289             :         {
     290             :             // nothing to do
     291             :         }
     292           0 :         else if (nVal == 6499)  // Height metre
     293             :         {
     294           0 :             bCSIsElevation = true;
     295             :         }
     296             :         else
     297             :         {
     298           0 :             CPLError(CE_Warning, CPLE_NotSupported, "Unsupported verticalCS=%d",
     299             :                      nVal);
     300             :         }
     301             :     }
     302             : 
     303             :     const bool bUseElevation =
     304          16 :         EQUAL(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     305             :                                    "DEPTH_OR_ELEVATION", "DEPTH"),
     306             :               "ELEVATION");
     307          31 :     const bool bInvertDepth = (bUseElevation && !bCSIsElevation) ||
     308          15 :                               (!bUseElevation && bCSIsElevation);
     309          16 :     const double dfDepthNoData = poDepth->GetNoDataValueAsDouble();
     310          33 :     auto poDepthDS = [&poDepth, bInvertDepth, dfDepthNoData]()
     311             :     {
     312          16 :         if (bInvertDepth)
     313             :         {
     314           1 :             auto poInverted = poDepth->GetUnscaled(-1, 0, dfDepthNoData);
     315             :             return std::unique_ptr<GDALDataset>(
     316           1 :                 poInverted->AsClassicDataset(1, 0));
     317             :         }
     318             :         else
     319             :         {
     320             :             return std::unique_ptr<GDALDataset>(
     321          15 :                 poDepth->AsClassicDataset(1, 0));
     322             :         }
     323          32 :     }();
     324             : 
     325          16 :     poDS->nRasterXSize = poDepthDS->GetRasterXSize();
     326          16 :     poDS->nRasterYSize = poDepthDS->GetRasterYSize();
     327             : 
     328             :     // Create depth (or elevation) band
     329          16 :     auto poDepthBand = new S102RasterBand(std::move(poDepthDS));
     330          16 :     poDepthBand->SetDescription(bUseElevation ? "elevation" : "depth");
     331             : 
     332          48 :     auto poMinimumDepth = poGroup001->GetAttribute("minimumDepth");
     333          32 :     if (poMinimumDepth &&
     334          32 :         poMinimumDepth->GetDataType().GetClass() == GEDTC_NUMERIC)
     335             :     {
     336          16 :         const double dfVal = poMinimumDepth->ReadAsDouble();
     337          16 :         if (dfVal != dfDepthNoData)
     338             :         {
     339          13 :             if (bInvertDepth)
     340           1 :                 poDepthBand->m_dfMaximum = -dfVal;
     341             :             else
     342          12 :                 poDepthBand->m_dfMinimum = dfVal;
     343             :         }
     344             :     }
     345             : 
     346          48 :     auto poMaximumDepth = poGroup001->GetAttribute("maximumDepth");
     347          32 :     if (poMaximumDepth &&
     348          32 :         poMaximumDepth->GetDataType().GetClass() == GEDTC_NUMERIC)
     349             :     {
     350          16 :         const double dfVal = poMaximumDepth->ReadAsDouble();
     351          16 :         if (dfVal != dfDepthNoData)
     352             :         {
     353          16 :             if (bInvertDepth)
     354           1 :                 poDepthBand->m_dfMinimum = -dfVal;
     355             :             else
     356          15 :                 poDepthBand->m_dfMaximum = dfVal;
     357             :         }
     358             :     }
     359             : 
     360          16 :     poDS->SetBand(1, poDepthBand);
     361             : 
     362             :     const bool bHasUncertainty =
     363          16 :         oComponents.size() >= 2 && oComponents[1]->GetName() == "uncertainty";
     364          16 :     if (bHasUncertainty)
     365             :     {
     366             :         // Create uncertainty band
     367          45 :         auto poUncertainty = poValuesArray->GetView("[\"uncertainty\"]");
     368             :         const double dfUncertaintyNoData =
     369          15 :             poUncertainty->GetNoDataValueAsDouble();
     370             :         auto poUncertaintyDS =
     371          30 :             std::unique_ptr<GDALDataset>(poUncertainty->AsClassicDataset(1, 0));
     372             : 
     373          15 :         auto poUncertaintyBand = new S102RasterBand(std::move(poUncertaintyDS));
     374          15 :         poUncertaintyBand->SetDescription("uncertainty");
     375             : 
     376             :         auto poMinimumUncertainty =
     377          45 :             poGroup001->GetAttribute("minimumUncertainty");
     378          30 :         if (poMinimumUncertainty &&
     379          30 :             poMinimumUncertainty->GetDataType().GetClass() == GEDTC_NUMERIC)
     380             :         {
     381          15 :             const double dfVal = poMinimumUncertainty->ReadAsDouble();
     382          15 :             if (dfVal != dfUncertaintyNoData)
     383             :             {
     384          15 :                 poUncertaintyBand->m_dfMinimum = dfVal;
     385             :             }
     386             :         }
     387             : 
     388             :         auto poMaximumUncertainty =
     389          45 :             poGroup001->GetAttribute("maximumUncertainty");
     390          30 :         if (poMaximumUncertainty &&
     391          30 :             poMaximumUncertainty->GetDataType().GetClass() == GEDTC_NUMERIC)
     392             :         {
     393          15 :             const double dfVal = poMaximumUncertainty->ReadAsDouble();
     394          15 :             if (dfVal != dfUncertaintyNoData)
     395             :             {
     396          15 :                 poUncertaintyBand->m_dfMaximum = dfVal;
     397             :             }
     398             :         }
     399             : 
     400          15 :         poDS->SetBand(2, poUncertaintyBand);
     401             :     }
     402             : 
     403          16 :     poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
     404             : 
     405          48 :     auto poGroupQuality = poRootGroup->OpenGroup("QualityOfSurvey");
     406          16 :     const bool bIsNamedQualityOfSurvey = poGroupQuality != nullptr;
     407          16 :     if (!bIsNamedQualityOfSurvey)
     408             :     {
     409             :         // S102 v3 now uses QualityOfBathymetryCoverage instead of QualityOfSurvey
     410          14 :         poGroupQuality = poRootGroup->OpenGroup("QualityOfBathymetryCoverage");
     411             :     }
     412          16 :     if (!bIsSubdataset && poGroupQuality)
     413             :     {
     414           3 :         const char *pszNameOfQualityGroup = bIsNamedQualityOfSurvey
     415           3 :                                                 ? "QualityOfSurvey"
     416             :                                                 : "QualityOfBathymetryCoverage";
     417           3 :         auto poGroupQuality01 = poGroupQuality->OpenGroup(
     418           9 :             CPLSPrintf("%s.01", pszNameOfQualityGroup));
     419           3 :         if (poGroupQuality01)
     420             :         {
     421           3 :             poDS->GDALDataset::SetMetadataItem(
     422             :                 "SUBDATASET_1_NAME",
     423             :                 CPLSPrintf("S102:\"%s\":BathymetryCoverage",
     424             :                            osFilename.c_str()),
     425             :                 "SUBDATASETS");
     426           3 :             poDS->GDALDataset::SetMetadataItem(
     427             :                 "SUBDATASET_1_DESC", "Bathymetric gridded data", "SUBDATASETS");
     428             : 
     429           3 :             poDS->GDALDataset::SetMetadataItem(
     430             :                 "SUBDATASET_2_NAME",
     431             :                 CPLSPrintf("S102:\"%s\":%s", osFilename.c_str(),
     432             :                            pszNameOfQualityGroup),
     433             :                 "SUBDATASETS");
     434           3 :             poDS->GDALDataset::SetMetadataItem(
     435             :                 "SUBDATASET_2_DESC",
     436             :                 CPLSPrintf("Georeferenced metadata %s", pszNameOfQualityGroup),
     437             :                 "SUBDATASETS");
     438             :         }
     439             :     }
     440             : 
     441             :     // Setup/check for pam .aux.xml.
     442          16 :     poDS->SetDescription(osFilename.c_str());
     443          16 :     poDS->TryLoadXML();
     444             : 
     445             :     // Setup overviews.
     446          16 :     poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     447             : 
     448          16 :     return poDS.release();
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                       OpenQuality()                          */
     453             : /************************************************************************/
     454             : 
     455           6 : bool S102Dataset::OpenQuality(GDALOpenInfo *poOpenInfo,
     456             :                               const std::shared_ptr<GDALGroup> &poRootGroup)
     457             : {
     458           6 :     const bool bNorthUp = CPLTestBool(
     459           6 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
     460             : 
     461           6 :     const char *pszNameOfQualityGroup = "QualityOfSurvey";
     462          18 :     auto poGroupQuality = poRootGroup->OpenGroup(pszNameOfQualityGroup);
     463           6 :     if (!poGroupQuality)
     464             :     {
     465           4 :         pszNameOfQualityGroup = "QualityOfBathymetryCoverage";
     466           4 :         poGroupQuality = poRootGroup->OpenGroup(pszNameOfQualityGroup);
     467           4 :         if (!poGroupQuality)
     468             :         {
     469           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     470             :                      "Cannot find group /QualityOfSurvey or "
     471             :                      "/QualityOfBathymetryCoverage");
     472           2 :             return false;
     473             :         }
     474             :     }
     475             : 
     476             :     const std::string osQuality01Name =
     477          12 :         std::string(pszNameOfQualityGroup).append(".01");
     478           8 :     const std::string osQuality01FullName = std::string("/")
     479           4 :                                                 .append(pszNameOfQualityGroup)
     480           4 :                                                 .append("/")
     481           8 :                                                 .append(osQuality01Name);
     482           8 :     auto poGroupQuality01 = poGroupQuality->OpenGroup(osQuality01Name);
     483           4 :     if (!poGroupQuality01)
     484             :     {
     485           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
     486             :                  osQuality01FullName.c_str());
     487           0 :         return false;
     488             :     }
     489             : 
     490           8 :     if (auto poStartSequence = poGroupQuality01->GetAttribute("startSequence"))
     491             :     {
     492           0 :         const char *pszStartSequence = poStartSequence->ReadAsString();
     493           0 :         if (pszStartSequence && !EQUAL(pszStartSequence, "0,0"))
     494             :         {
     495           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     496             :                      "startSequence (=%s) != 0,0 is not supported",
     497             :                      pszStartSequence);
     498           0 :             return false;
     499             :         }
     500             :     }
     501             : 
     502             :     // Compute geotransform
     503           4 :     m_bHasGT = S100GetGeoTransform(poGroupQuality01.get(), m_gt, bNorthUp);
     504             : 
     505          12 :     auto poGroup001 = poGroupQuality01->OpenGroup("Group_001");
     506           4 :     if (!poGroup001)
     507             :     {
     508           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s/Group_001",
     509             :                  osQuality01FullName.c_str());
     510           0 :         return false;
     511             :     }
     512             : 
     513          12 :     auto poValuesArray = poGroup001->OpenMDArray("values");
     514           4 :     if (!poValuesArray)
     515             :     {
     516           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     517             :                  "Cannot find array "
     518             :                  "%s/Group_001/values",
     519             :                  osQuality01FullName.c_str());
     520           0 :         return false;
     521             :     }
     522             : 
     523             :     {
     524           4 :         const auto &oType = poValuesArray->GetDataType();
     525           6 :         if (oType.GetClass() == GEDTC_NUMERIC &&
     526           2 :             oType.GetNumericDataType() == GDT_UInt32)
     527             :         {
     528             :             // ok
     529             :         }
     530           4 :         else if (oType.GetClass() == GEDTC_COMPOUND &&
     531           4 :                  oType.GetComponents().size() == 1 &&
     532           2 :                  oType.GetComponents()[0]->GetType().GetClass() ==
     533           4 :                      GEDTC_NUMERIC &&
     534           2 :                  oType.GetComponents()[0]->GetType().GetNumericDataType() ==
     535             :                      GDT_UInt32)
     536             :         {
     537             :             // seen in a S102 v3 product (102DE00CA22_UNC_MD.H5), although
     538             :             // I believe this is non-conformant.
     539             : 
     540             :             // Escape potentials single-quote and double-quote with back-slash
     541           2 :             CPLString osEscapedCompName(oType.GetComponents()[0]->GetName());
     542           4 :             osEscapedCompName.replaceAll("\\", "\\\\")
     543           4 :                 .replaceAll("'", "\\'")
     544           2 :                 .replaceAll("\"", "\\\"");
     545             : 
     546             :             // Gets a view with that single component extracted.
     547           4 :             poValuesArray = poValuesArray->GetView(
     548           4 :                 std::string("['").append(osEscapedCompName).append("']"));
     549           2 :             if (!poValuesArray)
     550           0 :                 return false;
     551             :         }
     552             :         else
     553             :         {
     554           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     555             :                      "Unsupported data type for %s",
     556           0 :                      poValuesArray->GetFullName().c_str());
     557           0 :             return false;
     558             :         }
     559             :     }
     560             : 
     561           4 :     if (poValuesArray->GetDimensionCount() != 2)
     562             :     {
     563           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     564             :                  "Unsupported number of dimensions for %s",
     565           0 :                  poValuesArray->GetFullName().c_str());
     566           0 :         return false;
     567             :     }
     568             : 
     569             :     auto poFeatureAttributeTable =
     570          12 :         poGroupQuality->OpenMDArray("featureAttributeTable");
     571           4 :     if (!poFeatureAttributeTable)
     572             :     {
     573           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     574             :                  "Cannot find array /%s/featureAttributeTable",
     575             :                  pszNameOfQualityGroup);
     576           0 :         return false;
     577             :     }
     578             : 
     579             :     {
     580           4 :         const auto &oType = poFeatureAttributeTable->GetDataType();
     581           4 :         if (oType.GetClass() != GEDTC_COMPOUND)
     582             :         {
     583           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     584             :                      "Unsupported data type for %s",
     585           0 :                      poFeatureAttributeTable->GetFullName().c_str());
     586           0 :             return false;
     587             :         }
     588             : 
     589           4 :         const auto &poComponents = oType.GetComponents();
     590           4 :         if (poComponents.size() >= 1 && poComponents[0]->GetName() != "id")
     591             :         {
     592           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     593             :                      "Missing 'id' component in %s",
     594           0 :                      poFeatureAttributeTable->GetFullName().c_str());
     595           0 :             return false;
     596             :         }
     597             :     }
     598             : 
     599           4 :     if (bNorthUp)
     600           2 :         poValuesArray = poValuesArray->GetView("[::-1,...]");
     601             : 
     602             :     auto poDS =
     603           8 :         std::unique_ptr<GDALDataset>(poValuesArray->AsClassicDataset(1, 0));
     604           4 :     if (!poDS)
     605           0 :         return false;
     606             : 
     607           4 :     nRasterXSize = poDS->GetRasterXSize();
     608           4 :     nRasterYSize = poDS->GetRasterYSize();
     609             : 
     610             :     auto poRAT =
     611           8 :         HDF5CreateRAT(poFeatureAttributeTable, /* bFirstColIsMinMax = */ true);
     612             :     auto poBand = std::make_unique<S102GeoreferencedMetadataRasterBand>(
     613           4 :         std::move(poDS), std::move(poRAT));
     614           4 :     SetBand(1, poBand.release());
     615             : 
     616           4 :     return true;
     617             : }
     618             : 
     619             : /************************************************************************/
     620             : /*                      S102DatasetDriverUnload()                       */
     621             : /************************************************************************/
     622             : 
     623           6 : static void S102DatasetDriverUnload(GDALDriver *)
     624             : {
     625           6 :     HDF5UnloadFileDriver();
     626           6 : }
     627             : 
     628             : /************************************************************************/
     629             : /*                         GDALRegister_S102()                          */
     630             : /************************************************************************/
     631          11 : void GDALRegister_S102()
     632             : 
     633             : {
     634          11 :     if (!GDAL_CHECK_VERSION("S102"))
     635           0 :         return;
     636             : 
     637          11 :     if (GDALGetDriverByName(S102_DRIVER_NAME) != nullptr)
     638           0 :         return;
     639             : 
     640          11 :     GDALDriver *poDriver = new GDALDriver();
     641             : 
     642          11 :     S102DriverSetCommonMetadata(poDriver);
     643          11 :     poDriver->pfnOpen = S102Dataset::Open;
     644          11 :     poDriver->pfnUnloadDriver = S102DatasetDriverUnload;
     645             : 
     646          11 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     647             : }

Generated by: LCOV version 1.14