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

Generated by: LCOV version 1.14