LCOV - code coverage report
Current view: top level - frmts/hdf5 - hdf5dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 643 811 79.3 %
Date: 2024-11-21 22:18:42 Functions: 23 24 95.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 5 (HDF5)
       4             :  * Purpose:  HDF5 Datasets. Open HDF5 file, fetch metadata and list of
       5             :  *           subdatasets.
       6             :  *           This driver initially based on code supplied by Markus Neteler
       7             :  * Author:  Denis Nadeau <denis.nadeau@gmail.com>
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
      11             :  * Copyright (c) 2008-2018, Even Rouault <even.rouault at spatialys.com>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #include "cpl_port.h"
      17             : 
      18             : #include "hdf5_api.h"
      19             : #include "hdf5dataset.h"
      20             : #include "hdf5drivercore.h"
      21             : #include "hdf5vfl.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <stdio.h>
      25             : #include <string.h>
      26             : #include <string>
      27             : #include <cctype>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_float.h"
      32             : #include "cpl_string.h"
      33             : #include "gdal.h"
      34             : #include "gdal_frmts.h"
      35             : #include "gdal_priv.h"
      36             : 
      37             : constexpr size_t MAX_METADATA_LEN = 32768;
      38             : 
      39             : #ifdef ENABLE_HDF5_GLOBAL_LOCK
      40             : 
      41             : /************************************************************************/
      42             : /*                          GetHDF5GlobalMutex()                        */
      43             : /************************************************************************/
      44             : 
      45             : std::recursive_mutex &GetHDF5GlobalMutex()
      46             : {
      47             :     static std::recursive_mutex oMutex;
      48             :     return oMutex;
      49             : }
      50             : 
      51             : #endif
      52             : 
      53             : /************************************************************************/
      54             : /*                          HDF5GetFileDriver()                         */
      55             : /************************************************************************/
      56             : 
      57         297 : hid_t HDF5GetFileDriver()
      58             : {
      59         297 :     return HDF5VFLGetFileDriver();
      60             : }
      61             : 
      62             : /************************************************************************/
      63             : /*                        HDF5UnloadFileDriver()                        */
      64             : /************************************************************************/
      65             : 
      66          36 : void HDF5UnloadFileDriver()
      67             : {
      68          36 :     HDF5VFLUnloadFileDriver();
      69          36 : }
      70             : 
      71             : /************************************************************************/
      72             : /*                     HDF5DatasetDriverUnload()                        */
      73             : /************************************************************************/
      74             : 
      75           6 : static void HDF5DatasetDriverUnload(GDALDriver *)
      76             : {
      77           6 :     HDF5UnloadFileDriver();
      78           6 : }
      79             : 
      80             : /************************************************************************/
      81             : /* ==================================================================== */
      82             : /*                              HDF5Dataset                             */
      83             : /* ==================================================================== */
      84             : /************************************************************************/
      85             : 
      86             : /************************************************************************/
      87             : /*                        GDALRegister_HDF5()                           */
      88             : /************************************************************************/
      89          10 : void GDALRegister_HDF5()
      90             : 
      91             : {
      92          10 :     if (GDALGetDriverByName(HDF5_DRIVER_NAME) != nullptr)
      93           0 :         return;
      94             : 
      95          10 :     GDALDriver *poDriver = new GDALDriver();
      96             : 
      97          10 :     HDF5DriverSetCommonMetadata(poDriver);
      98             : 
      99          10 :     poDriver->pfnOpen = HDF5Dataset::Open;
     100          10 :     poDriver->pfnUnloadDriver = HDF5DatasetDriverUnload;
     101          10 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     102             : 
     103             : #ifdef HDF5_PLUGIN
     104          10 :     GDALRegister_HDF5Image();
     105          10 :     GDALRegister_BAG();
     106          10 :     GDALRegister_S102();
     107          10 :     GDALRegister_S104();
     108          10 :     GDALRegister_S111();
     109             : #endif
     110             : }
     111             : 
     112             : /************************************************************************/
     113             : /*                           HDF5Dataset()                              */
     114             : /************************************************************************/
     115         160 : HDF5Dataset::HDF5Dataset()
     116             :     : hGroupID(-1), papszSubDatasets(nullptr), nDatasetType(-1),
     117         160 :       nSubDataCount(0), poH5RootGroup(nullptr)
     118             : {
     119         160 : }
     120             : 
     121             : /************************************************************************/
     122             : /*                            ~HDF5Dataset()                            */
     123             : /************************************************************************/
     124         257 : HDF5Dataset::~HDF5Dataset()
     125             : {
     126             :     HDF5_GLOBAL_LOCK();
     127             : 
     128         160 :     if (hGroupID > 0)
     129         137 :         H5Gclose(hGroupID);
     130         160 :     if (m_hHDF5 > 0)
     131         137 :         H5Fclose(m_hHDF5);
     132             : 
     133         160 :     CSLDestroy(papszSubDatasets);
     134         160 :     if (poH5RootGroup != nullptr)
     135             :     {
     136         137 :         DestroyH5Objects(poH5RootGroup);
     137         137 :         CPLFree(poH5RootGroup->pszName);
     138         137 :         CPLFree(poH5RootGroup->pszPath);
     139         137 :         CPLFree(poH5RootGroup->pszUnderscorePath);
     140         137 :         CPLFree(poH5RootGroup->poHchild);
     141         137 :         CPLFree(poH5RootGroup);
     142             :     }
     143         257 : }
     144             : 
     145             : /************************************************************************/
     146             : /*                            GetDataType()                             */
     147             : /*                                                                      */
     148             : /*      Transform HDF5 datatype to GDAL datatype                        */
     149             : /************************************************************************/
     150       16866 : GDALDataType HDF5Dataset::GetDataType(hid_t TypeID)
     151             : {
     152             :     // Check for native types first
     153       16866 :     if (H5Tget_class(TypeID) != H5T_COMPOUND)
     154             :     {
     155             : 
     156       15984 :         if (H5Tequal(H5T_NATIVE_SCHAR, TypeID))
     157          66 :             return GDT_Int8;
     158       31836 :         else if (H5Tequal(H5T_NATIVE_CHAR, TypeID) ||
     159       15918 :                  H5Tequal(H5T_NATIVE_UCHAR, TypeID))
     160         510 :             return GDT_Byte;
     161       15408 :         else if (H5Tequal(H5T_NATIVE_SHORT, TypeID))
     162         423 :             return GDT_Int16;
     163       14985 :         else if (H5Tequal(H5T_NATIVE_USHORT, TypeID))
     164         455 :             return GDT_UInt16;
     165       14530 :         else if (H5Tequal(H5T_NATIVE_INT, TypeID))
     166        1106 :             return GDT_Int32;
     167       13424 :         else if (H5Tequal(H5T_NATIVE_UINT, TypeID))
     168        3563 :             return GDT_UInt32;
     169        9861 :         else if (H5Tequal(H5T_NATIVE_INT64, TypeID))
     170          38 :             return GDT_Int64;
     171        9823 :         else if (H5Tequal(H5T_NATIVE_UINT64, TypeID))
     172         163 :             return GDT_UInt64;
     173        9660 :         else if (H5Tequal(H5T_NATIVE_LONG, TypeID))
     174             :         {
     175             : #if SIZEOF_UNSIGNED_LONG == 4
     176             :             return GDT_Int32;
     177             : #else
     178           0 :             return GDT_Unknown;
     179             : #endif
     180             :         }
     181        9660 :         else if (H5Tequal(H5T_NATIVE_ULONG, TypeID))
     182             :         {
     183             : #if SIZEOF_UNSIGNED_LONG == 4
     184             :             return GDT_UInt32;
     185             : #else
     186           0 :             return GDT_Unknown;
     187             : #endif
     188             :         }
     189             : #ifdef HDF5_HAVE_FLOAT16
     190             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, TypeID))
     191             :             return GDT_Float32;
     192             : #endif
     193        9660 :         else if (H5Tequal(H5T_NATIVE_FLOAT, TypeID))
     194        2805 :             return GDT_Float32;
     195        6855 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID))
     196        3601 :             return GDT_Float64;
     197        3254 :         else if (H5Tequal(H5T_NATIVE_LLONG, TypeID))
     198           0 :             return GDT_Unknown;
     199        3254 :         else if (H5Tequal(H5T_NATIVE_ULLONG, TypeID))
     200           0 :             return GDT_Unknown;
     201             :     }
     202             :     else  // Parse compound type to determine if data is complex
     203             :     {
     204             :         // For complex the compound type must contain 2 elements
     205         882 :         if (H5Tget_nmembers(TypeID) != 2)
     206         314 :             return GDT_Unknown;
     207             : 
     208             :         // For complex the native types of both elements should be the same
     209         568 :         hid_t ElemTypeID = H5Tget_member_type(TypeID, 0);
     210         568 :         hid_t Elem2TypeID = H5Tget_member_type(TypeID, 1);
     211         568 :         const bool bTypeEqual = H5Tequal(ElemTypeID, Elem2TypeID) > 0;
     212         568 :         H5Tclose(Elem2TypeID);
     213         568 :         if (!bTypeEqual)
     214             :         {
     215         272 :             H5Tclose(ElemTypeID);
     216         272 :             return GDT_Unknown;
     217             :         }
     218             : 
     219         296 :         char *pszName1 = H5Tget_member_name(TypeID, 0);
     220         296 :         const bool bIsReal =
     221         296 :             pszName1 && (pszName1[0] == 'r' || pszName1[0] == 'R');
     222         296 :         H5free_memory(pszName1);
     223             : 
     224         296 :         char *pszName2 = H5Tget_member_name(TypeID, 1);
     225         296 :         const bool bIsImaginary =
     226         296 :             pszName2 && (pszName2[0] == 'i' || pszName2[0] == 'I');
     227         296 :         H5free_memory(pszName2);
     228             : 
     229         296 :         if (!bIsReal || !bIsImaginary)
     230             :         {
     231         101 :             H5Tclose(ElemTypeID);
     232         101 :             return GDT_Unknown;
     233             :         }
     234             : 
     235             :         // Check the native types to determine CInt16, CFloat32 or CFloat64
     236         195 :         GDALDataType eDataType = GDT_Unknown;
     237             : 
     238         195 :         if (H5Tequal(H5T_NATIVE_SHORT, ElemTypeID))
     239          47 :             eDataType = GDT_CInt16;
     240         148 :         else if (H5Tequal(H5T_NATIVE_INT, ElemTypeID))
     241          44 :             eDataType = GDT_CInt32;
     242         104 :         else if (H5Tequal(H5T_NATIVE_LONG, ElemTypeID))
     243             :         {
     244             : #if SIZEOF_UNSIGNED_LONG == 4
     245             :             eDataType = GDT_CInt32;
     246             : #else
     247           0 :             eDataType = GDT_Unknown;
     248             : #endif
     249             :         }
     250             : #ifdef HDF5_HAVE_FLOAT16
     251             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, ElemTypeID))
     252             :             eDataType = GDT_CFloat32;
     253             : #endif
     254         104 :         else if (H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID))
     255          55 :             eDataType = GDT_CFloat32;
     256          49 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, ElemTypeID))
     257          49 :             eDataType = GDT_CFloat64;
     258             : 
     259             :         // Close the data type
     260         195 :         H5Tclose(ElemTypeID);
     261             : 
     262         195 :         return eDataType;
     263             :     }
     264             : 
     265        3254 :     return GDT_Unknown;
     266             : }
     267             : 
     268             : /************************************************************************/
     269             : /*                          IsNativeCFloat16()                          */
     270             : /************************************************************************/
     271             : 
     272           0 : /* static*/ bool HDF5Dataset::IsNativeCFloat16(hid_t hDataType)
     273             : {
     274             : #ifdef HDF5_HAVE_FLOAT16
     275             :     // For complex the compound type must contain 2 elements
     276             :     if (H5Tget_class(hDataType) != H5T_COMPOUND ||
     277             :         H5Tget_nmembers(hDataType) != 2)
     278             :         return false;
     279             : 
     280             :     // For complex the native types of both elements should be the same
     281             :     hid_t ElemTypeID = H5Tget_member_type(hDataType, 0);
     282             :     hid_t Elem2TypeID = H5Tget_member_type(hDataType, 1);
     283             :     const bool bRet = H5Tequal(ElemTypeID, H5T_NATIVE_FLOAT16) > 0 &&
     284             :                       H5Tequal(Elem2TypeID, H5T_NATIVE_FLOAT16) > 0;
     285             :     H5Tclose(ElemTypeID);
     286             :     H5Tclose(Elem2TypeID);
     287             :     return bRet;
     288             : #else
     289           0 :     CPL_IGNORE_RET_VAL(hDataType);
     290           0 :     return false;
     291             : #endif
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                          GetDataTypeName()                           */
     296             : /*                                                                      */
     297             : /*      Return the human readable name of data type                     */
     298             : /************************************************************************/
     299         365 : const char *HDF5Dataset::GetDataTypeName(hid_t TypeID)
     300             : {
     301             :     // Check for native types first
     302         365 :     if (H5Tget_class(TypeID) != H5T_COMPOUND)
     303             :     {
     304         354 :         if (H5Tequal(H5T_NATIVE_CHAR, TypeID))
     305           4 :             return "8-bit character";
     306         350 :         else if (H5Tequal(H5T_NATIVE_SCHAR, TypeID))
     307           0 :             return "8-bit signed character";
     308         350 :         else if (H5Tequal(H5T_NATIVE_UCHAR, TypeID))
     309          35 :             return "8-bit unsigned character";
     310         315 :         else if (H5Tequal(H5T_NATIVE_SHORT, TypeID))
     311           4 :             return "16-bit integer";
     312         311 :         else if (H5Tequal(H5T_NATIVE_USHORT, TypeID))
     313          13 :             return "16-bit unsigned integer";
     314         298 :         else if (H5Tequal(H5T_NATIVE_INT, TypeID))
     315          92 :             return "32-bit integer";
     316         206 :         else if (H5Tequal(H5T_NATIVE_UINT, TypeID))
     317           8 :             return "32-bit unsigned integer";
     318         198 :         else if (H5Tequal(H5T_NATIVE_INT64, TypeID))
     319           8 :             return "64-bit integer";
     320         190 :         else if (H5Tequal(H5T_NATIVE_UINT64, TypeID))
     321           8 :             return "64-bit unsigned integer";
     322         182 :         else if (H5Tequal(H5T_NATIVE_LONG, TypeID))
     323           0 :             return "32/64-bit integer";
     324         182 :         else if (H5Tequal(H5T_NATIVE_ULONG, TypeID))
     325           0 :             return "32/64-bit unsigned integer";
     326             : #ifdef HDF5_HAVE_FLOAT16
     327             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, TypeID))
     328             :             return "16-bit floating-point";
     329             : #endif
     330         182 :         else if (H5Tequal(H5T_NATIVE_FLOAT, TypeID))
     331          19 :             return "32-bit floating-point";
     332         163 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID))
     333           4 :             return "64-bit floating-point";
     334         159 :         else if (H5Tequal(H5T_NATIVE_LLONG, TypeID))
     335           0 :             return "64-bit integer";
     336         159 :         else if (H5Tequal(H5T_NATIVE_ULLONG, TypeID))
     337           0 :             return "64-bit unsigned integer";
     338         159 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, TypeID))
     339           0 :             return "64-bit floating-point";
     340             :     }
     341             :     else
     342             :     {
     343             :         // For complex the compound type must contain 2 elements
     344          11 :         if (H5Tget_nmembers(TypeID) != 2)
     345           1 :             return "Unknown";
     346             : 
     347             :         // For complex the native types of both elements should be the same
     348          10 :         hid_t ElemTypeID = H5Tget_member_type(TypeID, 0);
     349          10 :         hid_t Elem2TypeID = H5Tget_member_type(TypeID, 1);
     350          10 :         const bool bTypeEqual = H5Tequal(ElemTypeID, Elem2TypeID) > 0;
     351          10 :         H5Tclose(Elem2TypeID);
     352          10 :         if (!bTypeEqual)
     353             :         {
     354           0 :             H5Tclose(ElemTypeID);
     355           0 :             return "Unknown";
     356             :         }
     357             : 
     358             :         // Check the native types to determine CInt16, CFloat32 or CFloat64
     359          10 :         if (H5Tequal(H5T_NATIVE_SHORT, ElemTypeID))
     360             :         {
     361           0 :             H5Tclose(ElemTypeID);
     362           0 :             return "complex, 16-bit integer";
     363             :         }
     364          10 :         else if (H5Tequal(H5T_NATIVE_INT, ElemTypeID))
     365             :         {
     366           0 :             H5Tclose(ElemTypeID);
     367           0 :             return "complex, 32-bit integer";
     368             :         }
     369          10 :         else if (H5Tequal(H5T_NATIVE_LONG, ElemTypeID))
     370             :         {
     371           0 :             H5Tclose(ElemTypeID);
     372           0 :             return "complex, 32/64-bit integer";
     373             :         }
     374             : #ifdef HDF5_HAVE_FLOAT16
     375             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, ElemTypeID))
     376             :         {
     377             :             H5Tclose(ElemTypeID);
     378             :             return "complex, 16-bit floating-point";
     379             :         }
     380             : #endif
     381          10 :         else if (H5Tequal(H5T_NATIVE_FLOAT, ElemTypeID))
     382             :         {
     383           7 :             H5Tclose(ElemTypeID);
     384           7 :             return "complex, 32-bit floating-point";
     385             :         }
     386           3 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, ElemTypeID))
     387             :         {
     388           3 :             H5Tclose(ElemTypeID);
     389           3 :             return "complex, 64-bit floating-point";
     390             :         }
     391             :     }
     392             : 
     393         159 :     return "Unknown";
     394             : }
     395             : 
     396             : /************************************************************************/
     397             : /*                         GDAL_HDF5Open()                              */
     398             : /************************************************************************/
     399         161 : hid_t GDAL_HDF5Open(const std::string &osFilename)
     400             : {
     401             :     hid_t hHDF5;
     402             :     // Heuristics to able datasets split over several files, using the 'family'
     403             :     // driver. If passed the first file, and it contains a single 0, or
     404             :     // ends up with 0.h5 or 0.hdf5, replace the 0 with %d and try the family
     405             :     // driver.
     406         314 :     if (std::count(osFilename.begin(), osFilename.end(), '0') == 1 ||
     407         314 :         osFilename.find("0.h5") != std::string::npos ||
     408         153 :         osFilename.find("0.hdf5") != std::string::npos)
     409             :     {
     410           8 :         const auto zero_pos = osFilename.rfind('0');
     411          16 :         const auto osNewName = osFilename.substr(0, zero_pos) + "%d" +
     412          16 :                                osFilename.substr(zero_pos + 1);
     413           8 :         hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
     414           8 :         H5Pset_fapl_family(fapl, H5F_FAMILY_DEFAULT, H5P_DEFAULT);
     415             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
     416             : #pragma GCC diagnostic push
     417             : #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
     418             : #endif
     419           8 :         H5E_BEGIN_TRY
     420             :         {
     421           8 :             hHDF5 = H5Fopen(osNewName.c_str(), H5F_ACC_RDONLY, fapl);
     422             :         }
     423           8 :         H5E_END_TRY;
     424             : #ifdef HAVE_GCC_WARNING_ZERO_AS_NULL_POINTER_CONSTANT
     425             : #pragma GCC diagnostic pop
     426             : #endif
     427           8 :         H5Pclose(fapl);
     428           8 :         if (hHDF5 >= 0)
     429             :         {
     430           4 :             CPLDebug("HDF5", "Actually opening %s with 'family' driver",
     431             :                      osNewName.c_str());
     432           4 :             return hHDF5;
     433             :         }
     434             :     }
     435             : 
     436         157 :     hid_t fapl = H5Pcreate(H5P_FILE_ACCESS);
     437         157 :     H5Pset_driver(fapl, HDF5GetFileDriver(), nullptr);
     438         157 :     hHDF5 = H5Fopen(osFilename.c_str(), H5F_ACC_RDONLY, fapl);
     439         157 :     H5Pclose(fapl);
     440         157 :     return hHDF5;
     441             : }
     442             : 
     443             : /************************************************************************/
     444             : /*                                Open()                                */
     445             : /************************************************************************/
     446          94 : GDALDataset *HDF5Dataset::Open(GDALOpenInfo *poOpenInfo)
     447             : {
     448          94 :     if (!HDF5DatasetIdentify(poOpenInfo))
     449           0 :         return nullptr;
     450             : 
     451             :     HDF5_GLOBAL_LOCK();
     452             : 
     453          94 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
     454             :     {
     455          20 :         return OpenMultiDim(poOpenInfo);
     456             :     }
     457             : 
     458             :     // Create datasource.
     459          74 :     HDF5Dataset *const poDS = new HDF5Dataset();
     460             : 
     461          74 :     poDS->SetDescription(poOpenInfo->pszFilename);
     462             : 
     463             :     // Try opening the dataset.
     464          74 :     poDS->m_hHDF5 = GDAL_HDF5Open(poOpenInfo->pszFilename);
     465          74 :     if (poDS->m_hHDF5 < 0)
     466             :     {
     467           0 :         delete poDS;
     468           0 :         return nullptr;
     469             :     }
     470             : 
     471          74 :     poDS->hGroupID = H5Gopen(poDS->m_hHDF5, "/");
     472          74 :     if (poDS->hGroupID < 0)
     473             :     {
     474           0 :         delete poDS;
     475           0 :         return nullptr;
     476             :     }
     477             : 
     478          74 :     if (HDF5EOSParser::HasHDFEOS(poDS->hGroupID))
     479             :     {
     480           6 :         if (poDS->m_oHDFEOSParser.Parse(poDS->hGroupID))
     481             :         {
     482           6 :             CPLDebug("HDF5", "Successfully parsed HDFEOS metadata");
     483             :         }
     484             :     }
     485             : 
     486          74 :     poDS->ReadGlobalAttributes(true);
     487             : 
     488          74 :     if (STARTS_WITH(poDS->m_aosMetadata.FetchNameValueDef("mission_name", ""),
     489           0 :                     "Sentinel 3") &&
     490           0 :         EQUAL(
     491             :             poDS->m_aosMetadata.FetchNameValueDef("altimeter_sensor_name", ""),
     492           0 :             "SRAL") &&
     493           0 :         EQUAL(
     494             :             poDS->m_aosMetadata.FetchNameValueDef("radiometer_sensor_name", ""),
     495          74 :             "MWR") &&
     496           0 :         GDALGetDriverByName("netCDF") != nullptr)
     497             :     {
     498           0 :         delete poDS;
     499           0 :         return nullptr;
     500             :     }
     501             : 
     502             :     // Safety belt if S102Dataset::Identify() failed
     503          74 :     if (STARTS_WITH(
     504             :             poDS->m_aosMetadata.FetchNameValueDef("productSpecification", ""),
     505          75 :             "INT.IHO.S-102.") &&
     506           1 :         GDALGetDriverByName("S102") != nullptr)
     507             :     {
     508           1 :         delete poDS;
     509           2 :         std::string osS102Filename("S102:\"");
     510             :         osS102Filename +=
     511           1 :             CPLString(poOpenInfo->pszFilename).replaceAll("\"", "\\\"");
     512           1 :         osS102Filename += '"';
     513           1 :         return GDALDataset::Open(osS102Filename.c_str(), GDAL_OF_RASTER);
     514             :     }
     515             : 
     516             :     // Safety belt if S104Dataset::Identify() failed
     517          73 :     if (STARTS_WITH(
     518             :             poDS->m_aosMetadata.FetchNameValueDef("productSpecification", ""),
     519          73 :             "INT.IHO.S-104.") &&
     520           0 :         GDALGetDriverByName("S104") != nullptr)
     521             :     {
     522           0 :         delete poDS;
     523           0 :         std::string osS104Filename("S104:\"");
     524             :         osS104Filename +=
     525           0 :             CPLString(poOpenInfo->pszFilename).replaceAll("\"", "\\\"");
     526           0 :         osS104Filename += '"';
     527           0 :         return GDALDataset::Open(osS104Filename.c_str(), GDAL_OF_RASTER);
     528             :     }
     529             : 
     530             :     // Safety belt if S111Dataset::Identify() failed
     531          73 :     if (STARTS_WITH(
     532             :             poDS->m_aosMetadata.FetchNameValueDef("productSpecification", ""),
     533          73 :             "INT.IHO.S-111.") &&
     534           0 :         GDALGetDriverByName("S111") != nullptr)
     535             :     {
     536           0 :         delete poDS;
     537           0 :         std::string osS111Filename("S111:\"");
     538             :         osS111Filename +=
     539           0 :             CPLString(poOpenInfo->pszFilename).replaceAll("\"", "\\\"");
     540           0 :         osS111Filename += '"';
     541           0 :         return GDALDataset::Open(osS111Filename.c_str(), GDAL_OF_RASTER);
     542             :     }
     543             : 
     544          73 :     poDS->SetMetadata(poDS->m_aosMetadata.List());
     545             : 
     546          73 :     if (CSLCount(poDS->papszSubDatasets) / 2 >= 1)
     547          71 :         poDS->SetMetadata(poDS->papszSubDatasets, "SUBDATASETS");
     548             : 
     549             :     // Make sure we don't try to do any pam stuff with this dataset.
     550          73 :     poDS->nPamFlags |= GPF_NOSAVE;
     551             : 
     552             :     // If we have single subdataset only, open it immediately.
     553          73 :     int nSubDatasets = CSLCount(poDS->papszSubDatasets) / 2;
     554          73 :     if (nSubDatasets == 1)
     555             :     {
     556             :         CPLString osDSName =
     557          66 :             CSLFetchNameValue(poDS->papszSubDatasets, "SUBDATASET_1_NAME");
     558          33 :         delete poDS;
     559          33 :         return GDALDataset::Open(osDSName, poOpenInfo->nOpenFlags, nullptr,
     560          66 :                                  poOpenInfo->papszOpenOptions, nullptr);
     561             :     }
     562             :     else
     563             :     {
     564             :         // Confirm the requested access is supported.
     565          40 :         if (poOpenInfo->eAccess == GA_Update)
     566             :         {
     567           0 :             delete poDS;
     568           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     569             :                      "The HDF5 driver does not support update access to "
     570             :                      "existing datasets.");
     571           0 :             return nullptr;
     572             :         }
     573             :     }
     574          40 :     return poDS;
     575             : }
     576             : 
     577             : /************************************************************************/
     578             : /*                          DestroyH5Objects()                          */
     579             : /*                                                                      */
     580             : /*      Erase all objects                                               */
     581             : /************************************************************************/
     582        2053 : void HDF5Dataset::DestroyH5Objects(HDF5GroupObjects *poH5Object)
     583             : {
     584             :     // Visit all objects.
     585        3969 :     for (unsigned i = 0; i < poH5Object->nbObjs; i++)
     586        1916 :         DestroyH5Objects(poH5Object->poHchild + i);
     587             : 
     588        2053 :     if (poH5Object->poHparent == nullptr)
     589         144 :         return;
     590             : 
     591             :     // Erase some data.
     592        1909 :     CPLFree(poH5Object->paDims);
     593        1909 :     poH5Object->paDims = nullptr;
     594             : 
     595        1909 :     CPLFree(poH5Object->pszPath);
     596        1909 :     poH5Object->pszPath = nullptr;
     597             : 
     598        1909 :     CPLFree(poH5Object->pszName);
     599        1909 :     poH5Object->pszName = nullptr;
     600             : 
     601        1909 :     CPLFree(poH5Object->pszUnderscorePath);
     602        1909 :     poH5Object->pszUnderscorePath = nullptr;
     603             : 
     604        1909 :     if (poH5Object->native > 0)
     605        1128 :         H5Tclose(poH5Object->native);
     606        1909 :     poH5Object->native = 0;
     607             : 
     608             :     // All Children are visited and can be deleted.
     609        1909 :     if (poH5Object->nbObjs != 0)
     610             :     {
     611         422 :         CPLFree(poH5Object->poHchild);
     612         422 :         poH5Object->poHchild = nullptr;
     613             :     }
     614             : }
     615             : 
     616             : /************************************************************************/
     617             : /*                             CreatePath()                             */
     618             : /*                                                                      */
     619             : /*      Find Dataset path for HDopen                                    */
     620             : /************************************************************************/
     621        6160 : static void CreatePath(HDF5GroupObjects *poH5Object)
     622             : {
     623             :     // Recurse to the root path.
     624       12320 :     CPLString osPath;
     625        6160 :     if (poH5Object->poHparent != nullptr)
     626             :     {
     627        4251 :         CreatePath(poH5Object->poHparent);
     628        4251 :         osPath = poH5Object->poHparent->pszPath;
     629             :     }
     630             : 
     631             :     // Add name to the path.
     632        6160 :     if (!EQUAL(poH5Object->pszName, "/"))
     633             :     {
     634        4251 :         osPath.append("/");
     635        4251 :         osPath.append(poH5Object->pszName);
     636             :     }
     637             : 
     638             :     // Fill up path for each object.
     639       12320 :     CPLString osUnderscoreSpaceInName;
     640        6160 :     if (poH5Object->pszPath == nullptr)
     641             :     {
     642             :         // This is completely useless but needed if we want to keep
     643             :         // subdataset names as they have "always" been formatted,
     644             :         // with double slash at the beginning
     645        2046 :         if (osPath.empty())
     646         137 :             osPath = "/";
     647             : 
     648             :         // Change space for underscore.
     649             :         char **papszPath =
     650        2046 :             CSLTokenizeString2(osPath.c_str(), " ", CSLT_HONOURSTRINGS);
     651             : 
     652        4431 :         for (int i = 0; papszPath[i] != nullptr; i++)
     653             :         {
     654        2385 :             if (i > 0)
     655         339 :                 osUnderscoreSpaceInName.append("_");
     656        2385 :             osUnderscoreSpaceInName.append(papszPath[i]);
     657             :         }
     658        2046 :         CSLDestroy(papszPath);
     659             : 
     660             :         // -1 to give room for NUL in C strings.
     661        2046 :         constexpr size_t MAX_PATH = 8192 - 1;
     662             :         // TODO(schwehr): Is it an issue if the results are longer than 8192?
     663             :         // It appears that the output can never be longer than the source.
     664        2046 :         if (osUnderscoreSpaceInName.size() > MAX_PATH)
     665           0 :             CPLError(CE_Fatal, CPLE_AppDefined,
     666             :                      "osUnderscoreSpaceInName longer than MAX_PATH: "
     667             :                      "%u > %u",
     668           0 :                      static_cast<unsigned int>(osUnderscoreSpaceInName.size()),
     669             :                      static_cast<unsigned int>(MAX_PATH));
     670        2046 :         if (osPath.size() > MAX_PATH)
     671           0 :             CPLError(CE_Fatal, CPLE_AppDefined,
     672             :                      "osPath longer than MAX_PATH: %u > %u",
     673           0 :                      static_cast<unsigned int>(osPath.size()),
     674             :                      static_cast<unsigned int>(MAX_PATH));
     675             : 
     676        2046 :         poH5Object->pszUnderscorePath =
     677        2046 :             CPLStrdup(osUnderscoreSpaceInName.c_str());
     678        2046 :         poH5Object->pszPath = CPLStrdup(osPath.c_str());
     679             :     }
     680        6160 : }
     681             : 
     682             : /************************************************************************/
     683             : /*                      HDF5GroupCheckDuplicate()                       */
     684             : /*                                                                      */
     685             : /*      Returns TRUE if an ancestor has the same objno[] as passed      */
     686             : /*      in - used to avoid looping in files with "links up" #(3218).    */
     687             : /************************************************************************/
     688             : 
     689        2394 : static int HDF5GroupCheckDuplicate(HDF5GroupObjects *poHparent,
     690             :                                    unsigned long *objno)
     691             : 
     692             : {
     693        2394 :     while (poHparent != nullptr)
     694             :     {
     695        1619 :         if (poHparent->objno[0] == objno[0] && poHparent->objno[1] == objno[1])
     696           2 :             return TRUE;
     697             : 
     698        1617 :         poHparent = poHparent->poHparent;
     699             :     }
     700             : 
     701         775 :     return FALSE;
     702             : }
     703             : 
     704             : /************************************************************************/
     705             : /*                      HDF5CreateGroupObjs()                           */
     706             : /*                                                                      */
     707             : /*      Create HDF5 hierarchy into a linked list                        */
     708             : /************************************************************************/
     709        1909 : herr_t HDF5CreateGroupObjs(hid_t hHDF5, const char *pszObjName,
     710             :                            void *poHObjParent)
     711             : {
     712        1909 :     HDF5GroupObjects *const poHparent =
     713             :         static_cast<HDF5GroupObjects *>(poHObjParent);
     714        1909 :     HDF5GroupObjects *poHchild = poHparent->poHchild;
     715             :     H5G_stat_t oStatbuf;
     716             : 
     717        1909 :     if (H5Gget_objinfo(hHDF5, pszObjName, FALSE, &oStatbuf) < 0)
     718           0 :         return -1;
     719             : 
     720             :     // Look for next child.
     721        1909 :     unsigned idx = 0;  // idx is used after the for loop.
     722        6112 :     for (; idx < poHparent->nbObjs; idx++)
     723             :     {
     724        6112 :         if (poHchild->pszName == nullptr)
     725        1909 :             break;
     726        4203 :         poHchild++;
     727             :     }
     728             : 
     729        1909 :     if (idx == poHparent->nbObjs)
     730           0 :         return -1;  // All children parsed.
     731             : 
     732             :     // Save child information.
     733        1909 :     poHchild->pszName = CPLStrdup(pszObjName);
     734             : 
     735        1909 :     poHchild->nType = oStatbuf.type;
     736        1909 :     poHchild->nIndex = idx;
     737        1909 :     poHchild->poHparent = poHparent;
     738        1909 :     poHchild->nRank = 0;
     739        1909 :     poHchild->paDims = nullptr;
     740        1909 :     poHchild->HDatatype = 0;
     741        1909 :     poHchild->objno[0] = oStatbuf.objno[0];
     742        1909 :     poHchild->objno[1] = oStatbuf.objno[1];
     743        1909 :     if (poHchild->pszPath == nullptr)
     744             :     {
     745        1909 :         CreatePath(poHchild);
     746             :     }
     747        1909 :     if (poHparent->pszPath == nullptr)
     748             :     {
     749           0 :         CreatePath(poHparent);
     750             :     }
     751             : 
     752        1909 :     switch (oStatbuf.type)
     753             :     {
     754           3 :         case H5G_LINK:
     755             :         {
     756           3 :             poHchild->nbAttrs = 0;
     757           3 :             poHchild->nbObjs = 0;
     758           3 :             poHchild->poHchild = nullptr;
     759           3 :             poHchild->nRank = 0;
     760           3 :             poHchild->paDims = nullptr;
     761           3 :             poHchild->HDatatype = 0;
     762           3 :             break;
     763             :         }
     764         777 :         case H5G_GROUP:
     765             :         {
     766         777 :             hid_t hGroupID = H5I_INVALID_HID;  // Identifier of group.
     767         777 :             if ((hGroupID = H5Gopen(hHDF5, pszObjName)) == -1)
     768             :             {
     769           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     770             :                          "unable to access \"%s\" group.", pszObjName);
     771           0 :                 return -1;
     772             :             }
     773             :             // Number of attributes in object.
     774         777 :             const int nbAttrs = H5Aget_num_attrs(hGroupID);
     775         777 :             hsize_t nbObjs = 0;  // Number of objects in a group.
     776         777 :             H5Gget_num_objs(hGroupID, &nbObjs);
     777         777 :             poHchild->nbAttrs = nbAttrs;
     778         777 :             poHchild->nbObjs = static_cast<int>(nbObjs);
     779         777 :             poHchild->nRank = 0;
     780         777 :             poHchild->paDims = nullptr;
     781         777 :             poHchild->HDatatype = 0;
     782             : 
     783         777 :             if (nbObjs > 0)
     784             :             {
     785         422 :                 poHchild->poHchild = static_cast<HDF5GroupObjects *>(CPLCalloc(
     786             :                     static_cast<int>(nbObjs), sizeof(HDF5GroupObjects)));
     787         422 :                 memset(poHchild->poHchild, 0,
     788         422 :                        static_cast<size_t>(sizeof(HDF5GroupObjects) * nbObjs));
     789             :             }
     790             :             else
     791             :             {
     792         355 :                 poHchild->poHchild = nullptr;
     793             :             }
     794             : 
     795         777 :             if (!HDF5GroupCheckDuplicate(poHparent, oStatbuf.objno))
     796         775 :                 H5Giterate(hHDF5, pszObjName, nullptr, HDF5CreateGroupObjs,
     797             :                            poHchild);
     798             :             else
     799           2 :                 CPLDebug("HDF5", "avoiding link looping on node '%s'.",
     800             :                          pszObjName);
     801             : 
     802         777 :             H5Gclose(hGroupID);
     803         777 :             break;
     804             :         }
     805        1128 :         case H5G_DATASET:
     806             :         {
     807        1128 :             hid_t hDatasetID = H5I_INVALID_HID;  // Identifier of dataset.
     808        1128 :             if ((hDatasetID = H5Dopen(hHDF5, pszObjName)) == -1)
     809             :             {
     810           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     811             :                          "unable to access \"%s\" dataset.", pszObjName);
     812           0 :                 return -1;
     813             :             }
     814        1128 :             const int nbAttrs = H5Aget_num_attrs(hDatasetID);
     815        1128 :             const hid_t datatype = H5Dget_type(hDatasetID);
     816        1128 :             const hid_t dataspace = H5Dget_space(hDatasetID);
     817        1128 :             const int n_dims = H5Sget_simple_extent_ndims(dataspace);
     818        1128 :             const hid_t native = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
     819        1128 :             hsize_t *maxdims = nullptr;
     820        1128 :             hsize_t *dims = nullptr;
     821             : 
     822        1128 :             if (n_dims > 0)
     823             :             {
     824             :                 dims =
     825        1086 :                     static_cast<hsize_t *>(CPLCalloc(n_dims, sizeof(hsize_t)));
     826             :                 maxdims =
     827        1086 :                     static_cast<hsize_t *>(CPLCalloc(n_dims, sizeof(hsize_t)));
     828             :             }
     829        1128 :             H5Sget_simple_extent_dims(dataspace, dims, maxdims);
     830        1128 :             if (maxdims != nullptr)
     831        1086 :                 CPLFree(maxdims);
     832             : 
     833        1128 :             if (n_dims > 0)
     834             :             {
     835        1086 :                 poHchild->nRank = n_dims;        // rank of the array
     836        1086 :                 poHchild->paDims = dims;         // dimension of the array.
     837        1086 :                 poHchild->HDatatype = datatype;  // HDF5 datatype
     838             :             }
     839             :             else
     840             :             {
     841          42 :                 poHchild->nRank = -1;
     842          42 :                 poHchild->paDims = nullptr;
     843          42 :                 poHchild->HDatatype = 0;
     844             :             }
     845        1128 :             poHchild->nbAttrs = nbAttrs;
     846        1128 :             poHchild->nbObjs = 0;
     847        1128 :             poHchild->poHchild = nullptr;
     848        1128 :             poHchild->native = native;
     849        1128 :             H5Tclose(datatype);
     850        1128 :             H5Sclose(dataspace);
     851        1128 :             H5Dclose(hDatasetID);
     852        1128 :             break;
     853             :         }
     854           0 :         case H5G_TYPE:
     855             :         {
     856           0 :             poHchild->nbAttrs = 0;
     857           0 :             poHchild->nbObjs = 0;
     858           0 :             poHchild->poHchild = nullptr;
     859           0 :             poHchild->nRank = 0;
     860           0 :             poHchild->paDims = nullptr;
     861           0 :             poHchild->HDatatype = 0;
     862           0 :             break;
     863             :         }
     864           1 :         default:
     865           1 :             break;
     866             :     }
     867             : 
     868        1909 :     return 0;
     869             : }
     870             : 
     871             : /************************************************************************/
     872             : /*                     HDF5DatasetCreateMetadataContext                 */
     873             : /************************************************************************/
     874             : 
     875             : struct HDF5DatasetCreateMetadataContext
     876             : {
     877             :     std::string m_osKey{};
     878             :     CPLStringList &m_aosMetadata;
     879             : 
     880             :     // Work variables
     881             :     std::string m_osValue{};
     882             : 
     883        1587 :     explicit HDF5DatasetCreateMetadataContext(CPLStringList &aosMetadata)
     884        1587 :         : m_aosMetadata(aosMetadata)
     885             :     {
     886        1587 :     }
     887             : };
     888             : 
     889             : /************************************************************************/
     890             : /*                          HDF5AttrIterate()                           */
     891             : /************************************************************************/
     892             : 
     893        1389 : static herr_t HDF5AttrIterate(hid_t hH5ObjID, const char *pszAttrName,
     894             :                               void *pContext)
     895             : {
     896        1389 :     HDF5DatasetCreateMetadataContext *const psContext =
     897             :         static_cast<HDF5DatasetCreateMetadataContext *>(pContext);
     898             : 
     899        1389 :     psContext->m_osValue.clear();
     900             : 
     901        2778 :     std::string osKey(psContext->m_osKey);
     902             :     // Convert whitespaces into "_" for the attribute name component
     903             :     const CPLStringList aosTokens(CSLTokenizeString2(
     904        2778 :         pszAttrName, " ", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
     905        3521 :     for (int i = 0; i < aosTokens.size(); ++i)
     906             :     {
     907        2132 :         if (!osKey.empty())
     908        1715 :             osKey += '_';
     909        2132 :         osKey += aosTokens[i];
     910             :     }
     911             : 
     912        1389 :     const hid_t hAttrID = H5Aopen_name(hH5ObjID, pszAttrName);
     913        1389 :     const hid_t hAttrTypeID = H5Aget_type(hAttrID);
     914             :     const hid_t hAttrNativeType =
     915        1389 :         H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
     916        1389 :     const hid_t hAttrSpace = H5Aget_space(hAttrID);
     917             : 
     918        1389 :     if (H5Tget_class(hAttrNativeType) == H5T_VLEN)
     919             :     {
     920          40 :         H5Sclose(hAttrSpace);
     921          40 :         H5Tclose(hAttrNativeType);
     922          40 :         H5Tclose(hAttrTypeID);
     923          40 :         H5Aclose(hAttrID);
     924          40 :         return 0;
     925             :     }
     926             : 
     927        1349 :     hsize_t nSize[64] = {};
     928             :     const unsigned int nAttrDims =
     929        1349 :         H5Sget_simple_extent_dims(hAttrSpace, nSize, nullptr);
     930             : 
     931        1349 :     unsigned int nAttrElmts = 1;
     932        1977 :     for (hsize_t i = 0; i < nAttrDims; i++)
     933             :     {
     934         628 :         nAttrElmts *= static_cast<int>(nSize[i]);
     935             :     }
     936             : 
     937        1349 :     if (H5Tget_class(hAttrNativeType) == H5T_STRING)
     938             :     {
     939         665 :         if (H5Tis_variable_str(hAttrNativeType))
     940             :         {
     941             :             char **papszStrings =
     942          46 :                 static_cast<char **>(CPLMalloc(nAttrElmts * sizeof(char *)));
     943             : 
     944             :             // Read the values.
     945          46 :             H5Aread(hAttrID, hAttrNativeType, papszStrings);
     946             : 
     947             :             // Concatenate all values as one string separated by a space.
     948          46 :             psContext->m_osValue = papszStrings[0] ? papszStrings[0] : "{NULL}";
     949         128 :             for (hsize_t i = 1; i < nAttrElmts; i++)
     950             :             {
     951          82 :                 psContext->m_osValue += " ";
     952             :                 psContext->m_osValue +=
     953          82 :                     papszStrings[i] ? papszStrings[i] : "{NULL}";
     954             :             }
     955             : 
     956          46 :             H5Dvlen_reclaim(hAttrNativeType, hAttrSpace, H5P_DEFAULT,
     957             :                             papszStrings);
     958          46 :             CPLFree(papszStrings);
     959             :         }
     960             :         else
     961             :         {
     962         619 :             const hsize_t nAttrSize = H5Aget_storage_size(hAttrID);
     963         619 :             psContext->m_osValue.resize(static_cast<size_t>(nAttrSize));
     964         619 :             H5Aread(hAttrID, hAttrNativeType, &psContext->m_osValue[0]);
     965             :         }
     966             :     }
     967             :     else
     968             :     {
     969         684 :         constexpr size_t nDataLen = 32;
     970             :         char szData[nDataLen];
     971             : 
     972         684 :         void *buf = nullptr;
     973             : 
     974         684 :         if (nAttrElmts > 0)
     975             :         {
     976         684 :             buf = CPLMalloc(nAttrElmts * H5Tget_size(hAttrNativeType));
     977         684 :             H5Aread(hAttrID, hAttrNativeType, buf);
     978             :         }
     979         684 :         const bool bIsSCHAR = H5Tequal(H5T_NATIVE_SCHAR, hAttrNativeType) > 0;
     980         684 :         const bool bIsUCHAR = H5Tequal(H5T_NATIVE_UCHAR, hAttrNativeType) > 0;
     981         692 :         if ((bIsSCHAR || bIsUCHAR) &&
     982           8 :             CPLTestBool(CPLGetConfigOption("GDAL_HDF5_CHAR_AS_STRING", "NO")))
     983             :         {
     984             :             // Compatibility mode with ancient GDAL versions where we consider
     985             :             // array of SCHAR/UCHAR as strings. Likely inappropriate mode...
     986           0 :             for (hsize_t i = 0; i < nAttrElmts; i++)
     987             :             {
     988           0 :                 snprintf(szData, nDataLen, "%c", static_cast<char *>(buf)[i]);
     989           0 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
     990             :                 {
     991           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
     992             :                              "Header data too long. Truncated");
     993           0 :                     break;
     994             :                 }
     995           0 :                 psContext->m_osValue += szData;
     996             :             }
     997             :         }
     998         684 :         else if (bIsSCHAR)
     999             :         {
    1000           4 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1001             :             {
    1002           2 :                 snprintf(szData, nDataLen, "%d",
    1003           2 :                          static_cast<signed char *>(buf)[i]);
    1004           2 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1005             :                 {
    1006           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1007             :                              "Header data too long. Truncated");
    1008           0 :                     break;
    1009             :                 }
    1010           2 :                 if (i > 0)
    1011           0 :                     psContext->m_osValue += ' ';
    1012           2 :                 psContext->m_osValue += szData;
    1013             :             }
    1014             :         }
    1015         682 :         else if (bIsUCHAR)
    1016             :         {
    1017          12 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1018             :             {
    1019           6 :                 snprintf(szData, nDataLen, "%u",
    1020           6 :                          static_cast<unsigned char *>(buf)[i]);
    1021           6 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1022             :                 {
    1023           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1024             :                              "Header data too long. Truncated");
    1025           0 :                     break;
    1026             :                 }
    1027           6 :                 if (i > 0)
    1028           0 :                     psContext->m_osValue += ' ';
    1029           6 :                 psContext->m_osValue += szData;
    1030             :             }
    1031             :         }
    1032         676 :         else if (H5Tequal(H5T_NATIVE_SHORT, hAttrNativeType) > 0)
    1033             :         {
    1034          38 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1035             :             {
    1036          19 :                 snprintf(szData, nDataLen, "%d", static_cast<short *>(buf)[i]);
    1037          19 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1038             :                 {
    1039           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1040             :                              "Header data too long. Truncated");
    1041           0 :                     break;
    1042             :                 }
    1043          19 :                 if (i > 0)
    1044           0 :                     psContext->m_osValue += ' ';
    1045          19 :                 psContext->m_osValue += szData;
    1046             :             }
    1047             :         }
    1048         657 :         else if (H5Tequal(H5T_NATIVE_USHORT, hAttrNativeType) > 0)
    1049             :         {
    1050         168 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1051             :             {
    1052          84 :                 snprintf(szData, nDataLen, "%u",
    1053          84 :                          static_cast<unsigned short *>(buf)[i]);
    1054          84 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1055             :                 {
    1056           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1057             :                              "Header data too long. Truncated");
    1058           0 :                     break;
    1059             :                 }
    1060          84 :                 if (i > 0)
    1061           0 :                     psContext->m_osValue += ' ';
    1062          84 :                 psContext->m_osValue += szData;
    1063             :             }
    1064             :         }
    1065         573 :         else if (H5Tequal(H5T_NATIVE_INT, hAttrNativeType) > 0)
    1066             :         {
    1067         348 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1068             :             {
    1069         188 :                 snprintf(szData, nDataLen, "%d", static_cast<int *>(buf)[i]);
    1070         188 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1071             :                 {
    1072           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1073             :                              "Header data too long. Truncated");
    1074           0 :                     break;
    1075             :                 }
    1076         188 :                 if (i > 0)
    1077          28 :                     psContext->m_osValue += ' ';
    1078         188 :                 psContext->m_osValue += szData;
    1079             :             }
    1080             :         }
    1081         413 :         else if (H5Tequal(H5T_NATIVE_UINT, hAttrNativeType) > 0)
    1082             :         {
    1083         186 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1084             :             {
    1085          93 :                 snprintf(szData, nDataLen, "%u",
    1086          93 :                          static_cast<unsigned int *>(buf)[i]);
    1087          93 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1088             :                 {
    1089           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1090             :                              "Header data too long. Truncated");
    1091           0 :                     break;
    1092             :                 }
    1093          93 :                 if (i > 0)
    1094           0 :                     psContext->m_osValue += ' ';
    1095          93 :                 psContext->m_osValue += szData;
    1096             :             }
    1097             :         }
    1098         320 :         else if (H5Tequal(H5T_NATIVE_INT64, hAttrNativeType) > 0)
    1099             :         {
    1100          28 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1101             :             {
    1102          17 :                 snprintf(szData, nDataLen, CPL_FRMT_GIB,
    1103          17 :                          static_cast<GIntBig *>(buf)[i]);
    1104          17 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1105             :                 {
    1106           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1107             :                              "Header data too long. Truncated");
    1108           0 :                     break;
    1109             :                 }
    1110          17 :                 if (i > 0)
    1111           6 :                     psContext->m_osValue += ' ';
    1112          17 :                 psContext->m_osValue += szData;
    1113             :             }
    1114             :         }
    1115         309 :         else if (H5Tequal(H5T_NATIVE_UINT64, hAttrNativeType) > 0)
    1116             :         {
    1117          10 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1118             :             {
    1119           5 :                 snprintf(szData, nDataLen, CPL_FRMT_GUIB,
    1120           5 :                          static_cast<GUIntBig *>(buf)[i]);
    1121           5 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1122             :                 {
    1123           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1124             :                              "Header data too long. Truncated");
    1125           0 :                     break;
    1126             :                 }
    1127           5 :                 if (i > 0)
    1128           0 :                     psContext->m_osValue += ' ';
    1129           5 :                 psContext->m_osValue += szData;
    1130             :             }
    1131             :         }
    1132         304 :         else if (H5Tequal(H5T_NATIVE_LONG, hAttrNativeType) > 0)
    1133             :         {
    1134           0 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1135             :             {
    1136           0 :                 snprintf(szData, nDataLen, "%ld", static_cast<long *>(buf)[i]);
    1137           0 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1138             :                 {
    1139           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1140             :                              "Header data too long. Truncated");
    1141           0 :                     break;
    1142             :                 }
    1143           0 :                 if (i > 0)
    1144           0 :                     psContext->m_osValue += ' ';
    1145           0 :                 psContext->m_osValue += szData;
    1146             :             }
    1147             :         }
    1148         304 :         else if (H5Tequal(H5T_NATIVE_ULONG, hAttrNativeType) > 0)
    1149             :         {
    1150           0 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1151             :             {
    1152           0 :                 snprintf(szData, nDataLen, "%lu",
    1153           0 :                          static_cast<unsigned long *>(buf)[i]);
    1154           0 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1155             :                 {
    1156           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1157             :                              "Header data too long. Truncated");
    1158           0 :                     break;
    1159             :                 }
    1160           0 :                 if (i > 0)
    1161           0 :                     psContext->m_osValue += ' ';
    1162           0 :                 psContext->m_osValue += szData;
    1163             :             }
    1164             :         }
    1165             : #ifdef HDF5_HAVE_FLOAT16
    1166             :         else if (H5Tequal(H5T_NATIVE_FLOAT16, hAttrNativeType) > 0)
    1167             :         {
    1168             :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1169             :             {
    1170             :                 const uint16_t nVal16 = static_cast<uint16_t *>(buf)[i];
    1171             :                 const uint32_t nVal32 = CPLHalfToFloat(nVal16);
    1172             :                 float fVal;
    1173             :                 memcpy(&fVal, &nVal32, sizeof(fVal));
    1174             :                 CPLsnprintf(szData, nDataLen, "%.8g", fVal);
    1175             :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1176             :                 {
    1177             :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1178             :                              "Header data too long. Truncated");
    1179             :                     break;
    1180             :                 }
    1181             :                 if (i > 0)
    1182             :                     psContext->m_osValue += ' ';
    1183             :                 psContext->m_osValue += szData;
    1184             :             }
    1185             :         }
    1186             : #endif
    1187         304 :         else if (H5Tequal(H5T_NATIVE_FLOAT, hAttrNativeType) > 0)
    1188             :         {
    1189         217 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1190             :             {
    1191         111 :                 CPLsnprintf(szData, nDataLen, "%.8g",
    1192         111 :                             static_cast<float *>(buf)[i]);
    1193         111 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1194             :                 {
    1195           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1196             :                              "Header data too long. Truncated");
    1197           0 :                     break;
    1198             :                 }
    1199         111 :                 if (i > 0)
    1200           5 :                     psContext->m_osValue += ' ';
    1201         111 :                 psContext->m_osValue += szData;
    1202             :             }
    1203             :         }
    1204         198 :         else if (H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType) > 0)
    1205             :         {
    1206         482 :             for (hsize_t i = 0; i < nAttrElmts; i++)
    1207             :             {
    1208         316 :                 CPLsnprintf(szData, nDataLen, "%.15g",
    1209         316 :                             static_cast<double *>(buf)[i]);
    1210         316 :                 if (psContext->m_osValue.size() > MAX_METADATA_LEN)
    1211             :                 {
    1212           0 :                     CPLError(CE_Warning, CPLE_OutOfMemory,
    1213             :                              "Header data too long. Truncated");
    1214           0 :                     break;
    1215             :                 }
    1216         316 :                 if (i > 0)
    1217         150 :                     psContext->m_osValue += ' ';
    1218         316 :                 psContext->m_osValue += szData;
    1219             :             }
    1220             :         }
    1221         684 :         CPLFree(buf);
    1222             :     }
    1223        1349 :     H5Sclose(hAttrSpace);
    1224        1349 :     H5Tclose(hAttrNativeType);
    1225        1349 :     H5Tclose(hAttrTypeID);
    1226        1349 :     H5Aclose(hAttrID);
    1227        1349 :     psContext->m_aosMetadata.SetNameValue(osKey.c_str(),
    1228        1349 :                                           psContext->m_osValue.c_str());
    1229             : 
    1230        1349 :     return 0;
    1231             : }
    1232             : 
    1233             : /************************************************************************/
    1234             : /*                           CreateMetadata()                           */
    1235             : /************************************************************************/
    1236        1594 : CPLErr HDF5Dataset::CreateMetadata(hid_t hHDF5, HDF5GroupObjects *poH5Object,
    1237             :                                    int nType, bool bPrefixWithDatasetName,
    1238             :                                    CPLStringList &aosMetadata)
    1239             : {
    1240             : 
    1241        1594 :     if (!poH5Object->pszPath)
    1242           7 :         return CE_None;
    1243             : 
    1244        1587 :     if (EQUAL(poH5Object->pszPath, ""))
    1245           0 :         return CE_None;
    1246             : 
    1247        1587 :     const int nbAttrs = poH5Object->nbAttrs;
    1248             : 
    1249        1587 :     HDF5DatasetCreateMetadataContext sContext(aosMetadata);
    1250             : 
    1251        1587 :     if (bPrefixWithDatasetName)
    1252             :     {
    1253             :         // Convert "/" into "_" for the path component
    1254        1524 :         const char *pszPath = poH5Object->pszUnderscorePath;
    1255        1524 :         if (pszPath != nullptr && strlen(pszPath) > 0)
    1256             :         {
    1257             :             const CPLStringList aosTokens(
    1258        3048 :                 CSLTokenizeString2(pszPath, "/", CSLT_HONOURSTRINGS));
    1259        4504 :             for (int i = 0; i < aosTokens.size(); ++i)
    1260             :             {
    1261        2980 :                 if (i != 0)
    1262        1593 :                     sContext.m_osKey += '_';
    1263        2980 :                 sContext.m_osKey += aosTokens[i];
    1264             :             }
    1265             :         }
    1266             :     }
    1267             : 
    1268        1587 :     switch (nType)
    1269             :     {
    1270         914 :         case H5G_GROUP:
    1271         914 :             if (nbAttrs > 0)
    1272             :             {
    1273             :                 // Identifier of group.
    1274          90 :                 const hid_t l_hGroupID = H5Gopen(hHDF5, poH5Object->pszPath);
    1275          90 :                 H5Aiterate(l_hGroupID, nullptr, HDF5AttrIterate, &sContext);
    1276          90 :                 H5Gclose(l_hGroupID);
    1277             :             }
    1278         914 :             break;
    1279         673 :         case H5G_DATASET:
    1280         673 :             if (nbAttrs > 0)
    1281             :             {
    1282         230 :                 const hid_t hDatasetID = H5Dopen(hHDF5, poH5Object->pszPath);
    1283         230 :                 H5Aiterate(hDatasetID, nullptr, HDF5AttrIterate, &sContext);
    1284         230 :                 H5Dclose(hDatasetID);
    1285             :             }
    1286         673 :             break;
    1287             : 
    1288           0 :         default:
    1289           0 :             break;
    1290             :     }
    1291             : 
    1292        1587 :     return CE_None;
    1293             : }
    1294             : 
    1295             : /************************************************************************/
    1296             : /*                       HDF5FindDatasetObjectsbyPath()                 */
    1297             : /*      Find object by name                                             */
    1298             : /************************************************************************/
    1299             : HDF5GroupObjects *
    1300         376 : HDF5Dataset::HDF5FindDatasetObjectsbyPath(HDF5GroupObjects *poH5Objects,
    1301             :                                           const char *pszDatasetPath)
    1302             : {
    1303         376 :     if (poH5Objects->nType == H5G_DATASET &&
    1304         135 :         EQUAL(poH5Objects->pszUnderscorePath, pszDatasetPath))
    1305             :     {
    1306             : 
    1307             : #ifdef DEBUG_VERBOSE
    1308             :         printf("found it! %p\n", poH5Objects); /*ok*/
    1309             : #endif
    1310          63 :         return poH5Objects;
    1311             :     }
    1312             : 
    1313         313 :     HDF5Dataset *const poDS = this;
    1314             : 
    1315         313 :     if (poH5Objects->nbObjs > 0)
    1316             :     {
    1317         360 :         for (unsigned int i = 0; i < poH5Objects->nbObjs; i++)
    1318             :         {
    1319             :             HDF5GroupObjects *poObjectsFound =
    1320         313 :                 poDS->HDF5FindDatasetObjectsbyPath(poH5Objects->poHchild + i,
    1321             :                                                    pszDatasetPath);
    1322             :             // Is this our dataset?
    1323         313 :             if (poObjectsFound != nullptr)
    1324         149 :                 return poObjectsFound;
    1325             :         }
    1326             :     }
    1327             :     // Dataset has not been found.
    1328         164 :     return nullptr;
    1329             : }
    1330             : 
    1331             : /************************************************************************/
    1332             : /*                       HDF5FindDatasetObjects()                       */
    1333             : /*      Find object by name                                             */
    1334             : /************************************************************************/
    1335             : HDF5GroupObjects *
    1336         302 : HDF5Dataset::HDF5FindDatasetObjects(HDF5GroupObjects *poH5Objects,
    1337             :                                     const char *pszDatasetName)
    1338             : {
    1339         302 :     if (poH5Objects->nType == H5G_DATASET &&
    1340         183 :         EQUAL(poH5Objects->pszName, pszDatasetName))
    1341             :     {
    1342             : 
    1343             : #ifdef DEBUG_VERBOSE
    1344             :         printf("found it! %p\n", poH5Objects); /*ok*/
    1345             : #endif
    1346           0 :         return poH5Objects;
    1347             :     }
    1348             : 
    1349         302 :     HDF5Dataset *poDS = this;
    1350             : 
    1351         302 :     if (poH5Objects->nbObjs > 0)
    1352             :     {
    1353         342 :         for (unsigned int i = 0; i < poH5Objects->nbObjs; i++)
    1354             :         {
    1355         568 :             HDF5GroupObjects *poObjectsFound = poDS->HDF5FindDatasetObjects(
    1356         284 :                 poH5Objects->poHchild + i, pszDatasetName);
    1357             :             // Is this our dataset?
    1358         284 :             if (poObjectsFound != nullptr)
    1359           0 :                 return poObjectsFound;
    1360             :         }
    1361             :     }
    1362             : 
    1363             :     // Dataset has not been found.
    1364         302 :     return nullptr;
    1365             : }
    1366             : 
    1367             : /************************************************************************/
    1368             : /*                        HDF5ListGroupObjects()                        */
    1369             : /*                                                                      */
    1370             : /*      List all objects in HDF5                                        */
    1371             : /************************************************************************/
    1372        2053 : CPLErr HDF5Dataset::HDF5ListGroupObjects(HDF5GroupObjects *poRootGroup,
    1373             :                                          int bSUBDATASET)
    1374             : {
    1375        2053 :     HDF5Dataset *poDS = this;
    1376             : 
    1377        2053 :     if (poRootGroup->nbObjs > 0)
    1378        2475 :         for (hsize_t i = 0; i < poRootGroup->nbObjs; i++)
    1379             :         {
    1380        1916 :             poDS->HDF5ListGroupObjects(poRootGroup->poHchild + i, bSUBDATASET);
    1381             :         }
    1382             : 
    1383        2053 :     if (poRootGroup->nType == H5G_GROUP)
    1384             :     {
    1385         921 :         CreateMetadata(m_hHDF5, poRootGroup, H5G_GROUP, true, m_aosMetadata);
    1386             :     }
    1387             : 
    1388             :     // Create Sub dataset list.
    1389             : 
    1390        2830 :     if (poRootGroup->nType == H5G_DATASET && bSUBDATASET &&
    1391         777 :         poDS->GetDataType(poRootGroup->native) == GDT_Unknown)
    1392             :     {
    1393         167 :         if (!EQUAL(poRootGroup->pszUnderscorePath,
    1394             :                    "//HDFEOS_INFORMATION/StructMetadata.0"))
    1395             :         {
    1396         161 :             CPLDebug("HDF5", "Skipping unsupported %s of type %s",
    1397             :                      poRootGroup->pszUnderscorePath,
    1398             :                      poDS->GetDataTypeName(poRootGroup->native));
    1399             :         }
    1400             :     }
    1401        1886 :     else if (poRootGroup->nType == H5G_DATASET && bSUBDATASET)
    1402             :     {
    1403         610 :         CreateMetadata(m_hHDF5, poRootGroup, H5G_DATASET, true, m_aosMetadata);
    1404             : 
    1405         610 :         CPLString osStr;
    1406         610 :         switch (poRootGroup->nRank)
    1407             :         {
    1408         198 :             case 2:
    1409         198 :                 osStr.Printf("%dx%d", static_cast<int>(poRootGroup->paDims[0]),
    1410         198 :                              static_cast<int>(poRootGroup->paDims[1]));
    1411         198 :                 break;
    1412           6 :             case 3:
    1413             :                 osStr.Printf("%dx%dx%d",
    1414           6 :                              static_cast<int>(poRootGroup->paDims[0]),
    1415           6 :                              static_cast<int>(poRootGroup->paDims[1]),
    1416           6 :                              static_cast<int>(poRootGroup->paDims[2]));
    1417           6 :                 break;
    1418         406 :             default:
    1419         406 :                 return CE_None;
    1420             :         }
    1421             : 
    1422         408 :         HDF5EOSParser::GridMetadata oGridMetadata;
    1423         408 :         HDF5EOSParser::SwathDataFieldMetadata oSwathDataFieldMetadata;
    1424         418 :         if (m_oHDFEOSParser.GetDataModel() == HDF5EOSParser::DataModel::GRID &&
    1425         209 :             m_oHDFEOSParser.GetGridMetadata(poRootGroup->pszUnderscorePath,
    1426         209 :                                             oGridMetadata) &&
    1427           0 :             static_cast<int>(oGridMetadata.aoDimensions.size()) ==
    1428           0 :                 poRootGroup->nRank)
    1429             :         {
    1430           0 :             int nXDimSize = 0;
    1431           0 :             int nYDimSize = 0;
    1432           0 :             int nOtherDimSize = 0;
    1433           0 :             std::string osOtherDimName;
    1434           0 :             for (const auto &oDim : oGridMetadata.aoDimensions)
    1435             :             {
    1436           0 :                 if (oDim.osName == "XDim")
    1437           0 :                     nXDimSize = oDim.nSize;
    1438           0 :                 else if (oDim.osName == "YDim")
    1439           0 :                     nYDimSize = oDim.nSize;
    1440             :                 else
    1441             :                 {
    1442           0 :                     osOtherDimName = oDim.osName;
    1443           0 :                     nOtherDimSize = oDim.nSize;
    1444             :                 }
    1445             :             }
    1446           0 :             switch (poRootGroup->nRank)
    1447             :             {
    1448           0 :                 case 2:
    1449           0 :                     osStr.Printf("(y=%d)x(x=%d)", nYDimSize, nXDimSize);
    1450           0 :                     break;
    1451           0 :                 case 3:
    1452             :                 {
    1453           0 :                     if (osOtherDimName == oGridMetadata.aoDimensions[0].osName)
    1454             :                         osStr.Printf("(%s=%d)x(y=%d)x(x=%d)",
    1455             :                                      osOtherDimName.c_str(), nOtherDimSize,
    1456           0 :                                      nYDimSize, nXDimSize);
    1457             :                     else
    1458             :                         osStr.Printf("(y=%d)x(x=%d)x(%s=%d)", nYDimSize,
    1459             :                                      nXDimSize, osOtherDimName.c_str(),
    1460           0 :                                      nOtherDimSize);
    1461           0 :                     break;
    1462             :                 }
    1463           0 :                 default:
    1464           0 :                     break;
    1465             :             }
    1466             :         }
    1467         204 :         else if (m_oHDFEOSParser.GetDataModel() ==
    1468           3 :                      HDF5EOSParser::DataModel::SWATH &&
    1469           3 :                  m_oHDFEOSParser.GetSwathDataFieldMetadata(
    1470           3 :                      poRootGroup->pszUnderscorePath, oSwathDataFieldMetadata) &&
    1471             :                  static_cast<int>(
    1472           1 :                      oSwathDataFieldMetadata.aoDimensions.size()) ==
    1473           1 :                      poRootGroup->nRank &&
    1474         208 :                  oSwathDataFieldMetadata.iXDim >= 0 &&
    1475           1 :                  oSwathDataFieldMetadata.iYDim >= 0)
    1476             :         {
    1477             :             const std::string &osXDimName =
    1478             :                 oSwathDataFieldMetadata
    1479           1 :                     .aoDimensions[oSwathDataFieldMetadata.iXDim]
    1480           1 :                     .osName;
    1481             :             const int nXDimSize =
    1482             :                 oSwathDataFieldMetadata
    1483           1 :                     .aoDimensions[oSwathDataFieldMetadata.iXDim]
    1484           1 :                     .nSize;
    1485             :             const std::string &osYDimName =
    1486             :                 oSwathDataFieldMetadata
    1487           1 :                     .aoDimensions[oSwathDataFieldMetadata.iYDim]
    1488           1 :                     .osName;
    1489             :             const int nYDimSize =
    1490             :                 oSwathDataFieldMetadata
    1491           1 :                     .aoDimensions[oSwathDataFieldMetadata.iYDim]
    1492           1 :                     .nSize;
    1493           1 :             switch (poRootGroup->nRank)
    1494             :             {
    1495           0 :                 case 2:
    1496             :                     osStr.Printf("(%s=%d)x(%s=%d)", osYDimName.c_str(),
    1497           0 :                                  nYDimSize, osXDimName.c_str(), nXDimSize);
    1498           0 :                     break;
    1499           1 :                 case 3:
    1500             :                 {
    1501             :                     const std::string &osOtherDimName =
    1502             :                         oSwathDataFieldMetadata
    1503           1 :                             .aoDimensions[oSwathDataFieldMetadata.iOtherDim]
    1504           1 :                             .osName;
    1505             :                     const int nOtherDimSize =
    1506             :                         oSwathDataFieldMetadata
    1507           1 :                             .aoDimensions[oSwathDataFieldMetadata.iOtherDim]
    1508           1 :                             .nSize;
    1509           1 :                     if (oSwathDataFieldMetadata.iOtherDim == 0)
    1510             :                     {
    1511             :                         osStr.Printf("(%s=%d)x(%s=%d)x(%s=%d)",
    1512             :                                      osOtherDimName.c_str(), nOtherDimSize,
    1513             :                                      osYDimName.c_str(), nYDimSize,
    1514           1 :                                      osXDimName.c_str(), nXDimSize);
    1515             :                     }
    1516             :                     else
    1517             :                     {
    1518             :                         osStr.Printf("(%s=%d)x(%s=%d)x(%s=%d)",
    1519             :                                      osYDimName.c_str(), nYDimSize,
    1520             :                                      osXDimName.c_str(), nXDimSize,
    1521           0 :                                      osOtherDimName.c_str(), nOtherDimSize);
    1522             :                     }
    1523           1 :                     break;
    1524             :                 }
    1525           0 :                 default:
    1526           0 :                     break;
    1527             :             }
    1528             :         }
    1529             : 
    1530         204 :         const std::string osDim = osStr;
    1531             : 
    1532         204 :         osStr.Printf("SUBDATASET_%d_NAME", ++(poDS->nSubDataCount));
    1533             : 
    1534         204 :         poDS->papszSubDatasets =
    1535         204 :             CSLSetNameValue(poDS->papszSubDatasets, osStr.c_str(),
    1536         204 :                             CPLSPrintf("HDF5:\"%s\":%s", poDS->GetDescription(),
    1537             :                                        poRootGroup->pszUnderscorePath));
    1538             : 
    1539         204 :         osStr.Printf("SUBDATASET_%d_DESC", poDS->nSubDataCount);
    1540             : 
    1541         204 :         poDS->papszSubDatasets = CSLSetNameValue(
    1542             :             poDS->papszSubDatasets, osStr.c_str(),
    1543             :             CPLSPrintf("[%s] %s (%s)", osDim.c_str(),
    1544             :                        poRootGroup->pszUnderscorePath,
    1545             :                        poDS->GetDataTypeName(poRootGroup->native)));
    1546             :     }
    1547             : 
    1548        1647 :     return CE_None;
    1549             : }
    1550             : 
    1551             : /************************************************************************/
    1552             : /*                       ReadGlobalAttributes()                         */
    1553             : /************************************************************************/
    1554         137 : CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET)
    1555             : {
    1556             :     HDF5GroupObjects *poRootGroup =
    1557         137 :         static_cast<HDF5GroupObjects *>(CPLCalloc(sizeof(HDF5GroupObjects), 1));
    1558             : 
    1559         137 :     poH5RootGroup = poRootGroup;
    1560         137 :     poRootGroup->pszName = CPLStrdup("/");
    1561         137 :     poRootGroup->nType = H5G_GROUP;
    1562         137 :     poRootGroup->poHparent = nullptr;
    1563         137 :     poRootGroup->pszPath = nullptr;
    1564         137 :     poRootGroup->pszUnderscorePath = nullptr;
    1565             : 
    1566         137 :     if (m_hHDF5 < 0)
    1567             :     {
    1568           0 :         CPLError(CE_Failure, CPLE_AppDefined, "hHDF5 < 0!");
    1569           0 :         return CE_None;
    1570             :     }
    1571             : 
    1572         137 :     H5G_stat_t oStatbuf = {{0, 0}, {0, 0}, 0, H5G_UNKNOWN, 0, 0, {0, 0, 0, 0}};
    1573             : 
    1574         137 :     if (H5Gget_objinfo(m_hHDF5, "/", FALSE, &oStatbuf) < 0)
    1575           0 :         return CE_Failure;
    1576         137 :     poRootGroup->objno[0] = oStatbuf.objno[0];
    1577         137 :     poRootGroup->objno[1] = oStatbuf.objno[1];
    1578             : 
    1579         137 :     if (hGroupID > 0)
    1580         137 :         H5Gclose(hGroupID);
    1581         137 :     hGroupID = H5Gopen(m_hHDF5, "/");
    1582         137 :     if (hGroupID < 0)
    1583             :     {
    1584           0 :         CPLError(CE_Failure, CPLE_AppDefined, "hGroupId <0!");
    1585           0 :         return CE_None;
    1586             :     }
    1587             : 
    1588         137 :     poRootGroup->nbAttrs = H5Aget_num_attrs(hGroupID);
    1589             : 
    1590         137 :     H5Gget_num_objs(hGroupID, &(poRootGroup->nbObjs));
    1591             : 
    1592         137 :     if (poRootGroup->nbObjs > 0)
    1593             :     {
    1594         137 :         poRootGroup->poHchild = static_cast<HDF5GroupObjects *>(
    1595         137 :             CPLCalloc(static_cast<size_t>(poRootGroup->nbObjs),
    1596             :                       sizeof(HDF5GroupObjects)));
    1597         137 :         H5Giterate(hGroupID, "/", nullptr, HDF5CreateGroupObjs, poRootGroup);
    1598             :     }
    1599             :     else
    1600             :     {
    1601           0 :         poRootGroup->poHchild = nullptr;
    1602             :     }
    1603             : 
    1604         137 :     HDF5ListGroupObjects(poRootGroup, bSUBDATASET);
    1605         137 :     return CE_None;
    1606             : }
    1607             : 
    1608             : /**
    1609             :  * Reads an array of double attributes from the HDF5 metadata.
    1610             :  * It reads the attributes directly on its binary form directly,
    1611             :  * thus avoiding string conversions.
    1612             :  *
    1613             :  * Important: It allocates the memory for the attributes internally,
    1614             :  * so the caller must free the returned array after using it.
    1615             :  * @param pszAttrFullPath Name of the attribute to be read.
    1616             :  *        the attribute name must be the form:
    1617             :  *            root attribute name
    1618             :  *            SUBDATASET/subdataset attribute name
    1619             :  * @param pdfValues pointer which will store the array of doubles read.
    1620             :  * @param nLen it stores the length of the array read. If NULL it doesn't
    1621             :  *        inform the length of the array.
    1622             :  * @return CPLErr CE_None in case of success, CE_Failure in case of failure
    1623             :  */
    1624          10 : CPLErr HDF5Dataset::HDF5ReadDoubleAttr(const char *pszAttrFullPath,
    1625             :                                        double **pdfValues, int *nLen)
    1626             : {
    1627          20 :     CPLString osAttrFullPath(pszAttrFullPath);
    1628             : 
    1629             :     // Search for the last "/" in order to get the path to the attribute.
    1630          10 :     const size_t nSlashPos = osAttrFullPath.find_last_of("/");
    1631             : 
    1632          20 :     CPLString osObjName;
    1633          10 :     CPLString osAttrName;
    1634             : 
    1635             :     // If objects name have been found.
    1636          10 :     if (nSlashPos != CPLString::npos)
    1637             :     {
    1638             :         // Split Object name (dataset, group).
    1639           7 :         osObjName = osAttrFullPath.substr(0, nSlashPos);
    1640             :         // Split attribute name.
    1641           7 :         osAttrName = osAttrFullPath.substr(nSlashPos + 1);
    1642             :     }
    1643             :     else
    1644             :     {
    1645             :         // By default the group is root, and
    1646             :         // the attribute is the full path.
    1647           3 :         osObjName = "/";
    1648           3 :         osAttrName = pszAttrFullPath;
    1649             :     }
    1650             : 
    1651          10 :     const hid_t hObjAttrID = H5Oopen(m_hHDF5, osObjName.c_str(), H5P_DEFAULT);
    1652             : 
    1653          10 :     CPLErr retVal = CE_Failure;
    1654             : 
    1655          10 :     if (hObjAttrID < 0)
    1656             :     {
    1657           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Object %s could not be opened",
    1658             :                  pszAttrFullPath);
    1659           0 :         retVal = CE_Failure;
    1660             :     }
    1661             :     else
    1662             :     {
    1663             :         // Open attribute handler by name, from the object handler opened
    1664             :         // earlier.
    1665          10 :         const hid_t hAttrID = H5Aopen_name(hObjAttrID, osAttrName.c_str());
    1666             : 
    1667             :         // Check for errors opening the attribute.
    1668          10 :         if (hAttrID < 0)
    1669             :         {
    1670           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1671             :                      "Attribute %s could not be opened", pszAttrFullPath);
    1672           0 :             retVal = CE_Failure;
    1673             :         }
    1674             :         else
    1675             :         {
    1676          10 :             const hid_t hAttrTypeID = H5Aget_type(hAttrID);
    1677             :             const hid_t hAttrNativeType =
    1678          10 :                 H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT);
    1679          10 :             const hid_t hAttrSpace = H5Aget_space(hAttrID);
    1680          10 :             hsize_t nSize[64] = {};
    1681             :             const unsigned int nAttrDims =
    1682          10 :                 H5Sget_simple_extent_dims(hAttrSpace, nSize, nullptr);
    1683             : 
    1684          10 :             if (!H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType))
    1685             :             {
    1686           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
    1687             :                          "Attribute %s is not of type double", pszAttrFullPath);
    1688           0 :                 retVal = CE_Failure;
    1689             :             }
    1690             :             else
    1691             :             {
    1692             :                 // Get the amount of elements.
    1693          10 :                 unsigned int nAttrElmts = 1;
    1694          17 :                 for (hsize_t i = 0; i < nAttrDims; i++)
    1695             :                 {
    1696             :                     // For multidimensional attributes
    1697           7 :                     nAttrElmts *= static_cast<unsigned int>(nSize[i]);
    1698             :                 }
    1699             : 
    1700          10 :                 if (nLen != nullptr)
    1701           0 :                     *nLen = nAttrElmts;
    1702             : 
    1703          10 :                 *pdfValues = static_cast<double *>(
    1704          10 :                     CPLMalloc(nAttrElmts * sizeof(double)));
    1705             : 
    1706             :                 // Read the attribute contents
    1707          10 :                 if (H5Aread(hAttrID, hAttrNativeType, *pdfValues) < 0)
    1708             :                 {
    1709           0 :                     CPLError(CE_Failure, CPLE_OpenFailed,
    1710             :                              "Attribute %s could not be opened",
    1711             :                              pszAttrFullPath);
    1712           0 :                     retVal = CE_Failure;
    1713             :                 }
    1714             :                 else
    1715             :                 {
    1716          10 :                     retVal = CE_None;
    1717             :                 }
    1718             :             }
    1719             : 
    1720          10 :             H5Tclose(hAttrNativeType);
    1721          10 :             H5Tclose(hAttrTypeID);
    1722          10 :             H5Sclose(hAttrSpace);
    1723          10 :             H5Aclose(hAttrID);
    1724             :         }
    1725          10 :         H5Oclose(hObjAttrID);
    1726             :     }
    1727             : 
    1728          20 :     return retVal;
    1729             : }

Generated by: LCOV version 1.14