Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: HDF5 read Driver
4 : * Author: Even Rouault <even.rouault at spatialys.com>
5 : *
6 : ******************************************************************************
7 : * Copyright (c) 2019, Even Rouault <even.rouault at spatialys.com>
8 : *
9 : * SPDX-License-Identifier: MIT
10 : ****************************************************************************/
11 :
12 : #include <numeric>
13 : #ifdef _POSIX_C_SOURCE
14 : #undef _POSIX_C_SOURCE
15 : #endif
16 :
17 : #include "hdf5dataset.h"
18 : #include "hdf5eosparser.h"
19 : #include "s100.h"
20 :
21 : #include "cpl_float.h"
22 :
23 : #include <algorithm>
24 : #include <cinttypes>
25 : #include <limits>
26 : #include <set>
27 : #include <utility>
28 :
29 : #if (defined(H5_VERS_MAJOR) && \
30 : (H5_VERS_MAJOR >= 2 || (H5_VERS_MAJOR == 1 && H5_VERS_MINOR >= 14) || \
31 : (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 12 && H5_VERS_RELEASE >= 3) || \
32 : (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 10 && H5_VERS_RELEASE >= 9)))
33 : #define HAVE_H5Dchunk_iter
34 : #endif
35 :
36 : namespace GDAL
37 : {
38 :
39 : /************************************************************************/
40 : /* HDF5Group */
41 : /************************************************************************/
42 :
43 : class HDF5Group final : public GDALGroup
44 : {
45 : std::shared_ptr<HDF5SharedResources> m_poShared;
46 : hid_t m_hGroup;
47 : std::set<std::pair<unsigned long, unsigned long>> m_oSetParentIds{};
48 : const bool m_bIsEOSGridGroup;
49 : const bool m_bIsEOSSwathGroup;
50 : mutable std::shared_ptr<GDALMDArray> m_poXIndexingArray{};
51 : mutable std::shared_ptr<GDALMDArray> m_poYIndexingArray{};
52 : mutable std::vector<std::string> m_osListSubGroups{};
53 : mutable std::vector<std::string> m_osListArrays{};
54 : mutable std::vector<std::shared_ptr<GDALAttribute>> m_oListAttributes{};
55 : mutable bool m_bShowAllAttributes = false;
56 : mutable bool m_bGotDims = false;
57 : mutable std::vector<std::shared_ptr<GDALDimension>> m_cachedDims{};
58 :
59 : static herr_t GetGroupNamesCallback(hid_t hGroup, const char *pszObjName,
60 : void *);
61 :
62 : static herr_t GetArrayNamesCallback(hid_t hGroup, const char *pszObjName,
63 : void *);
64 :
65 : static herr_t GetAttributesCallback(hid_t hGroup, const char *pszObjName,
66 : void *);
67 :
68 : protected:
69 2220 : HDF5Group(
70 : const std::string &osParentName, const std::string &osName,
71 : const std::shared_ptr<HDF5SharedResources> &poShared,
72 : const std::set<std::pair<unsigned long, unsigned long>> &oSetParentIds,
73 : hid_t hGroup, unsigned long objIds[2])
74 2220 : : GDALGroup(osParentName, osName), m_poShared(poShared),
75 : m_hGroup(hGroup), m_oSetParentIds(oSetParentIds),
76 2220 : m_bIsEOSGridGroup(osParentName == "/HDFEOS/GRIDS"),
77 4440 : m_bIsEOSSwathGroup(osParentName == "/HDFEOS/SWATHS")
78 : {
79 2220 : m_oSetParentIds.insert(std::pair(objIds[0], objIds[1]));
80 :
81 : // Force registration of EOS dimensions
82 2220 : if (m_bIsEOSGridGroup || m_bIsEOSSwathGroup)
83 : {
84 9 : HDF5Group::GetDimensions();
85 : }
86 2220 : }
87 :
88 : public:
89 2220 : static std::shared_ptr<HDF5Group> Create(
90 : const std::string &osParentName, const std::string &osName,
91 : const std::shared_ptr<HDF5SharedResources> &poShared,
92 : const std::set<std::pair<unsigned long, unsigned long>> &oSetParentIds,
93 : hid_t hGroup, unsigned long objIds[2])
94 : {
95 : auto poGroup = std::shared_ptr<HDF5Group>(new HDF5Group(
96 2220 : osParentName, osName, poShared, oSetParentIds, hGroup, objIds));
97 2220 : poGroup->SetSelf(poGroup);
98 2220 : return poGroup;
99 : }
100 :
101 4440 : ~HDF5Group() override
102 2220 : {
103 2220 : H5Gclose(m_hGroup);
104 4440 : }
105 :
106 354 : hid_t GetID() const
107 : {
108 354 : return m_hGroup;
109 : }
110 :
111 : std::vector<std::shared_ptr<GDALDimension>>
112 : GetDimensions(CSLConstList papszOptions = nullptr) const override;
113 :
114 : std::vector<std::string>
115 : GetGroupNames(CSLConstList papszOptions) const override;
116 : std::shared_ptr<GDALGroup> OpenGroup(const std::string &osName,
117 : CSLConstList) const override;
118 :
119 : std::vector<std::string>
120 : GetMDArrayNames(CSLConstList papszOptions) const override;
121 : std::shared_ptr<GDALMDArray>
122 : OpenMDArray(const std::string &osName,
123 : CSLConstList papszOptions) const override;
124 :
125 : std::vector<std::shared_ptr<GDALAttribute>>
126 : GetAttributes(CSLConstList papszOptions = nullptr) const override;
127 : };
128 :
129 : /************************************************************************/
130 : /* HDF5Dimension */
131 : /************************************************************************/
132 :
133 : class HDF5Dimension final : public GDALDimension
134 : {
135 : std::string m_osGroupFullname;
136 : std::shared_ptr<HDF5SharedResources> m_poShared;
137 :
138 : public:
139 51 : HDF5Dimension(const std::string &osParentName, const std::string &osName,
140 : const std::string &osType, const std::string &osDirection,
141 : GUInt64 nSize,
142 : const std::shared_ptr<HDF5SharedResources> &poShared)
143 51 : : GDALDimension(osParentName, osName, osType, osDirection, nSize),
144 51 : m_osGroupFullname(osParentName), m_poShared(poShared)
145 : {
146 51 : }
147 :
148 : std::shared_ptr<GDALMDArray> GetIndexingVariable() const override;
149 : };
150 :
151 : /************************************************************************/
152 : /* BuildDataType() */
153 : /************************************************************************/
154 :
155 : static GDALExtendedDataType
156 183273 : BuildDataType(hid_t hDataType, bool &bHasString, bool &bNonNativeDataType,
157 : const std::vector<std::pair<std::string, hid_t>> &oTypes)
158 : {
159 183273 : const auto klass = H5Tget_class(hDataType);
160 183273 : GDALDataType eDT = ::HDF5Dataset::GetDataType(hDataType);
161 183273 : if (eDT != GDT_Unknown)
162 : {
163 : #ifdef HDF5_HAVE_FLOAT16
164 : if (H5Tequal(hDataType, H5T_NATIVE_FLOAT16) ||
165 : HDF5Dataset::IsNativeCFloat16(hDataType))
166 : {
167 : bNonNativeDataType = true;
168 : }
169 : #endif
170 133358 : return GDALExtendedDataType::Create(eDT);
171 : }
172 49915 : else if (klass == H5T_STRING)
173 : {
174 31449 : bHasString = true;
175 31449 : return GDALExtendedDataType::CreateString();
176 : }
177 18466 : else if (klass == H5T_COMPOUND)
178 : {
179 1453 : const unsigned nMembers = H5Tget_nmembers(hDataType);
180 2906 : std::vector<std::unique_ptr<GDALEDTComponent>> components;
181 1453 : size_t nOffset = 0;
182 6770 : for (unsigned i = 0; i < nMembers; i++)
183 : {
184 5593 : char *pszName = H5Tget_member_name(hDataType, i);
185 5593 : if (!pszName)
186 276 : return GDALExtendedDataType::Create(GDT_Unknown);
187 5593 : CPLString osCompName(pszName);
188 5593 : H5free_memory(pszName);
189 5593 : const auto hMemberType = H5Tget_member_type(hDataType, i);
190 5593 : if (hMemberType < 0)
191 0 : return GDALExtendedDataType::Create(GDT_Unknown);
192 : const hid_t hNativeMemberType =
193 5593 : H5Tget_native_type(hMemberType, H5T_DIR_ASCEND);
194 : auto memberDT = BuildDataType(hNativeMemberType, bHasString,
195 5593 : bNonNativeDataType, oTypes);
196 5593 : H5Tclose(hNativeMemberType);
197 5593 : H5Tclose(hMemberType);
198 8934 : if (memberDT.GetClass() == GEDTC_NUMERIC &&
199 3341 : memberDT.GetNumericDataType() == GDT_Unknown)
200 276 : return GDALExtendedDataType::Create(GDT_Unknown);
201 5317 : if ((nOffset % memberDT.GetSize()) != 0)
202 274 : nOffset += memberDT.GetSize() - (nOffset % memberDT.GetSize());
203 5317 : if (nOffset != H5Tget_member_offset(hDataType, i))
204 523 : bNonNativeDataType = true;
205 10634 : components.emplace_back(std::unique_ptr<GDALEDTComponent>(
206 10634 : new GDALEDTComponent(osCompName, nOffset, memberDT)));
207 5317 : nOffset += memberDT.GetSize();
208 : }
209 2354 : if (!components.empty() &&
210 1177 : (nOffset % components[0]->GetType().GetSize()) != 0)
211 324 : nOffset += components[0]->GetType().GetSize() -
212 324 : (nOffset % components[0]->GetType().GetSize());
213 1177 : if (nOffset != H5Tget_size(hDataType))
214 85 : bNonNativeDataType = true;
215 2354 : std::string osName("unnamed");
216 1402 : for (const auto &oPair : oTypes)
217 : {
218 : const auto hPairNativeType =
219 275 : H5Tget_native_type(oPair.second, H5T_DIR_ASCEND);
220 275 : const auto matches = H5Tequal(hPairNativeType, hDataType);
221 275 : H5Tclose(hPairNativeType);
222 275 : if (matches)
223 : {
224 50 : osName = oPair.first;
225 50 : break;
226 : }
227 : }
228 : return GDALExtendedDataType::Create(osName, nOffset,
229 1177 : std::move(components));
230 : }
231 17013 : else if (klass == H5T_ENUM)
232 : {
233 16571 : const auto hParent = H5Tget_super(hDataType);
234 16571 : const hid_t hNativeParent = H5Tget_native_type(hParent, H5T_DIR_ASCEND);
235 : auto ret(BuildDataType(hNativeParent, bHasString, bNonNativeDataType,
236 33142 : oTypes));
237 16571 : H5Tclose(hNativeParent);
238 16571 : H5Tclose(hParent);
239 16571 : return ret;
240 : }
241 : else
242 : {
243 442 : return GDALExtendedDataType::Create(GDT_Unknown);
244 : }
245 : }
246 :
247 : /************************************************************************/
248 : /* GetDataTypesInGroup() */
249 : /************************************************************************/
250 :
251 : static void
252 748 : GetDataTypesInGroup(hid_t hHDF5, const std::string &osGroupFullName,
253 : std::vector<std::pair<std::string, hid_t>> &oTypes)
254 : {
255 : struct Callback
256 : {
257 10744 : static herr_t f(hid_t hGroup, const char *pszObjName, void *user_data)
258 : {
259 10744 : auto *poTypes =
260 : static_cast<std::vector<std::pair<std::string, hid_t>> *>(
261 : user_data);
262 : H5G_stat_t oStatbuf;
263 :
264 10744 : if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
265 0 : return -1;
266 :
267 10744 : if (oStatbuf.type == H5G_TYPE)
268 : {
269 2007 : poTypes->push_back(
270 4014 : std::pair(pszObjName, H5Topen(hGroup, pszObjName)));
271 : }
272 :
273 10744 : return 0;
274 : }
275 : };
276 :
277 748 : H5Giterate(hHDF5, osGroupFullName.c_str(), nullptr, &(Callback::f),
278 : &oTypes);
279 748 : }
280 :
281 : /************************************************************************/
282 : /* HDF5Array */
283 : /************************************************************************/
284 :
285 : class HDF5Array final : public GDALMDArray
286 : {
287 : public:
288 : struct BlockInfo
289 : {
290 : uint64_t nOffset = 0;
291 : uint64_t nSize = 0;
292 : uint32_t nFilterMask = 0;
293 : };
294 :
295 : private:
296 : std::string m_osGroupFullname;
297 : std::shared_ptr<HDF5SharedResources> m_poShared;
298 : hid_t m_hArray;
299 : hid_t m_hDataSpace;
300 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
301 : GDALExtendedDataType m_dt = GDALExtendedDataType::Create(GDT_Unknown);
302 : hid_t m_hNativeDT = H5I_INVALID_HID;
303 : mutable std::vector<std::shared_ptr<GDALAttribute>> m_oListAttributes{};
304 : mutable bool m_bShowAllAttributes = false;
305 : bool m_bHasString = false;
306 : bool m_bHasNonNativeDataType = false;
307 : mutable bool m_bWarnedNoData = false;
308 : mutable std::vector<GByte> m_abyNoData{};
309 : mutable std::string m_osUnit{};
310 : mutable bool m_bHasDimensionList = false;
311 : mutable bool m_bHasDimensionLabels = false;
312 : std::shared_ptr<OGRSpatialReference> m_poSRS{};
313 : haddr_t m_nOffset;
314 : mutable CPLStringList m_aosStructuralInfo{};
315 :
316 : #ifdef HAVE_H5Dchunk_iter
317 : mutable bool m_bFirstBlockInfo = true;
318 : mutable std::vector<BlockInfo> m_asBlockInfo{};
319 : #endif
320 :
321 : HDF5Array(const std::string &osParentName, const std::string &osName,
322 : const std::shared_ptr<HDF5SharedResources> &poShared,
323 : hid_t hArray, const HDF5Group *poGroup,
324 : bool bSkipFullDimensionInstantiation);
325 :
326 : void InstantiateDimensions(const std::string &osParentName,
327 : const HDF5Group *poGroup);
328 :
329 : bool ReadSlow(const GUInt64 *arrayStartIdx, const size_t *count,
330 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
331 : const GDALExtendedDataType &bufferDataType,
332 : void *pDstBuffer) const;
333 :
334 : static herr_t GetAttributesCallback(hid_t hArray, const char *pszObjName,
335 : void *);
336 :
337 : protected:
338 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
339 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
340 : const GDALExtendedDataType &bufferDataType,
341 : void *pDstBuffer) const override;
342 :
343 : public:
344 : ~HDF5Array() override;
345 :
346 : static std::shared_ptr<HDF5Array>
347 2048 : Create(const std::string &osParentName, const std::string &osName,
348 : const std::shared_ptr<HDF5SharedResources> &poShared, hid_t hArray,
349 : const HDF5Group *poGroup, bool bSkipFullDimensionInstantiation)
350 : {
351 : HDF5_GLOBAL_LOCK();
352 :
353 : auto ar(std::shared_ptr<HDF5Array>(
354 : new HDF5Array(osParentName, osName, poShared, hArray, poGroup,
355 4096 : bSkipFullDimensionInstantiation)));
356 2763 : if (ar->m_dt.GetClass() == GEDTC_NUMERIC &&
357 715 : ar->m_dt.GetNumericDataType() == GDT_Unknown)
358 : {
359 0 : return nullptr;
360 : }
361 2048 : ar->SetSelf(ar);
362 2048 : return ar;
363 : }
364 :
365 209 : bool IsWritable() const override
366 : {
367 209 : return !m_poShared->IsReadOnly();
368 : }
369 :
370 1836 : const std::string &GetFilename() const override
371 : {
372 1836 : return m_poShared->GetFilename();
373 : }
374 :
375 : const std::vector<std::shared_ptr<GDALDimension>> &
376 4243 : GetDimensions() const override
377 : {
378 4243 : return m_dims;
379 : }
380 :
381 3675 : const GDALExtendedDataType &GetDataType() const override
382 : {
383 3675 : return m_dt;
384 : }
385 :
386 : std::shared_ptr<GDALAttribute>
387 : GetAttribute(const std::string &osName) const override;
388 :
389 : std::vector<std::shared_ptr<GDALAttribute>>
390 : GetAttributes(CSLConstList papszOptions = nullptr) const override;
391 :
392 : std::vector<GUInt64> GetBlockSize() const override;
393 :
394 : CSLConstList GetStructuralInfo() const override;
395 :
396 129 : const void *GetRawNoDataValue() const override
397 : {
398 129 : return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
399 : }
400 :
401 20 : const std::string &GetUnit() const override
402 : {
403 20 : return m_osUnit;
404 : }
405 :
406 : haddr_t GetFileOffset() const
407 : {
408 : return m_nOffset;
409 : }
410 :
411 13 : std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
412 : {
413 13 : return m_poSRS;
414 : }
415 :
416 : std::vector<std::shared_ptr<GDALMDArray>>
417 : GetCoordinateVariables() const override;
418 :
419 108 : std::shared_ptr<GDALGroup> GetRootGroup() const override
420 : {
421 108 : return m_poShared->GetRootGroup();
422 : }
423 :
424 : bool GetRawBlockInfo(const uint64_t *panBlockCoordinates,
425 : GDALMDArrayRawBlockInfo &info) const override;
426 : };
427 :
428 : /************************************************************************/
429 : /* HDF5Attribute */
430 : /************************************************************************/
431 :
432 : class HDF5Attribute final : public GDALAttribute
433 : {
434 : std::shared_ptr<HDF5SharedResources> m_poShared;
435 : hid_t m_hAttribute;
436 : hid_t m_hDataSpace;
437 : std::vector<std::shared_ptr<GDALDimension>> m_dims{};
438 : GDALExtendedDataType m_dt = GDALExtendedDataType::Create(GDT_Unknown);
439 : hid_t m_hNativeDT = H5I_INVALID_HID;
440 : size_t m_nElements = 1;
441 : bool m_bHasString = false;
442 : bool m_bHasNonNativeDataType = false;
443 :
444 159061 : HDF5Attribute(const std::string &osGroupFullName,
445 : const std::string &osParentName, const std::string &osName,
446 : const std::shared_ptr<HDF5SharedResources> &poShared,
447 : hid_t hAttribute)
448 159061 : : GDALAbstractMDArray(osParentName, osName),
449 : GDALAttribute(osParentName, osName), m_poShared(poShared),
450 159061 : m_hAttribute(hAttribute), m_hDataSpace(H5Aget_space(hAttribute))
451 : {
452 159061 : const int nDims = H5Sget_simple_extent_ndims(m_hDataSpace);
453 318122 : std::vector<hsize_t> anDimSizes(nDims);
454 159061 : if (nDims)
455 : {
456 1366 : H5Sget_simple_extent_dims(m_hDataSpace, &anDimSizes[0], nullptr);
457 1981 : for (int i = 0; i < nDims; ++i)
458 : {
459 1366 : m_nElements *= static_cast<size_t>(anDimSizes[i]);
460 1366 : if (nDims == 1 && m_nElements == 1)
461 : {
462 : // Expose 1-dim of size 1 as scalar
463 751 : break;
464 : }
465 1230 : m_dims.emplace_back(std::make_shared<GDALDimension>(
466 1230 : std::string(), CPLSPrintf("dim%d", i), std::string(),
467 1845 : std::string(), anDimSizes[i]));
468 : }
469 : }
470 :
471 159061 : const hid_t hDataType = H5Aget_type(hAttribute);
472 159061 : m_hNativeDT = H5Tget_native_type(hDataType, H5T_DIR_ASCEND);
473 159061 : H5Tclose(hDataType);
474 :
475 318122 : std::vector<std::pair<std::string, hid_t>> oTypes;
476 314528 : if (!osGroupFullName.empty() &&
477 155467 : H5Tget_class(m_hNativeDT) == H5T_COMPOUND)
478 : {
479 265 : GetDataTypesInGroup(m_poShared->GetHDF5(), osGroupFullName, oTypes);
480 : }
481 :
482 318122 : m_dt = BuildDataType(m_hNativeDT, m_bHasString, m_bHasNonNativeDataType,
483 159061 : oTypes);
484 160690 : for (auto &oPair : oTypes)
485 1629 : H5Tclose(oPair.second);
486 289081 : if (m_dt.GetClass() == GEDTC_NUMERIC &&
487 130020 : m_dt.GetNumericDataType() == GDT_Unknown)
488 : {
489 442 : CPLDebug("HDF5",
490 : "Cannot map data type of %s to a type handled by GDAL",
491 : osName.c_str());
492 : }
493 159061 : }
494 :
495 : protected:
496 : bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
497 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
498 : const GDALExtendedDataType &bufferDataType,
499 : void *pDstBuffer) const override;
500 :
501 : public:
502 : ~HDF5Attribute() override;
503 :
504 : static std::shared_ptr<HDF5Attribute>
505 159061 : Create(const std::string &osGroupFullName, const std::string &osParentName,
506 : const std::string &osName,
507 : const std::shared_ptr<HDF5SharedResources> &poShared,
508 : hid_t hAttribute)
509 : {
510 : HDF5_GLOBAL_LOCK();
511 :
512 : auto ar(std::shared_ptr<HDF5Attribute>(new HDF5Attribute(
513 318122 : osGroupFullName, osParentName, osName, poShared, hAttribute)));
514 289081 : if (ar->m_dt.GetClass() == GEDTC_NUMERIC &&
515 130020 : ar->m_dt.GetNumericDataType() == GDT_Unknown)
516 : {
517 442 : return nullptr;
518 : }
519 158619 : return ar;
520 : }
521 :
522 : const std::vector<std::shared_ptr<GDALDimension>> &
523 20437 : GetDimensions() const override
524 : {
525 20437 : return m_dims;
526 : }
527 :
528 17742 : const GDALExtendedDataType &GetDataType() const override
529 : {
530 17742 : return m_dt;
531 : }
532 : };
533 :
534 : /************************************************************************/
535 : /* HDF5SharedResources() */
536 : /************************************************************************/
537 :
538 349 : HDF5SharedResources::HDF5SharedResources(const std::string &osFilename)
539 : : m_osFilename(osFilename),
540 349 : m_poPAM(std::make_shared<GDALPamMultiDim>(osFilename))
541 : {
542 349 : }
543 :
544 : /************************************************************************/
545 : /* ~HDF5SharedResources() */
546 : /************************************************************************/
547 :
548 349 : HDF5SharedResources::~HDF5SharedResources()
549 : {
550 : HDF5_GLOBAL_LOCK();
551 :
552 349 : if (m_hHDF5 > 0)
553 349 : H5Fclose(m_hHDF5);
554 349 : }
555 :
556 : /************************************************************************/
557 : /* Create() */
558 : /************************************************************************/
559 :
560 : std::shared_ptr<HDF5SharedResources>
561 349 : HDF5SharedResources::Create(const std::string &osFilename)
562 : {
563 : auto poSharedResources = std::shared_ptr<HDF5SharedResources>(
564 349 : new HDF5SharedResources(osFilename));
565 349 : poSharedResources->m_poSelf = poSharedResources;
566 349 : return poSharedResources;
567 : }
568 :
569 : /************************************************************************/
570 : /* GetRootGroup() */
571 : /************************************************************************/
572 :
573 951 : std::shared_ptr<HDF5Group> HDF5SharedResources::GetRootGroup()
574 : {
575 :
576 : H5G_stat_t oStatbuf;
577 951 : if (H5Gget_objinfo(m_hHDF5, "/", FALSE, &oStatbuf) < 0)
578 : {
579 0 : return nullptr;
580 : }
581 951 : auto hGroup = H5Gopen(m_hHDF5, "/");
582 951 : if (hGroup < 0)
583 : {
584 0 : return nullptr;
585 : }
586 :
587 951 : auto poSharedResources = m_poSelf.lock();
588 951 : CPLAssert(poSharedResources != nullptr);
589 1902 : return HDF5Group::Create(std::string(), "/", poSharedResources, {}, hGroup,
590 2853 : oStatbuf.objno);
591 : }
592 :
593 : /************************************************************************/
594 : /* GetDimensions() */
595 : /************************************************************************/
596 :
597 : std::vector<std::shared_ptr<GDALDimension>>
598 57 : HDF5Group::GetDimensions(CSLConstList) const
599 : {
600 : HDF5_GLOBAL_LOCK();
601 :
602 57 : if (m_bGotDims)
603 25 : return m_cachedDims;
604 :
605 : struct CallbackData
606 : {
607 : std::shared_ptr<HDF5SharedResources> poShared{};
608 : std::string osFullName{};
609 : std::map<std::string, std::shared_ptr<GDALDimension>> oDimMap{};
610 : };
611 :
612 : struct Callback
613 : {
614 315 : static herr_t f(hid_t hGroup, const char *pszObjName, void *user_data)
615 : {
616 315 : CallbackData *data = static_cast<CallbackData *>(user_data);
617 : H5G_stat_t oStatbuf;
618 :
619 315 : if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
620 0 : return -1;
621 :
622 315 : if (oStatbuf.type == H5G_DATASET)
623 : {
624 239 : auto hArray = H5Dopen(hGroup, pszObjName);
625 239 : if (hArray >= 0)
626 : {
627 239 : auto ar = HDF5Array::Create(data->osFullName, pszObjName,
628 239 : data->poShared, hArray, nullptr,
629 478 : true);
630 239 : if (ar && ar->GetDimensionCount() == 1)
631 : {
632 146 : auto attrCLASS = ar->GetAttribute("CLASS");
633 138 : if (attrCLASS && attrCLASS->GetDimensionCount() == 0 &&
634 65 : attrCLASS->GetDataType().GetClass() == GEDTC_STRING)
635 : {
636 65 : const char *pszStr = attrCLASS->ReadAsString();
637 65 : if (pszStr && EQUAL(pszStr, "DIMENSION_SCALE"))
638 : {
639 65 : std::string osType;
640 65 : if (strcmp(pszObjName, "XDim") == 0)
641 4 : osType = GDAL_DIM_TYPE_HORIZONTAL_X;
642 61 : else if (strcmp(pszObjName, "YDim") == 0)
643 4 : osType = GDAL_DIM_TYPE_HORIZONTAL_Y;
644 :
645 130 : auto attrNAME = ar->GetAttribute("NAME");
646 128 : if (attrNAME &&
647 128 : attrNAME->GetDimensionCount() == 0 &&
648 63 : attrNAME->GetDataType().GetClass() ==
649 : GEDTC_STRING)
650 : {
651 : const char *pszName =
652 63 : attrNAME->ReadAsString();
653 63 : if (pszName &&
654 63 : STARTS_WITH(
655 : pszName,
656 : "This is a netCDF dimension but "
657 : "not a netCDF variable"))
658 : {
659 72 : data->oDimMap[pszObjName] =
660 36 : std::make_shared<GDALDimension>(
661 36 : data->osFullName, pszObjName,
662 36 : osType, std::string(),
663 36 : ar->GetDimensions()[0]
664 72 : ->GetSize());
665 36 : return 0;
666 : }
667 : }
668 :
669 58 : data->oDimMap[pszObjName] =
670 29 : std::make_shared<HDF5Dimension>(
671 29 : data->osFullName, pszObjName, osType,
672 29 : std::string(),
673 58 : ar->GetDimensions()[0]->GetSize(),
674 58 : data->poShared);
675 : }
676 : }
677 : }
678 : }
679 : }
680 :
681 279 : return 0;
682 : }
683 : };
684 :
685 64 : CallbackData data;
686 32 : data.poShared = m_poShared;
687 32 : data.osFullName = GetFullName();
688 32 : H5Giterate(m_poShared->GetHDF5(), GetFullName().c_str(), nullptr,
689 : &(Callback::f), &data);
690 32 : m_bGotDims = true;
691 :
692 32 : if (m_bIsEOSGridGroup)
693 : {
694 6 : const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
695 6 : HDF5EOSParser::GridMetadata oGridMetadata;
696 12 : if (poHDF5EOSParser &&
697 6 : poHDF5EOSParser->GetGridMetadata(GetName(), oGridMetadata))
698 : {
699 6 : GDALGeoTransform gt;
700 6 : const bool bHasGT = oGridMetadata.GetGeoTransform(gt) &&
701 6 : gt.xrot == 0 && gt.yrot == 0;
702 25 : for (auto &oDim : oGridMetadata.aoDimensions)
703 : {
704 19 : auto iterInDimMap = data.oDimMap.find(oDim.osName);
705 19 : if (iterInDimMap != data.oDimMap.end())
706 : {
707 : auto indexingVar =
708 16 : iterInDimMap->second->GetIndexingVariable();
709 8 : if (indexingVar != nullptr)
710 : {
711 12 : if (oDim.osName == "XDim" && bHasGT &&
712 4 : indexingVar->GetDimensions()[0]->GetSize() > 0)
713 : {
714 4 : const GUInt64 anArrayStartIdx0[] = {0};
715 4 : const size_t anCount[] = {1};
716 4 : const GInt64 anArrayStep[] = {0};
717 4 : const GPtrDiff_t anBufferStride[] = {0};
718 4 : double dfFirstVal = 0;
719 8 : if (indexingVar->Read(
720 : anArrayStartIdx0, anCount, anArrayStep,
721 : anBufferStride,
722 8 : GDALExtendedDataType::Create(GDT_Float64),
723 : &dfFirstVal))
724 : {
725 4 : CPLDebug(
726 : "HDF5",
727 : "XDim FirstX: (from XDim array:%.17g,) "
728 : "from HDF5EOS metadata:%.17g",
729 : dfFirstVal, gt.xorig);
730 : }
731 :
732 : const GUInt64 anArrayStartIdx1[] = {
733 4 : indexingVar->GetDimensions()[0]->GetSize() - 1};
734 4 : double dfLastVal = 0;
735 8 : if (indexingVar->Read(
736 : anArrayStartIdx1, anCount, anArrayStep,
737 : anBufferStride,
738 8 : GDALExtendedDataType::Create(GDT_Float64),
739 : &dfLastVal))
740 : {
741 4 : CPLDebug("HDF5",
742 : "XDim LastX: (from XDim array:%.17g,) "
743 : "from HDF5EOS metadata:%.17g",
744 : dfLastVal,
745 4 : gt.xorig + oDim.nSize * gt.xscale);
746 : }
747 : }
748 8 : else if (oDim.osName == "YDim" && bHasGT &&
749 4 : indexingVar->GetDimensions()[0]->GetSize() > 0)
750 : {
751 4 : const GUInt64 anArrayStartIdx0[] = {0};
752 4 : const size_t anCount[] = {1};
753 4 : const GInt64 anArrayStep[] = {0};
754 4 : const GPtrDiff_t anBufferStride[] = {0};
755 4 : double dfFirstVal = 0;
756 8 : if (indexingVar->Read(
757 : anArrayStartIdx0, anCount, anArrayStep,
758 : anBufferStride,
759 8 : GDALExtendedDataType::Create(GDT_Float64),
760 : &dfFirstVal))
761 : {
762 4 : CPLDebug(
763 : "HDF5",
764 : "YDim FirstY: (from YDim array:%.17g,) "
765 : "from HDF5EOS metadata:%.17g",
766 : dfFirstVal, gt.yorig);
767 : }
768 :
769 : const GUInt64 anArrayStartIdx1[] = {
770 4 : indexingVar->GetDimensions()[0]->GetSize() - 1};
771 4 : double dfLastVal = 0;
772 8 : if (indexingVar->Read(
773 : anArrayStartIdx1, anCount, anArrayStep,
774 : anBufferStride,
775 8 : GDALExtendedDataType::Create(GDT_Float64),
776 : &dfLastVal))
777 : {
778 4 : CPLDebug("HDF5",
779 : "YDim LastY: (from YDim array:%.17g,) "
780 : "from HDF5EOS metadata:%.17g",
781 : dfLastVal,
782 4 : gt.yorig + oDim.nSize * gt.yscale);
783 : }
784 : }
785 : }
786 : }
787 :
788 19 : if (oDim.osName == "XDim" && bHasGT)
789 : {
790 6 : if (iterInDimMap != data.oDimMap.end())
791 4 : data.oDimMap.erase(iterInDimMap);
792 : auto poDim = std::make_shared<GDALDimensionWeakIndexingVar>(
793 6 : GetFullName(), oDim.osName, GDAL_DIM_TYPE_HORIZONTAL_X,
794 18 : std::string(), oDim.nSize);
795 : auto poIndexingVar = GDALMDArrayRegularlySpaced::Create(
796 6 : GetFullName(), oDim.osName, poDim,
797 12 : gt.xorig + gt.xscale / 2, gt.xscale, 0);
798 6 : poDim->SetIndexingVariable(poIndexingVar);
799 6 : m_poXIndexingArray = poIndexingVar;
800 6 : m_poShared->KeepRef(poIndexingVar);
801 6 : m_cachedDims.emplace_back(poDim);
802 : }
803 13 : else if (oDim.osName == "YDim" && bHasGT)
804 : {
805 6 : if (iterInDimMap != data.oDimMap.end())
806 4 : data.oDimMap.erase(iterInDimMap);
807 : auto poDim = std::make_shared<GDALDimensionWeakIndexingVar>(
808 6 : GetFullName(), oDim.osName, GDAL_DIM_TYPE_HORIZONTAL_Y,
809 18 : std::string(), oDim.nSize);
810 : auto poIndexingVar = GDALMDArrayRegularlySpaced::Create(
811 6 : GetFullName(), oDim.osName, poDim,
812 12 : gt.yorig + gt.yscale / 2, gt.yscale, 0);
813 6 : poDim->SetIndexingVariable(poIndexingVar);
814 6 : m_poYIndexingArray = poIndexingVar;
815 6 : m_poShared->KeepRef(poIndexingVar);
816 6 : m_cachedDims.emplace_back(poDim);
817 : }
818 7 : else if (iterInDimMap == data.oDimMap.end())
819 : {
820 14 : m_cachedDims.emplace_back(std::make_shared<GDALDimension>(
821 14 : GetFullName(), oDim.osName, std::string(),
822 21 : std::string(), oDim.nSize));
823 : }
824 : }
825 6 : for (auto &iter : data.oDimMap)
826 0 : m_cachedDims.emplace_back(std::move(iter.second));
827 6 : m_poShared->RegisterEOSGridDimensions(GetName(), m_cachedDims);
828 6 : return m_cachedDims;
829 : }
830 : }
831 26 : else if (m_bIsEOSSwathGroup)
832 : {
833 3 : const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
834 3 : HDF5EOSParser::SwathMetadata oSwathMetadata;
835 6 : if (poHDF5EOSParser &&
836 3 : poHDF5EOSParser->GetSwathMetadata(GetName(), oSwathMetadata))
837 : {
838 12 : for (auto &oDim : oSwathMetadata.aoDimensions)
839 : {
840 9 : auto iterInDimMap = data.oDimMap.find(oDim.osName);
841 9 : if (iterInDimMap != data.oDimMap.end())
842 : {
843 0 : if (iterInDimMap->second->GetIndexingVariable() != nullptr)
844 : {
845 0 : continue;
846 : }
847 0 : data.oDimMap.erase(iterInDimMap);
848 : }
849 :
850 18 : m_cachedDims.emplace_back(std::make_shared<GDALDimension>(
851 18 : GetFullName(), oDim.osName, std::string(), std::string(),
852 18 : oDim.nSize));
853 : }
854 3 : for (auto &iter : data.oDimMap)
855 0 : m_cachedDims.emplace_back(std::move(iter.second));
856 3 : m_poShared->RegisterEOSSwathDimensions(GetName(), m_cachedDims);
857 3 : return m_cachedDims;
858 : }
859 : }
860 :
861 80 : for (auto &iter : data.oDimMap)
862 57 : m_cachedDims.emplace_back(std::move(iter.second));
863 23 : return m_cachedDims;
864 : }
865 :
866 : /************************************************************************/
867 : /* GetGroupNamesCallback() */
868 : /************************************************************************/
869 :
870 2942 : herr_t HDF5Group::GetGroupNamesCallback(hid_t hGroup, const char *pszObjName,
871 : void *selfIn)
872 : {
873 2942 : HDF5Group *self = static_cast<HDF5Group *>(selfIn);
874 : H5G_stat_t oStatbuf;
875 :
876 2942 : if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
877 0 : return -1;
878 :
879 2942 : if (oStatbuf.type == H5G_GROUP)
880 : {
881 1718 : if (self->m_oSetParentIds.find(
882 1718 : std::pair(oStatbuf.objno[0], oStatbuf.objno[1])) ==
883 3436 : self->m_oSetParentIds.end())
884 : {
885 1718 : self->m_osListSubGroups.push_back(pszObjName);
886 : }
887 : else
888 : {
889 0 : CPLDebug("HDF5",
890 : "Group %s contains a link to group %s which is "
891 : "itself, or one of its ancestor.",
892 0 : self->GetFullName().c_str(), pszObjName);
893 : }
894 : }
895 2942 : return 0;
896 : }
897 :
898 : /************************************************************************/
899 : /* GetGroupNames() */
900 : /************************************************************************/
901 :
902 1238 : std::vector<std::string> HDF5Group::GetGroupNames(CSLConstList) const
903 : {
904 : HDF5_GLOBAL_LOCK();
905 :
906 1238 : m_osListSubGroups.clear();
907 1238 : H5Giterate(m_poShared->GetHDF5(), GetFullName().c_str(), nullptr,
908 : GetGroupNamesCallback,
909 : const_cast<void *>(static_cast<const void *>(this)));
910 1238 : return m_osListSubGroups;
911 : }
912 :
913 : /************************************************************************/
914 : /* OpenGroup() */
915 : /************************************************************************/
916 :
917 1452 : std::shared_ptr<GDALGroup> HDF5Group::OpenGroup(const std::string &osName,
918 : CSLConstList) const
919 : {
920 : HDF5_GLOBAL_LOCK();
921 :
922 1452 : if (m_osListSubGroups.empty())
923 1110 : GetGroupNames(nullptr);
924 1452 : if (std::find(m_osListSubGroups.begin(), m_osListSubGroups.end(), osName) ==
925 2904 : m_osListSubGroups.end())
926 : {
927 183 : return nullptr;
928 : }
929 :
930 : H5G_stat_t oStatbuf;
931 1269 : if (H5Gget_objinfo(m_hGroup, osName.c_str(), FALSE, &oStatbuf) < 0)
932 0 : return nullptr;
933 :
934 1269 : auto hSubGroup = H5Gopen(m_hGroup, osName.c_str());
935 1269 : if (hSubGroup < 0)
936 : {
937 0 : return nullptr;
938 : }
939 2538 : return HDF5Group::Create(GetFullName(), osName, m_poShared, m_oSetParentIds,
940 1269 : hSubGroup, oStatbuf.objno);
941 : }
942 :
943 : /************************************************************************/
944 : /* GetArrayNamesCallback() */
945 : /************************************************************************/
946 :
947 1486 : herr_t HDF5Group::GetArrayNamesCallback(hid_t hGroup, const char *pszObjName,
948 : void *selfIn)
949 : {
950 1486 : HDF5Group *self = static_cast<HDF5Group *>(selfIn);
951 : H5G_stat_t oStatbuf;
952 :
953 1486 : if (H5Gget_objinfo(hGroup, pszObjName, FALSE, &oStatbuf) < 0)
954 0 : return -1;
955 :
956 1486 : if (oStatbuf.type == H5G_DATASET)
957 : {
958 1258 : auto hArray = H5Dopen(hGroup, pszObjName);
959 1258 : if (hArray >= 0)
960 : {
961 2516 : auto ar(HDF5Array::Create(std::string(), pszObjName,
962 3774 : self->m_poShared, hArray, self, true));
963 1258 : if (ar)
964 : {
965 2516 : auto attrNAME = ar->GetAttribute("NAME");
966 1313 : if (attrNAME && attrNAME->GetDimensionCount() == 0 &&
967 55 : attrNAME->GetDataType().GetClass() == GEDTC_STRING)
968 : {
969 55 : const char *pszName = attrNAME->ReadAsString();
970 55 : if (pszName &&
971 55 : STARTS_WITH(pszName, "This is a netCDF dimension but "
972 : "not a netCDF variable"))
973 : {
974 34 : return 0;
975 : }
976 : }
977 : }
978 : }
979 1224 : self->m_osListArrays.push_back(pszObjName);
980 : }
981 1452 : return 0;
982 : }
983 :
984 : /************************************************************************/
985 : /* GetMDArrayNames() */
986 : /************************************************************************/
987 :
988 479 : std::vector<std::string> HDF5Group::GetMDArrayNames(CSLConstList) const
989 : {
990 : HDF5_GLOBAL_LOCK();
991 :
992 479 : m_osListArrays.clear();
993 479 : H5Giterate(m_poShared->GetHDF5(), GetFullName().c_str(), nullptr,
994 : GetArrayNamesCallback,
995 : const_cast<void *>(static_cast<const void *>(this)));
996 :
997 479 : if (m_poXIndexingArray)
998 0 : m_osListArrays.push_back(m_poXIndexingArray->GetName());
999 479 : if (m_poYIndexingArray)
1000 0 : m_osListArrays.push_back(m_poYIndexingArray->GetName());
1001 :
1002 479 : return m_osListArrays;
1003 : }
1004 :
1005 : /************************************************************************/
1006 : /* OpenMDArray() */
1007 : /************************************************************************/
1008 :
1009 554 : std::shared_ptr<GDALMDArray> HDF5Group::OpenMDArray(const std::string &osName,
1010 : CSLConstList) const
1011 : {
1012 : HDF5_GLOBAL_LOCK();
1013 :
1014 554 : if (m_osListArrays.empty())
1015 442 : GetMDArrayNames(nullptr);
1016 554 : if (std::find(m_osListArrays.begin(), m_osListArrays.end(), osName) ==
1017 1108 : m_osListArrays.end())
1018 : {
1019 23 : return nullptr;
1020 : }
1021 531 : if (m_poXIndexingArray && m_poXIndexingArray->GetName() == osName)
1022 0 : return m_poXIndexingArray;
1023 531 : if (m_poYIndexingArray && m_poYIndexingArray->GetName() == osName)
1024 0 : return m_poYIndexingArray;
1025 :
1026 531 : auto hArray = H5Dopen(m_hGroup, osName.c_str());
1027 531 : if (hArray < 0)
1028 : {
1029 0 : return nullptr;
1030 : }
1031 1062 : return HDF5Array::Create(GetFullName(), osName, m_poShared, hArray, this,
1032 531 : false);
1033 : }
1034 :
1035 : /************************************************************************/
1036 : /* GetAttributesCallback() */
1037 : /************************************************************************/
1038 :
1039 154052 : herr_t HDF5Group::GetAttributesCallback(hid_t hGroup, const char *pszObjName,
1040 : void *selfIn)
1041 : {
1042 154052 : HDF5Group *self = static_cast<HDF5Group *>(selfIn);
1043 154052 : if (self->m_bShowAllAttributes || (!EQUAL(pszObjName, "_Netcdf4Dimid") &&
1044 154052 : !EQUAL(pszObjName, "_NCProperties")))
1045 : {
1046 154050 : hid_t hAttr = H5Aopen_name(hGroup, pszObjName);
1047 154050 : if (hAttr > 0)
1048 : {
1049 : auto attr(HDF5Attribute::Create(self->GetFullName(),
1050 : self->GetFullName(), pszObjName,
1051 462150 : self->m_poShared, hAttr));
1052 154050 : if (attr)
1053 : {
1054 154050 : self->m_oListAttributes.emplace_back(attr);
1055 : }
1056 : }
1057 : }
1058 154052 : return 0;
1059 : }
1060 :
1061 : /************************************************************************/
1062 : /* GetAttributes() */
1063 : /************************************************************************/
1064 :
1065 : std::vector<std::shared_ptr<GDALAttribute>>
1066 10919 : HDF5Group::GetAttributes(CSLConstList papszOptions) const
1067 : {
1068 : HDF5_GLOBAL_LOCK();
1069 :
1070 10919 : m_oListAttributes.clear();
1071 10919 : m_bShowAllAttributes =
1072 10919 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
1073 10919 : H5Aiterate(m_hGroup, nullptr, GetAttributesCallback,
1074 : const_cast<void *>(static_cast<const void *>(this)));
1075 10919 : return m_oListAttributes;
1076 : }
1077 :
1078 : /************************************************************************/
1079 : /* ~HDF5Array() */
1080 : /************************************************************************/
1081 :
1082 4096 : HDF5Array::~HDF5Array()
1083 : {
1084 : HDF5_GLOBAL_LOCK();
1085 :
1086 2048 : if (m_hArray > 0)
1087 2048 : H5Dclose(m_hArray);
1088 2048 : if (m_hNativeDT > 0)
1089 2048 : H5Tclose(m_hNativeDT);
1090 2048 : if (m_hDataSpace > 0)
1091 2048 : H5Sclose(m_hDataSpace);
1092 4096 : }
1093 :
1094 : /************************************************************************/
1095 : /* HDF5Array() */
1096 : /************************************************************************/
1097 :
1098 2048 : HDF5Array::HDF5Array(const std::string &osParentName, const std::string &osName,
1099 : const std::shared_ptr<HDF5SharedResources> &poShared,
1100 : hid_t hArray, const HDF5Group *poGroup,
1101 2048 : bool bSkipFullDimensionInstantiation)
1102 : : GDALAbstractMDArray(osParentName, osName),
1103 : GDALMDArray(osParentName, osName), m_osGroupFullname(osParentName),
1104 : m_poShared(poShared), m_hArray(hArray),
1105 2048 : m_hDataSpace(H5Dget_space(hArray)), m_nOffset(H5Dget_offset(hArray))
1106 : {
1107 2048 : const hid_t hDataType = H5Dget_type(hArray);
1108 2048 : m_hNativeDT = H5Tget_native_type(hDataType, H5T_DIR_ASCEND);
1109 2048 : H5Tclose(hDataType);
1110 :
1111 2048 : std::vector<std::pair<std::string, hid_t>> oTypes;
1112 2048 : if (!osParentName.empty() && H5Tget_class(m_hNativeDT) == H5T_COMPOUND)
1113 : {
1114 483 : GetDataTypesInGroup(m_poShared->GetHDF5(), osParentName, oTypes);
1115 : }
1116 :
1117 4096 : m_dt = BuildDataType(m_hNativeDT, m_bHasString, m_bHasNonNativeDataType,
1118 2048 : oTypes);
1119 2426 : for (auto &oPair : oTypes)
1120 378 : H5Tclose(oPair.second);
1121 :
1122 2763 : if (m_dt.GetClass() == GEDTC_NUMERIC &&
1123 715 : m_dt.GetNumericDataType() == GDT_Unknown)
1124 : {
1125 0 : CPLDebug("HDF5", "Cannot map data type of %s to a type handled by GDAL",
1126 : osName.c_str());
1127 0 : return;
1128 : }
1129 :
1130 2048 : HDF5Array::GetAttributes();
1131 :
1132 : // Special case for S102 nodata value that is typically at 1e6
1133 2048 : if (GetFullName() ==
1134 38 : "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values" &&
1135 76 : m_dt.GetClass() == GEDTC_COMPOUND &&
1136 59 : (m_dt.GetComponents().size() == 1 ||
1137 21 : m_dt.GetComponents().size() == 2) &&
1138 76 : m_dt.GetSize() == m_dt.GetComponents().size() * sizeof(float) &&
1139 38 : m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
1140 2107 : GDT_Float32 &&
1141 59 : (m_dt.GetComponents().size() == 1 ||
1142 21 : m_dt.GetComponents()[1]->GetType().GetNumericDataType() ==
1143 : GDT_Float32))
1144 : {
1145 38 : m_abyNoData.resize(m_dt.GetSize());
1146 38 : float afNoData[2] = {1e6f, 1e6f};
1147 :
1148 76 : if (auto poRootGroup = HDF5Array::GetRootGroup())
1149 : {
1150 114 : if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
1151 : {
1152 : const auto poGroupFArray =
1153 114 : poGroupF->OpenMDArray("BathymetryCoverage");
1154 38 : if (poGroupFArray &&
1155 114 : poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
1156 76 : poGroupFArray->GetDataType().GetComponents().size() == 8 &&
1157 38 : poGroupFArray->GetDataType()
1158 38 : .GetComponents()[0]
1159 76 : ->GetName() == "code" &&
1160 38 : poGroupFArray->GetDataType()
1161 38 : .GetComponents()[3]
1162 38 : ->GetName() == "fillValue" &&
1163 114 : poGroupFArray->GetDimensionCount() == 1 &&
1164 38 : poGroupFArray->GetDimensions()[0]->GetSize() ==
1165 38 : m_dt.GetComponents().size())
1166 : {
1167 : auto poFillValue =
1168 114 : poGroupFArray->GetView("[\"fillValue\"]");
1169 38 : if (poFillValue)
1170 : {
1171 38 : char *pszVal0 = nullptr;
1172 38 : char *pszVal1 = nullptr;
1173 38 : const GUInt64 anArrayStartIdx0[] = {0};
1174 38 : const GUInt64 anArrayStartIdx1[] = {1};
1175 38 : const size_t anCount[] = {1};
1176 38 : const GInt64 anArrayStep[] = {0};
1177 38 : const GPtrDiff_t anBufferStride[] = {0};
1178 76 : poFillValue->Read(anArrayStartIdx0, anCount,
1179 : anArrayStep, anBufferStride,
1180 76 : GDALExtendedDataType::CreateString(),
1181 : &pszVal0);
1182 38 : if (poGroupFArray->GetDimensions()[0]->GetSize() == 2)
1183 : {
1184 42 : poFillValue->Read(
1185 : anArrayStartIdx1, anCount, anArrayStep,
1186 : anBufferStride,
1187 42 : GDALExtendedDataType::CreateString(), &pszVal1);
1188 : }
1189 38 : if (pszVal0)
1190 : {
1191 38 : afNoData[0] = static_cast<float>(CPLAtof(pszVal0));
1192 38 : if (pszVal1)
1193 : {
1194 21 : afNoData[1] =
1195 21 : static_cast<float>(CPLAtof(pszVal1));
1196 : }
1197 : }
1198 38 : CPLFree(pszVal0);
1199 38 : CPLFree(pszVal1);
1200 : }
1201 : }
1202 : }
1203 : }
1204 :
1205 38 : m_abyNoData.resize(m_dt.GetSize());
1206 38 : memcpy(m_abyNoData.data(), afNoData, m_abyNoData.size());
1207 : }
1208 :
1209 : // Special case for S102 QualityOfSurvey nodata value that is typically at 0
1210 : const bool bIsQualityOfSurvey =
1211 2048 : (GetFullName() ==
1212 : "/QualityOfSurvey/QualityOfSurvey.01/Group_001/values");
1213 : const bool bIsQualityOfBathymetryCoverage =
1214 2048 : (GetFullName() == "/QualityOfBathymetryCoverage/"
1215 : "QualityOfBathymetryCoverage.01/Group_001/values");
1216 2064 : if ((bIsQualityOfSurvey || bIsQualityOfBathymetryCoverage) &&
1217 14 : ((m_dt.GetClass() == GEDTC_NUMERIC &&
1218 8 : m_dt.GetNumericDataType() == GDT_UInt32) ||
1219 4 : (m_dt.GetClass() == GEDTC_COMPOUND &&
1220 4 : m_dt.GetComponents().size() == 1 &&
1221 4 : m_dt.GetComponents()[0]->GetType().GetClass() == GEDTC_NUMERIC &&
1222 2 : m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
1223 : GDT_UInt32)))
1224 : {
1225 16 : if (auto poRootGroup = HDF5Array::GetRootGroup())
1226 : {
1227 24 : if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
1228 : {
1229 8 : const auto poGroupFArray = poGroupF->OpenMDArray(
1230 : bIsQualityOfSurvey ? "QualityOfSurvey"
1231 24 : : "QualityOfBathymetryCoverage");
1232 8 : if (poGroupFArray &&
1233 24 : poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
1234 16 : poGroupFArray->GetDataType().GetComponents().size() == 8 &&
1235 8 : poGroupFArray->GetDataType()
1236 8 : .GetComponents()[0]
1237 16 : ->GetName() == "code" &&
1238 8 : poGroupFArray->GetDataType()
1239 8 : .GetComponents()[3]
1240 8 : ->GetName() == "fillValue" &&
1241 24 : poGroupFArray->GetDimensionCount() == 1 &&
1242 8 : poGroupFArray->GetDimensions()[0]->GetSize() == 1)
1243 : {
1244 : auto poFillValue =
1245 24 : poGroupFArray->GetView("[\"fillValue\"]");
1246 8 : if (poFillValue)
1247 : {
1248 8 : char *pszVal0 = nullptr;
1249 8 : const GUInt64 anArrayStartIdx0[] = {0};
1250 8 : const size_t anCount[] = {1};
1251 8 : const GInt64 anArrayStep[] = {0};
1252 8 : const GPtrDiff_t anBufferStride[] = {0};
1253 16 : poFillValue->Read(anArrayStartIdx0, anCount,
1254 : anArrayStep, anBufferStride,
1255 16 : GDALExtendedDataType::CreateString(),
1256 : &pszVal0);
1257 8 : if (pszVal0)
1258 : {
1259 8 : const uint32_t nNoData = atoi(pszVal0);
1260 8 : m_abyNoData.resize(m_dt.GetSize());
1261 8 : memcpy(m_abyNoData.data(), &nNoData,
1262 : m_abyNoData.size());
1263 : }
1264 8 : CPLFree(pszVal0);
1265 : }
1266 : }
1267 : }
1268 : }
1269 : }
1270 :
1271 : // Special case for S104 nodata value that is typically -9999
1272 2107 : if (STARTS_WITH(GetFullName().c_str(), "/WaterLevel/WaterLevel.01/") &&
1273 91 : GetFullName().find("/values") != std::string::npos &&
1274 95 : m_dt.GetClass() == GEDTC_COMPOUND && m_dt.GetSize() == 8 &&
1275 62 : m_dt.GetComponents().size() == 2 &&
1276 31 : m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
1277 2107 : GDT_Float32 &&
1278 : // In theory should be Byte, but 104US00_ches_dcf2_20190606T12Z.h5 uses Int32
1279 31 : (m_dt.GetComponents()[1]->GetType().GetNumericDataType() == GDT_UInt8 ||
1280 0 : m_dt.GetComponents()[1]->GetType().GetNumericDataType() == GDT_Int32))
1281 : {
1282 31 : m_abyNoData.resize(m_dt.GetSize());
1283 31 : float fNoData = -9999.0f;
1284 :
1285 62 : if (auto poRootGroup = HDF5Array::GetRootGroup())
1286 : {
1287 93 : if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
1288 : {
1289 93 : const auto poGroupFArray = poGroupF->OpenMDArray("WaterLevel");
1290 31 : if (poGroupFArray &&
1291 93 : poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
1292 62 : poGroupFArray->GetDataType().GetComponents().size() == 8 &&
1293 31 : poGroupFArray->GetDataType()
1294 31 : .GetComponents()[0]
1295 62 : ->GetName() == "code" &&
1296 31 : poGroupFArray->GetDataType()
1297 31 : .GetComponents()[3]
1298 31 : ->GetName() == "fillValue" &&
1299 93 : poGroupFArray->GetDimensionCount() == 1 &&
1300 31 : poGroupFArray->GetDimensions()[0]->GetSize() >= 2)
1301 : {
1302 : auto poFillValue =
1303 93 : poGroupFArray->GetView("[\"fillValue\"]");
1304 31 : if (poFillValue)
1305 : {
1306 31 : char *pszVal0 = nullptr;
1307 31 : const GUInt64 anArrayStartIdx0[] = {0};
1308 31 : const size_t anCount[] = {1};
1309 31 : const GInt64 anArrayStep[] = {0};
1310 31 : const GPtrDiff_t anBufferStride[] = {0};
1311 62 : poFillValue->Read(anArrayStartIdx0, anCount,
1312 : anArrayStep, anBufferStride,
1313 62 : GDALExtendedDataType::CreateString(),
1314 : &pszVal0);
1315 31 : if (pszVal0)
1316 : {
1317 31 : fNoData = static_cast<float>(CPLAtof(pszVal0));
1318 : }
1319 31 : CPLFree(pszVal0);
1320 : }
1321 : }
1322 : }
1323 : }
1324 :
1325 31 : memcpy(m_abyNoData.data(), &fNoData, sizeof(float));
1326 : }
1327 :
1328 : // Special case for S111 nodata value that is typically -9999
1329 2048 : if (STARTS_WITH(GetFullName().c_str(),
1330 87 : "/SurfaceCurrent/SurfaceCurrent.01/") &&
1331 119 : GetFullName().find("/values") != std::string::npos &&
1332 64 : m_dt.GetClass() == GEDTC_COMPOUND &&
1333 63 : m_dt.GetSize() == 2 * sizeof(float) &&
1334 62 : m_dt.GetComponents().size() == 2 &&
1335 31 : m_dt.GetComponents()[0]->GetType().GetNumericDataType() ==
1336 2135 : GDT_Float32 &&
1337 31 : m_dt.GetComponents()[1]->GetType().GetNumericDataType() == GDT_Float32)
1338 : {
1339 31 : float afNoData[2] = {-9999.0f, -9999.0f};
1340 :
1341 62 : if (auto poRootGroup = HDF5Array::GetRootGroup())
1342 : {
1343 93 : if (const auto poGroupF = poRootGroup->OpenGroup("Group_F"))
1344 : {
1345 : const auto poGroupFArray =
1346 93 : poGroupF->OpenMDArray("SurfaceCurrent");
1347 31 : if (poGroupFArray &&
1348 93 : poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND &&
1349 62 : poGroupFArray->GetDataType().GetComponents().size() == 8 &&
1350 31 : poGroupFArray->GetDataType()
1351 31 : .GetComponents()[0]
1352 62 : ->GetName() == "code" &&
1353 31 : poGroupFArray->GetDataType()
1354 31 : .GetComponents()[3]
1355 31 : ->GetName() == "fillValue" &&
1356 93 : poGroupFArray->GetDimensionCount() == 1 &&
1357 31 : poGroupFArray->GetDimensions()[0]->GetSize() >= 2)
1358 : {
1359 : auto poFillValue =
1360 93 : poGroupFArray->GetView("[\"fillValue\"]");
1361 31 : if (poFillValue)
1362 : {
1363 31 : char *pszVal0 = nullptr;
1364 31 : char *pszVal1 = nullptr;
1365 31 : const GUInt64 anArrayStartIdx0[] = {0};
1366 31 : const GUInt64 anArrayStartIdx1[] = {1};
1367 31 : const size_t anCount[] = {1};
1368 31 : const GInt64 anArrayStep[] = {0};
1369 31 : const GPtrDiff_t anBufferStride[] = {0};
1370 62 : poFillValue->Read(anArrayStartIdx0, anCount,
1371 : anArrayStep, anBufferStride,
1372 62 : GDALExtendedDataType::CreateString(),
1373 : &pszVal0);
1374 62 : poFillValue->Read(anArrayStartIdx1, anCount,
1375 : anArrayStep, anBufferStride,
1376 62 : GDALExtendedDataType::CreateString(),
1377 : &pszVal1);
1378 31 : if (pszVal0 && pszVal1)
1379 : {
1380 31 : afNoData[0] = static_cast<float>(CPLAtof(pszVal0));
1381 31 : afNoData[1] = static_cast<float>(CPLAtof(pszVal1));
1382 : }
1383 31 : CPLFree(pszVal0);
1384 31 : CPLFree(pszVal1);
1385 : }
1386 : }
1387 : }
1388 : }
1389 :
1390 31 : m_abyNoData.resize(m_dt.GetSize());
1391 31 : memcpy(m_abyNoData.data(), afNoData, m_abyNoData.size());
1392 : }
1393 :
1394 2048 : if (bSkipFullDimensionInstantiation)
1395 : {
1396 1497 : const int nDims = H5Sget_simple_extent_ndims(m_hDataSpace);
1397 2994 : std::vector<hsize_t> anDimSizes(nDims);
1398 1497 : if (nDims)
1399 : {
1400 1478 : H5Sget_simple_extent_dims(m_hDataSpace, &anDimSizes[0], nullptr);
1401 3805 : for (int i = 0; i < nDims; ++i)
1402 : {
1403 4654 : m_dims.emplace_back(std::make_shared<GDALDimension>(
1404 4654 : std::string(), CPLSPrintf("dim%d", i), std::string(),
1405 6981 : std::string(), anDimSizes[i]));
1406 : }
1407 : }
1408 : }
1409 : else
1410 : {
1411 551 : InstantiateDimensions(osParentName, poGroup);
1412 : }
1413 : }
1414 :
1415 : /************************************************************************/
1416 : /* InstantiateDimensions() */
1417 : /************************************************************************/
1418 :
1419 551 : void HDF5Array::InstantiateDimensions(const std::string &osParentName,
1420 : const HDF5Group *poGroup)
1421 : {
1422 551 : const int nDims = H5Sget_simple_extent_ndims(m_hDataSpace);
1423 551 : std::vector<hsize_t> anDimSizes(nDims);
1424 551 : if (nDims)
1425 : {
1426 548 : H5Sget_simple_extent_dims(m_hDataSpace, &anDimSizes[0], nullptr);
1427 : }
1428 :
1429 : // Build a "classic" subdataset name from group and array names
1430 : const std::string osSubdatasetName(
1431 551 : "/" +
1432 551 : CPLString(osParentName)
1433 1102 : .replaceAll("Data Fields", "Data_Fields")
1434 1653 : .replaceAll("Geolocation Fields", "Geolocation_Fields") +
1435 1102 : "/" + GetName());
1436 :
1437 551 : if (nDims == 1)
1438 : {
1439 720 : auto attrCLASS = GetAttribute("CLASS");
1440 382 : if (attrCLASS && attrCLASS->GetDimensionCount() == 0 &&
1441 22 : attrCLASS->GetDataType().GetClass() == GEDTC_STRING)
1442 : {
1443 22 : const char *pszStr = attrCLASS->ReadAsString();
1444 22 : if (pszStr && EQUAL(pszStr, "DIMENSION_SCALE"))
1445 : {
1446 66 : auto attrName = GetAttribute("NAME");
1447 42 : if (attrName &&
1448 42 : attrName->GetDataType().GetClass() == GEDTC_STRING)
1449 : {
1450 20 : const char *pszName = attrName->ReadAsString();
1451 20 : if (pszName &&
1452 20 : STARTS_WITH(pszName, "This is a netCDF dimension but "
1453 : "not a netCDF variable"))
1454 : {
1455 0 : m_dims.emplace_back(std::make_shared<GDALDimension>(
1456 0 : std::string(), GetName(), std::string(),
1457 0 : std::string(), anDimSizes[0]));
1458 0 : return;
1459 : }
1460 : }
1461 :
1462 44 : m_dims.emplace_back(std::make_shared<HDF5Dimension>(
1463 44 : osParentName, GetName(), std::string(), std::string(),
1464 44 : anDimSizes[0], m_poShared));
1465 22 : return;
1466 : }
1467 : }
1468 : }
1469 :
1470 529 : std::map<size_t, std::string> mapDimIndexToDimFullName;
1471 :
1472 529 : if (m_bHasDimensionList)
1473 : {
1474 32 : hid_t hAttr = H5Aopen_name(m_hArray, "DIMENSION_LIST");
1475 32 : const hid_t hAttrDataType = H5Aget_type(hAttr);
1476 32 : const hid_t hAttrSpace = H5Aget_space(hAttr);
1477 64 : if (H5Tget_class(hAttrDataType) == H5T_VLEN &&
1478 32 : H5Sget_simple_extent_ndims(hAttrSpace) == 1)
1479 : {
1480 32 : const hid_t hBaseType = H5Tget_super(hAttrDataType);
1481 32 : if (H5Tget_class(hBaseType) == H5T_REFERENCE)
1482 : {
1483 32 : hsize_t nSize = 0;
1484 32 : H5Sget_simple_extent_dims(hAttrSpace, &nSize, nullptr);
1485 32 : if (nSize == static_cast<hsize_t>(nDims))
1486 : {
1487 64 : std::vector<hvl_t> aHvl(static_cast<size_t>(nSize));
1488 32 : H5Aread(hAttr, hAttrDataType, &aHvl[0]);
1489 105 : for (size_t i = 0; i < nSize; i++)
1490 : {
1491 145 : if (aHvl[i].len == 1 &&
1492 72 : H5Rget_obj_type(m_hArray, H5R_OBJECT, aHvl[i].p) ==
1493 : H5G_DATASET)
1494 : {
1495 144 : std::string referenceName;
1496 72 : referenceName.resize(256);
1497 144 : auto ret = H5Rget_name(
1498 72 : m_poShared->GetHDF5(), H5R_OBJECT, aHvl[i].p,
1499 72 : &referenceName[0], referenceName.size());
1500 72 : if (ret > 0)
1501 : {
1502 72 : referenceName.resize(ret);
1503 72 : mapDimIndexToDimFullName[i] =
1504 144 : std::move(referenceName);
1505 : }
1506 : }
1507 : }
1508 32 : H5Dvlen_reclaim(hAttrDataType, hAttrSpace, H5P_DEFAULT,
1509 32 : &aHvl[0]);
1510 : }
1511 : }
1512 32 : H5Tclose(hBaseType);
1513 : }
1514 32 : H5Tclose(hAttrDataType);
1515 32 : H5Sclose(hAttrSpace);
1516 32 : H5Aclose(hAttr);
1517 : }
1518 497 : else if (m_bHasDimensionLabels)
1519 : {
1520 2 : hid_t hAttr = H5Aopen_name(m_hArray, "DIMENSION_LABELS");
1521 2 : auto attr(HDF5Attribute::Create(m_osGroupFullname, GetFullName(),
1522 6 : "DIMENSION_LABELS", m_poShared, hAttr));
1523 4 : if (attr && attr->GetDimensionCount() == 1 &&
1524 2 : attr->GetDataType().GetClass() == GEDTC_STRING)
1525 : {
1526 4 : auto aosList = attr->ReadAsStringArray();
1527 2 : if (aosList.size() == nDims)
1528 : {
1529 8 : for (int i = 0; i < nDims; ++i)
1530 : {
1531 6 : if (aosList[i][0] != '\0')
1532 : {
1533 2 : mapDimIndexToDimFullName[i] = aosList[i];
1534 : }
1535 : }
1536 : }
1537 : }
1538 : }
1539 : else
1540 : {
1541 : // Use HDF-EOS5 metadata if available to create dimensions
1542 495 : HDF5EOSParser::GridDataFieldMetadata oGridDataFieldMetadata;
1543 495 : HDF5EOSParser::SwathFieldMetadata oSwathFieldMetadata;
1544 495 : const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
1545 501 : if (poHDF5EOSParser &&
1546 6 : poHDF5EOSParser->GetGridDataFieldMetadata(osSubdatasetName.c_str(),
1547 501 : oGridDataFieldMetadata) &&
1548 3 : oGridDataFieldMetadata.aoDimensions.size() ==
1549 3 : static_cast<size_t>(nDims))
1550 : {
1551 6 : std::map<std::string, std::shared_ptr<GDALDimension>> oMap;
1552 3 : const auto groupDims = m_poShared->GetEOSGridDimensions(
1553 6 : oGridDataFieldMetadata.poGridMetadata->osGridName);
1554 10 : for (const auto &dim : groupDims)
1555 : {
1556 7 : oMap[dim->GetName()] = dim;
1557 : }
1558 3 : int iDimX = 0;
1559 3 : int iDimY = 0;
1560 3 : int iCount = 1;
1561 9 : for (const auto &oDim : oGridDataFieldMetadata.aoDimensions)
1562 : {
1563 6 : auto oIter = oMap.find(oDim.osName);
1564 : // HDF5EOSParser guarantees that
1565 6 : CPLAssert(oIter != oMap.end());
1566 6 : const auto &poDim = oIter->second;
1567 6 : if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
1568 3 : iDimX = iCount;
1569 3 : else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
1570 3 : iDimY = iCount;
1571 6 : ++iCount;
1572 6 : m_dims.emplace_back(poDim);
1573 : }
1574 :
1575 3 : auto poSRS = oGridDataFieldMetadata.poGridMetadata->GetSRS();
1576 3 : if (poSRS)
1577 : {
1578 3 : m_poSRS = std::shared_ptr<OGRSpatialReference>(poSRS->Clone());
1579 3 : if (iDimX > 0 && iDimY > 0)
1580 : {
1581 3 : if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
1582 6 : std::vector<int>{2, 1})
1583 1 : m_poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
1584 : else
1585 2 : m_poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
1586 : }
1587 : }
1588 :
1589 3 : return;
1590 : }
1591 495 : else if (poHDF5EOSParser &&
1592 3 : poHDF5EOSParser->GetSwathFieldMetadata(
1593 495 : osSubdatasetName.c_str(), oSwathFieldMetadata) &&
1594 3 : oSwathFieldMetadata.aoDimensions.size() ==
1595 3 : static_cast<size_t>(nDims))
1596 : {
1597 6 : std::map<std::string, std::shared_ptr<GDALDimension>> oMap;
1598 3 : const auto groupDims = m_poShared->GetEOSSwathDimensions(
1599 3 : oSwathFieldMetadata.poSwathMetadata->osSwathName);
1600 12 : for (const auto &dim : groupDims)
1601 : {
1602 9 : oMap[dim->GetName()] = dim;
1603 : }
1604 10 : for (const auto &oDim : oSwathFieldMetadata.aoDimensions)
1605 : {
1606 7 : auto oIter = oMap.find(oDim.osName);
1607 : // HDF5EOSParser guarantees that
1608 7 : CPLAssert(oIter != oMap.end());
1609 7 : const auto &poDim = oIter->second;
1610 7 : m_dims.emplace_back(poDim);
1611 : }
1612 :
1613 3 : return;
1614 : }
1615 :
1616 : // Special case for S100-family of products (S102, S104, S111)
1617 104 : const auto SpecialCaseS100 = [&](const std::string &osCoverageName)
1618 : {
1619 208 : auto poRootGroup = m_poShared->GetRootGroup();
1620 104 : if (poRootGroup)
1621 : {
1622 104 : m_poSRS = std::make_shared<OGRSpatialReference>();
1623 104 : if (S100ReadSRS(poRootGroup.get(), *(m_poSRS.get())))
1624 : {
1625 104 : if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
1626 208 : std::vector<int>{2, 1})
1627 32 : m_poSRS->SetDataAxisToSRSAxisMapping({1, 2});
1628 : else
1629 72 : m_poSRS->SetDataAxisToSRSAxisMapping({2, 1});
1630 : }
1631 : else
1632 : {
1633 0 : m_poSRS.reset();
1634 : }
1635 :
1636 : auto poCoverage =
1637 104 : poRootGroup->OpenGroupFromFullname(osCoverageName);
1638 104 : if (poCoverage)
1639 : {
1640 104 : std::vector<std::shared_ptr<GDALMDArray>> apoIndexingVars;
1641 104 : if (S100GetDimensions(poCoverage.get(), m_dims,
1642 104 : apoIndexingVars) &&
1643 104 : m_dims.size() == 2 &&
1644 312 : m_dims[0]->GetSize() == anDimSizes[0] &&
1645 104 : m_dims[1]->GetSize() == anDimSizes[1])
1646 : {
1647 312 : for (const auto &poIndexingVar : apoIndexingVars)
1648 208 : m_poShared->KeepRef(poIndexingVar);
1649 104 : return true;
1650 : }
1651 : else
1652 : {
1653 0 : m_dims.clear();
1654 : }
1655 : }
1656 : }
1657 0 : return false;
1658 489 : };
1659 :
1660 634 : if (nDims == 2 &&
1661 145 : GetFullName() ==
1662 : "/BathymetryCoverage/BathymetryCoverage.01/Group_001/values")
1663 : {
1664 : // S102
1665 37 : if (SpecialCaseS100("/BathymetryCoverage/BathymetryCoverage.01"))
1666 37 : return;
1667 : }
1668 560 : else if (nDims == 2 &&
1669 108 : GetFullName() ==
1670 : "/QualityOfSurvey/QualityOfSurvey.01/Group_001/values")
1671 : {
1672 : // S102
1673 3 : if (SpecialCaseS100("/QualityOfSurvey/QualityOfSurvey.01"))
1674 3 : return;
1675 : }
1676 554 : else if (nDims == 2 &&
1677 105 : STARTS_WITH(GetFullName().c_str(),
1678 554 : "/WaterLevel/WaterLevel.01/") &&
1679 32 : GetFullName().find("/values"))
1680 : {
1681 : // S104
1682 32 : if (SpecialCaseS100("/WaterLevel/WaterLevel.01"))
1683 32 : return;
1684 : }
1685 490 : else if (nDims == 2 &&
1686 73 : STARTS_WITH(GetFullName().c_str(),
1687 490 : "/SurfaceCurrent/SurfaceCurrent.01/") &&
1688 32 : GetFullName().find("/values"))
1689 : {
1690 : // S111
1691 32 : if (SpecialCaseS100("/SurfaceCurrent/SurfaceCurrent.01"))
1692 32 : return;
1693 : }
1694 : }
1695 :
1696 838 : std::map<std::string, std::shared_ptr<GDALDimension>> oMapFullNameToDim;
1697 419 : bool bMissingDims = true;
1698 : // Fill oMapFullNameToDim with dimensions from the current group
1699 419 : if (!mapDimIndexToDimFullName.empty())
1700 : {
1701 34 : CPLAssert(poGroup);
1702 68 : const auto groupDims = poGroup->GetDimensions();
1703 191 : for (const auto &dim : groupDims)
1704 : {
1705 157 : oMapFullNameToDim[dim->GetFullName()] = dim;
1706 : }
1707 :
1708 34 : bMissingDims = false;
1709 106 : for (int i = 0; i < nDims; ++i)
1710 : {
1711 : const auto oIter =
1712 76 : mapDimIndexToDimFullName.find(static_cast<size_t>(i));
1713 76 : if (oIter != mapDimIndexToDimFullName.end())
1714 : {
1715 71 : const auto oIter2 = oMapFullNameToDim.find(oIter->second);
1716 71 : if (oIter2 == oMapFullNameToDim.end())
1717 : {
1718 4 : bMissingDims = true;
1719 4 : break;
1720 : }
1721 : }
1722 : }
1723 : }
1724 :
1725 : // If not enough, look for dimensions in other groups
1726 419 : if (bMissingDims)
1727 : {
1728 778 : auto poRootGroup = m_poShared->GetRootGroup();
1729 389 : if (poRootGroup)
1730 : {
1731 832 : for (int i = 0; i < nDims; ++i)
1732 : {
1733 : auto oIter =
1734 443 : mapDimIndexToDimFullName.find(static_cast<size_t>(i));
1735 443 : if (oIter != mapDimIndexToDimFullName.end())
1736 : {
1737 7 : const auto &osDimFullName = oIter->second;
1738 : auto poDim =
1739 14 : poRootGroup->OpenDimensionFromFullname(osDimFullName);
1740 7 : if (poDim)
1741 : {
1742 5 : oMapFullNameToDim[osDimFullName] = std::move(poDim);
1743 : }
1744 : }
1745 : }
1746 : }
1747 : }
1748 :
1749 929 : for (int i = 0; i < nDims; ++i)
1750 : {
1751 510 : auto oIter = mapDimIndexToDimFullName.find(static_cast<size_t>(i));
1752 510 : if (oIter != mapDimIndexToDimFullName.end())
1753 : {
1754 74 : auto oIter2 = oMapFullNameToDim.find(oIter->second);
1755 74 : if (oIter2 != oMapFullNameToDim.end())
1756 : {
1757 72 : m_dims.emplace_back(oIter2->second);
1758 72 : continue;
1759 : }
1760 :
1761 4 : std::string osDimName(oIter->second);
1762 2 : auto nPos = osDimName.rfind('/');
1763 2 : if (nPos != std::string::npos)
1764 : {
1765 0 : const std::string osDimParentName(osDimName.substr(0, nPos));
1766 0 : osDimName = osDimName.substr(nPos + 1);
1767 :
1768 0 : m_dims.emplace_back(std::make_shared<HDF5Dimension>(
1769 0 : osDimParentName.empty() ? "/" : osDimParentName, osDimName,
1770 0 : std::string(), std::string(), anDimSizes[i], m_poShared));
1771 : }
1772 : else
1773 : {
1774 4 : m_dims.emplace_back(std::make_shared<GDALDimension>(
1775 4 : std::string(), osDimName, std::string(), std::string(),
1776 4 : anDimSizes[i]));
1777 : }
1778 : }
1779 : else
1780 : {
1781 872 : m_dims.emplace_back(std::make_shared<GDALDimension>(
1782 872 : std::string(), CPLSPrintf("dim%d", i), std::string(),
1783 1308 : std::string(), anDimSizes[i]));
1784 : }
1785 : }
1786 :
1787 : // Use HDF-EOS5 metadata if available to create SRS
1788 838 : HDF5EOSParser::GridDataFieldMetadata oGridDataFieldMetadata;
1789 419 : const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
1790 420 : if (poHDF5EOSParser &&
1791 1 : poHDF5EOSParser->GetGridDataFieldMetadata(osSubdatasetName.c_str(),
1792 : oGridDataFieldMetadata))
1793 : {
1794 2 : auto poSRS = oGridDataFieldMetadata.poGridMetadata->GetSRS();
1795 1 : if (poSRS)
1796 : {
1797 1 : int iDimX = 0;
1798 1 : int iDimY = 0;
1799 4 : for (int iDim = 0; iDim < static_cast<int>(m_dims.size()); ++iDim)
1800 : {
1801 3 : const auto &poDim = m_dims[iDim];
1802 3 : if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_X)
1803 1 : iDimX = iDim + 1;
1804 2 : else if (poDim->GetType() == GDAL_DIM_TYPE_HORIZONTAL_Y)
1805 1 : iDimY = iDim + 1;
1806 : }
1807 :
1808 1 : if (iDimX > 0 && iDimY > 0)
1809 : {
1810 1 : m_poSRS = std::shared_ptr<OGRSpatialReference>(poSRS->Clone());
1811 1 : if (m_poSRS->GetDataAxisToSRSAxisMapping() ==
1812 2 : std::vector<int>{2, 1})
1813 0 : m_poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
1814 : else
1815 1 : m_poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});
1816 : }
1817 : }
1818 : }
1819 : }
1820 :
1821 : /************************************************************************/
1822 : /* GetCoordinateVariables() */
1823 : /************************************************************************/
1824 :
1825 : std::vector<std::shared_ptr<GDALMDArray>>
1826 3 : HDF5Array::GetCoordinateVariables() const
1827 : {
1828 3 : std::vector<std::shared_ptr<GDALMDArray>> ret;
1829 :
1830 6 : HDF5EOSParser::SwathFieldMetadata oSwathFieldMetadata;
1831 3 : const auto poHDF5EOSParser = m_poShared->GetHDF5EOSParser();
1832 : // Build a "classic" subdataset name from group and array names
1833 : const std::string osSubdatasetName(
1834 3 : "/" + CPLString(GetFullName())
1835 6 : .replaceAll("Data Fields", "Data_Fields")
1836 9 : .replaceAll("Geolocation Fields", "Geolocation_Fields"));
1837 6 : if (poHDF5EOSParser &&
1838 3 : poHDF5EOSParser->GetSwathFieldMetadata(osSubdatasetName.c_str(),
1839 6 : oSwathFieldMetadata) &&
1840 1 : oSwathFieldMetadata.aoDimensions.size() == GetDimensionCount())
1841 : {
1842 1 : if (!oSwathFieldMetadata.osLongitudeSubdataset.empty() &&
1843 1 : oSwathFieldMetadata.nPixelOffset == 0 &&
1844 1 : oSwathFieldMetadata.nLineOffset == 0 &&
1845 3 : oSwathFieldMetadata.nPixelStep == 1 &&
1846 1 : oSwathFieldMetadata.nLineStep == 1)
1847 : {
1848 2 : auto poRootGroup = m_poShared->GetRootGroup();
1849 1 : if (poRootGroup)
1850 : {
1851 1 : auto poLongitude = poRootGroup->OpenMDArrayFromFullname(
1852 2 : CPLString(
1853 0 : oSwathFieldMetadata.osLongitudeSubdataset.substr(1))
1854 : .replaceAll("Geolocation_Fields",
1855 4 : "Geolocation Fields"));
1856 1 : auto poLatitude = poRootGroup->OpenMDArrayFromFullname(
1857 2 : CPLString(
1858 0 : oSwathFieldMetadata.osLatitudeSubdataset.substr(1))
1859 : .replaceAll("Geolocation_Fields",
1860 4 : "Geolocation Fields"));
1861 1 : if (poLongitude && poLatitude)
1862 : {
1863 1 : ret.push_back(std::move(poLongitude));
1864 1 : ret.push_back(std::move(poLatitude));
1865 : }
1866 : }
1867 : }
1868 : }
1869 :
1870 6 : return ret;
1871 : }
1872 :
1873 : /************************************************************************/
1874 : /* GetFilterInfo() */
1875 : /************************************************************************/
1876 :
1877 8 : static CPLStringList GetFilterInfo(hid_t hArray, unsigned nFilterMask)
1878 : {
1879 : static const struct
1880 : {
1881 : int nFilterId;
1882 : const char *pszName;
1883 : } gasHDF5Filters[] = {
1884 : // { H5Z_FILTER_DEFLATE, "DEFLATE" },
1885 : {H5Z_FILTER_SHUFFLE, "SHUFFLE"},
1886 : {H5Z_FILTER_FLETCHER32, "FLETCHER32"},
1887 : // { H5Z_FILTER_SZIP, "SZIP" },
1888 : {H5Z_FILTER_NBIT, "NBIT"},
1889 : {H5Z_FILTER_SCALEOFFSET, "SCALEOFFSET"},
1890 : // Below from netcdf_filter.h
1891 : {/*H5Z_FILTER_ZSTD */ 32015, "ZSTD"},
1892 : {/*H5Z_FILTER_BZIP2 */ 307, "BZIP2"},
1893 : {/*H5Z_FILTER_BLOSC */ 32001, "BLOSC"},
1894 : };
1895 :
1896 8 : CPLStringList aosInfo;
1897 8 : const hid_t nListId = H5Dget_create_plist(hArray);
1898 8 : if (nListId > 0)
1899 : {
1900 8 : const int nFilters = H5Pget_nfilters(nListId);
1901 8 : const char *pszCompression = nullptr;
1902 16 : std::string osFilters;
1903 12 : for (int i = 0; i < nFilters; ++i)
1904 : {
1905 4 : unsigned int flags = 0;
1906 4 : size_t cd_nelmts = 0;
1907 4 : char szName[64 + 1] = {0};
1908 : const auto eFilter =
1909 4 : H5Pget_filter(nListId, i, &flags, &cd_nelmts, nullptr,
1910 : sizeof(szName) - 1, szName);
1911 4 : if ((flags & nFilterMask) == 0)
1912 : {
1913 4 : if (eFilter == H5Z_FILTER_DEFLATE)
1914 : {
1915 2 : pszCompression = "DEFLATE";
1916 : }
1917 2 : else if (eFilter == H5Z_FILTER_SZIP)
1918 : {
1919 0 : pszCompression = "SZIP";
1920 : }
1921 : else
1922 : {
1923 2 : bool bFound = false;
1924 2 : if (!osFilters.empty())
1925 0 : osFilters += ',';
1926 2 : for (const auto &sFilterInfo : gasHDF5Filters)
1927 : {
1928 2 : if (sFilterInfo.nFilterId == eFilter)
1929 : {
1930 2 : bFound = true;
1931 2 : osFilters += sFilterInfo.pszName;
1932 2 : break;
1933 : }
1934 : }
1935 2 : if (!bFound)
1936 0 : osFilters += szName;
1937 : }
1938 : }
1939 : }
1940 8 : H5Pclose(nListId);
1941 8 : if (pszCompression)
1942 2 : aosInfo.SetNameValue(GDALMD_COMPRESSION, pszCompression);
1943 8 : if (!osFilters.empty())
1944 2 : aosInfo.SetNameValue("FILTER", osFilters.c_str());
1945 : }
1946 8 : return aosInfo;
1947 : }
1948 :
1949 : /************************************************************************/
1950 : /* HDF5Array::GetRawBlockInfo() */
1951 : /************************************************************************/
1952 :
1953 4 : bool HDF5Array::GetRawBlockInfo(const uint64_t *panBlockCoordinates,
1954 : GDALMDArrayRawBlockInfo &info) const
1955 : {
1956 4 : info.clear();
1957 :
1958 8 : const auto anBlockSize = GetBlockSize();
1959 4 : CPLAssert(anBlockSize.size() == m_dims.size());
1960 :
1961 7 : const auto AddExtraInfo = [this, &info]()
1962 : {
1963 3 : if (m_dt.GetSize() > 1)
1964 : {
1965 2 : const hid_t hDataType = H5Dget_type(m_hArray);
1966 2 : switch (H5Tget_order(hDataType))
1967 : {
1968 1 : case H5T_ORDER_LE:
1969 1 : info.papszInfo =
1970 1 : CSLSetNameValue(info.papszInfo, "ENDIANNESS", "LITTLE");
1971 1 : break;
1972 1 : case H5T_ORDER_BE:
1973 1 : info.papszInfo =
1974 1 : CSLSetNameValue(info.papszInfo, "ENDIANNESS", "BIG");
1975 1 : break;
1976 0 : case H5T_ORDER_VAX:
1977 0 : info.papszInfo =
1978 0 : CSLSetNameValue(info.papszInfo, "ENDIANNESS", "VAX");
1979 0 : break;
1980 0 : case H5T_ORDER_MIXED:
1981 0 : info.papszInfo =
1982 0 : CSLSetNameValue(info.papszInfo, "ENDIANNESS", "MIXED");
1983 0 : break;
1984 0 : case H5T_ORDER_NONE:
1985 : case H5T_ORDER_ERROR:
1986 0 : break;
1987 : }
1988 2 : H5Tclose(hDataType);
1989 : }
1990 7 : };
1991 :
1992 4 : if (!anBlockSize.empty() && anBlockSize[0] == 0)
1993 : {
1994 : HDF5_GLOBAL_LOCK();
1995 4 : const auto nOffset = H5Dget_offset(m_hArray);
1996 4 : if (nOffset != HADDR_UNDEF)
1997 : {
1998 4 : bool bAllZeroes = true;
1999 12 : for (size_t i = 0; i < m_dims.size() && bAllZeroes; ++i)
2000 8 : bAllZeroes = panBlockCoordinates[i] == 0;
2001 4 : if (bAllZeroes)
2002 : {
2003 3 : info.pszFilename = CPLStrdup(m_poShared->GetFilename().c_str());
2004 3 : info.nOffset = nOffset;
2005 3 : info.nSize = H5Dget_storage_size(m_hArray);
2006 3 : info.papszInfo =
2007 3 : GetFilterInfo(m_hArray, /* nFilterMask = */ 0).StealList();
2008 3 : AddExtraInfo();
2009 3 : return true;
2010 : }
2011 : else
2012 : {
2013 1 : CPLError(CE_Failure, CPLE_AppDefined,
2014 : "GetRawBlockInfo() failed: invalid block coordinates. "
2015 : "Should be all 0");
2016 1 : return false;
2017 : }
2018 : }
2019 : else
2020 : {
2021 0 : CPLError(CE_Failure, CPLE_AppDefined,
2022 : "GetRawBlockInfo() failed: array %s is not chunked or "
2023 : "contiguous",
2024 0 : GetName().c_str());
2025 0 : return false;
2026 : }
2027 : }
2028 :
2029 0 : [[maybe_unused]] uint64_t nAdditionalOffset = 0;
2030 : #if !(defined(H5_VERS_MAJOR) && \
2031 : (H5_VERS_MAJOR >= 2 || \
2032 : (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 14 && H5_VERS_RELEASE >= 4)))
2033 : #ifdef HAVE_H5Dchunk_iter
2034 : if (m_asBlockInfo.empty())
2035 : #endif
2036 : {
2037 : // Before HDF5 1.14.4, H5Dget_chunk_info() doesn't take into account the
2038 : // user block
2039 0 : const hid_t nListId = H5Fget_create_plist(m_poShared->GetHDF5());
2040 0 : if (nListId > 0)
2041 : {
2042 0 : hsize_t nUserBlockSize = 0;
2043 0 : if (H5Pget_userblock(nListId, &nUserBlockSize) >= 0)
2044 : {
2045 0 : nAdditionalOffset = nUserBlockSize;
2046 : }
2047 0 : H5Pclose(nListId);
2048 : }
2049 : }
2050 : #endif
2051 :
2052 : #if (defined(H5_VERS_MAJOR) && \
2053 : (H5_VERS_MAJOR >= 2 || (H5_VERS_MAJOR == 1 && H5_VERS_MINOR > 10) || \
2054 : (H5_VERS_MAJOR == 1 && H5_VERS_MINOR == 10 && H5_VERS_RELEASE >= 5)))
2055 : // Compute the block index from the coordinates
2056 : hsize_t nBlockIdx = 0;
2057 : hsize_t nMult = 1;
2058 : std::vector<uint64_t> anBlockCount(anBlockSize.size());
2059 : for (size_t nDimIdx = anBlockSize.size(); nDimIdx > 0;)
2060 : {
2061 : --nDimIdx;
2062 : const auto nBlockSize = anBlockSize[nDimIdx];
2063 : const auto nBlockCount =
2064 : cpl::div_round_up(m_dims[nDimIdx]->GetSize(), nBlockSize);
2065 : if (panBlockCoordinates[nDimIdx] >= nBlockCount)
2066 : {
2067 : CPLError(CE_Failure, CPLE_AppDefined,
2068 : "GetRawBlockInfo() failed: array %s: "
2069 : "invalid block coordinate (%u) for dimension %u",
2070 : GetName().c_str(),
2071 : static_cast<unsigned>(panBlockCoordinates[nDimIdx]),
2072 : static_cast<unsigned>(nDimIdx));
2073 : return false;
2074 : }
2075 : anBlockCount[nDimIdx] = nBlockCount;
2076 : nBlockIdx += panBlockCoordinates[nDimIdx] * nMult;
2077 : nMult *= nBlockCount;
2078 : }
2079 :
2080 : HDF5_GLOBAL_LOCK();
2081 :
2082 : #ifdef HAVE_H5Dchunk_iter
2083 : if (nBlockIdx == 0 && m_asBlockInfo.empty() && m_bFirstBlockInfo)
2084 : {
2085 : // If asking block 0, we assume that all blocks will be iterated
2086 : // and thus use H5Dchunk_iter() instead of H5Dget_chunk_info() for
2087 : // faster execution.
2088 :
2089 : m_bFirstBlockInfo = false;
2090 : const hsize_t nTotalBlockCount = nMult;
2091 : bool bTooManyBlocks = false;
2092 :
2093 : if constexpr (sizeof(size_t) < sizeof(hsize_t))
2094 : {
2095 : if (nTotalBlockCount > std::numeric_limits<size_t>::max())
2096 : {
2097 : bTooManyBlocks = true;
2098 : }
2099 : }
2100 :
2101 : if (!bTooManyBlocks)
2102 : {
2103 : try
2104 : {
2105 : m_asBlockInfo.resize(static_cast<size_t>(nTotalBlockCount));
2106 : }
2107 : catch (const std::exception &)
2108 : {
2109 : bTooManyBlocks = true;
2110 : }
2111 : }
2112 :
2113 : if (bTooManyBlocks)
2114 : {
2115 : CPLDebugOnce("HDF5",
2116 : "Too many blocks w.r.t. RAM to use H5Dchunk_iter(). "
2117 : "Falling back to slower method");
2118 : }
2119 : else
2120 : {
2121 : struct FillStruct
2122 : {
2123 : std::vector<BlockInfo> *pasBlockInfo = nullptr;
2124 : std::vector<GUInt64> anBlockSize{};
2125 : std::vector<uint64_t> anBlockCount{};
2126 : uint64_t nAdditionalOffset = 0;
2127 :
2128 : static int callback(const hsize_t *chunkCoords,
2129 : unsigned filter_mask, haddr_t addr,
2130 : hsize_t size, void *op_data)
2131 : {
2132 : FillStruct *fillStruct = static_cast<FillStruct *>(op_data);
2133 : uint64_t nIdx64 = 0;
2134 : uint64_t nMult = 1;
2135 : for (size_t iDim = fillStruct->anBlockCount.size();
2136 : iDim > 0;
2137 : /* */)
2138 : {
2139 : --iDim;
2140 : const auto nBlockCoord =
2141 : chunkCoords[iDim] / fillStruct->anBlockSize[iDim];
2142 : CPLAssert(nBlockCoord < fillStruct->anBlockCount[iDim]);
2143 : nIdx64 += nBlockCoord * nMult;
2144 : nMult *= fillStruct->anBlockCount[iDim];
2145 : }
2146 : CPLAssert(nIdx64 <= fillStruct->pasBlockInfo->size());
2147 : auto &sBlockInfo =
2148 : (*fillStruct
2149 : ->pasBlockInfo)[static_cast<size_t>(nIdx64)];
2150 : sBlockInfo.nOffset =
2151 : addr == HADDR_UNDEF
2152 : ? 0
2153 : : addr + fillStruct->nAdditionalOffset;
2154 : sBlockInfo.nSize = size;
2155 : sBlockInfo.nFilterMask = filter_mask;
2156 : #ifdef DEBUG_VERBOSE
2157 : CPLDebug("HDF5",
2158 : "Block %" PRIu64 ": offset = %" PRIu64
2159 : ", size = %" PRIu64,
2160 : nIdx64, sBlockInfo.nOffset, sBlockInfo.nSize);
2161 : #endif
2162 : return 0;
2163 : }
2164 : };
2165 :
2166 : FillStruct fillStruct;
2167 : fillStruct.pasBlockInfo = &m_asBlockInfo;
2168 : fillStruct.nAdditionalOffset = nAdditionalOffset;
2169 : fillStruct.anBlockSize = anBlockSize;
2170 : fillStruct.anBlockCount = anBlockCount;
2171 :
2172 : if (H5Dchunk_iter(m_hArray, H5P_DEFAULT, &FillStruct::callback,
2173 : &fillStruct) < 0)
2174 : {
2175 : m_asBlockInfo.clear();
2176 : CPLError(CE_Failure, CPLE_AppDefined,
2177 : "GetRawBlockInfo() failed: array %s: H5Dchunk_iter() "
2178 : "failed",
2179 : GetName().c_str());
2180 : return false;
2181 : }
2182 : }
2183 : }
2184 :
2185 : if (!m_asBlockInfo.empty())
2186 : {
2187 : CPLAssert(nBlockIdx < m_asBlockInfo.size());
2188 : const size_t nIdx = static_cast<size_t>(nBlockIdx);
2189 : info.pszFilename = CPLStrdup(m_poShared->GetFilename().c_str());
2190 : info.nOffset = m_asBlockInfo[nIdx].nOffset;
2191 : info.nSize = m_asBlockInfo[nIdx].nSize;
2192 : info.papszInfo =
2193 : GetFilterInfo(m_hArray, m_asBlockInfo[nIdx].nFilterMask)
2194 : .StealList();
2195 : AddExtraInfo();
2196 : return true;
2197 : }
2198 : #endif
2199 :
2200 : hsize_t nActualChunks = 0;
2201 : if (H5Dget_num_chunks(m_hArray, m_hDataSpace, &nActualChunks) < 0)
2202 : {
2203 : CPLError(
2204 : CE_Failure, CPLE_AppDefined,
2205 : "GetRawBlockInfo() failed: array %s: H5Dget_num_chunks() failed",
2206 : GetName().c_str());
2207 : return false;
2208 : }
2209 : const auto nExpectedChunks =
2210 : std::accumulate(anBlockCount.begin(), anBlockCount.end(),
2211 : std::uint64_t{1}, std::multiplies<>());
2212 : const bool bSparse = nExpectedChunks != nActualChunks;
2213 :
2214 : unsigned nFilterMask = 0;
2215 : haddr_t nChunkOffset = 0;
2216 : hsize_t nChunkSize = 0;
2217 :
2218 : if (bSparse)
2219 : {
2220 : std::vector<hsize_t> anCoordOffsets(GetDimensionCount());
2221 : for (size_t i = 0; i < GetDimensionCount(); i++)
2222 : {
2223 : anCoordOffsets[i] = panBlockCoordinates[i] * anBlockSize[i];
2224 : }
2225 : if (H5Dget_chunk_info_by_coord(m_hArray, anCoordOffsets.data(),
2226 : &nFilterMask, &nChunkOffset,
2227 : &nChunkSize) < 0)
2228 : {
2229 : CPLError(CE_Failure, CPLE_AppDefined,
2230 : "GetRawBlockInfo() failed: array %s: "
2231 : "H5Dget_chunk_info_by_coord() failed",
2232 : GetName().c_str());
2233 : return false;
2234 : }
2235 : }
2236 : else
2237 : {
2238 : std::vector<hsize_t> anOffset(GetDimensionCount());
2239 : if (H5Dget_chunk_info(m_hArray, m_hDataSpace, nBlockIdx,
2240 : anOffset.data(), &nFilterMask, &nChunkOffset,
2241 : &nChunkSize) < 0)
2242 : {
2243 : CPLError(CE_Failure, CPLE_AppDefined,
2244 : "GetRawBlockInfo() failed: array %s: H5Dget_chunk_info() "
2245 : "failed",
2246 : GetName().c_str());
2247 : return false;
2248 : }
2249 : }
2250 :
2251 : info.pszFilename = CPLStrdup(m_poShared->GetFilename().c_str());
2252 : info.nOffset =
2253 : nChunkOffset == HADDR_UNDEF ? 0 : nChunkOffset + nAdditionalOffset;
2254 : info.nSize = nChunkSize;
2255 : info.papszInfo = GetFilterInfo(m_hArray, nFilterMask).StealList();
2256 : AddExtraInfo();
2257 :
2258 : return true;
2259 : #else
2260 0 : CPLDebug("HDF5", "H5Dget_chunk_info() only available in HDF5 >= 1.10.5");
2261 0 : return false;
2262 : #endif
2263 : }
2264 :
2265 : /************************************************************************/
2266 : /* GetAttributesCallback() */
2267 : /************************************************************************/
2268 :
2269 6076 : herr_t HDF5Array::GetAttributesCallback(hid_t hArray, const char *pszObjName,
2270 : void *selfIn)
2271 : {
2272 6076 : HDF5Array *self = static_cast<HDF5Array *>(selfIn);
2273 6076 : if (self->m_bShowAllAttributes ||
2274 3519 : (strcmp(pszObjName, "_Netcdf4Dimid") != 0 &&
2275 3103 : strcmp(pszObjName, "_Netcdf4Coordinates") != 0 &&
2276 3091 : strcmp(pszObjName, "CLASS") != 0 && strcmp(pszObjName, "NAME") != 0))
2277 : {
2278 5368 : if (EQUAL(pszObjName, "DIMENSION_LIST"))
2279 : {
2280 519 : self->m_bHasDimensionList = true;
2281 519 : if (!self->m_bShowAllAttributes)
2282 353 : return 0;
2283 : }
2284 5015 : if (EQUAL(pszObjName, "DIMENSION_LABELS"))
2285 : {
2286 9 : self->m_bHasDimensionLabels = true;
2287 9 : if (!self->m_bShowAllAttributes)
2288 6 : return 0;
2289 : }
2290 :
2291 5009 : hid_t hAttr = H5Aopen_name(hArray, pszObjName);
2292 5009 : if (hAttr > 0)
2293 : {
2294 5009 : auto attr(HDF5Attribute::Create(self->m_osGroupFullname,
2295 : self->GetFullName(), pszObjName,
2296 10018 : self->m_poShared, hAttr));
2297 5009 : if (attr)
2298 : {
2299 : // Used by HDF5-EOS products
2300 58 : if (EQUAL(pszObjName, "_FillValue") &&
2301 116 : self->GetDataType().GetClass() == GEDTC_NUMERIC &&
2302 4683 : attr->GetDataType().GetClass() == GEDTC_NUMERIC &&
2303 58 : attr->GetDimensionCount() == 0)
2304 : {
2305 58 : auto oRawResult(attr->ReadAsRaw());
2306 58 : if (oRawResult.data())
2307 : {
2308 : // Round-trip attribute value to target data type and back
2309 : // to attribute data type to ensure there is no loss
2310 : // Normally _FillValue data type should be the same
2311 : // as the array one, but this is not always the case.
2312 : // For example NASA GEDI L2B products have Float64
2313 : // _FillValue for Float32 variables.
2314 58 : self->m_abyNoData.resize(self->GetDataType().GetSize());
2315 174 : GDALExtendedDataType::CopyValue(
2316 58 : oRawResult.data(), attr->GetDataType(),
2317 58 : self->m_abyNoData.data(), self->GetDataType());
2318 : std::vector<GByte> abyTmp(
2319 58 : attr->GetDataType().GetSize());
2320 174 : GDALExtendedDataType::CopyValue(
2321 58 : self->m_abyNoData.data(), self->GetDataType(),
2322 58 : abyTmp.data(), attr->GetDataType());
2323 58 : std::vector<GByte> abyOri;
2324 58 : abyOri.assign(oRawResult.data(),
2325 58 : oRawResult.data() + oRawResult.size());
2326 58 : if (abyOri == abyTmp)
2327 : {
2328 54 : if (!self->m_bShowAllAttributes)
2329 38 : return 0;
2330 : }
2331 : else
2332 : {
2333 4 : self->m_abyNoData.clear();
2334 4 : if (!self->m_bWarnedNoData)
2335 : {
2336 2 : self->m_bWarnedNoData = true;
2337 2 : char *pszVal = nullptr;
2338 4 : GDALExtendedDataType::CopyValue(
2339 2 : oRawResult.data(), attr->GetDataType(),
2340 : &pszVal,
2341 4 : GDALExtendedDataType::CreateString());
2342 6 : CPLError(CE_Warning, CPLE_AppDefined,
2343 : "Array %s: %s attribute value (%s) is "
2344 : "not in "
2345 : "the range of the "
2346 : "array data type (%s)",
2347 2 : self->GetName().c_str(), pszObjName,
2348 2 : pszVal ? pszVal : "(null)",
2349 : GDALGetDataTypeName(
2350 2 : self->GetDataType()
2351 : .GetNumericDataType()));
2352 2 : CPLFree(pszVal);
2353 : }
2354 : }
2355 : }
2356 : }
2357 :
2358 78 : if (EQUAL(pszObjName, "units") &&
2359 4607 : attr->GetDataType().GetClass() == GEDTC_STRING &&
2360 78 : attr->GetDimensionCount() == 0)
2361 : {
2362 78 : const char *pszStr = attr->ReadAsString();
2363 78 : if (pszStr)
2364 : {
2365 78 : self->m_osUnit = pszStr;
2366 78 : if (!self->m_bShowAllAttributes)
2367 39 : return 0;
2368 : }
2369 : }
2370 :
2371 4490 : self->m_oListAttributes.emplace_back(attr);
2372 : }
2373 : }
2374 : }
2375 5640 : return 0;
2376 : }
2377 :
2378 : /************************************************************************/
2379 : /* GetAttributeFromAttributes() */
2380 : /************************************************************************/
2381 :
2382 : /** Possible fallback implementation for GetAttribute() using GetAttributes().
2383 : */
2384 : std::shared_ptr<GDALAttribute>
2385 1820 : HDF5Array::GetAttribute(const std::string &osName) const
2386 : {
2387 1820 : const char *const apszOptions[] = {"SHOW_ALL=YES", nullptr};
2388 1820 : if (!m_bShowAllAttributes)
2389 1697 : GetAttributes(apszOptions);
2390 3760 : for (const auto &attr : m_oListAttributes)
2391 : {
2392 2186 : if (attr->GetName() == osName)
2393 246 : return attr;
2394 : }
2395 1574 : return nullptr;
2396 : }
2397 :
2398 : /************************************************************************/
2399 : /* GetAttributes() */
2400 : /************************************************************************/
2401 :
2402 : std::vector<std::shared_ptr<GDALAttribute>>
2403 3759 : HDF5Array::GetAttributes(CSLConstList papszOptions) const
2404 : {
2405 : HDF5_GLOBAL_LOCK();
2406 :
2407 3759 : m_oListAttributes.clear();
2408 3759 : m_bShowAllAttributes =
2409 3759 : CPLTestBool(CSLFetchNameValueDef(papszOptions, "SHOW_ALL", "NO"));
2410 3759 : H5Aiterate(m_hArray, nullptr, GetAttributesCallback,
2411 : const_cast<void *>(static_cast<const void *>(this)));
2412 3759 : return m_oListAttributes;
2413 : }
2414 :
2415 : /************************************************************************/
2416 : /* GetBlockSize() */
2417 : /************************************************************************/
2418 :
2419 234 : std::vector<GUInt64> HDF5Array::GetBlockSize() const
2420 : {
2421 : HDF5_GLOBAL_LOCK();
2422 :
2423 234 : const auto nDimCount = GetDimensionCount();
2424 234 : std::vector<GUInt64> res(nDimCount);
2425 234 : if (res.empty())
2426 1 : return res;
2427 :
2428 233 : const hid_t nListId = H5Dget_create_plist(m_hArray);
2429 233 : if (nListId > 0)
2430 : {
2431 233 : if (H5Pget_layout(nListId) == H5D_CHUNKED)
2432 : {
2433 318 : std::vector<hsize_t> anChunkDims(nDimCount);
2434 159 : const int nDimSize = H5Pget_chunk(
2435 159 : nListId, static_cast<int>(nDimCount), &anChunkDims[0]);
2436 159 : if (static_cast<size_t>(nDimSize) == nDimCount)
2437 : {
2438 477 : for (size_t i = 0; i < nDimCount; ++i)
2439 : {
2440 318 : res[i] = anChunkDims[i];
2441 : }
2442 : }
2443 : }
2444 :
2445 233 : H5Pclose(nListId);
2446 : }
2447 :
2448 233 : return res;
2449 : }
2450 :
2451 : /************************************************************************/
2452 : /* GetStructuralInfo() */
2453 : /************************************************************************/
2454 :
2455 5 : CSLConstList HDF5Array::GetStructuralInfo() const
2456 : {
2457 5 : if (m_aosStructuralInfo.empty())
2458 : {
2459 : HDF5_GLOBAL_LOCK();
2460 5 : m_aosStructuralInfo = GetFilterInfo(m_hArray, /* nFilterMask = */ 0);
2461 : }
2462 5 : return m_aosStructuralInfo.List();
2463 : }
2464 :
2465 : /************************************************************************/
2466 : /* CopyBuffer() */
2467 : /************************************************************************/
2468 :
2469 95 : static void CopyBuffer(size_t nDims, const size_t *count,
2470 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
2471 : const GDALExtendedDataType &bufferDataType,
2472 : GByte *pabySrc, void *pDstBuffer)
2473 : {
2474 95 : const size_t nBufferDataTypeSize(bufferDataType.GetSize());
2475 190 : std::vector<size_t> anStackCount(nDims);
2476 190 : std::vector<GByte *> pabySrcBufferStack(nDims + 1);
2477 190 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
2478 190 : std::vector<GPtrDiff_t> anSrcStride(nDims);
2479 190 : std::vector<size_t> anSrcOffset(nDims + 1);
2480 95 : size_t nCurStride = nBufferDataTypeSize;
2481 286 : for (size_t i = nDims; i > 0;)
2482 : {
2483 191 : --i;
2484 191 : anSrcStride[i] = arrayStep[i] > 0
2485 191 : ? nCurStride
2486 105 : : -static_cast<GPtrDiff_t>(nCurStride);
2487 191 : anSrcOffset[i] = arrayStep[i] > 0 ? 0 : (count[i] - 1) * nCurStride;
2488 191 : nCurStride *= count[i];
2489 : }
2490 95 : pabySrcBufferStack[0] = pabySrc + anSrcOffset[0];
2491 95 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
2492 95 : size_t iDim = 0;
2493 1469 : lbl_next_depth:
2494 1469 : if (iDim == nDims)
2495 : {
2496 1113 : memcpy(pabyDstBufferStack[nDims], pabySrcBufferStack[nDims],
2497 : nBufferDataTypeSize);
2498 : }
2499 : else
2500 : {
2501 356 : anStackCount[iDim] = count[iDim];
2502 : while (true)
2503 : {
2504 1374 : ++iDim;
2505 2748 : pabySrcBufferStack[iDim] =
2506 1374 : pabySrcBufferStack[iDim - 1] + anSrcOffset[iDim];
2507 1374 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
2508 1374 : goto lbl_next_depth;
2509 1374 : lbl_return_to_caller_in_loop:
2510 1374 : --iDim;
2511 1374 : --anStackCount[iDim];
2512 1374 : if (anStackCount[iDim] == 0)
2513 356 : break;
2514 1018 : if (bufferStride[iDim] >= 0)
2515 978 : pabyDstBufferStack[iDim] +=
2516 978 : bufferStride[iDim] * nBufferDataTypeSize;
2517 : else
2518 40 : pabyDstBufferStack[iDim] -=
2519 40 : (-bufferStride[iDim]) * nBufferDataTypeSize;
2520 1018 : pabySrcBufferStack[iDim] += anSrcStride[iDim];
2521 : }
2522 : }
2523 1469 : if (iDim > 0)
2524 1374 : goto lbl_return_to_caller_in_loop;
2525 95 : }
2526 :
2527 : /************************************************************************/
2528 : /* ReadSlow() */
2529 : /************************************************************************/
2530 :
2531 100 : bool HDF5Array::ReadSlow(const GUInt64 *arrayStartIdx, const size_t *count,
2532 : const GInt64 *arrayStep,
2533 : const GPtrDiff_t *bufferStride,
2534 : const GDALExtendedDataType &bufferDataType,
2535 : void *pDstBuffer) const
2536 : {
2537 100 : const size_t nBufferDataTypeSize(bufferDataType.GetSize());
2538 100 : if (nBufferDataTypeSize == 0)
2539 0 : return false;
2540 100 : const size_t nDims(m_dims.size());
2541 100 : size_t nEltCount = 1;
2542 301 : for (size_t i = 0; i < nDims; ++i)
2543 : {
2544 201 : nEltCount *= count[i];
2545 : }
2546 :
2547 : // Only for testing
2548 : const char *pszThreshold =
2549 100 : CPLGetConfigOption("GDAL_HDF5_TEMP_ARRAY_ALLOC_SIZE", "16777216");
2550 : const GUIntBig nThreshold =
2551 100 : CPLScanUIntBig(pszThreshold, static_cast<int>(strlen(pszThreshold)));
2552 100 : if (nEltCount == 1 || nEltCount <= nThreshold / nBufferDataTypeSize)
2553 : {
2554 95 : CPLDebug("HDF5", "Using slow path");
2555 190 : std::vector<GByte> abyTemp;
2556 : try
2557 : {
2558 95 : abyTemp.resize(nEltCount * nBufferDataTypeSize);
2559 : }
2560 0 : catch (const std::exception &e)
2561 : {
2562 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
2563 0 : return false;
2564 : }
2565 190 : std::vector<GUInt64> anStart(nDims);
2566 190 : std::vector<GInt64> anStep(nDims);
2567 286 : for (size_t i = 0; i < nDims; i++)
2568 : {
2569 191 : if (arrayStep[i] >= 0)
2570 : {
2571 87 : anStart[i] = arrayStartIdx[i];
2572 87 : anStep[i] = arrayStep[i];
2573 : }
2574 : else
2575 : {
2576 : // Use double negation so that operations occur only on
2577 : // positive quantities to avoid an artificial negative signed
2578 : // integer to unsigned conversion.
2579 208 : anStart[i] =
2580 104 : arrayStartIdx[i] - ((-arrayStep[i]) * (count[i] - 1));
2581 104 : anStep[i] = -arrayStep[i];
2582 : }
2583 : }
2584 190 : std::vector<GPtrDiff_t> anStride(nDims);
2585 95 : size_t nCurStride = 1;
2586 286 : for (size_t i = nDims; i > 0;)
2587 : {
2588 191 : --i;
2589 191 : anStride[i] = nCurStride;
2590 191 : nCurStride *= count[i];
2591 : }
2592 95 : if (!IRead(anStart.data(), count, anStep.data(), anStride.data(),
2593 95 : bufferDataType, &abyTemp[0]))
2594 : {
2595 0 : return false;
2596 : }
2597 95 : CopyBuffer(nDims, count, arrayStep, bufferStride, bufferDataType,
2598 95 : &abyTemp[0], pDstBuffer);
2599 95 : return true;
2600 : }
2601 :
2602 10 : std::vector<GUInt64> arrayStartIdxHalf;
2603 10 : std::vector<size_t> countHalf;
2604 5 : size_t iDimToSplit = nDims;
2605 : // Find the first dimension that has at least 2 elements, to split along
2606 : // it
2607 15 : for (size_t i = 0; i < nDims; ++i)
2608 : {
2609 10 : arrayStartIdxHalf.push_back(arrayStartIdx[i]);
2610 10 : countHalf.push_back(count[i]);
2611 10 : if (count[i] >= 2 && iDimToSplit == nDims)
2612 : {
2613 5 : iDimToSplit = i;
2614 : }
2615 : }
2616 :
2617 5 : CPLAssert(iDimToSplit != nDims);
2618 :
2619 5 : countHalf[iDimToSplit] /= 2;
2620 5 : if (!ReadSlow(arrayStartIdxHalf.data(), countHalf.data(), arrayStep,
2621 : bufferStride, bufferDataType, pDstBuffer))
2622 : {
2623 0 : return false;
2624 : }
2625 5 : arrayStartIdxHalf[iDimToSplit] = static_cast<GUInt64>(
2626 5 : arrayStep[iDimToSplit] > 0
2627 0 : ? arrayStartIdx[iDimToSplit] +
2628 0 : arrayStep[iDimToSplit] * countHalf[iDimToSplit]
2629 5 : : arrayStartIdx[iDimToSplit] -
2630 5 : (-arrayStep[iDimToSplit]) * countHalf[iDimToSplit]);
2631 : GByte *pOtherHalfDstBuffer =
2632 10 : static_cast<GByte *>(pDstBuffer) + bufferStride[iDimToSplit] *
2633 5 : countHalf[iDimToSplit] *
2634 5 : nBufferDataTypeSize;
2635 5 : countHalf[iDimToSplit] = count[iDimToSplit] - countHalf[iDimToSplit];
2636 5 : return ReadSlow(arrayStartIdxHalf.data(), countHalf.data(), arrayStep,
2637 5 : bufferStride, bufferDataType, pOtherHalfDstBuffer);
2638 : }
2639 :
2640 : /************************************************************************/
2641 : /* IngestVariableStrings() */
2642 : /************************************************************************/
2643 :
2644 1 : static void IngestVariableStrings(void *pDstBuffer, hid_t hBufferType,
2645 : size_t nDims, const size_t *count,
2646 : const GPtrDiff_t *bufferStride)
2647 : {
2648 2 : std::vector<hsize_t> anCountOne(nDims, 1);
2649 : const hid_t hMemSpaceOne =
2650 1 : nDims == 0 ? H5Screate(H5S_SCALAR)
2651 1 : : H5Screate_simple(static_cast<int>(nDims),
2652 1 : anCountOne.data(), nullptr);
2653 2 : std::vector<size_t> anStackCount(nDims);
2654 2 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
2655 1 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
2656 1 : size_t iDim = 0;
2657 4 : lbl_next_depth:
2658 4 : if (iDim == nDims)
2659 : {
2660 2 : void *old_ptr = pabyDstBufferStack[nDims];
2661 2 : const char *pszSrcStr = *static_cast<char **>(old_ptr);
2662 2 : char *pszNewStr = pszSrcStr ? VSIStrdup(pszSrcStr) : nullptr;
2663 2 : H5Dvlen_reclaim(hBufferType, hMemSpaceOne, H5P_DEFAULT, old_ptr);
2664 2 : *static_cast<char **>(old_ptr) = pszNewStr;
2665 : }
2666 : else
2667 : {
2668 2 : anStackCount[iDim] = count[iDim];
2669 : while (true)
2670 : {
2671 3 : ++iDim;
2672 3 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
2673 3 : goto lbl_next_depth;
2674 3 : lbl_return_to_caller_in_loop:
2675 3 : --iDim;
2676 3 : --anStackCount[iDim];
2677 3 : if (anStackCount[iDim] == 0)
2678 2 : break;
2679 1 : pabyDstBufferStack[iDim] += bufferStride[iDim] * sizeof(char *);
2680 : }
2681 : }
2682 4 : if (iDim > 0)
2683 3 : goto lbl_return_to_caller_in_loop;
2684 1 : H5Sclose(hMemSpaceOne);
2685 1 : }
2686 :
2687 : /************************************************************************/
2688 : /* IngestFixedLengthStrings() */
2689 : /************************************************************************/
2690 :
2691 0 : static void IngestFixedLengthStrings(void *pDstBuffer, const void *pTemp,
2692 : hid_t hBufferType, size_t nDims,
2693 : const size_t *count,
2694 : const GPtrDiff_t *bufferStride)
2695 : {
2696 0 : const size_t nStringSize = H5Tget_size(hBufferType);
2697 0 : std::vector<size_t> anStackCount(nDims);
2698 0 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
2699 0 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pTemp);
2700 0 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
2701 0 : size_t iDim = 0;
2702 0 : const bool bSpacePad = H5Tget_strpad(hBufferType) == H5T_STR_SPACEPAD;
2703 0 : lbl_next_depth:
2704 0 : if (iDim == nDims)
2705 : {
2706 0 : char *pszStr = static_cast<char *>(VSIMalloc(nStringSize + 1));
2707 0 : if (pszStr)
2708 : {
2709 0 : memcpy(pszStr, pabySrcBuffer, nStringSize);
2710 0 : size_t nIter = nStringSize;
2711 0 : if (bSpacePad)
2712 : {
2713 0 : while (nIter >= 1 && pszStr[nIter - 1] == ' ')
2714 : {
2715 0 : nIter--;
2716 : }
2717 : }
2718 0 : pszStr[nIter] = 0;
2719 : }
2720 0 : void *ptr = pabyDstBufferStack[nDims];
2721 0 : *static_cast<char **>(ptr) = pszStr;
2722 : }
2723 : else
2724 : {
2725 0 : anStackCount[iDim] = count[iDim];
2726 : while (true)
2727 : {
2728 0 : ++iDim;
2729 0 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
2730 0 : goto lbl_next_depth;
2731 0 : lbl_return_to_caller_in_loop:
2732 0 : --iDim;
2733 0 : --anStackCount[iDim];
2734 0 : if (anStackCount[iDim] == 0)
2735 0 : break;
2736 0 : pabyDstBufferStack[iDim] += bufferStride[iDim] * sizeof(char *);
2737 0 : pabySrcBuffer += nStringSize;
2738 : }
2739 : }
2740 0 : if (iDim > 0)
2741 0 : goto lbl_return_to_caller_in_loop;
2742 0 : }
2743 :
2744 : /************************************************************************/
2745 : /* GetHDF5DataTypeFromGDALDataType() */
2746 : /************************************************************************/
2747 :
2748 : static hid_t
2749 6144 : GetHDF5DataTypeFromGDALDataType(const GDALExtendedDataType &dt, hid_t hNativeDT,
2750 : const GDALExtendedDataType &bufferDataType)
2751 : {
2752 6144 : hid_t hBufferType = H5I_INVALID_HID;
2753 6144 : switch (bufferDataType.GetNumericDataType())
2754 : {
2755 30 : case GDT_UInt8:
2756 30 : hBufferType = H5Tcopy(H5T_NATIVE_UCHAR);
2757 30 : break;
2758 0 : case GDT_Int8:
2759 0 : hBufferType = H5Tcopy(H5T_NATIVE_SCHAR);
2760 0 : break;
2761 21 : case GDT_UInt16:
2762 21 : hBufferType = H5Tcopy(H5T_NATIVE_USHORT);
2763 21 : break;
2764 18 : case GDT_Int16:
2765 18 : hBufferType = H5Tcopy(H5T_NATIVE_SHORT);
2766 18 : break;
2767 14 : case GDT_UInt32:
2768 14 : hBufferType = H5Tcopy(H5T_NATIVE_UINT);
2769 14 : break;
2770 2082 : case GDT_Int32:
2771 2082 : hBufferType = H5Tcopy(H5T_NATIVE_INT);
2772 2082 : break;
2773 1 : case GDT_UInt64:
2774 1 : hBufferType = H5Tcopy(H5T_NATIVE_UINT64);
2775 1 : break;
2776 2 : case GDT_Int64:
2777 2 : hBufferType = H5Tcopy(H5T_NATIVE_INT64);
2778 2 : break;
2779 0 : case GDT_Float16:
2780 : #ifdef HDF5_HAVE_FLOAT16
2781 : hBufferType = H5Tcopy(H5T_NATIVE_FLOAT16);
2782 : break;
2783 : #else
2784 0 : return H5I_INVALID_HID;
2785 : #endif
2786 22 : case GDT_Float32:
2787 22 : hBufferType = H5Tcopy(H5T_NATIVE_FLOAT);
2788 22 : break;
2789 3954 : case GDT_Float64:
2790 3954 : hBufferType = H5Tcopy(H5T_NATIVE_DOUBLE);
2791 3954 : break;
2792 0 : case GDT_CInt16:
2793 : case GDT_CInt32:
2794 : case GDT_CFloat16:
2795 : case GDT_CFloat32:
2796 : case GDT_CFloat64:
2797 0 : if (bufferDataType != dt)
2798 : {
2799 0 : return H5I_INVALID_HID;
2800 : }
2801 : else
2802 : {
2803 0 : hBufferType = H5Tcopy(hNativeDT);
2804 0 : break;
2805 : }
2806 0 : case GDT_Unknown:
2807 : case GDT_TypeCount:
2808 0 : return H5I_INVALID_HID;
2809 : }
2810 6144 : return hBufferType;
2811 : }
2812 :
2813 : /************************************************************************/
2814 : /* FreeDynamicMemory() */
2815 : /************************************************************************/
2816 :
2817 2424 : static void FreeDynamicMemory(GByte *pabyPtr, hid_t hDataType)
2818 : {
2819 2424 : const auto klass = H5Tget_class(hDataType);
2820 2424 : if (klass == H5T_STRING && H5Tis_variable_str(hDataType))
2821 : {
2822 1139 : auto hDataSpace = H5Screate(H5S_SCALAR);
2823 1139 : H5Dvlen_reclaim(hDataType, hDataSpace, H5P_DEFAULT, pabyPtr);
2824 1139 : H5Sclose(hDataSpace);
2825 : }
2826 1285 : else if (klass == H5T_COMPOUND)
2827 : {
2828 469 : const unsigned nMembers = H5Tget_nmembers(hDataType);
2829 2424 : for (unsigned i = 0; i < nMembers; i++)
2830 : {
2831 1955 : const auto nOffset = H5Tget_member_offset(hDataType, i);
2832 1955 : auto hMemberType = H5Tget_member_type(hDataType, i);
2833 1955 : if (hMemberType < 0)
2834 0 : continue;
2835 1955 : FreeDynamicMemory(pabyPtr + nOffset, hMemberType);
2836 1955 : H5Tclose(hMemberType);
2837 : }
2838 : }
2839 2424 : }
2840 :
2841 : /************************************************************************/
2842 : /* CreateMapTargetComponentsToSrc() */
2843 : /************************************************************************/
2844 :
2845 : static std::vector<unsigned>
2846 454 : CreateMapTargetComponentsToSrc(hid_t hSrcDataType,
2847 : const GDALExtendedDataType &dstDataType)
2848 : {
2849 454 : CPLAssert(H5Tget_class(hSrcDataType) == H5T_COMPOUND);
2850 454 : CPLAssert(dstDataType.GetClass() == GEDTC_COMPOUND);
2851 :
2852 454 : const unsigned nMembers = H5Tget_nmembers(hSrcDataType);
2853 908 : std::map<std::string, unsigned> oMapSrcCompNameToIdx;
2854 2381 : for (unsigned i = 0; i < nMembers; i++)
2855 : {
2856 1927 : char *pszName = H5Tget_member_name(hSrcDataType, i);
2857 1927 : if (pszName)
2858 : {
2859 1927 : oMapSrcCompNameToIdx[pszName] = i;
2860 1927 : H5free_memory(pszName);
2861 : }
2862 : }
2863 :
2864 454 : std::vector<unsigned> ret;
2865 454 : const auto &comps = dstDataType.GetComponents();
2866 454 : ret.reserve(comps.size());
2867 2163 : for (const auto &comp : comps)
2868 : {
2869 1709 : auto oIter = oMapSrcCompNameToIdx.find(comp->GetName());
2870 1709 : CPLAssert(oIter != oMapSrcCompNameToIdx.end());
2871 1709 : ret.emplace_back(oIter->second);
2872 : }
2873 908 : return ret;
2874 : }
2875 :
2876 : /************************************************************************/
2877 : /* CopyValue() */
2878 : /************************************************************************/
2879 :
2880 13642 : static void CopyValue(const GByte *pabySrcBuffer, hid_t hSrcDataType,
2881 : GByte *pabyDstBuffer,
2882 : const GDALExtendedDataType &dstDataType,
2883 : const std::vector<unsigned> &mapDstCompsToSrcComps)
2884 : {
2885 13642 : const auto klass = H5Tget_class(hSrcDataType);
2886 13642 : if (klass == H5T_STRING)
2887 : {
2888 3122 : if (H5Tis_variable_str(hSrcDataType))
2889 : {
2890 2341 : GDALExtendedDataType::CopyValue(
2891 4682 : pabySrcBuffer, GDALExtendedDataType::CreateString(),
2892 : pabyDstBuffer, dstDataType);
2893 : }
2894 : else
2895 : {
2896 781 : size_t nStringSize = H5Tget_size(hSrcDataType);
2897 781 : char *pszStr = static_cast<char *>(VSIMalloc(nStringSize + 1));
2898 781 : if (pszStr)
2899 : {
2900 781 : memcpy(pszStr, pabySrcBuffer, nStringSize);
2901 781 : pszStr[nStringSize] = 0;
2902 : }
2903 781 : GDALExtendedDataType::CopyValue(
2904 1562 : &pszStr, GDALExtendedDataType::CreateString(), pabyDstBuffer,
2905 : dstDataType);
2906 781 : CPLFree(pszStr);
2907 : }
2908 : }
2909 10520 : else if (klass == H5T_COMPOUND)
2910 : {
2911 474 : if (dstDataType.GetClass() != GEDTC_COMPOUND)
2912 : {
2913 3 : const auto eSrcDataType = ::HDF5Dataset::GetDataType(hSrcDataType);
2914 : // Typically source is complex data type
2915 : #ifdef HDF5_HAVE_FLOAT16
2916 : if (eSrcDataType == GDT_CFloat32 &&
2917 : ::HDF5Dataset::IsNativeCFloat16(hSrcDataType))
2918 : {
2919 : if (dstDataType.GetNumericDataType() == GDT_CFloat32)
2920 : {
2921 : for (int j = 0; j <= 1; ++j)
2922 : {
2923 : uint16_t nVal16;
2924 : memcpy(&nVal16, pabySrcBuffer + j * sizeof(nVal16),
2925 : sizeof(nVal16));
2926 : const uint32_t nVal32 = CPLHalfToFloat(nVal16);
2927 : memcpy(pabyDstBuffer + j * sizeof(float), &nVal32,
2928 : sizeof(nVal32));
2929 : }
2930 : }
2931 : else if (dstDataType.GetNumericDataType() == GDT_CFloat64)
2932 : {
2933 : for (int j = 0; j <= 1; ++j)
2934 : {
2935 : uint16_t nVal16;
2936 : memcpy(&nVal16, pabySrcBuffer + j * sizeof(nVal16),
2937 : sizeof(nVal16));
2938 : const uint32_t nVal32 = CPLHalfToFloat(nVal16);
2939 : float fVal;
2940 : memcpy(&fVal, &nVal32, sizeof(fVal));
2941 : double dfVal = fVal;
2942 : memcpy(pabyDstBuffer + j * sizeof(double), &dfVal,
2943 : sizeof(dfVal));
2944 : }
2945 : }
2946 : return;
2947 : }
2948 :
2949 : #endif
2950 6 : auto srcDataType(GDALExtendedDataType::Create(eSrcDataType));
2951 6 : if (srcDataType.GetClass() == GEDTC_NUMERIC &&
2952 3 : srcDataType.GetNumericDataType() != GDT_Unknown)
2953 : {
2954 3 : GDALExtendedDataType::CopyValue(pabySrcBuffer, srcDataType,
2955 : pabyDstBuffer, dstDataType);
2956 : }
2957 : }
2958 : else
2959 : {
2960 471 : const auto &comps = dstDataType.GetComponents();
2961 471 : CPLAssert(comps.size() == mapDstCompsToSrcComps.size());
2962 2197 : for (size_t iDst = 0; iDst < comps.size(); ++iDst)
2963 : {
2964 1726 : const unsigned iSrc = mapDstCompsToSrcComps[iDst];
2965 1726 : auto hMemberType = H5Tget_member_type(hSrcDataType, iSrc);
2966 : const std::vector<unsigned> mapDstSubCompsToSrcSubComps(
2967 1726 : (H5Tget_class(hMemberType) == H5T_COMPOUND &&
2968 0 : comps[iDst]->GetType().GetClass() == GEDTC_COMPOUND)
2969 1726 : ? CreateMapTargetComponentsToSrc(hMemberType,
2970 0 : comps[iDst]->GetType())
2971 3452 : : std::vector<unsigned>());
2972 1726 : CopyValue(pabySrcBuffer +
2973 1726 : H5Tget_member_offset(hSrcDataType, iSrc),
2974 1726 : hMemberType, pabyDstBuffer + comps[iDst]->GetOffset(),
2975 1726 : comps[iDst]->GetType(), mapDstSubCompsToSrcSubComps);
2976 1726 : H5Tclose(hMemberType);
2977 : }
2978 : }
2979 : }
2980 10046 : else if (klass == H5T_ENUM)
2981 : {
2982 1454 : auto hParent = H5Tget_super(hSrcDataType);
2983 1454 : CopyValue(pabySrcBuffer, hParent, pabyDstBuffer, dstDataType, {});
2984 1454 : H5Tclose(hParent);
2985 : }
2986 : #ifdef HDF5_HAVE_FLOAT16
2987 : else if (H5Tequal(hSrcDataType, H5T_NATIVE_FLOAT16))
2988 : {
2989 : uint16_t nVal16;
2990 : memcpy(&nVal16, pabySrcBuffer, sizeof(nVal16));
2991 : const uint32_t nVal32 = CPLHalfToFloat(nVal16);
2992 : float fVal;
2993 : memcpy(&fVal, &nVal32, sizeof(fVal));
2994 : GDALExtendedDataType::CopyValue(
2995 : &fVal, GDALExtendedDataType::Create(GDT_Float32), pabyDstBuffer,
2996 : dstDataType);
2997 : }
2998 : #endif
2999 : else
3000 : {
3001 8592 : GDALDataType eDT = ::HDF5Dataset::GetDataType(hSrcDataType);
3002 8592 : GDALExtendedDataType::CopyValue(pabySrcBuffer,
3003 17184 : GDALExtendedDataType::Create(eDT),
3004 : pabyDstBuffer, dstDataType);
3005 : }
3006 13642 : }
3007 :
3008 : /************************************************************************/
3009 : /* CopyToFinalBuffer() */
3010 : /************************************************************************/
3011 :
3012 454 : static void CopyToFinalBuffer(void *pDstBuffer, const void *pTemp, size_t nDims,
3013 : const size_t *count,
3014 : const GPtrDiff_t *bufferStride,
3015 : hid_t hSrcDataType,
3016 : const GDALExtendedDataType &bufferDataType)
3017 : {
3018 454 : const size_t nSrcDataTypeSize(H5Tget_size(hSrcDataType));
3019 908 : std::vector<size_t> anStackCount(nDims);
3020 908 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
3021 454 : const GByte *pabySrcBuffer = static_cast<const GByte *>(pTemp);
3022 454 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
3023 454 : size_t iDim = 0;
3024 : const std::vector<unsigned> mapDstCompsToSrcComps(
3025 907 : (H5Tget_class(hSrcDataType) == H5T_COMPOUND &&
3026 453 : bufferDataType.GetClass() == GEDTC_COMPOUND)
3027 454 : ? CreateMapTargetComponentsToSrc(hSrcDataType, bufferDataType)
3028 908 : : std::vector<unsigned>());
3029 :
3030 454 : bool bFastCopyOfCompoundToSingleComponentCompound = false;
3031 454 : GDALDataType eSrcTypeComp = GDT_Unknown;
3032 454 : size_t nSrcOffset = 0;
3033 454 : GDALDataType eDstTypeComp = GDT_Unknown;
3034 454 : int bufferStrideLastDim = 0;
3035 670 : if (nDims > 0 && mapDstCompsToSrcComps.size() == 1 &&
3036 216 : bufferDataType.GetComponents()[0]->GetType().GetClass() ==
3037 : GEDTC_NUMERIC)
3038 : {
3039 : auto hMemberType =
3040 158 : H5Tget_member_type(hSrcDataType, mapDstCompsToSrcComps[0]);
3041 158 : eSrcTypeComp = HDF5Dataset::GetDataType(hMemberType);
3042 158 : if (eSrcTypeComp != GDT_Unknown)
3043 : {
3044 : nSrcOffset =
3045 145 : H5Tget_member_offset(hSrcDataType, mapDstCompsToSrcComps[0]);
3046 145 : eDstTypeComp = bufferDataType.GetComponents()[0]
3047 145 : ->GetType()
3048 145 : .GetNumericDataType();
3049 290 : bufferStrideLastDim = static_cast<int>(bufferStride[nDims - 1] *
3050 145 : bufferDataType.GetSize());
3051 145 : bFastCopyOfCompoundToSingleComponentCompound = true;
3052 : }
3053 : }
3054 :
3055 296 : lbl_next_depth:
3056 1270 : if (bFastCopyOfCompoundToSingleComponentCompound && iDim == nDims - 1)
3057 : {
3058 285 : GDALCopyWords64(pabySrcBuffer + nSrcOffset, eSrcTypeComp,
3059 : static_cast<int>(nSrcDataTypeSize),
3060 285 : pabyDstBufferStack[iDim], eDstTypeComp,
3061 285 : static_cast<int>(bufferStrideLastDim), count[iDim]);
3062 285 : pabySrcBuffer += count[iDim] * nSrcDataTypeSize;
3063 : }
3064 985 : else if (iDim == nDims)
3065 : {
3066 530 : CopyValue(pabySrcBuffer, hSrcDataType, pabyDstBufferStack[nDims],
3067 : bufferDataType, mapDstCompsToSrcComps);
3068 530 : pabySrcBuffer += nSrcDataTypeSize;
3069 : }
3070 : else
3071 : {
3072 455 : anStackCount[iDim] = count[iDim];
3073 : while (true)
3074 : {
3075 816 : ++iDim;
3076 816 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
3077 816 : goto lbl_next_depth;
3078 816 : lbl_return_to_caller_in_loop:
3079 816 : --iDim;
3080 816 : --anStackCount[iDim];
3081 816 : if (anStackCount[iDim] == 0)
3082 455 : break;
3083 361 : pabyDstBufferStack[iDim] +=
3084 361 : bufferStride[iDim] * bufferDataType.GetSize();
3085 : }
3086 : }
3087 1270 : if (iDim > 0)
3088 816 : goto lbl_return_to_caller_in_loop;
3089 454 : }
3090 :
3091 : /************************************************************************/
3092 : /* IRead() */
3093 : /************************************************************************/
3094 :
3095 898 : bool HDF5Array::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
3096 : const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
3097 : const GDALExtendedDataType &bufferDataType,
3098 : void *pDstBuffer) const
3099 : {
3100 : HDF5_GLOBAL_LOCK();
3101 :
3102 898 : const size_t nDims(m_dims.size());
3103 1796 : std::vector<H5OFFSET_TYPE> anOffset(nDims);
3104 1796 : std::vector<hsize_t> anCount(nDims);
3105 1796 : std::vector<hsize_t> anStep(nDims);
3106 :
3107 898 : size_t nEltCount = 1;
3108 1881 : for (size_t i = 0; i < nDims; ++i)
3109 : {
3110 1073 : if (count[i] != 1 && (arrayStep[i] < 0 || bufferStride[i] < 0))
3111 : {
3112 90 : return ReadSlow(arrayStartIdx, count, arrayStep, bufferStride,
3113 90 : bufferDataType, pDstBuffer);
3114 : }
3115 983 : anOffset[i] = static_cast<hsize_t>(arrayStartIdx[i]);
3116 983 : anCount[i] = static_cast<hsize_t>(count[i]);
3117 983 : anStep[i] = static_cast<hsize_t>(count[i] == 1 ? 1 : arrayStep[i]);
3118 983 : nEltCount *= count[i];
3119 : }
3120 :
3121 808 : if (IsTransposedRequest(count, bufferStride))
3122 : {
3123 267 : return ReadForTransposedRequest(arrayStartIdx, count, arrayStep,
3124 : bufferStride, bufferDataType,
3125 267 : pDstBuffer);
3126 : }
3127 :
3128 541 : bool strideOK = true;
3129 541 : GPtrDiff_t nExpectedBufferStride = 1;
3130 1248 : for (size_t i = nDims; i != 0;)
3131 : {
3132 708 : --i;
3133 708 : if (count[i] != 1 && bufferStride[i] != nExpectedBufferStride)
3134 : {
3135 1 : strideOK = false;
3136 1 : break;
3137 : }
3138 707 : nExpectedBufferStride *= count[i];
3139 : }
3140 :
3141 541 : hid_t hBufferType = H5I_INVALID_HID;
3142 541 : GByte *pabyTemp = nullptr;
3143 541 : bool bUseTmpBuffer = false;
3144 541 : if (m_dt.GetClass() == GEDTC_STRING)
3145 : {
3146 1 : if (bufferDataType.GetClass() != GEDTC_STRING)
3147 : {
3148 0 : return false;
3149 : }
3150 1 : hBufferType = H5Tcopy(m_hNativeDT);
3151 1 : if (!H5Tis_variable_str(m_hNativeDT))
3152 : {
3153 0 : const size_t nStringSize = H5Tget_size(m_hNativeDT);
3154 : pabyTemp = static_cast<GByte *>(
3155 0 : VSI_MALLOC2_VERBOSE(nStringSize, nEltCount));
3156 0 : if (pabyTemp == nullptr)
3157 0 : return false;
3158 : }
3159 : }
3160 611 : else if (strideOK && bufferDataType.GetClass() == GEDTC_NUMERIC &&
3161 72 : m_dt.GetClass() == GEDTC_NUMERIC &&
3162 1146 : !GDALDataTypeIsComplex(m_dt.GetNumericDataType()) &&
3163 67 : !GDALDataTypeIsComplex(bufferDataType.GetNumericDataType()))
3164 : {
3165 : // Compatibility with older libhdf5 that doesn't like requesting
3166 : // an enum to an integer
3167 67 : if (H5Tget_class(m_hNativeDT) == H5T_ENUM)
3168 : {
3169 0 : auto hParent = H5Tget_super(m_hNativeDT);
3170 0 : if (H5Tequal(hParent, H5T_NATIVE_UCHAR) ||
3171 0 : H5Tequal(hParent, H5T_NATIVE_SCHAR) ||
3172 0 : H5Tequal(hParent, H5T_NATIVE_USHORT) ||
3173 0 : H5Tequal(hParent, H5T_NATIVE_SHORT) ||
3174 0 : H5Tequal(hParent, H5T_NATIVE_UINT) ||
3175 0 : H5Tequal(hParent, H5T_NATIVE_INT) ||
3176 0 : H5Tequal(hParent, H5T_NATIVE_UINT64) ||
3177 0 : H5Tequal(hParent, H5T_NATIVE_INT64))
3178 : {
3179 0 : hBufferType = H5Tcopy(m_hNativeDT);
3180 0 : if (m_dt != bufferDataType)
3181 : {
3182 0 : bUseTmpBuffer = true;
3183 : }
3184 : }
3185 0 : H5Tclose(hParent);
3186 : }
3187 67 : if (hBufferType == H5I_INVALID_HID)
3188 : {
3189 67 : hBufferType = GetHDF5DataTypeFromGDALDataType(m_dt, m_hNativeDT,
3190 : bufferDataType);
3191 67 : if (hBufferType == H5I_INVALID_HID)
3192 : {
3193 0 : return false;
3194 : }
3195 : }
3196 : }
3197 473 : else if (strideOK)
3198 : {
3199 472 : hBufferType = H5Tcopy(m_hNativeDT);
3200 472 : if (m_dt != bufferDataType || m_bHasString || m_bHasNonNativeDataType)
3201 : {
3202 453 : bUseTmpBuffer = true;
3203 : }
3204 : }
3205 : else
3206 : {
3207 1 : hBufferType = H5Tcopy(m_hNativeDT);
3208 1 : bUseTmpBuffer = true;
3209 : }
3210 :
3211 541 : if (bUseTmpBuffer)
3212 : {
3213 454 : const size_t nDataTypeSize = H5Tget_size(hBufferType);
3214 : pabyTemp =
3215 454 : static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nDataTypeSize, nEltCount));
3216 454 : if (pabyTemp == nullptr)
3217 : {
3218 0 : H5Tclose(hBufferType);
3219 0 : return false;
3220 : }
3221 : }
3222 :
3223 : // Select block from file space.
3224 : herr_t status;
3225 541 : if (nDims)
3226 : {
3227 : status =
3228 539 : H5Sselect_hyperslab(m_hDataSpace, H5S_SELECT_SET, anOffset.data(),
3229 539 : anStep.data(), anCount.data(), nullptr);
3230 539 : if (status < 0)
3231 : {
3232 0 : H5Tclose(hBufferType);
3233 0 : VSIFree(pabyTemp);
3234 0 : return false;
3235 : }
3236 : }
3237 :
3238 : // Create memory data space
3239 : const hid_t hMemSpace = nDims == 0
3240 541 : ? H5Screate(H5S_SCALAR)
3241 539 : : H5Screate_simple(static_cast<int>(nDims),
3242 539 : anCount.data(), nullptr);
3243 541 : if (nDims)
3244 : {
3245 539 : std::vector<H5OFFSET_TYPE> anMemOffset(nDims);
3246 : status =
3247 539 : H5Sselect_hyperslab(hMemSpace, H5S_SELECT_SET, anMemOffset.data(),
3248 539 : nullptr, anCount.data(), nullptr);
3249 539 : if (status < 0)
3250 : {
3251 0 : H5Tclose(hBufferType);
3252 0 : H5Sclose(hMemSpace);
3253 0 : VSIFree(pabyTemp);
3254 0 : return false;
3255 : }
3256 : }
3257 :
3258 541 : status = H5Dread(m_hArray, hBufferType, hMemSpace, m_hDataSpace,
3259 : H5P_DEFAULT, pabyTemp ? pabyTemp : pDstBuffer);
3260 :
3261 541 : if (status >= 0)
3262 : {
3263 541 : if (H5Tis_variable_str(hBufferType))
3264 : {
3265 1 : IngestVariableStrings(pDstBuffer, hBufferType, nDims, count,
3266 : bufferStride);
3267 : }
3268 540 : else if (pabyTemp && bufferDataType.GetClass() == GEDTC_STRING)
3269 : {
3270 0 : IngestFixedLengthStrings(pDstBuffer, pabyTemp, hBufferType, nDims,
3271 : count, bufferStride);
3272 : }
3273 540 : else if (pabyTemp)
3274 : {
3275 454 : CopyToFinalBuffer(pDstBuffer, pabyTemp, nDims, count, bufferStride,
3276 454 : m_hNativeDT, bufferDataType);
3277 :
3278 454 : if (m_bHasString)
3279 : {
3280 353 : const size_t nBufferTypeSize = H5Tget_size(hBufferType);
3281 353 : GByte *pabyPtr = pabyTemp;
3282 822 : for (size_t i = 0; i < nEltCount; ++i)
3283 : {
3284 469 : FreeDynamicMemory(pabyPtr, hBufferType);
3285 469 : pabyPtr += nBufferTypeSize;
3286 : }
3287 : }
3288 : }
3289 : }
3290 :
3291 541 : H5Tclose(hBufferType);
3292 541 : H5Sclose(hMemSpace);
3293 541 : VSIFree(pabyTemp);
3294 :
3295 541 : return status >= 0;
3296 : }
3297 :
3298 : /************************************************************************/
3299 : /* ~HDF5Attribute() */
3300 : /************************************************************************/
3301 :
3302 318122 : HDF5Attribute::~HDF5Attribute()
3303 : {
3304 : HDF5_GLOBAL_LOCK();
3305 :
3306 159061 : if (m_hAttribute > 0)
3307 159061 : H5Aclose(m_hAttribute);
3308 159061 : if (m_hNativeDT > 0)
3309 159061 : H5Tclose(m_hNativeDT);
3310 159061 : if (m_hDataSpace > 0)
3311 159061 : H5Sclose(m_hDataSpace);
3312 318122 : }
3313 :
3314 : /************************************************************************/
3315 : /* CopyAllAttrValuesInto() */
3316 : /************************************************************************/
3317 :
3318 9905 : static void CopyAllAttrValuesInto(size_t nDims, const GUInt64 *arrayStartIdx,
3319 : const size_t *count, const GInt64 *arrayStep,
3320 : const GPtrDiff_t *bufferStride,
3321 : const GDALExtendedDataType &bufferDataType,
3322 : void *pDstBuffer, hid_t hSrcBufferType,
3323 : const void *pabySrcBuffer)
3324 : {
3325 9905 : const size_t nBufferDataTypeSize(bufferDataType.GetSize());
3326 9905 : const size_t nSrcDataTypeSize(H5Tget_size(hSrcBufferType));
3327 19810 : std::vector<size_t> anStackCount(nDims);
3328 19810 : std::vector<const GByte *> pabySrcBufferStack(nDims + 1);
3329 19810 : std::vector<GByte *> pabyDstBufferStack(nDims + 1);
3330 : const std::vector<unsigned> mapDstCompsToSrcComps(
3331 9909 : (H5Tget_class(hSrcBufferType) == H5T_COMPOUND &&
3332 4 : bufferDataType.GetClass() == GEDTC_COMPOUND)
3333 9905 : ? CreateMapTargetComponentsToSrc(hSrcBufferType, bufferDataType)
3334 19810 : : std::vector<unsigned>());
3335 :
3336 9905 : pabySrcBufferStack[0] = static_cast<const GByte *>(pabySrcBuffer);
3337 9905 : if (nDims > 0)
3338 26 : pabySrcBufferStack[0] += arrayStartIdx[0] * nSrcDataTypeSize;
3339 9905 : pabyDstBufferStack[0] = static_cast<GByte *>(pDstBuffer);
3340 9905 : size_t iDim = 0;
3341 9958 : lbl_next_depth:
3342 9958 : if (iDim == nDims)
3343 : {
3344 9932 : CopyValue(pabySrcBufferStack[nDims], hSrcBufferType,
3345 9932 : pabyDstBufferStack[nDims], bufferDataType,
3346 : mapDstCompsToSrcComps);
3347 : }
3348 : else
3349 : {
3350 26 : anStackCount[iDim] = count[iDim];
3351 : while (true)
3352 : {
3353 53 : ++iDim;
3354 53 : pabyDstBufferStack[iDim] = pabyDstBufferStack[iDim - 1];
3355 53 : pabySrcBufferStack[iDim] = pabySrcBufferStack[iDim - 1];
3356 53 : if (iDim < nDims)
3357 : {
3358 0 : pabySrcBufferStack[iDim] +=
3359 0 : arrayStartIdx[iDim] * nSrcDataTypeSize;
3360 : }
3361 53 : goto lbl_next_depth;
3362 53 : lbl_return_to_caller_in_loop:
3363 53 : --iDim;
3364 53 : --anStackCount[iDim];
3365 53 : if (anStackCount[iDim] == 0)
3366 26 : break;
3367 27 : pabyDstBufferStack[iDim] +=
3368 27 : bufferStride[iDim] * nBufferDataTypeSize;
3369 27 : pabySrcBufferStack[iDim] += arrayStep[iDim] * nSrcDataTypeSize;
3370 : }
3371 : }
3372 9958 : if (iDim > 0)
3373 53 : goto lbl_return_to_caller_in_loop;
3374 9905 : }
3375 :
3376 : /************************************************************************/
3377 : /* IRead() */
3378 : /************************************************************************/
3379 :
3380 9905 : bool HDF5Attribute::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
3381 : const GInt64 *arrayStep,
3382 : const GPtrDiff_t *bufferStride,
3383 : const GDALExtendedDataType &bufferDataType,
3384 : void *pDstBuffer) const
3385 : {
3386 : HDF5_GLOBAL_LOCK();
3387 :
3388 9905 : const size_t nDims(m_dims.size());
3389 9905 : if (m_dt.GetClass() == GEDTC_STRING)
3390 : {
3391 1640 : if (bufferDataType.GetClass() != GEDTC_STRING)
3392 : {
3393 0 : return false;
3394 : }
3395 :
3396 1640 : if (!H5Tis_variable_str(m_hNativeDT))
3397 : {
3398 329 : const size_t nStringSize = H5Tget_size(m_hNativeDT);
3399 : GByte *pabyTemp = static_cast<GByte *>(
3400 329 : VSI_CALLOC_VERBOSE(nStringSize, m_nElements));
3401 329 : if (pabyTemp == nullptr)
3402 0 : return false;
3403 658 : if (H5Sget_simple_extent_type(m_hDataSpace) != H5S_NULL &&
3404 329 : H5Aread(m_hAttribute, m_hNativeDT, pabyTemp) < 0)
3405 : {
3406 0 : VSIFree(pabyTemp);
3407 0 : return false;
3408 : }
3409 329 : CopyAllAttrValuesInto(nDims, arrayStartIdx, count, arrayStep,
3410 : bufferStride, bufferDataType, pDstBuffer,
3411 329 : m_hNativeDT, pabyTemp);
3412 329 : VSIFree(pabyTemp);
3413 : }
3414 : else
3415 : {
3416 1311 : void *pabyTemp = VSI_CALLOC_VERBOSE(sizeof(char *), m_nElements);
3417 1311 : if (pabyTemp == nullptr)
3418 0 : return false;
3419 2622 : if (H5Sget_simple_extent_type(m_hDataSpace) != H5S_NULL &&
3420 1311 : H5Aread(m_hAttribute, m_hNativeDT, pabyTemp) < 0)
3421 : {
3422 0 : VSIFree(pabyTemp);
3423 0 : return false;
3424 : }
3425 1311 : CopyAllAttrValuesInto(nDims, arrayStartIdx, count, arrayStep,
3426 : bufferStride, bufferDataType, pDstBuffer,
3427 1311 : m_hNativeDT, pabyTemp);
3428 1311 : H5Dvlen_reclaim(m_hNativeDT, m_hDataSpace, H5P_DEFAULT, pabyTemp);
3429 1311 : VSIFree(pabyTemp);
3430 : }
3431 1640 : return true;
3432 : }
3433 :
3434 8265 : hid_t hBufferType = H5I_INVALID_HID;
3435 16529 : if (m_dt.GetClass() == GEDTC_NUMERIC &&
3436 8264 : bufferDataType.GetClass() == GEDTC_NUMERIC &&
3437 23410 : !GDALDataTypeIsComplex(m_dt.GetNumericDataType()) &&
3438 6881 : !GDALDataTypeIsComplex(bufferDataType.GetNumericDataType()))
3439 : {
3440 : // Compatibility with older libhdf5 that doesn't like requesting
3441 : // an enum to an integer
3442 6881 : if (H5Tget_class(m_hNativeDT) == H5T_ENUM)
3443 : {
3444 804 : auto hParent = H5Tget_super(m_hNativeDT);
3445 804 : if (H5Tequal(hParent, H5T_NATIVE_UCHAR) ||
3446 1 : H5Tequal(hParent, H5T_NATIVE_SCHAR) ||
3447 1 : H5Tequal(hParent, H5T_NATIVE_USHORT) ||
3448 1 : H5Tequal(hParent, H5T_NATIVE_SHORT) ||
3449 1 : H5Tequal(hParent, H5T_NATIVE_UINT) ||
3450 1 : H5Tequal(hParent, H5T_NATIVE_INT) ||
3451 805 : H5Tequal(hParent, H5T_NATIVE_UINT64) ||
3452 0 : H5Tequal(hParent, H5T_NATIVE_INT64))
3453 : {
3454 804 : hBufferType = H5Tcopy(m_hNativeDT);
3455 : }
3456 804 : H5Tclose(hParent);
3457 : }
3458 6881 : if (hBufferType == H5I_INVALID_HID)
3459 : {
3460 6077 : hBufferType = GetHDF5DataTypeFromGDALDataType(m_dt, m_hNativeDT,
3461 : bufferDataType);
3462 : }
3463 : }
3464 : else
3465 : {
3466 1384 : hBufferType = H5Tcopy(m_hNativeDT);
3467 : }
3468 :
3469 8265 : if (hBufferType == H5I_INVALID_HID)
3470 0 : return false;
3471 :
3472 8265 : const size_t nBufferTypeSize(H5Tget_size(hBufferType));
3473 : GByte *pabyTemp =
3474 8265 : static_cast<GByte *>(VSI_MALLOC2_VERBOSE(nBufferTypeSize, m_nElements));
3475 8265 : if (pabyTemp == nullptr)
3476 : {
3477 0 : H5Tclose(hBufferType);
3478 0 : return false;
3479 : }
3480 8265 : if (H5Aread(m_hAttribute, hBufferType, pabyTemp) < 0)
3481 : {
3482 0 : VSIFree(pabyTemp);
3483 0 : return false;
3484 : }
3485 8265 : CopyAllAttrValuesInto(nDims, arrayStartIdx, count, arrayStep, bufferStride,
3486 : bufferDataType, pDstBuffer, hBufferType, pabyTemp);
3487 8265 : if (bufferDataType.GetClass() == GEDTC_COMPOUND && m_bHasString)
3488 : {
3489 0 : GByte *pabyPtr = pabyTemp;
3490 0 : for (size_t i = 0; i < m_nElements; ++i)
3491 : {
3492 0 : FreeDynamicMemory(pabyPtr, hBufferType);
3493 0 : pabyPtr += nBufferTypeSize;
3494 : }
3495 : }
3496 8265 : VSIFree(pabyTemp);
3497 8265 : H5Tclose(hBufferType);
3498 8265 : return true;
3499 : }
3500 :
3501 : /************************************************************************/
3502 : /* GetIndexingVariable() */
3503 : /************************************************************************/
3504 :
3505 20 : std::shared_ptr<GDALMDArray> HDF5Dimension::GetIndexingVariable() const
3506 : {
3507 : HDF5_GLOBAL_LOCK();
3508 :
3509 20 : auto hGroup = H5Gopen(m_poShared->GetHDF5(), m_osGroupFullname.c_str());
3510 20 : if (hGroup >= 0)
3511 : {
3512 20 : auto hArray = H5Dopen(hGroup, GetName().c_str());
3513 20 : H5Gclose(hGroup);
3514 20 : if (hArray >= 0)
3515 : {
3516 40 : auto ar(HDF5Array::Create(m_osGroupFullname, GetName(), m_poShared,
3517 40 : hArray, nullptr, false));
3518 60 : auto attrName = ar->GetAttribute("NAME");
3519 20 : if (attrName && attrName->GetDataType().GetClass() == GEDTC_STRING)
3520 : {
3521 18 : const char *pszName = attrName->ReadAsString();
3522 18 : if (pszName &&
3523 18 : STARTS_WITH(
3524 : pszName,
3525 : "This is a netCDF dimension but not a netCDF variable"))
3526 : {
3527 0 : return nullptr;
3528 : }
3529 : }
3530 20 : return ar;
3531 : }
3532 : }
3533 0 : return nullptr;
3534 : }
3535 :
3536 : } // namespace GDAL
3537 :
3538 : /************************************************************************/
3539 : /* OpenMultiDim() */
3540 : /************************************************************************/
3541 :
3542 36 : GDALDataset *HDF5Dataset::OpenMultiDim(GDALOpenInfo *poOpenInfo)
3543 : {
3544 : HDF5_GLOBAL_LOCK();
3545 :
3546 36 : const char *pszFilename = STARTS_WITH(poOpenInfo->pszFilename, "HDF5:")
3547 8 : ? poOpenInfo->pszFilename + strlen("HDF5:")
3548 : : poOpenInfo->pszFilename;
3549 :
3550 : // Try opening the dataset.
3551 36 : auto hHDF5 = GDAL_HDF5Open(pszFilename);
3552 36 : if (hHDF5 < 0)
3553 : {
3554 1 : return nullptr;
3555 : }
3556 :
3557 105 : auto poSharedResources = GDAL::HDF5SharedResources::Create(pszFilename);
3558 35 : poSharedResources->m_hHDF5 = hHDF5;
3559 :
3560 70 : auto poGroup(OpenGroup(poSharedResources));
3561 35 : if (poGroup == nullptr)
3562 : {
3563 0 : return nullptr;
3564 : }
3565 :
3566 35 : auto poDS(new HDF5Dataset());
3567 35 : poDS->m_poRootGroup = std::move(poGroup);
3568 :
3569 35 : poDS->SetDescription(poOpenInfo->pszFilename);
3570 :
3571 : // Setup/check for pam .aux.xml.
3572 35 : poDS->TryLoadXML();
3573 :
3574 35 : return poDS;
3575 : }
3576 :
3577 : /************************************************************************/
3578 : /* OpenGroup() */
3579 : /************************************************************************/
3580 :
3581 349 : std::shared_ptr<GDALGroup> HDF5Dataset::OpenGroup(
3582 : const std::shared_ptr<GDAL::HDF5SharedResources> &poSharedResources)
3583 : {
3584 : HDF5_GLOBAL_LOCK();
3585 :
3586 698 : auto poGroup = poSharedResources->GetRootGroup();
3587 349 : if (!poGroup)
3588 0 : return nullptr;
3589 :
3590 349 : if (HDF5EOSParser::HasHDFEOS(poGroup->GetID()))
3591 : {
3592 5 : poSharedResources->m_poHDF5EOSParser =
3593 10 : std::make_unique<HDF5EOSParser>();
3594 5 : if (poSharedResources->m_poHDF5EOSParser->Parse(poGroup->GetID()))
3595 : {
3596 5 : CPLDebug("HDF5", "Successfully parsed HDFEOS metadata");
3597 : }
3598 : else
3599 : {
3600 0 : poSharedResources->m_poHDF5EOSParser.reset();
3601 : }
3602 : }
3603 :
3604 349 : return poGroup;
3605 : }
|