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

Generated by: LCOV version 1.14