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