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: 2024-11-21 22:18:42 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 :         CPLError(CE_Failure, CPLE_NotSupported,
     150             :                  "The S102 driver does not support update access.");
     151           0 :         return nullptr;
     152             :     }
     153             : 
     154          46 :     std::string osFilename(poOpenInfo->pszFilename);
     155          23 :     bool bIsSubdataset = false;
     156          23 :     bool bIsQuality = false;
     157          23 :     if (STARTS_WITH(poOpenInfo->pszFilename, "S102:"))
     158             :     {
     159             :         const CPLStringList aosTokens(
     160          11 :             CSLTokenizeString2(poOpenInfo->pszFilename, ":",
     161          11 :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES));
     162             : 
     163          11 :         if (aosTokens.size() == 2)
     164             :         {
     165           1 :             osFilename = aosTokens[1];
     166             :         }
     167          10 :         else if (aosTokens.size() == 3)
     168             :         {
     169          10 :             bIsSubdataset = true;
     170          10 :             osFilename = aosTokens[1];
     171          10 :             if (EQUAL(aosTokens[2], "BathymetryCoverage"))
     172             :             {
     173             :                 // Default dataset
     174             :             }
     175          12 :             else if (EQUAL(aosTokens[2], "QualityOfSurvey") ||  // < v3
     176           4 :                      EQUAL(aosTokens[2], "QualityOfBathymetryCoverage"))  // v3
     177             :             {
     178           6 :                 bIsQuality = true;
     179             :             }
     180             :             else
     181             :             {
     182           2 :                 CPLError(CE_Failure, CPLE_NotSupported,
     183             :                          "Unsupported subdataset component: '%s'. Expected "
     184             :                          "'QualityOfSurvey'",
     185             :                          aosTokens[2]);
     186           2 :                 return nullptr;
     187             :             }
     188             :         }
     189             :         else
     190             :         {
     191           0 :             return nullptr;
     192             :         }
     193             :     }
     194             : 
     195          42 :     auto poDS = std::make_unique<S102Dataset>(osFilename);
     196          21 :     if (!poDS->Init())
     197           0 :         return nullptr;
     198             : 
     199          21 :     const auto &poRootGroup = poDS->m_poRootGroup;
     200             :     auto poBathymetryCoverage01 = poRootGroup->OpenGroupFromFullname(
     201          63 :         "/BathymetryCoverage/BathymetryCoverage.01");
     202          21 :     if (!poBathymetryCoverage01)
     203           0 :         return nullptr;
     204             : 
     205          21 :     const bool bNorthUp = CPLTestBool(
     206          21 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
     207             : 
     208          21 :     if (bIsQuality)
     209             :     {
     210           6 :         if (!poDS->OpenQuality(poOpenInfo, poRootGroup))
     211           2 :             return nullptr;
     212             : 
     213             :         // Setup/check for pam .aux.xml.
     214           4 :         poDS->SetDescription(osFilename.c_str());
     215           4 :         poDS->TryLoadXML();
     216             : 
     217             :         // Setup overviews.
     218           4 :         poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     219             : 
     220           4 :         return poDS.release();
     221             :     }
     222             : 
     223             :     // Compute geotransform
     224          15 :     poDS->m_bHasGT = S100GetGeoTransform(poBathymetryCoverage01.get(),
     225          15 :                                          poDS->m_adfGeoTransform, bNorthUp);
     226             : 
     227          45 :     auto poGroup001 = poBathymetryCoverage01->OpenGroup("Group_001");
     228          15 :     if (!poGroup001)
     229           0 :         return nullptr;
     230          45 :     auto poValuesArray = poGroup001->OpenMDArray("values");
     231          15 :     if (!poValuesArray || poValuesArray->GetDimensionCount() != 2)
     232           0 :         return nullptr;
     233             : 
     234          15 :     const auto &oType = poValuesArray->GetDataType();
     235          15 :     if (oType.GetClass() != GEDTC_COMPOUND)
     236           0 :         return nullptr;
     237          15 :     const auto &oComponents = oType.GetComponents();
     238          30 :     if (oComponents.size() != 2 || oComponents[0]->GetName() != "depth" ||
     239          15 :         oComponents[1]->GetName() != "uncertainty")
     240             :     {
     241           0 :         return nullptr;
     242             :     }
     243             : 
     244          15 :     if (bNorthUp)
     245          14 :         poValuesArray = poValuesArray->GetView("[::-1,...]");
     246             : 
     247          45 :     auto poDepth = poValuesArray->GetView("[\"depth\"]");
     248             : 
     249             :     // Mandatory in v2.2
     250          15 :     bool bCSIsElevation = false;
     251          45 :     auto poVerticalCS = poRootGroup->GetAttribute("verticalCS");
     252          15 :     if (poVerticalCS && poVerticalCS->GetDataType().GetClass() == GEDTC_NUMERIC)
     253             :     {
     254           5 :         const auto nVal = poVerticalCS->ReadAsInt();
     255           5 :         if (nVal == 6498)  // Depth metre
     256             :         {
     257             :             // nothing to do
     258             :         }
     259           0 :         else if (nVal == 6499)  // Height metre
     260             :         {
     261           0 :             bCSIsElevation = true;
     262             :         }
     263             :         else
     264             :         {
     265           0 :             CPLError(CE_Warning, CPLE_NotSupported, "Unsupported verticalCS=%d",
     266             :                      nVal);
     267             :         }
     268             :     }
     269             : 
     270             :     const bool bUseElevation =
     271          15 :         EQUAL(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     272             :                                    "DEPTH_OR_ELEVATION", "DEPTH"),
     273             :               "ELEVATION");
     274          29 :     const bool bInvertDepth = (bUseElevation && !bCSIsElevation) ||
     275          14 :                               (!bUseElevation && bCSIsElevation);
     276          15 :     const double dfDepthNoData = poDepth->GetNoDataValueAsDouble();
     277          31 :     auto poDepthDS = [&poDepth, bInvertDepth, dfDepthNoData]()
     278             :     {
     279          15 :         if (bInvertDepth)
     280             :         {
     281           1 :             auto poInverted = poDepth->GetUnscaled(-1, 0, dfDepthNoData);
     282             :             return std::unique_ptr<GDALDataset>(
     283           1 :                 poInverted->AsClassicDataset(1, 0));
     284             :         }
     285             :         else
     286             :         {
     287             :             return std::unique_ptr<GDALDataset>(
     288          14 :                 poDepth->AsClassicDataset(1, 0));
     289             :         }
     290          30 :     }();
     291             : 
     292          45 :     auto poUncertainty = poValuesArray->GetView("[\"uncertainty\"]");
     293          15 :     const double dfUncertaintyNoData = poUncertainty->GetNoDataValueAsDouble();
     294             :     auto poUncertaintyDS =
     295          30 :         std::unique_ptr<GDALDataset>(poUncertainty->AsClassicDataset(1, 0));
     296             : 
     297          15 :     poDS->nRasterXSize = poDepthDS->GetRasterXSize();
     298          15 :     poDS->nRasterYSize = poDepthDS->GetRasterYSize();
     299             : 
     300             :     // Create depth (or elevation) band
     301          15 :     auto poDepthBand = new S102RasterBand(std::move(poDepthDS));
     302          15 :     poDepthBand->SetDescription(bUseElevation ? "elevation" : "depth");
     303             : 
     304          45 :     auto poMinimumDepth = poGroup001->GetAttribute("minimumDepth");
     305          30 :     if (poMinimumDepth &&
     306          30 :         poMinimumDepth->GetDataType().GetClass() == GEDTC_NUMERIC)
     307             :     {
     308          15 :         const double dfVal = poMinimumDepth->ReadAsDouble();
     309          15 :         if (dfVal != dfDepthNoData)
     310             :         {
     311          15 :             if (bInvertDepth)
     312           1 :                 poDepthBand->m_dfMaximum = -dfVal;
     313             :             else
     314          14 :                 poDepthBand->m_dfMinimum = dfVal;
     315             :         }
     316             :     }
     317             : 
     318          45 :     auto poMaximumDepth = poGroup001->GetAttribute("maximumDepth");
     319          30 :     if (poMaximumDepth &&
     320          30 :         poMaximumDepth->GetDataType().GetClass() == GEDTC_NUMERIC)
     321             :     {
     322          15 :         const double dfVal = poMaximumDepth->ReadAsDouble();
     323          15 :         if (dfVal != dfDepthNoData)
     324             :         {
     325          15 :             if (bInvertDepth)
     326           1 :                 poDepthBand->m_dfMinimum = -dfVal;
     327             :             else
     328          14 :                 poDepthBand->m_dfMaximum = dfVal;
     329             :         }
     330             :     }
     331             : 
     332          15 :     poDS->SetBand(1, poDepthBand);
     333             : 
     334             :     // Create uncertainty band
     335          15 :     auto poUncertaintyBand = new S102RasterBand(std::move(poUncertaintyDS));
     336          15 :     poUncertaintyBand->SetDescription("uncertainty");
     337             : 
     338          45 :     auto poMinimumUncertainty = poGroup001->GetAttribute("minimumUncertainty");
     339          30 :     if (poMinimumUncertainty &&
     340          30 :         poMinimumUncertainty->GetDataType().GetClass() == GEDTC_NUMERIC)
     341             :     {
     342          15 :         const double dfVal = poMinimumUncertainty->ReadAsDouble();
     343          15 :         if (dfVal != dfUncertaintyNoData)
     344             :         {
     345          15 :             poUncertaintyBand->m_dfMinimum = dfVal;
     346             :         }
     347             :     }
     348             : 
     349          45 :     auto poMaximumUncertainty = poGroup001->GetAttribute("maximumUncertainty");
     350          30 :     if (poMaximumUncertainty &&
     351          30 :         poMaximumUncertainty->GetDataType().GetClass() == GEDTC_NUMERIC)
     352             :     {
     353          15 :         const double dfVal = poMaximumUncertainty->ReadAsDouble();
     354          15 :         if (dfVal != dfUncertaintyNoData)
     355             :         {
     356          15 :             poUncertaintyBand->m_dfMaximum = dfVal;
     357             :         }
     358             :     }
     359             : 
     360          15 :     poDS->SetBand(2, poUncertaintyBand);
     361             : 
     362          15 :     poDS->GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, GDALMD_AOP_POINT);
     363             : 
     364          45 :     auto poGroupQuality = poRootGroup->OpenGroup("QualityOfSurvey");
     365          15 :     const bool bIsNamedQualityOfSurvey = poGroupQuality != nullptr;
     366          15 :     if (!bIsNamedQualityOfSurvey)
     367             :     {
     368             :         // S102 v3 now uses QualityOfBathymetryCoverage instead of QualityOfSurvey
     369          13 :         poGroupQuality = poRootGroup->OpenGroup("QualityOfBathymetryCoverage");
     370             :     }
     371          15 :     if (!bIsSubdataset && poGroupQuality)
     372             :     {
     373           2 :         const char *pszNameOfQualityGroup = bIsNamedQualityOfSurvey
     374           2 :                                                 ? "QualityOfSurvey"
     375             :                                                 : "QualityOfBathymetryCoverage";
     376           2 :         auto poGroupQuality01 = poGroupQuality->OpenGroup(
     377           6 :             CPLSPrintf("%s.01", pszNameOfQualityGroup));
     378           2 :         if (poGroupQuality01)
     379             :         {
     380           2 :             poDS->GDALDataset::SetMetadataItem(
     381             :                 "SUBDATASET_1_NAME",
     382             :                 CPLSPrintf("S102:\"%s\":BathymetryCoverage",
     383             :                            osFilename.c_str()),
     384             :                 "SUBDATASETS");
     385           2 :             poDS->GDALDataset::SetMetadataItem(
     386             :                 "SUBDATASET_1_DESC", "Bathymetric gridded data", "SUBDATASETS");
     387             : 
     388           2 :             poDS->GDALDataset::SetMetadataItem(
     389             :                 "SUBDATASET_2_NAME",
     390             :                 CPLSPrintf("S102:\"%s\":%s", osFilename.c_str(),
     391             :                            pszNameOfQualityGroup),
     392             :                 "SUBDATASETS");
     393           2 :             poDS->GDALDataset::SetMetadataItem(
     394             :                 "SUBDATASET_2_DESC",
     395             :                 CPLSPrintf("Georeferenced metadata %s", pszNameOfQualityGroup),
     396             :                 "SUBDATASETS");
     397             :         }
     398             :     }
     399             : 
     400             :     // Setup/check for pam .aux.xml.
     401          15 :     poDS->SetDescription(osFilename.c_str());
     402          15 :     poDS->TryLoadXML();
     403             : 
     404             :     // Setup overviews.
     405          15 :     poDS->oOvManager.Initialize(poDS.get(), osFilename.c_str());
     406             : 
     407          15 :     return poDS.release();
     408             : }
     409             : 
     410             : /************************************************************************/
     411             : /*                       OpenQuality()                          */
     412             : /************************************************************************/
     413             : 
     414           6 : bool S102Dataset::OpenQuality(GDALOpenInfo *poOpenInfo,
     415             :                               const std::shared_ptr<GDALGroup> &poRootGroup)
     416             : {
     417           6 :     const bool bNorthUp = CPLTestBool(
     418           6 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "NORTH_UP", "YES"));
     419             : 
     420           6 :     const char *pszNameOfQualityGroup = "QualityOfSurvey";
     421          18 :     auto poGroupQuality = poRootGroup->OpenGroup(pszNameOfQualityGroup);
     422           6 :     if (!poGroupQuality)
     423             :     {
     424           4 :         pszNameOfQualityGroup = "QualityOfBathymetryCoverage";
     425           4 :         poGroupQuality = poRootGroup->OpenGroup(pszNameOfQualityGroup);
     426           4 :         if (!poGroupQuality)
     427             :         {
     428           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     429             :                      "Cannot find group /QualityOfSurvey or "
     430             :                      "/QualityOfBathymetryCoverage");
     431           2 :             return false;
     432             :         }
     433             :     }
     434             : 
     435             :     const std::string osQuality01Name =
     436          12 :         std::string(pszNameOfQualityGroup).append(".01");
     437           8 :     const std::string osQuality01FullName = std::string("/")
     438           4 :                                                 .append(pszNameOfQualityGroup)
     439           4 :                                                 .append("/")
     440           8 :                                                 .append(osQuality01Name);
     441           8 :     auto poGroupQuality01 = poGroupQuality->OpenGroup(osQuality01Name);
     442           4 :     if (!poGroupQuality01)
     443             :     {
     444           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s",
     445             :                  osQuality01FullName.c_str());
     446           0 :         return false;
     447             :     }
     448             : 
     449           8 :     if (auto poStartSequence = poGroupQuality01->GetAttribute("startSequence"))
     450             :     {
     451           0 :         const char *pszStartSequence = poStartSequence->ReadAsString();
     452           0 :         if (pszStartSequence && !EQUAL(pszStartSequence, "0,0"))
     453             :         {
     454           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     455             :                      "startSequence (=%s) != 0,0 is not supported",
     456             :                      pszStartSequence);
     457           0 :             return false;
     458             :         }
     459             :     }
     460             : 
     461             :     // Compute geotransform
     462           4 :     m_bHasGT = S100GetGeoTransform(poGroupQuality01.get(), m_adfGeoTransform,
     463             :                                    bNorthUp);
     464             : 
     465          12 :     auto poGroup001 = poGroupQuality01->OpenGroup("Group_001");
     466           4 :     if (!poGroup001)
     467             :     {
     468           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Cannot find group %s/Group_001",
     469             :                  osQuality01FullName.c_str());
     470           0 :         return false;
     471             :     }
     472             : 
     473          12 :     auto poValuesArray = poGroup001->OpenMDArray("values");
     474           4 :     if (!poValuesArray)
     475             :     {
     476           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     477             :                  "Cannot find array "
     478             :                  "%s/Group_001/values",
     479             :                  osQuality01FullName.c_str());
     480           0 :         return false;
     481             :     }
     482             : 
     483             :     {
     484           4 :         const auto &oType = poValuesArray->GetDataType();
     485           6 :         if (oType.GetClass() == GEDTC_NUMERIC &&
     486           2 :             oType.GetNumericDataType() == GDT_UInt32)
     487             :         {
     488             :             // ok
     489             :         }
     490           4 :         else if (oType.GetClass() == GEDTC_COMPOUND &&
     491           4 :                  oType.GetComponents().size() == 1 &&
     492           2 :                  oType.GetComponents()[0]->GetType().GetClass() ==
     493           4 :                      GEDTC_NUMERIC &&
     494           2 :                  oType.GetComponents()[0]->GetType().GetNumericDataType() ==
     495             :                      GDT_UInt32)
     496             :         {
     497             :             // seen in a S102 v3 product (102DE00CA22_UNC_MD.H5), although
     498             :             // I believe this is non-conformant.
     499             : 
     500             :             // Escape potentials single-quote and double-quote with back-slash
     501           2 :             CPLString osEscapedCompName(oType.GetComponents()[0]->GetName());
     502           4 :             osEscapedCompName.replaceAll("\\", "\\\\")
     503           4 :                 .replaceAll("'", "\\'")
     504           2 :                 .replaceAll("\"", "\\\"");
     505             : 
     506             :             // Gets a view with that single component extracted.
     507           4 :             poValuesArray = poValuesArray->GetView(
     508           4 :                 std::string("['").append(osEscapedCompName).append("']"));
     509           2 :             if (!poValuesArray)
     510           0 :                 return false;
     511             :         }
     512             :         else
     513             :         {
     514           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     515             :                      "Unsupported data type for %s",
     516           0 :                      poValuesArray->GetFullName().c_str());
     517           0 :             return false;
     518             :         }
     519             :     }
     520             : 
     521           4 :     if (poValuesArray->GetDimensionCount() != 2)
     522             :     {
     523           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     524             :                  "Unsupported number of dimensions for %s",
     525           0 :                  poValuesArray->GetFullName().c_str());
     526           0 :         return false;
     527             :     }
     528             : 
     529             :     auto poFeatureAttributeTable =
     530          12 :         poGroupQuality->OpenMDArray("featureAttributeTable");
     531           4 :     if (!poFeatureAttributeTable)
     532             :     {
     533           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     534             :                  "Cannot find array /%s/featureAttributeTable",
     535             :                  pszNameOfQualityGroup);
     536           0 :         return false;
     537             :     }
     538             : 
     539             :     {
     540           4 :         const auto &oType = poFeatureAttributeTable->GetDataType();
     541           4 :         if (oType.GetClass() != GEDTC_COMPOUND)
     542             :         {
     543           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     544             :                      "Unsupported data type for %s",
     545           0 :                      poFeatureAttributeTable->GetFullName().c_str());
     546           0 :             return false;
     547             :         }
     548             : 
     549           4 :         const auto &poComponents = oType.GetComponents();
     550           4 :         if (poComponents.size() >= 1 && poComponents[0]->GetName() != "id")
     551             :         {
     552           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     553             :                      "Missing 'id' component in %s",
     554           0 :                      poFeatureAttributeTable->GetFullName().c_str());
     555           0 :             return false;
     556             :         }
     557             :     }
     558             : 
     559           4 :     if (bNorthUp)
     560           2 :         poValuesArray = poValuesArray->GetView("[::-1,...]");
     561             : 
     562             :     auto poDS =
     563           8 :         std::unique_ptr<GDALDataset>(poValuesArray->AsClassicDataset(1, 0));
     564           4 :     if (!poDS)
     565           0 :         return false;
     566             : 
     567           4 :     nRasterXSize = poDS->GetRasterXSize();
     568           4 :     nRasterYSize = poDS->GetRasterYSize();
     569             : 
     570             :     auto poRAT =
     571           8 :         HDF5CreateRAT(poFeatureAttributeTable, /* bFirstColIsMinMax = */ true);
     572             :     auto poBand = std::make_unique<S102GeoreferencedMetadataRasterBand>(
     573           4 :         std::move(poDS), std::move(poRAT));
     574           4 :     SetBand(1, poBand.release());
     575             : 
     576           4 :     return true;
     577             : }
     578             : 
     579             : /************************************************************************/
     580             : /*                      S102DatasetDriverUnload()                       */
     581             : /************************************************************************/
     582             : 
     583           6 : static void S102DatasetDriverUnload(GDALDriver *)
     584             : {
     585           6 :     HDF5UnloadFileDriver();
     586           6 : }
     587             : 
     588             : /************************************************************************/
     589             : /*                         GDALRegister_S102()                          */
     590             : /************************************************************************/
     591          10 : void GDALRegister_S102()
     592             : 
     593             : {
     594          10 :     if (!GDAL_CHECK_VERSION("S102"))
     595           0 :         return;
     596             : 
     597          10 :     if (GDALGetDriverByName(S102_DRIVER_NAME) != nullptr)
     598           0 :         return;
     599             : 
     600          10 :     GDALDriver *poDriver = new GDALDriver();
     601             : 
     602          10 :     S102DriverSetCommonMetadata(poDriver);
     603          10 :     poDriver->pfnOpen = S102Dataset::Open;
     604          10 :     poDriver->pfnUnloadDriver = S102DatasetDriverUnload;
     605             : 
     606          10 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     607             : }

Generated by: LCOV version 1.14