Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Hierarchical Data Format Release 5 (HDF5)
4 : * Purpose: Read subdatasets of HDF5 file.
5 : * Author: Denis Nadeau <denis.nadeau@gmail.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "hdf5_api.h"
15 :
16 : #include "cpl_float.h"
17 : #include "cpl_string.h"
18 : #include "gdal_frmts.h"
19 : #include "gdal_pam.h"
20 : #include "gdal_priv.h"
21 : #include "gh5_convenience.h"
22 : #include "hdf5dataset.h"
23 : #include "hdf5drivercore.h"
24 : #include "ogr_spatialref.h"
25 : #include "../mem/memdataset.h"
26 :
27 : #include <algorithm>
28 :
29 : class HDF5ImageDataset final : public HDF5Dataset
30 : {
31 : typedef enum
32 : {
33 : UNKNOWN_PRODUCT = 0,
34 : CSK_PRODUCT
35 : } Hdf5ProductType;
36 :
37 : typedef enum
38 : {
39 : PROD_UNKNOWN = 0,
40 : PROD_CSK_L0,
41 : PROD_CSK_L1A,
42 : PROD_CSK_L1B,
43 : PROD_CSK_L1C,
44 : PROD_CSK_L1D
45 : } HDF5CSKProductEnum;
46 :
47 : friend class HDF5ImageRasterBand;
48 :
49 : OGRSpatialReference m_oSRS{};
50 : OGRSpatialReference m_oGCPSRS{};
51 : std::vector<gdal::GCP> m_aoGCPs{};
52 :
53 : hsize_t *dims;
54 : hsize_t *maxdims;
55 : HDF5GroupObjects *poH5Objects;
56 : int ndims;
57 : int dimensions;
58 : hid_t dataset_id;
59 : hid_t dataspace_id;
60 : hid_t native;
61 : #ifdef HDF5_HAVE_FLOAT16
62 : bool m_bConvertFromFloat16 = false;
63 : #endif
64 : Hdf5ProductType iSubdatasetType;
65 : HDF5CSKProductEnum iCSKProductType;
66 : double adfGeoTransform[6];
67 : bool bHasGeoTransform;
68 : int m_nXIndex = -1;
69 : int m_nYIndex = -1;
70 : int m_nOtherDimIndex = -1;
71 :
72 : int m_nBlockXSize = 0;
73 : int m_nBlockYSize = 0;
74 : int m_nBandChunkSize = 1; //! Number of bands in a chunk
75 :
76 : enum WholeBandChunkOptim
77 : {
78 : WBC_DETECTION_IN_PROGRESS,
79 : WBC_DISABLED,
80 : WBC_ENABLED,
81 : };
82 :
83 : //! Flag to detect if the read pattern of HDF5ImageRasterBand::IRasterIO()
84 : // is whole band after whole band.
85 : WholeBandChunkOptim m_eWholeBandChunkOptim = WBC_DETECTION_IN_PROGRESS;
86 : //! Value of nBand during last HDF5ImageRasterBand::IRasterIO() call
87 : int m_nLastRasterIOBand = -1;
88 : //! Value of nXOff during last HDF5ImageRasterBand::IRasterIO() call
89 : int m_nLastRasterIOXOff = -1;
90 : //! Value of nYOff during last HDF5ImageRasterBand::IRasterIO() call
91 : int m_nLastRasterIOYOff = -1;
92 : //! Value of nXSize during last HDF5ImageRasterBand::IRasterIO() call
93 : int m_nLastRasterIOXSize = -1;
94 : //! Value of nYSize during last HDF5ImageRasterBand::IRasterIO() call
95 : int m_nLastRasterIOYSize = -1;
96 : //! Value such that m_abyBandChunk represent band data in the range
97 : // [m_iCurrentBandChunk * m_nBandChunkSize, (m_iCurrentBandChunk+1) * m_nBandChunkSize[
98 : int m_iCurrentBandChunk = -1;
99 : //! Cached values (in native data type) for bands in the range
100 : // [m_iCurrentBandChunk * m_nBandChunkSize, (m_iCurrentBandChunk+1) * m_nBandChunkSize[
101 : std::vector<GByte> m_abyBandChunk{};
102 :
103 : CPLErr CreateODIMH5Projection();
104 :
105 : public:
106 : HDF5ImageDataset();
107 : virtual ~HDF5ImageDataset();
108 :
109 : CPLErr CreateProjections();
110 : static GDALDataset *Open(GDALOpenInfo *);
111 : static int Identify(GDALOpenInfo *);
112 :
113 : const OGRSpatialReference *GetSpatialRef() const override;
114 : virtual int GetGCPCount() override;
115 : const OGRSpatialReference *GetGCPSpatialRef() const override;
116 : virtual const GDAL_GCP *GetGCPs() override;
117 : virtual CPLErr GetGeoTransform(double *padfTransform) override;
118 :
119 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
120 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
121 : GDALDataType eBufType, int nBandCount,
122 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
123 : GSpacing nLineSpace, GSpacing nBandSpace,
124 : GDALRasterIOExtraArg *psExtraArg) override;
125 :
126 : const char *GetMetadataItem(const char *pszName,
127 : const char *pszDomain = "") override;
128 :
129 189 : Hdf5ProductType GetSubdatasetType() const
130 : {
131 189 : return iSubdatasetType;
132 : }
133 :
134 6 : HDF5CSKProductEnum GetCSKProductType() const
135 : {
136 6 : return iCSKProductType;
137 : }
138 :
139 189 : int IsComplexCSKL1A() const
140 : {
141 195 : return GetSubdatasetType() == CSK_PRODUCT &&
142 195 : GetCSKProductType() == PROD_CSK_L1A && ndims == 3;
143 : }
144 :
145 672 : int GetYIndex() const
146 : {
147 672 : return m_nYIndex;
148 : }
149 :
150 792 : int GetXIndex() const
151 : {
152 792 : return m_nXIndex;
153 : }
154 :
155 : /**
156 : * Identify if the subdataset has a known product format
157 : * It stores a product identifier in iSubdatasetType,
158 : * UNKNOWN_PRODUCT, if it isn't a recognizable format.
159 : */
160 : void IdentifyProductType();
161 :
162 : /**
163 : * Captures Geolocation information from a COSMO-SKYMED
164 : * file.
165 : * The geoid will always be WGS84
166 : * The projection type may be UTM or UPS, depending on the
167 : * latitude from the center of the image.
168 : * @param iProductType type of HDF5 subproduct, see HDF5CSKProduct
169 : */
170 : void CaptureCSKGeolocation(int iProductType);
171 :
172 : /**
173 : * Get Geotransform information for COSMO-SKYMED files
174 : * In case of success it stores the transformation
175 : * in adfGeoTransform. In case of failure it doesn't
176 : * modify adfGeoTransform
177 : * @param iProductType type of HDF5 subproduct, see HDF5CSKProduct
178 : */
179 : void CaptureCSKGeoTransform(int iProductType);
180 :
181 : /**
182 : * @param iProductType type of HDF5 subproduct, see HDF5CSKProduct
183 : */
184 : void CaptureCSKGCPs(int iProductType);
185 : };
186 :
187 : /************************************************************************/
188 : /* ==================================================================== */
189 : /* HDF5ImageDataset */
190 : /* ==================================================================== */
191 : /************************************************************************/
192 :
193 : /************************************************************************/
194 : /* HDF5ImageDataset() */
195 : /************************************************************************/
196 63 : HDF5ImageDataset::HDF5ImageDataset()
197 : : dims(nullptr), maxdims(nullptr), poH5Objects(nullptr), ndims(0),
198 : dimensions(0), dataset_id(-1), dataspace_id(-1), native(-1),
199 : iSubdatasetType(UNKNOWN_PRODUCT), iCSKProductType(PROD_UNKNOWN),
200 63 : bHasGeoTransform(false)
201 : {
202 63 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
203 63 : m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
204 63 : adfGeoTransform[0] = 0.0;
205 63 : adfGeoTransform[1] = 1.0;
206 63 : adfGeoTransform[2] = 0.0;
207 63 : adfGeoTransform[3] = 0.0;
208 63 : adfGeoTransform[4] = 0.0;
209 63 : adfGeoTransform[5] = 1.0;
210 63 : }
211 :
212 : /************************************************************************/
213 : /* ~HDF5ImageDataset() */
214 : /************************************************************************/
215 126 : HDF5ImageDataset::~HDF5ImageDataset()
216 : {
217 : HDF5_GLOBAL_LOCK();
218 :
219 63 : FlushCache(true);
220 :
221 63 : if (dataset_id > 0)
222 63 : H5Dclose(dataset_id);
223 63 : if (dataspace_id > 0)
224 63 : H5Sclose(dataspace_id);
225 63 : if (native > 0)
226 63 : H5Tclose(native);
227 :
228 63 : CPLFree(dims);
229 63 : CPLFree(maxdims);
230 126 : }
231 :
232 : /************************************************************************/
233 : /* ==================================================================== */
234 : /* Hdf5imagerasterband */
235 : /* ==================================================================== */
236 : /************************************************************************/
237 : class HDF5ImageRasterBand final : public GDALPamRasterBand
238 : {
239 : friend class HDF5ImageDataset;
240 :
241 : bool bNoDataSet = false;
242 : double dfNoDataValue = -9999.0;
243 : bool m_bHasOffset = false;
244 : double m_dfOffset = 0.0;
245 : bool m_bHasScale = false;
246 : double m_dfScale = 1.0;
247 : int m_nIRasterIORecCounter = 0;
248 :
249 : public:
250 : HDF5ImageRasterBand(HDF5ImageDataset *, int, GDALDataType);
251 : virtual ~HDF5ImageRasterBand();
252 :
253 : virtual CPLErr IReadBlock(int, int, void *) override;
254 : virtual double GetNoDataValue(int *) override;
255 : virtual double GetOffset(int *) override;
256 : virtual double GetScale(int *) override;
257 : // virtual CPLErr IWriteBlock( int, int, void * );
258 :
259 : CPLErr IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize,
260 : int nYSize, void *pData, int nBufXSize, int nBufYSize,
261 : GDALDataType eBufType, GSpacing nPixelSpace,
262 : GSpacing nLineSpace,
263 : GDALRasterIOExtraArg *psExtraArg) override;
264 : };
265 :
266 : /************************************************************************/
267 : /* ~HDF5ImageRasterBand() */
268 : /************************************************************************/
269 :
270 250 : HDF5ImageRasterBand::~HDF5ImageRasterBand()
271 : {
272 250 : }
273 :
274 : /************************************************************************/
275 : /* HDF5ImageRasterBand() */
276 : /************************************************************************/
277 125 : HDF5ImageRasterBand::HDF5ImageRasterBand(HDF5ImageDataset *poDSIn, int nBandIn,
278 125 : GDALDataType eType)
279 : {
280 125 : poDS = poDSIn;
281 125 : nBand = nBandIn;
282 125 : eDataType = eType;
283 125 : nBlockXSize = poDSIn->m_nBlockXSize;
284 125 : nBlockYSize = poDSIn->m_nBlockYSize;
285 :
286 : // netCDF convention for nodata
287 125 : bNoDataSet =
288 125 : GH5_FetchAttribute(poDSIn->dataset_id, "_FillValue", dfNoDataValue);
289 125 : if (!bNoDataSet)
290 116 : dfNoDataValue = -9999.0;
291 :
292 : // netCDF conventions for scale and offset
293 125 : m_bHasOffset =
294 125 : GH5_FetchAttribute(poDSIn->dataset_id, "add_offset", m_dfOffset);
295 125 : if (!m_bHasOffset)
296 124 : m_dfOffset = 0.0;
297 125 : m_bHasScale =
298 125 : GH5_FetchAttribute(poDSIn->dataset_id, "scale_factor", m_dfScale);
299 125 : if (!m_bHasScale)
300 124 : m_dfScale = 1.0;
301 125 : }
302 :
303 : /************************************************************************/
304 : /* GetNoDataValue() */
305 : /************************************************************************/
306 48 : double HDF5ImageRasterBand::GetNoDataValue(int *pbSuccess)
307 :
308 : {
309 48 : if (bNoDataSet)
310 : {
311 3 : if (pbSuccess)
312 3 : *pbSuccess = bNoDataSet;
313 :
314 3 : return dfNoDataValue;
315 : }
316 :
317 45 : return GDALPamRasterBand::GetNoDataValue(pbSuccess);
318 : }
319 :
320 : /************************************************************************/
321 : /* GetOffset() */
322 : /************************************************************************/
323 :
324 22 : double HDF5ImageRasterBand::GetOffset(int *pbSuccess)
325 :
326 : {
327 22 : if (m_bHasOffset)
328 : {
329 1 : if (pbSuccess)
330 1 : *pbSuccess = m_bHasOffset;
331 :
332 1 : return m_dfOffset;
333 : }
334 :
335 21 : return GDALPamRasterBand::GetOffset(pbSuccess);
336 : }
337 :
338 : /************************************************************************/
339 : /* GetScale() */
340 : /************************************************************************/
341 :
342 22 : double HDF5ImageRasterBand::GetScale(int *pbSuccess)
343 :
344 : {
345 22 : if (m_bHasScale)
346 : {
347 1 : if (pbSuccess)
348 1 : *pbSuccess = m_bHasScale;
349 :
350 1 : return m_dfScale;
351 : }
352 :
353 21 : return GDALPamRasterBand::GetScale(pbSuccess);
354 : }
355 :
356 : /************************************************************************/
357 : /* IReadBlock() */
358 : /************************************************************************/
359 103 : CPLErr HDF5ImageRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
360 : void *pImage)
361 : {
362 103 : HDF5ImageDataset *poGDS = static_cast<HDF5ImageDataset *>(poDS);
363 :
364 103 : memset(pImage, 0,
365 103 : static_cast<size_t>(nBlockXSize) * nBlockYSize *
366 103 : GDALGetDataTypeSizeBytes(eDataType));
367 :
368 103 : if (poGDS->eAccess == GA_Update)
369 : {
370 0 : return CE_None;
371 : }
372 :
373 103 : const int nXOff = nBlockXOff * nBlockXSize;
374 103 : const int nYOff = nBlockYOff * nBlockYSize;
375 103 : const int nXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
376 103 : const int nYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
377 103 : if (poGDS->m_eWholeBandChunkOptim == HDF5ImageDataset::WBC_ENABLED)
378 : {
379 : const bool bIsBandInterleavedData =
380 2 : poGDS->ndims == 3 && poGDS->m_nOtherDimIndex == 0 &&
381 3 : poGDS->GetYIndex() == 1 && poGDS->GetXIndex() == 2;
382 1 : if (poGDS->nBands == 1 || bIsBandInterleavedData)
383 : {
384 : GDALRasterIOExtraArg sExtraArg;
385 1 : INIT_RASTERIO_EXTRA_ARG(sExtraArg);
386 1 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
387 2 : return IRasterIO(GF_Read, nXOff, nYOff, nXSize, nYSize, pImage,
388 : nXSize, nYSize, eDataType, nDTSize,
389 1 : static_cast<GSpacing>(nDTSize) * nBlockXSize,
390 1 : &sExtraArg);
391 : }
392 : }
393 :
394 : HDF5_GLOBAL_LOCK();
395 :
396 102 : hsize_t count[3] = {0, 0, 0};
397 102 : H5OFFSET_TYPE offset[3] = {0, 0, 0};
398 102 : hsize_t col_dims[3] = {0, 0, 0};
399 102 : hsize_t rank = std::min(poGDS->ndims, 2);
400 :
401 102 : if (poGDS->ndims == 3)
402 : {
403 1 : rank = 3;
404 1 : offset[poGDS->m_nOtherDimIndex] = nBand - 1;
405 1 : count[poGDS->m_nOtherDimIndex] = 1;
406 1 : col_dims[poGDS->m_nOtherDimIndex] = 1;
407 : }
408 :
409 102 : const int nYIndex = poGDS->GetYIndex();
410 : // Blocksize may not be a multiple of imagesize.
411 102 : if (nYIndex >= 0)
412 : {
413 101 : offset[nYIndex] = nYOff;
414 101 : count[nYIndex] = nYSize;
415 : }
416 102 : offset[poGDS->GetXIndex()] = nXOff;
417 102 : count[poGDS->GetXIndex()] = nXSize;
418 :
419 : // Select block from file space.
420 102 : herr_t status = H5Sselect_hyperslab(poGDS->dataspace_id, H5S_SELECT_SET,
421 : offset, nullptr, count, nullptr);
422 102 : if (status < 0)
423 0 : return CE_Failure;
424 :
425 : // Create memory space to receive the data.
426 102 : if (nYIndex >= 0)
427 101 : col_dims[nYIndex] = nBlockYSize;
428 102 : col_dims[poGDS->GetXIndex()] = nBlockXSize;
429 :
430 : const hid_t memspace =
431 102 : H5Screate_simple(static_cast<int>(rank), col_dims, nullptr);
432 102 : H5OFFSET_TYPE mem_offset[3] = {0, 0, 0};
433 102 : status = H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset, nullptr,
434 : count, nullptr);
435 102 : if (status < 0)
436 : {
437 0 : H5Sclose(memspace);
438 0 : return CE_Failure;
439 : }
440 :
441 102 : status = H5Dread(poGDS->dataset_id, poGDS->native, memspace,
442 : poGDS->dataspace_id, H5P_DEFAULT, pImage);
443 :
444 102 : H5Sclose(memspace);
445 :
446 102 : if (status < 0)
447 : {
448 0 : CPLError(CE_Failure, CPLE_AppDefined, "H5Dread() failed for block.");
449 0 : return CE_Failure;
450 : }
451 :
452 : #ifdef HDF5_HAVE_FLOAT16
453 : if (eDataType == GDT_Float32 && poGDS->m_bConvertFromFloat16)
454 : {
455 : for (size_t i = static_cast<size_t>(nBlockXSize) * nBlockYSize; i > 0;
456 : /* do nothing */)
457 : {
458 : --i;
459 : uint16_t nVal16;
460 : memcpy(&nVal16, static_cast<uint16_t *>(pImage) + i,
461 : sizeof(nVal16));
462 : const uint32_t nVal32 = CPLHalfToFloat(nVal16);
463 : float fVal;
464 : memcpy(&fVal, &nVal32, sizeof(fVal));
465 : *(static_cast<float *>(pImage) + i) = fVal;
466 : }
467 : }
468 : else if (eDataType == GDT_CFloat32 && poGDS->m_bConvertFromFloat16)
469 : {
470 : for (size_t i = static_cast<size_t>(nBlockXSize) * nBlockYSize; i > 0;
471 : /* do nothing */)
472 : {
473 : --i;
474 : for (int j = 1; j >= 0; --j)
475 : {
476 : uint16_t nVal16;
477 : memcpy(&nVal16, static_cast<uint16_t *>(pImage) + 2 * i + j,
478 : sizeof(nVal16));
479 : const uint32_t nVal32 = CPLHalfToFloat(nVal16);
480 : float fVal;
481 : memcpy(&fVal, &nVal32, sizeof(fVal));
482 : *(static_cast<float *>(pImage) + 2 * i + j) = fVal;
483 : }
484 : }
485 : }
486 : #endif
487 :
488 102 : return CE_None;
489 : }
490 :
491 : /************************************************************************/
492 : /* IRasterIO() */
493 : /************************************************************************/
494 :
495 311 : CPLErr HDF5ImageRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
496 : int nXSize, int nYSize, void *pData,
497 : int nBufXSize, int nBufYSize,
498 : GDALDataType eBufType,
499 : GSpacing nPixelSpace, GSpacing nLineSpace,
500 : GDALRasterIOExtraArg *psExtraArg)
501 :
502 : {
503 311 : HDF5ImageDataset *poGDS = static_cast<HDF5ImageDataset *>(poDS);
504 :
505 : #ifdef HDF5_HAVE_FLOAT16
506 : if (poGDS->m_bConvertFromFloat16)
507 : {
508 : return GDALPamRasterBand::IRasterIO(
509 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
510 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
511 : }
512 : #endif
513 :
514 : const bool bIsBandInterleavedData =
515 450 : poGDS->ndims == 3 && poGDS->m_nOtherDimIndex == 0 &&
516 761 : poGDS->GetYIndex() == 1 && poGDS->GetXIndex() == 2;
517 :
518 311 : const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
519 :
520 : // Try to detect if we read whole bands by chunks of whole lines
521 : // If so, then read and cache whole band (or group of m_nBandChunkSize bands)
522 : // to save HDF5 decompression.
523 311 : if (m_nIRasterIORecCounter == 0)
524 : {
525 263 : bool bInvalidateWholeBandChunkOptim = false;
526 263 : if (!(nXSize == nBufXSize && nYSize == nBufYSize))
527 : {
528 1 : bInvalidateWholeBandChunkOptim = true;
529 : }
530 : // Is the first request on band 1, line 0 and one or several full lines?
531 262 : else if (poGDS->m_eWholeBandChunkOptim !=
532 70 : HDF5ImageDataset::WBC_ENABLED &&
533 70 : nBand == 1 && nXOff == 0 && nYOff == 0 &&
534 20 : nXSize == nRasterXSize)
535 : {
536 19 : poGDS->m_eWholeBandChunkOptim =
537 : HDF5ImageDataset::WBC_DETECTION_IN_PROGRESS;
538 19 : poGDS->m_nLastRasterIOBand = 1;
539 19 : poGDS->m_nLastRasterIOXOff = nXOff;
540 19 : poGDS->m_nLastRasterIOYOff = nYOff;
541 19 : poGDS->m_nLastRasterIOXSize = nXSize;
542 19 : poGDS->m_nLastRasterIOYSize = nYSize;
543 : }
544 243 : else if (poGDS->m_eWholeBandChunkOptim ==
545 : HDF5ImageDataset::WBC_DETECTION_IN_PROGRESS)
546 : {
547 50 : if (poGDS->m_nLastRasterIOBand == 1 && nBand == 1)
548 : {
549 : // Is this request a continuation of the previous one?
550 44 : if (nXOff == 0 && poGDS->m_nLastRasterIOXOff == 0 &&
551 44 : nYOff == poGDS->m_nLastRasterIOYOff +
552 44 : poGDS->m_nLastRasterIOYSize &&
553 43 : poGDS->m_nLastRasterIOXSize == nRasterXSize &&
554 43 : nXSize == nRasterXSize)
555 : {
556 43 : poGDS->m_nLastRasterIOXOff = nXOff;
557 43 : poGDS->m_nLastRasterIOYOff = nYOff;
558 43 : poGDS->m_nLastRasterIOXSize = nXSize;
559 43 : poGDS->m_nLastRasterIOYSize = nYSize;
560 : }
561 : else
562 : {
563 1 : bInvalidateWholeBandChunkOptim = true;
564 : }
565 : }
566 6 : else if (poGDS->m_nLastRasterIOBand == 1 && nBand == 2)
567 : {
568 : // Are we switching to band 2 while having fully read band 1?
569 5 : if (nXOff == 0 && nYOff == 0 && nXSize == nRasterXSize &&
570 5 : poGDS->m_nLastRasterIOXOff == 0 &&
571 5 : poGDS->m_nLastRasterIOXSize == nRasterXSize &&
572 5 : poGDS->m_nLastRasterIOYOff + poGDS->m_nLastRasterIOYSize ==
573 5 : nRasterYSize)
574 : {
575 11 : if ((poGDS->m_nBandChunkSize > 1 ||
576 5 : nBufYSize < nRasterYSize) &&
577 1 : static_cast<int64_t>(poGDS->m_nBandChunkSize) *
578 1 : nRasterXSize * nRasterYSize * nDTSize <
579 1 : CPLGetUsablePhysicalRAM() / 10)
580 : {
581 1 : poGDS->m_eWholeBandChunkOptim =
582 : HDF5ImageDataset::WBC_ENABLED;
583 : }
584 : else
585 : {
586 3 : bInvalidateWholeBandChunkOptim = true;
587 : }
588 : }
589 : else
590 : {
591 1 : bInvalidateWholeBandChunkOptim = true;
592 : }
593 : }
594 : else
595 : {
596 1 : bInvalidateWholeBandChunkOptim = true;
597 : }
598 : }
599 263 : if (bInvalidateWholeBandChunkOptim)
600 : {
601 7 : poGDS->m_eWholeBandChunkOptim = HDF5ImageDataset::WBC_DISABLED;
602 7 : poGDS->m_nLastRasterIOBand = -1;
603 7 : poGDS->m_nLastRasterIOXOff = -1;
604 7 : poGDS->m_nLastRasterIOYOff = -1;
605 7 : poGDS->m_nLastRasterIOXSize = -1;
606 7 : poGDS->m_nLastRasterIOYSize = -1;
607 : }
608 : }
609 :
610 311 : if (poGDS->m_eWholeBandChunkOptim == HDF5ImageDataset::WBC_ENABLED &&
611 193 : nXSize == nBufXSize && nYSize == nBufYSize)
612 : {
613 193 : if (poGDS->nBands == 1 || bIsBandInterleavedData)
614 : {
615 193 : if (poGDS->m_iCurrentBandChunk < 0)
616 1 : CPLDebug("HDF5", "Using whole band chunk caching");
617 193 : const int iBandChunk = (nBand - 1) / poGDS->m_nBandChunkSize;
618 193 : if (iBandChunk != poGDS->m_iCurrentBandChunk)
619 : {
620 15 : poGDS->m_abyBandChunk.resize(
621 15 : static_cast<size_t>(poGDS->m_nBandChunkSize) *
622 15 : nRasterXSize * nRasterYSize * nDTSize);
623 :
624 : HDF5_GLOBAL_LOCK();
625 :
626 : hsize_t count[3] = {
627 30 : std::min(static_cast<hsize_t>(poGDS->nBands),
628 30 : static_cast<hsize_t>(iBandChunk + 1) *
629 15 : poGDS->m_nBandChunkSize) -
630 15 : static_cast<hsize_t>(iBandChunk) *
631 15 : poGDS->m_nBandChunkSize,
632 15 : static_cast<hsize_t>(nRasterYSize),
633 15 : static_cast<hsize_t>(nRasterXSize)};
634 15 : H5OFFSET_TYPE offset[3] = {
635 15 : static_cast<H5OFFSET_TYPE>(iBandChunk) *
636 15 : poGDS->m_nBandChunkSize,
637 : static_cast<H5OFFSET_TYPE>(0),
638 15 : static_cast<H5OFFSET_TYPE>(0)};
639 : herr_t status =
640 15 : H5Sselect_hyperslab(poGDS->dataspace_id, H5S_SELECT_SET,
641 : offset, nullptr, count, nullptr);
642 15 : if (status < 0)
643 0 : return CE_Failure;
644 :
645 : const hid_t memspace =
646 15 : H5Screate_simple(poGDS->ndims, count, nullptr);
647 15 : H5OFFSET_TYPE mem_offset[3] = {0, 0, 0};
648 : status =
649 15 : H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
650 : nullptr, count, nullptr);
651 15 : if (status < 0)
652 : {
653 0 : H5Sclose(memspace);
654 0 : return CE_Failure;
655 : }
656 :
657 15 : status = H5Dread(poGDS->dataset_id, poGDS->native, memspace,
658 : poGDS->dataspace_id, H5P_DEFAULT,
659 15 : poGDS->m_abyBandChunk.data());
660 :
661 15 : H5Sclose(memspace);
662 :
663 15 : if (status < 0)
664 : {
665 0 : CPLError(
666 : CE_Failure, CPLE_AppDefined,
667 : "HDF5ImageRasterBand::IRasterIO(): H5Dread() failed");
668 0 : return CE_Failure;
669 : }
670 :
671 15 : poGDS->m_iCurrentBandChunk = iBandChunk;
672 : }
673 :
674 1447 : for (int iY = 0; iY < nYSize; ++iY)
675 : {
676 2508 : GDALCopyWords(poGDS->m_abyBandChunk.data() +
677 1254 : static_cast<size_t>((nBand - 1) %
678 1254 : poGDS->m_nBandChunkSize) *
679 1254 : nRasterYSize * nRasterXSize * nDTSize +
680 1254 : static_cast<size_t>(nYOff + iY) *
681 1254 : nRasterXSize * nDTSize +
682 1254 : nXOff * nDTSize,
683 : eDataType, nDTSize,
684 : static_cast<GByte *>(pData) +
685 1254 : static_cast<size_t>(iY) * nLineSpace,
686 : eBufType, static_cast<int>(nPixelSpace), nXSize);
687 : }
688 193 : return CE_None;
689 : }
690 : }
691 :
692 : const bool bIsExpectedLayout =
693 204 : (bIsBandInterleavedData ||
694 171 : (poGDS->ndims == 2 && poGDS->GetYIndex() == 0 &&
695 85 : poGDS->GetXIndex() == 1));
696 118 : if (eRWFlag == GF_Read && bIsExpectedLayout && nXSize == nBufXSize &&
697 116 : nYSize == nBufYSize && eBufType == eDataType &&
698 68 : nPixelSpace == nDTSize && nLineSpace == nXSize * nPixelSpace)
699 : {
700 : HDF5_GLOBAL_LOCK();
701 :
702 68 : hsize_t count[3] = {1, static_cast<hsize_t>(nYSize),
703 68 : static_cast<hsize_t>(nXSize)};
704 68 : H5OFFSET_TYPE offset[3] = {static_cast<H5OFFSET_TYPE>(nBand - 1),
705 68 : static_cast<H5OFFSET_TYPE>(nYOff),
706 68 : static_cast<H5OFFSET_TYPE>(nXOff)};
707 68 : if (poGDS->ndims == 2)
708 : {
709 47 : count[0] = count[1];
710 47 : count[1] = count[2];
711 :
712 47 : offset[0] = offset[1];
713 47 : offset[1] = offset[2];
714 : }
715 68 : herr_t status = H5Sselect_hyperslab(poGDS->dataspace_id, H5S_SELECT_SET,
716 : offset, nullptr, count, nullptr);
717 68 : if (status < 0)
718 0 : return CE_Failure;
719 :
720 68 : const hid_t memspace = H5Screate_simple(poGDS->ndims, count, nullptr);
721 68 : H5OFFSET_TYPE mem_offset[3] = {0, 0, 0};
722 68 : status = H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
723 : nullptr, count, nullptr);
724 68 : if (status < 0)
725 : {
726 0 : H5Sclose(memspace);
727 0 : return CE_Failure;
728 : }
729 :
730 68 : status = H5Dread(poGDS->dataset_id, poGDS->native, memspace,
731 : poGDS->dataspace_id, H5P_DEFAULT, pData);
732 :
733 68 : H5Sclose(memspace);
734 :
735 68 : if (status < 0)
736 : {
737 0 : CPLError(CE_Failure, CPLE_AppDefined,
738 : "HDF5ImageRasterBand::IRasterIO(): H5Dread() failed");
739 0 : return CE_Failure;
740 : }
741 :
742 68 : return CE_None;
743 : }
744 :
745 : // If the request is still small enough, try to read from libhdf5 with
746 : // the natural interleaving into a temporary MEMDataset, and then read
747 : // from it with the requested interleaving and data type.
748 50 : if (eRWFlag == GF_Read && bIsExpectedLayout && nXSize == nBufXSize &&
749 100 : nYSize == nBufYSize &&
750 48 : static_cast<GIntBig>(nXSize) * nYSize < CPLGetUsablePhysicalRAM() / 10)
751 : {
752 : auto poMemDS = std::unique_ptr<GDALDataset>(
753 48 : MEMDataset::Create("", nXSize, nYSize, 1, eDataType, nullptr));
754 48 : if (poMemDS)
755 : {
756 48 : void *pMemData = poMemDS->GetInternalHandle("MEMORY1");
757 48 : CPLAssert(pMemData);
758 : // Read from HDF5 into the temporary MEMDataset using the
759 : // natural interleaving of the HDF5 dataset
760 48 : ++m_nIRasterIORecCounter;
761 : CPLErr eErr =
762 96 : IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pMemData,
763 : nXSize, nYSize, eDataType, nDTSize,
764 48 : static_cast<GSpacing>(nXSize) * nDTSize, psExtraArg);
765 48 : --m_nIRasterIORecCounter;
766 48 : if (eErr != CE_None)
767 : {
768 0 : return CE_Failure;
769 : }
770 : // Copy to the final buffer using requested data type and spacings.
771 48 : return poMemDS->GetRasterBand(1)->RasterIO(
772 : GF_Read, 0, 0, nXSize, nYSize, pData, nXSize, nYSize, eBufType,
773 48 : nPixelSpace, nLineSpace, nullptr);
774 : }
775 : }
776 :
777 2 : return GDALPamRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
778 : pData, nBufXSize, nBufYSize, eBufType,
779 2 : nPixelSpace, nLineSpace, psExtraArg);
780 : }
781 :
782 : /************************************************************************/
783 : /* IRasterIO() */
784 : /************************************************************************/
785 :
786 90 : CPLErr HDF5ImageDataset::IRasterIO(
787 : GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
788 : void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
789 : int nBandCount, BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
790 : GSpacing nLineSpace, GSpacing nBandSpace, GDALRasterIOExtraArg *psExtraArg)
791 :
792 : {
793 : #ifdef HDF5_HAVE_FLOAT16
794 : if (m_bConvertFromFloat16)
795 : {
796 : return HDF5Dataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
797 : pData, nBufXSize, nBufYSize, eBufType,
798 : nBandCount, panBandMap, nPixelSpace,
799 : nLineSpace, nBandSpace, psExtraArg);
800 : }
801 : #endif
802 :
803 134 : const auto IsConsecutiveBands = [](const int *panVals, int nCount)
804 : {
805 151 : for (int i = 1; i < nCount; ++i)
806 : {
807 19 : if (panVals[i] != panVals[i - 1] + 1)
808 2 : return false;
809 : }
810 132 : return true;
811 : };
812 :
813 90 : const auto eDT = GetRasterBand(1)->GetRasterDataType();
814 90 : const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
815 :
816 : // Band-interleaved data and request
817 176 : const bool bIsBandInterleavedData = ndims == 3 && m_nOtherDimIndex == 0 &&
818 266 : GetYIndex() == 1 && GetXIndex() == 2;
819 90 : if (eRWFlag == GF_Read && bIsBandInterleavedData && nXSize == nBufXSize &&
820 86 : nYSize == nBufYSize && IsConsecutiveBands(panBandMap, nBandCount) &&
821 84 : eBufType == eDT && nPixelSpace == nDTSize &&
822 180 : nLineSpace == nXSize * nPixelSpace && nBandSpace == nYSize * nLineSpace)
823 : {
824 : HDF5_GLOBAL_LOCK();
825 :
826 43 : hsize_t count[3] = {static_cast<hsize_t>(nBandCount),
827 43 : static_cast<hsize_t>(nYSize),
828 43 : static_cast<hsize_t>(nXSize)};
829 : H5OFFSET_TYPE offset[3] = {
830 43 : static_cast<H5OFFSET_TYPE>(panBandMap[0] - 1),
831 43 : static_cast<H5OFFSET_TYPE>(nYOff),
832 43 : static_cast<H5OFFSET_TYPE>(nXOff)};
833 43 : herr_t status = H5Sselect_hyperslab(dataspace_id, H5S_SELECT_SET,
834 : offset, nullptr, count, nullptr);
835 43 : if (status < 0)
836 0 : return CE_Failure;
837 :
838 43 : const hid_t memspace = H5Screate_simple(ndims, count, nullptr);
839 43 : H5OFFSET_TYPE mem_offset[3] = {0, 0, 0};
840 43 : status = H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
841 : nullptr, count, nullptr);
842 43 : if (status < 0)
843 : {
844 0 : H5Sclose(memspace);
845 0 : return CE_Failure;
846 : }
847 :
848 43 : status = H5Dread(dataset_id, native, memspace, dataspace_id,
849 : H5P_DEFAULT, pData);
850 :
851 43 : H5Sclose(memspace);
852 :
853 43 : if (status < 0)
854 : {
855 0 : CPLError(CE_Failure, CPLE_AppDefined,
856 : "HDF5ImageDataset::IRasterIO(): H5Dread() failed");
857 0 : return CE_Failure;
858 : }
859 :
860 43 : return CE_None;
861 : }
862 :
863 : // Pixel-interleaved data and request
864 :
865 51 : const bool bIsPixelInterleaveData = ndims == 3 && m_nOtherDimIndex == 2 &&
866 98 : GetYIndex() == 0 && GetXIndex() == 1;
867 47 : if (eRWFlag == GF_Read && bIsPixelInterleaveData && nXSize == nBufXSize &&
868 4 : nYSize == nBufYSize && IsConsecutiveBands(panBandMap, nBandCount) &&
869 4 : eBufType == eDT && nBandSpace == nDTSize &&
870 97 : nPixelSpace == nBandCount * nBandSpace &&
871 3 : nLineSpace == nXSize * nPixelSpace)
872 : {
873 : HDF5_GLOBAL_LOCK();
874 :
875 3 : hsize_t count[3] = {static_cast<hsize_t>(nYSize),
876 3 : static_cast<hsize_t>(nXSize),
877 3 : static_cast<hsize_t>(nBandCount)};
878 : H5OFFSET_TYPE offset[3] = {
879 3 : static_cast<H5OFFSET_TYPE>(nYOff),
880 3 : static_cast<H5OFFSET_TYPE>(nXOff),
881 3 : static_cast<H5OFFSET_TYPE>(panBandMap[0] - 1)};
882 3 : herr_t status = H5Sselect_hyperslab(dataspace_id, H5S_SELECT_SET,
883 : offset, nullptr, count, nullptr);
884 3 : if (status < 0)
885 0 : return CE_Failure;
886 :
887 3 : const hid_t memspace = H5Screate_simple(ndims, count, nullptr);
888 3 : H5OFFSET_TYPE mem_offset[3] = {0, 0, 0};
889 3 : status = H5Sselect_hyperslab(memspace, H5S_SELECT_SET, mem_offset,
890 : nullptr, count, nullptr);
891 3 : if (status < 0)
892 : {
893 0 : H5Sclose(memspace);
894 0 : return CE_Failure;
895 : }
896 :
897 3 : status = H5Dread(dataset_id, native, memspace, dataspace_id,
898 : H5P_DEFAULT, pData);
899 :
900 3 : H5Sclose(memspace);
901 :
902 3 : if (status < 0)
903 : {
904 0 : CPLError(CE_Failure, CPLE_AppDefined,
905 : "HDF5ImageDataset::IRasterIO(): H5Dread() failed");
906 0 : return CE_Failure;
907 : }
908 :
909 3 : return CE_None;
910 : }
911 :
912 : // If the request is still small enough, try to read from libhdf5 with
913 : // the natural interleaving into a temporary MEMDataset, and then read
914 : // from it with the requested interleaving and data type.
915 44 : if (eRWFlag == GF_Read &&
916 44 : (bIsBandInterleavedData || bIsPixelInterleaveData) &&
917 88 : nXSize == nBufXSize && nYSize == nBufYSize &&
918 132 : IsConsecutiveBands(panBandMap, nBandCount) &&
919 43 : static_cast<GIntBig>(nXSize) * nYSize <
920 43 : CPLGetUsablePhysicalRAM() / 10 / nBandCount)
921 : {
922 43 : const char *const apszOptions[] = {
923 43 : bIsPixelInterleaveData ? "INTERLEAVE=PIXEL" : nullptr, nullptr};
924 : auto poMemDS = std::unique_ptr<GDALDataset>(
925 43 : MEMDataset::Create("", nXSize, nYSize, nBandCount, eDT,
926 43 : const_cast<char **>(apszOptions)));
927 43 : if (poMemDS)
928 : {
929 43 : void *pMemData = poMemDS->GetInternalHandle("MEMORY1");
930 43 : CPLAssert(pMemData);
931 : // Read from HDF5 into the temporary MEMDataset using the
932 : // natural interleaving of the HDF5 dataset
933 129 : if (IRasterIO(
934 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pMemData, nXSize,
935 : nYSize, eDT, nBandCount, panBandMap,
936 1 : bIsBandInterleavedData ? nDTSize : nDTSize * nBandCount,
937 : bIsBandInterleavedData
938 42 : ? static_cast<GSpacing>(nXSize) * nDTSize
939 1 : : static_cast<GSpacing>(nXSize) * nDTSize * nBandCount,
940 : bIsBandInterleavedData
941 42 : ? static_cast<GSpacing>(nYSize) * nXSize * nDTSize
942 : : nDTSize,
943 43 : psExtraArg) != CE_None)
944 : {
945 0 : return CE_Failure;
946 : }
947 : // Copy to the final buffer using requested data type and spacings.
948 43 : return poMemDS->RasterIO(GF_Read, 0, 0, nXSize, nYSize, pData,
949 : nXSize, nYSize, eBufType, nBandCount,
950 : nullptr, nPixelSpace, nLineSpace,
951 43 : nBandSpace, nullptr);
952 : }
953 : }
954 :
955 1 : return HDF5Dataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
956 : nBufXSize, nBufYSize, eBufType, nBandCount,
957 : panBandMap, nPixelSpace, nLineSpace,
958 1 : nBandSpace, psExtraArg);
959 : }
960 :
961 : /************************************************************************/
962 : /* Open() */
963 : /************************************************************************/
964 63 : GDALDataset *HDF5ImageDataset::Open(GDALOpenInfo *poOpenInfo)
965 : {
966 63 : if (!STARTS_WITH_CI(poOpenInfo->pszFilename, "HDF5:"))
967 0 : return nullptr;
968 :
969 : HDF5_GLOBAL_LOCK();
970 :
971 : // Confirm the requested access is supported.
972 63 : if (poOpenInfo->eAccess == GA_Update)
973 : {
974 0 : CPLError(CE_Failure, CPLE_NotSupported,
975 : "The HDF5ImageDataset driver does not support update access "
976 : "to existing datasets.");
977 0 : return nullptr;
978 : }
979 :
980 63 : HDF5ImageDataset *poDS = new HDF5ImageDataset();
981 :
982 : // Create a corresponding GDALDataset.
983 : char **papszName =
984 63 : CSLTokenizeString2(poOpenInfo->pszFilename, ":",
985 : CSLT_HONOURSTRINGS | CSLT_PRESERVEESCAPES);
986 :
987 63 : if (!(CSLCount(papszName) == 3 || CSLCount(papszName) == 4))
988 : {
989 0 : CSLDestroy(papszName);
990 0 : delete poDS;
991 0 : return nullptr;
992 : }
993 :
994 63 : poDS->SetDescription(poOpenInfo->pszFilename);
995 :
996 : // Check for drive name in windows HDF5:"D:\...
997 126 : CPLString osSubdatasetName;
998 :
999 126 : CPLString osFilename(papszName[1]);
1000 :
1001 63 : if ((strlen(papszName[1]) == 1 && papszName[3] != nullptr) ||
1002 63 : (STARTS_WITH(papszName[1], "/vsicurl/http") && papszName[3] != nullptr))
1003 : {
1004 0 : osFilename += ":";
1005 0 : osFilename += papszName[2];
1006 0 : osSubdatasetName = papszName[3];
1007 : }
1008 : else
1009 : {
1010 63 : osSubdatasetName = papszName[2];
1011 : }
1012 :
1013 63 : poDS->SetSubdatasetName(osSubdatasetName);
1014 :
1015 63 : CSLDestroy(papszName);
1016 63 : papszName = nullptr;
1017 :
1018 63 : poDS->SetPhysicalFilename(osFilename);
1019 :
1020 : // Try opening the dataset.
1021 63 : poDS->m_hHDF5 = GDAL_HDF5Open(osFilename);
1022 63 : if (poDS->m_hHDF5 < 0)
1023 : {
1024 0 : delete poDS;
1025 0 : return nullptr;
1026 : }
1027 :
1028 63 : poDS->hGroupID = H5Gopen(poDS->m_hHDF5, "/");
1029 63 : if (poDS->hGroupID < 0)
1030 : {
1031 0 : delete poDS;
1032 0 : return nullptr;
1033 : }
1034 :
1035 : // This is an HDF5 file.
1036 63 : poDS->ReadGlobalAttributes(FALSE);
1037 :
1038 : // Create HDF5 Data Hierarchy in a link list.
1039 63 : poDS->poH5Objects = poDS->HDF5FindDatasetObjectsbyPath(poDS->poH5RootGroup,
1040 : osSubdatasetName);
1041 :
1042 63 : if (poDS->poH5Objects == nullptr)
1043 : {
1044 0 : delete poDS;
1045 0 : return nullptr;
1046 : }
1047 :
1048 : // Retrieve HDF5 data information.
1049 63 : poDS->dataset_id = H5Dopen(poDS->m_hHDF5, poDS->poH5Objects->pszPath);
1050 63 : poDS->dataspace_id = H5Dget_space(poDS->dataset_id);
1051 63 : poDS->ndims = H5Sget_simple_extent_ndims(poDS->dataspace_id);
1052 63 : if (poDS->ndims <= 0)
1053 : {
1054 0 : delete poDS;
1055 0 : return nullptr;
1056 : }
1057 63 : poDS->dims =
1058 63 : static_cast<hsize_t *>(CPLCalloc(poDS->ndims, sizeof(hsize_t)));
1059 63 : poDS->maxdims =
1060 63 : static_cast<hsize_t *>(CPLCalloc(poDS->ndims, sizeof(hsize_t)));
1061 63 : poDS->dimensions = H5Sget_simple_extent_dims(poDS->dataspace_id, poDS->dims,
1062 : poDS->maxdims);
1063 63 : auto datatype = H5Dget_type(poDS->dataset_id);
1064 63 : poDS->native = H5Tget_native_type(datatype, H5T_DIR_ASCEND);
1065 63 : H5Tclose(datatype);
1066 :
1067 63 : const auto eGDALDataType = poDS->GetDataType(poDS->native);
1068 63 : if (eGDALDataType == GDT_Unknown)
1069 : {
1070 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unhandled HDF5 data type");
1071 0 : delete poDS;
1072 0 : return nullptr;
1073 : }
1074 :
1075 : #ifdef HDF5_HAVE_FLOAT16
1076 : if (H5Tequal(H5T_NATIVE_FLOAT16, poDS->native) ||
1077 : IsNativeCFloat16(poDS->native))
1078 : {
1079 : poDS->m_bConvertFromFloat16 = true;
1080 : }
1081 : #endif
1082 :
1083 : // CSK code in IdentifyProductType() and CreateProjections()
1084 : // uses dataset metadata.
1085 63 : poDS->SetMetadata(poDS->m_aosMetadata.List());
1086 :
1087 : // Check if the hdf5 is a well known product type
1088 63 : poDS->IdentifyProductType();
1089 :
1090 63 : poDS->m_nYIndex = poDS->IsComplexCSKL1A() ? 0 : poDS->ndims - 2;
1091 63 : poDS->m_nXIndex = poDS->IsComplexCSKL1A() ? 1 : poDS->ndims - 1;
1092 :
1093 63 : if (poDS->IsComplexCSKL1A())
1094 : {
1095 0 : poDS->m_nOtherDimIndex = 2;
1096 : }
1097 63 : else if (poDS->ndims == 3)
1098 : {
1099 10 : poDS->m_nOtherDimIndex = 0;
1100 : }
1101 :
1102 63 : if (HDF5EOSParser::HasHDFEOS(poDS->hGroupID))
1103 : {
1104 34 : HDF5EOSParser oHDFEOSParser;
1105 17 : if (oHDFEOSParser.Parse(poDS->hGroupID))
1106 : {
1107 17 : CPLDebug("HDF5", "Successfully parsed HDFEOS metadata");
1108 34 : HDF5EOSParser::GridDataFieldMetadata oGridDataFieldMetadata;
1109 34 : HDF5EOSParser::SwathDataFieldMetadata oSwathDataFieldMetadata;
1110 17 : if (oHDFEOSParser.GetDataModel() ==
1111 5 : HDF5EOSParser::DataModel::GRID &&
1112 5 : oHDFEOSParser.GetGridDataFieldMetadata(
1113 22 : osSubdatasetName.c_str(), oGridDataFieldMetadata) &&
1114 5 : static_cast<int>(oGridDataFieldMetadata.aoDimensions.size()) ==
1115 5 : poDS->ndims)
1116 : {
1117 5 : int iDim = 0;
1118 17 : for (const auto &oDim : oGridDataFieldMetadata.aoDimensions)
1119 : {
1120 12 : if (oDim.osName == "XDim")
1121 5 : poDS->m_nXIndex = iDim;
1122 7 : else if (oDim.osName == "YDim")
1123 5 : poDS->m_nYIndex = iDim;
1124 : else
1125 2 : poDS->m_nOtherDimIndex = iDim;
1126 12 : ++iDim;
1127 : }
1128 :
1129 5 : if (oGridDataFieldMetadata.poGridMetadata->GetGeoTransform(
1130 5 : poDS->adfGeoTransform))
1131 5 : poDS->bHasGeoTransform = true;
1132 :
1133 10 : auto poSRS = oGridDataFieldMetadata.poGridMetadata->GetSRS();
1134 5 : if (poSRS)
1135 5 : poDS->m_oSRS = *(poSRS.get());
1136 : }
1137 12 : else if (oHDFEOSParser.GetDataModel() ==
1138 12 : HDF5EOSParser::DataModel::SWATH &&
1139 12 : oHDFEOSParser.GetSwathDataFieldMetadata(
1140 6 : osSubdatasetName.c_str(), oSwathDataFieldMetadata) &&
1141 : static_cast<int>(
1142 6 : oSwathDataFieldMetadata.aoDimensions.size()) ==
1143 6 : poDS->ndims &&
1144 30 : oSwathDataFieldMetadata.iXDim >= 0 &&
1145 6 : oSwathDataFieldMetadata.iYDim >= 0)
1146 : {
1147 6 : poDS->m_nXIndex = oSwathDataFieldMetadata.iXDim;
1148 6 : poDS->m_nYIndex = oSwathDataFieldMetadata.iYDim;
1149 6 : poDS->m_nOtherDimIndex = oSwathDataFieldMetadata.iOtherDim;
1150 6 : if (!oSwathDataFieldMetadata.osLongitudeSubdataset.empty())
1151 : {
1152 : // Arbitrary
1153 6 : poDS->SetMetadataItem("SRS", SRS_WKT_WGS84_LAT_LONG,
1154 6 : "GEOLOCATION");
1155 6 : poDS->SetMetadataItem(
1156 : "X_DATASET",
1157 12 : ("HDF5:\"" + osFilename +
1158 12 : "\":" + oSwathDataFieldMetadata.osLongitudeSubdataset)
1159 : .c_str(),
1160 6 : "GEOLOCATION");
1161 6 : poDS->SetMetadataItem("X_BAND", "1", "GEOLOCATION");
1162 6 : poDS->SetMetadataItem(
1163 : "Y_DATASET",
1164 12 : ("HDF5:\"" + osFilename +
1165 12 : "\":" + oSwathDataFieldMetadata.osLatitudeSubdataset)
1166 : .c_str(),
1167 6 : "GEOLOCATION");
1168 6 : poDS->SetMetadataItem("Y_BAND", "1", "GEOLOCATION");
1169 6 : poDS->SetMetadataItem(
1170 : "PIXEL_OFFSET",
1171 : CPLSPrintf("%d", oSwathDataFieldMetadata.nPixelOffset),
1172 6 : "GEOLOCATION");
1173 6 : poDS->SetMetadataItem(
1174 : "PIXEL_STEP",
1175 : CPLSPrintf("%d", oSwathDataFieldMetadata.nPixelStep),
1176 6 : "GEOLOCATION");
1177 6 : poDS->SetMetadataItem(
1178 : "LINE_OFFSET",
1179 : CPLSPrintf("%d", oSwathDataFieldMetadata.nLineOffset),
1180 6 : "GEOLOCATION");
1181 6 : poDS->SetMetadataItem(
1182 : "LINE_STEP",
1183 : CPLSPrintf("%d", oSwathDataFieldMetadata.nLineStep),
1184 6 : "GEOLOCATION");
1185 : // Not totally sure about that
1186 6 : poDS->SetMetadataItem("GEOREFERENCING_CONVENTION",
1187 6 : "PIXEL_CENTER", "GEOLOCATION");
1188 : }
1189 : }
1190 : }
1191 : }
1192 :
1193 63 : poDS->nRasterYSize =
1194 63 : poDS->GetYIndex() < 0
1195 63 : ? 1
1196 62 : : static_cast<int>(poDS->dims[poDS->GetYIndex()]); // nRows
1197 63 : poDS->nRasterXSize =
1198 63 : static_cast<int>(poDS->dims[poDS->GetXIndex()]); // nCols
1199 63 : int nBands = 1;
1200 63 : if (poDS->m_nOtherDimIndex >= 0)
1201 : {
1202 10 : nBands = static_cast<int>(poDS->dims[poDS->m_nOtherDimIndex]);
1203 : }
1204 :
1205 126 : CPLStringList aosMetadata;
1206 63 : std::map<std::string, CPLStringList> oMapBandSpecificMetadata;
1207 63 : if (poDS->poH5Objects->nType == H5G_DATASET)
1208 : {
1209 63 : HDF5Dataset::CreateMetadata(poDS->m_hHDF5, poDS->poH5Objects,
1210 : H5G_DATASET, false, aosMetadata);
1211 63 : if (nBands > 1 && poDS->nRasterXSize != nBands &&
1212 9 : poDS->nRasterYSize != nBands)
1213 : {
1214 : // Heuristics to detect non-scalar attributes, that are intended
1215 : // to be attached to a specific band.
1216 18 : const CPLStringList aosMetadataDup(aosMetadata);
1217 12 : for (const auto &[pszKey, pszValue] :
1218 21 : cpl::IterateNameValue(aosMetadataDup))
1219 : {
1220 6 : const hid_t hAttrID = H5Aopen_name(poDS->dataset_id, pszKey);
1221 6 : const hid_t hAttrSpace = H5Aget_space(hAttrID);
1222 11 : if (H5Sget_simple_extent_ndims(hAttrSpace) == 1 &&
1223 5 : H5Sget_simple_extent_npoints(hAttrSpace) == nBands)
1224 : {
1225 : CPLStringList aosTokens(
1226 8 : CSLTokenizeString2(pszValue, " ", 0));
1227 4 : if (aosTokens.size() == nBands)
1228 : {
1229 8 : std::string osAttrName(pszKey);
1230 6 : if (osAttrName.size() > strlen("_coefficients") &&
1231 6 : osAttrName.substr(osAttrName.size() -
1232 2 : strlen("_coefficients")) ==
1233 : "_coefficients")
1234 : {
1235 1 : osAttrName.pop_back();
1236 : }
1237 5 : else if (osAttrName.size() > strlen("_wavelengths") &&
1238 5 : osAttrName.substr(osAttrName.size() -
1239 2 : strlen("_wavelengths")) ==
1240 : "_wavelengths")
1241 : {
1242 1 : osAttrName.pop_back();
1243 : }
1244 3 : else if (osAttrName.size() > strlen("_list") &&
1245 3 : osAttrName.substr(osAttrName.size() -
1246 1 : strlen("_list")) == "_list")
1247 : {
1248 1 : osAttrName.resize(osAttrName.size() -
1249 : strlen("_list"));
1250 : }
1251 4 : oMapBandSpecificMetadata[osAttrName] =
1252 8 : std::move(aosTokens);
1253 4 : aosMetadata.SetNameValue(pszKey, nullptr);
1254 : }
1255 : }
1256 6 : H5Sclose(hAttrSpace);
1257 6 : H5Aclose(hAttrID);
1258 : }
1259 : }
1260 : }
1261 :
1262 63 : poDS->m_nBlockXSize = poDS->GetRasterXSize();
1263 63 : poDS->m_nBlockYSize = 1;
1264 63 : poDS->m_nBandChunkSize = 1;
1265 :
1266 : // Check for chunksize and set it as the blocksize (optimizes read).
1267 63 : const hid_t listid = H5Dget_create_plist(poDS->dataset_id);
1268 63 : if (listid > 0)
1269 : {
1270 63 : if (H5Pget_layout(listid) == H5D_CHUNKED)
1271 : {
1272 22 : hsize_t panChunkDims[3] = {0, 0, 0};
1273 22 : const int nDimSize = H5Pget_chunk(listid, 3, panChunkDims);
1274 22 : CPL_IGNORE_RET_VAL(nDimSize);
1275 22 : CPLAssert(nDimSize == poDS->ndims);
1276 22 : poDS->m_nBlockXSize =
1277 22 : static_cast<int>(panChunkDims[poDS->GetXIndex()]);
1278 22 : if (poDS->GetYIndex() >= 0)
1279 22 : poDS->m_nBlockYSize =
1280 22 : static_cast<int>(panChunkDims[poDS->GetYIndex()]);
1281 22 : if (nBands > 1)
1282 : {
1283 3 : poDS->m_nBandChunkSize =
1284 3 : static_cast<int>(panChunkDims[poDS->m_nOtherDimIndex]);
1285 :
1286 3 : poDS->SetMetadataItem("BAND_CHUNK_SIZE",
1287 : CPLSPrintf("%d", poDS->m_nBandChunkSize),
1288 3 : "IMAGE_STRUCTURE");
1289 : }
1290 : }
1291 :
1292 63 : const int nFilters = H5Pget_nfilters(listid);
1293 89 : for (int i = 0; i < nFilters; ++i)
1294 : {
1295 26 : unsigned int flags = 0;
1296 26 : size_t cd_nelmts = 0;
1297 26 : char szName[64 + 1] = {0};
1298 26 : const auto eFilter = H5Pget_filter(listid, i, &flags, &cd_nelmts,
1299 : nullptr, 64, szName);
1300 26 : if (eFilter == H5Z_FILTER_DEFLATE)
1301 : {
1302 15 : poDS->SetMetadataItem("COMPRESSION", "DEFLATE",
1303 15 : "IMAGE_STRUCTURE");
1304 : }
1305 11 : else if (eFilter == H5Z_FILTER_SZIP)
1306 : {
1307 0 : poDS->SetMetadataItem("COMPRESSION", "SZIP", "IMAGE_STRUCTURE");
1308 : }
1309 : }
1310 :
1311 63 : H5Pclose(listid);
1312 : }
1313 :
1314 188 : for (int i = 0; i < nBands; i++)
1315 : {
1316 : HDF5ImageRasterBand *const poBand =
1317 125 : new HDF5ImageRasterBand(poDS, i + 1, eGDALDataType);
1318 :
1319 125 : poDS->SetBand(i + 1, poBand);
1320 :
1321 125 : if (poDS->poH5Objects->nType == H5G_DATASET)
1322 : {
1323 125 : poBand->SetMetadata(aosMetadata.List());
1324 133 : for (const auto &oIter : oMapBandSpecificMetadata)
1325 : {
1326 8 : poBand->SetMetadataItem(oIter.first.c_str(), oIter.second[i]);
1327 : }
1328 : }
1329 : }
1330 :
1331 63 : if (!poDS->GetMetadata("GEOLOCATION"))
1332 57 : poDS->CreateProjections();
1333 :
1334 : // Setup/check for pam .aux.xml.
1335 63 : poDS->TryLoadXML();
1336 :
1337 : // Setup overviews.
1338 63 : poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
1339 :
1340 63 : return poDS;
1341 : }
1342 :
1343 : /************************************************************************/
1344 : /* HDF5ImageDatasetDriverUnload() */
1345 : /************************************************************************/
1346 :
1347 6 : static void HDF5ImageDatasetDriverUnload(GDALDriver *)
1348 : {
1349 6 : HDF5UnloadFileDriver();
1350 6 : }
1351 :
1352 : /************************************************************************/
1353 : /* GDALRegister_HDF5Image() */
1354 : /************************************************************************/
1355 10 : void GDALRegister_HDF5Image()
1356 :
1357 : {
1358 10 : if (!GDAL_CHECK_VERSION("HDF5Image driver"))
1359 0 : return;
1360 :
1361 10 : if (GDALGetDriverByName(HDF5_IMAGE_DRIVER_NAME) != nullptr)
1362 0 : return;
1363 :
1364 10 : GDALDriver *poDriver = new GDALDriver();
1365 :
1366 10 : HDF5ImageDriverSetCommonMetadata(poDriver);
1367 :
1368 10 : poDriver->pfnOpen = HDF5ImageDataset::Open;
1369 10 : poDriver->pfnUnloadDriver = HDF5ImageDatasetDriverUnload;
1370 :
1371 10 : GetGDALDriverManager()->RegisterDriver(poDriver);
1372 : }
1373 :
1374 : /************************************************************************/
1375 : /* CreateODIMH5Projection() */
1376 : /************************************************************************/
1377 :
1378 : // Reference:
1379 : // http://www.knmi.nl/opera/opera3/OPERA_2008_03_WP2.1b_ODIM_H5_v2.1.pdf
1380 : //
1381 : // 4.3.2 where for geographically referenced image Groups
1382 : // We don't use the where_xscale and where_yscale parameters, but recompute them
1383 : // from the lower-left and upper-right coordinates. There's some difference.
1384 : // As all those parameters are linked together, I'm not sure which one should be
1385 : // considered as the reference.
1386 :
1387 0 : CPLErr HDF5ImageDataset::CreateODIMH5Projection()
1388 : {
1389 0 : const char *const pszProj4String = GetMetadataItem("where_projdef");
1390 0 : const char *const pszLL_lon = GetMetadataItem("where_LL_lon");
1391 0 : const char *const pszLL_lat = GetMetadataItem("where_LL_lat");
1392 0 : const char *const pszUR_lon = GetMetadataItem("where_UR_lon");
1393 0 : const char *const pszUR_lat = GetMetadataItem("where_UR_lat");
1394 0 : if (pszProj4String == nullptr || pszLL_lon == nullptr ||
1395 0 : pszLL_lat == nullptr || pszUR_lon == nullptr || pszUR_lat == nullptr)
1396 0 : return CE_Failure;
1397 :
1398 0 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1399 0 : if (m_oSRS.importFromProj4(pszProj4String) != OGRERR_NONE)
1400 0 : return CE_Failure;
1401 :
1402 0 : OGRSpatialReference oSRSWGS84;
1403 0 : oSRSWGS84.SetWellKnownGeogCS("WGS84");
1404 0 : oSRSWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
1405 :
1406 : OGRCoordinateTransformation *poCT =
1407 0 : OGRCreateCoordinateTransformation(&oSRSWGS84, &m_oSRS);
1408 0 : if (poCT == nullptr)
1409 0 : return CE_Failure;
1410 :
1411 : // Reproject corners from long,lat WGS84 to the target SRS.
1412 0 : double dfLLX = CPLAtof(pszLL_lon);
1413 0 : double dfLLY = CPLAtof(pszLL_lat);
1414 0 : double dfURX = CPLAtof(pszUR_lon);
1415 0 : double dfURY = CPLAtof(pszUR_lat);
1416 0 : if (!poCT->Transform(1, &dfLLX, &dfLLY) ||
1417 0 : !poCT->Transform(1, &dfURX, &dfURY))
1418 : {
1419 0 : delete poCT;
1420 0 : return CE_Failure;
1421 : }
1422 0 : delete poCT;
1423 :
1424 : // Compute the geotransform now.
1425 0 : const double dfPixelX = (dfURX - dfLLX) / nRasterXSize;
1426 0 : const double dfPixelY = (dfURY - dfLLY) / nRasterYSize;
1427 :
1428 0 : bHasGeoTransform = true;
1429 0 : adfGeoTransform[0] = dfLLX;
1430 0 : adfGeoTransform[1] = dfPixelX;
1431 0 : adfGeoTransform[2] = 0;
1432 0 : adfGeoTransform[3] = dfURY;
1433 0 : adfGeoTransform[4] = 0;
1434 0 : adfGeoTransform[5] = -dfPixelY;
1435 :
1436 0 : return CE_None;
1437 : }
1438 :
1439 : /************************************************************************/
1440 : /* CreateProjections() */
1441 : /************************************************************************/
1442 57 : CPLErr HDF5ImageDataset::CreateProjections()
1443 : {
1444 57 : switch (iSubdatasetType)
1445 : {
1446 2 : case CSK_PRODUCT:
1447 : {
1448 2 : int productType = PROD_UNKNOWN;
1449 :
1450 2 : if (GetMetadataItem("Product_Type") != nullptr)
1451 : {
1452 : // Get the format's level.
1453 : const char *osMissionLevel =
1454 2 : HDF5Dataset::GetMetadataItem("Product_Type");
1455 :
1456 2 : if (STARTS_WITH_CI(osMissionLevel, "RAW"))
1457 0 : productType = PROD_CSK_L0;
1458 :
1459 2 : if (STARTS_WITH_CI(osMissionLevel, "SSC"))
1460 0 : productType = PROD_CSK_L1A;
1461 :
1462 2 : if (STARTS_WITH_CI(osMissionLevel, "DGM"))
1463 1 : productType = PROD_CSK_L1B;
1464 :
1465 2 : if (STARTS_WITH_CI(osMissionLevel, "GEC"))
1466 1 : productType = PROD_CSK_L1C;
1467 :
1468 2 : if (STARTS_WITH_CI(osMissionLevel, "GTC"))
1469 0 : productType = PROD_CSK_L1D;
1470 : }
1471 :
1472 2 : CaptureCSKGeoTransform(productType);
1473 2 : CaptureCSKGeolocation(productType);
1474 2 : CaptureCSKGCPs(productType);
1475 :
1476 2 : break;
1477 : }
1478 55 : case UNKNOWN_PRODUCT:
1479 : {
1480 55 : constexpr int NBGCPLAT = 100;
1481 55 : constexpr int NBGCPLON = 30;
1482 :
1483 55 : const int nDeltaLat = nRasterYSize / NBGCPLAT;
1484 55 : const int nDeltaLon = nRasterXSize / NBGCPLON;
1485 :
1486 55 : if (nDeltaLat == 0 || nDeltaLon == 0)
1487 37 : return CE_None;
1488 :
1489 : // Create HDF5 Data Hierarchy in a link list.
1490 18 : poH5Objects = HDF5FindDatasetObjects(poH5RootGroup, "Latitude");
1491 18 : if (!poH5Objects)
1492 : {
1493 18 : if (GetMetadataItem("where_projdef") != nullptr)
1494 0 : return CreateODIMH5Projection();
1495 18 : return CE_None;
1496 : }
1497 :
1498 : // The Latitude and Longitude arrays must have a rank of 2 to
1499 : // retrieve GCPs.
1500 0 : if (poH5Objects->nRank != 2 ||
1501 0 : poH5Objects->paDims[0] != static_cast<size_t>(nRasterYSize) ||
1502 0 : poH5Objects->paDims[1] != static_cast<size_t>(nRasterXSize))
1503 : {
1504 0 : return CE_None;
1505 : }
1506 :
1507 : // Retrieve HDF5 data information.
1508 : const hid_t LatitudeDatasetID =
1509 0 : H5Dopen(m_hHDF5, poH5Objects->pszPath);
1510 : // LatitudeDataspaceID = H5Dget_space(dataset_id);
1511 :
1512 0 : poH5Objects = HDF5FindDatasetObjects(poH5RootGroup, "Longitude");
1513 : // GCPs.
1514 0 : if (poH5Objects == nullptr || poH5Objects->nRank != 2 ||
1515 0 : poH5Objects->paDims[0] != static_cast<size_t>(nRasterYSize) ||
1516 0 : poH5Objects->paDims[1] != static_cast<size_t>(nRasterXSize))
1517 : {
1518 0 : if (LatitudeDatasetID > 0)
1519 0 : H5Dclose(LatitudeDatasetID);
1520 0 : return CE_None;
1521 : }
1522 :
1523 : const hid_t LongitudeDatasetID =
1524 0 : H5Dopen(m_hHDF5, poH5Objects->pszPath);
1525 : // LongitudeDataspaceID = H5Dget_space(dataset_id);
1526 :
1527 0 : if (LatitudeDatasetID > 0 && LongitudeDatasetID > 0)
1528 : {
1529 : float *const Latitude =
1530 0 : static_cast<float *>(VSI_MALLOC3_VERBOSE(
1531 : nRasterYSize, nRasterXSize, sizeof(float)));
1532 : float *const Longitude =
1533 0 : static_cast<float *>(VSI_MALLOC3_VERBOSE(
1534 : nRasterYSize, nRasterXSize, sizeof(float)));
1535 0 : if (!Latitude || !Longitude)
1536 : {
1537 0 : CPLFree(Latitude);
1538 0 : CPLFree(Longitude);
1539 0 : H5Dclose(LatitudeDatasetID);
1540 0 : H5Dclose(LongitudeDatasetID);
1541 0 : return CE_Failure;
1542 : }
1543 0 : memset(Latitude, 0,
1544 0 : static_cast<size_t>(nRasterXSize) * nRasterYSize *
1545 : sizeof(float));
1546 0 : memset(Longitude, 0,
1547 0 : static_cast<size_t>(nRasterXSize) * nRasterYSize *
1548 : sizeof(float));
1549 :
1550 : // netCDF convention for nodata
1551 0 : double dfLatNoData = 0;
1552 0 : bool bHasLatNoData = GH5_FetchAttribute(
1553 : LatitudeDatasetID, "_FillValue", dfLatNoData);
1554 :
1555 0 : double dfLongNoData = 0;
1556 0 : bool bHasLongNoData = GH5_FetchAttribute(
1557 : LongitudeDatasetID, "_FillValue", dfLongNoData);
1558 :
1559 0 : H5Dread(LatitudeDatasetID, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL,
1560 : H5P_DEFAULT, Latitude);
1561 :
1562 0 : H5Dread(LongitudeDatasetID, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL,
1563 : H5P_DEFAULT, Longitude);
1564 :
1565 0 : m_oSRS.Clear();
1566 0 : m_oGCPSRS.SetWellKnownGeogCS("WGS84");
1567 :
1568 0 : const int nYLimit =
1569 0 : (static_cast<int>(nRasterYSize) / nDeltaLat) * nDeltaLat;
1570 0 : const int nXLimit =
1571 0 : (static_cast<int>(nRasterXSize) / nDeltaLon) * nDeltaLon;
1572 :
1573 : // The original code in
1574 : // https://trac.osgeo.org/gdal/changeset/8066 always add +180 to
1575 : // the longitudes, but without justification I suspect this
1576 : // might be due to handling products crossing the antimeridian.
1577 : // Trying to do it just when needed through a heuristics.
1578 0 : bool bHasLonNearMinus180 = false;
1579 0 : bool bHasLonNearPlus180 = false;
1580 0 : bool bHasLonNearZero = false;
1581 0 : for (int j = 0; j < nYLimit; j += nDeltaLat)
1582 : {
1583 0 : for (int i = 0; i < nXLimit; i += nDeltaLon)
1584 : {
1585 0 : const int iGCP = j * nRasterXSize + i;
1586 0 : if ((bHasLatNoData && static_cast<float>(dfLatNoData) ==
1587 0 : Latitude[iGCP]) ||
1588 0 : (bHasLongNoData &&
1589 0 : static_cast<float>(dfLongNoData) ==
1590 0 : Longitude[iGCP]))
1591 0 : continue;
1592 0 : if (Longitude[iGCP] > 170 && Longitude[iGCP] <= 180)
1593 0 : bHasLonNearPlus180 = true;
1594 0 : if (Longitude[iGCP] < -170 && Longitude[iGCP] >= -180)
1595 0 : bHasLonNearMinus180 = true;
1596 0 : if (fabs(Longitude[iGCP]) < 90)
1597 0 : bHasLonNearZero = true;
1598 : }
1599 : }
1600 :
1601 : // Fill the GCPs list.
1602 : const char *pszShiftGCP =
1603 0 : CPLGetConfigOption("HDF5_SHIFT_GCPX_BY_180", nullptr);
1604 : const bool bAdd180 =
1605 0 : (bHasLonNearPlus180 && bHasLonNearMinus180 &&
1606 0 : !bHasLonNearZero && pszShiftGCP == nullptr) ||
1607 0 : (pszShiftGCP != nullptr && CPLTestBool(pszShiftGCP));
1608 :
1609 0 : for (int j = 0; j < nYLimit; j += nDeltaLat)
1610 : {
1611 0 : for (int i = 0; i < nXLimit; i += nDeltaLon)
1612 : {
1613 0 : const int iGCP = j * nRasterXSize + i;
1614 0 : if ((bHasLatNoData && static_cast<float>(dfLatNoData) ==
1615 0 : Latitude[iGCP]) ||
1616 0 : (bHasLongNoData &&
1617 0 : static_cast<float>(dfLongNoData) ==
1618 0 : Longitude[iGCP]))
1619 0 : continue;
1620 0 : double dfGCPX = static_cast<double>(Longitude[iGCP]);
1621 0 : if (bAdd180)
1622 0 : dfGCPX += 180.0;
1623 0 : const double dfGCPY =
1624 0 : static_cast<double>(Latitude[iGCP]);
1625 :
1626 0 : m_aoGCPs.emplace_back("", "", i + 0.5, j + 0.5, dfGCPX,
1627 0 : dfGCPY);
1628 : }
1629 : }
1630 :
1631 0 : CPLFree(Latitude);
1632 0 : CPLFree(Longitude);
1633 : }
1634 :
1635 0 : if (LatitudeDatasetID > 0)
1636 0 : H5Dclose(LatitudeDatasetID);
1637 0 : if (LongitudeDatasetID > 0)
1638 0 : H5Dclose(LongitudeDatasetID);
1639 :
1640 0 : break;
1641 : }
1642 : }
1643 :
1644 2 : return CE_None;
1645 : }
1646 :
1647 : /************************************************************************/
1648 : /* GetMetadataItem() */
1649 : /************************************************************************/
1650 :
1651 130 : const char *HDF5ImageDataset::GetMetadataItem(const char *pszName,
1652 : const char *pszDomain)
1653 : {
1654 130 : if (pszDomain && EQUAL(pszDomain, "__DEBUG__") &&
1655 66 : EQUAL(pszName, "WholeBandChunkOptim"))
1656 : {
1657 66 : switch (m_eWholeBandChunkOptim)
1658 : {
1659 4 : case WBC_DETECTION_IN_PROGRESS:
1660 4 : return "DETECTION_IN_PROGRESS";
1661 3 : case WBC_DISABLED:
1662 3 : return "DISABLED";
1663 59 : case WBC_ENABLED:
1664 59 : return "ENABLED";
1665 : }
1666 : }
1667 64 : return GDALPamDataset::GetMetadataItem(pszName, pszDomain);
1668 : }
1669 :
1670 : /************************************************************************/
1671 : /* GetSpatialRef() */
1672 : /************************************************************************/
1673 :
1674 12 : const OGRSpatialReference *HDF5ImageDataset::GetSpatialRef() const
1675 : {
1676 12 : if (!m_oSRS.IsEmpty())
1677 11 : return &m_oSRS;
1678 1 : return GDALPamDataset::GetSpatialRef();
1679 : }
1680 :
1681 : /************************************************************************/
1682 : /* GetGCPCount() */
1683 : /************************************************************************/
1684 :
1685 3 : int HDF5ImageDataset::GetGCPCount()
1686 :
1687 : {
1688 3 : if (!m_aoGCPs.empty())
1689 1 : return static_cast<int>(m_aoGCPs.size());
1690 :
1691 2 : return GDALPamDataset::GetGCPCount();
1692 : }
1693 :
1694 : /************************************************************************/
1695 : /* GetGCPSpatialRef() */
1696 : /************************************************************************/
1697 :
1698 1 : const OGRSpatialReference *HDF5ImageDataset::GetGCPSpatialRef() const
1699 :
1700 : {
1701 1 : if (!m_aoGCPs.empty() && !m_oGCPSRS.IsEmpty())
1702 1 : return &m_oGCPSRS;
1703 :
1704 0 : return GDALPamDataset::GetGCPSpatialRef();
1705 : }
1706 :
1707 : /************************************************************************/
1708 : /* GetGCPs() */
1709 : /************************************************************************/
1710 :
1711 2 : const GDAL_GCP *HDF5ImageDataset::GetGCPs()
1712 : {
1713 2 : if (!m_aoGCPs.empty())
1714 1 : return gdal::GCP::c_ptr(m_aoGCPs);
1715 :
1716 1 : return GDALPamDataset::GetGCPs();
1717 : }
1718 :
1719 : /************************************************************************/
1720 : /* GetGeoTransform() */
1721 : /************************************************************************/
1722 :
1723 6 : CPLErr HDF5ImageDataset::GetGeoTransform(double *padfTransform)
1724 : {
1725 6 : if (bHasGeoTransform)
1726 : {
1727 5 : memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
1728 5 : return CE_None;
1729 : }
1730 :
1731 1 : return GDALPamDataset::GetGeoTransform(padfTransform);
1732 : }
1733 :
1734 : /************************************************************************/
1735 : /* IdentifyProductType() */
1736 : /************************************************************************/
1737 :
1738 : /**
1739 : * Identify if the subdataset has a known product format
1740 : * It stores a product identifier in iSubdatasetType,
1741 : * UNKNOWN_PRODUCT, if it isn't a recognizable format.
1742 : */
1743 63 : void HDF5ImageDataset::IdentifyProductType()
1744 : {
1745 63 : iSubdatasetType = UNKNOWN_PRODUCT;
1746 :
1747 : // COSMO-SKYMED
1748 :
1749 : // Get the Mission Id as a char *, because the field may not exist.
1750 63 : const char *const pszMissionId = HDF5Dataset::GetMetadataItem("Mission_ID");
1751 :
1752 : // If there is a Mission_ID field.
1753 63 : if (pszMissionId != nullptr && strstr(GetDescription(), "QLK") == nullptr)
1754 : {
1755 : // Check if the mission type is CSK, KMPS or CSG.
1756 : // KMPS: Komsat-5 is Korean mission with a SAR instrument.
1757 : // CSG: Cosmo Skymed 2nd Generation
1758 2 : if (EQUAL(pszMissionId, "CSK") || EQUAL(pszMissionId, "KMPS") ||
1759 0 : EQUAL(pszMissionId, "CSG"))
1760 : {
1761 2 : iSubdatasetType = CSK_PRODUCT;
1762 :
1763 2 : if (GetMetadataItem("Product_Type") != nullptr)
1764 : {
1765 : // Get the format's level.
1766 : const char *osMissionLevel =
1767 2 : HDF5Dataset::GetMetadataItem("Product_Type");
1768 :
1769 2 : if (STARTS_WITH_CI(osMissionLevel, "RAW"))
1770 0 : iCSKProductType = PROD_CSK_L0;
1771 :
1772 2 : if (STARTS_WITH_CI(osMissionLevel, "SCS"))
1773 0 : iCSKProductType = PROD_CSK_L1A;
1774 :
1775 2 : if (STARTS_WITH_CI(osMissionLevel, "DGM"))
1776 1 : iCSKProductType = PROD_CSK_L1B;
1777 :
1778 2 : if (STARTS_WITH_CI(osMissionLevel, "GEC"))
1779 1 : iCSKProductType = PROD_CSK_L1C;
1780 :
1781 2 : if (STARTS_WITH_CI(osMissionLevel, "GTC"))
1782 0 : iCSKProductType = PROD_CSK_L1D;
1783 : }
1784 : }
1785 : }
1786 63 : }
1787 :
1788 : /************************************************************************/
1789 : /* CaptureCSKGeolocation() */
1790 : /************************************************************************/
1791 :
1792 : /**
1793 : * Captures Geolocation information from a COSMO-SKYMED
1794 : * file.
1795 : * The geoid will always be WGS84
1796 : * The projection type may be UTM or UPS, depending on the
1797 : * latitude from the center of the image.
1798 : * @param iProductType type of CSK subproduct, see HDF5CSKProduct
1799 : */
1800 2 : void HDF5ImageDataset::CaptureCSKGeolocation(int iProductType)
1801 : {
1802 : // Set the ellipsoid to WGS84.
1803 2 : m_oSRS.SetWellKnownGeogCS("WGS84");
1804 :
1805 2 : if (iProductType == PROD_CSK_L1C || iProductType == PROD_CSK_L1D)
1806 : {
1807 1 : double *dfProjFalseEastNorth = nullptr;
1808 1 : double *dfProjScaleFactor = nullptr;
1809 1 : double *dfCenterCoord = nullptr;
1810 :
1811 : // Check if all the metadata attributes are present.
1812 1 : if (HDF5ReadDoubleAttr("Map Projection False East-North",
1813 1 : &dfProjFalseEastNorth) == CE_Failure ||
1814 1 : HDF5ReadDoubleAttr("Map Projection Scale Factor",
1815 1 : &dfProjScaleFactor) == CE_Failure ||
1816 1 : HDF5ReadDoubleAttr("Map Projection Centre", &dfCenterCoord) ==
1817 2 : CE_Failure ||
1818 1 : GetMetadataItem("Projection_ID") == nullptr)
1819 : {
1820 0 : m_oSRS.Clear();
1821 0 : m_oGCPSRS.Clear();
1822 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1823 : "The CSK hdf5 file geolocation information is "
1824 : "malformed");
1825 : }
1826 : else
1827 : {
1828 : // Fetch projection Type.
1829 2 : CPLString osProjectionID = GetMetadataItem("Projection_ID");
1830 :
1831 : // If the projection is UTM.
1832 1 : if (EQUAL(osProjectionID, "UTM"))
1833 : {
1834 : // @TODO: use SetUTM
1835 1 : m_oSRS.SetProjCS(SRS_PT_TRANSVERSE_MERCATOR);
1836 1 : m_oSRS.SetTM(dfCenterCoord[0], dfCenterCoord[1],
1837 : dfProjScaleFactor[0], dfProjFalseEastNorth[0],
1838 1 : dfProjFalseEastNorth[1]);
1839 : }
1840 : else
1841 : {
1842 : // TODO: No UPS projected files to test!
1843 : // If the projection is UPS.
1844 0 : if (EQUAL(osProjectionID, "UPS"))
1845 : {
1846 0 : m_oSRS.SetProjCS(SRS_PT_POLAR_STEREOGRAPHIC);
1847 0 : m_oSRS.SetPS(dfCenterCoord[0], dfCenterCoord[1],
1848 : dfProjScaleFactor[0], dfProjFalseEastNorth[0],
1849 0 : dfProjFalseEastNorth[1]);
1850 : }
1851 : }
1852 :
1853 1 : CPLFree(dfCenterCoord);
1854 1 : CPLFree(dfProjScaleFactor);
1855 1 : CPLFree(dfProjFalseEastNorth);
1856 1 : }
1857 : }
1858 : else
1859 : {
1860 1 : m_oGCPSRS = m_oSRS;
1861 : }
1862 2 : }
1863 :
1864 : /************************************************************************/
1865 : /* CaptureCSKGeoTransform() */
1866 : /************************************************************************/
1867 :
1868 : /**
1869 : * Get Geotransform information for COSMO-SKYMED files
1870 : * In case of success it stores the transformation
1871 : * in adfGeoTransform. In case of failure it doesn't
1872 : * modify adfGeoTransform
1873 : * @param iProductType type of CSK subproduct, see HDF5CSKProduct
1874 : */
1875 2 : void HDF5ImageDataset::CaptureCSKGeoTransform(int iProductType)
1876 : {
1877 2 : const char *pszSubdatasetName = GetSubdatasetName();
1878 :
1879 2 : bHasGeoTransform = false;
1880 : // If the product level is not L1C or L1D then
1881 : // it doesn't have a valid projection.
1882 2 : if (iProductType == PROD_CSK_L1C || iProductType == PROD_CSK_L1D)
1883 : {
1884 : // If there is a subdataset.
1885 1 : if (pszSubdatasetName != nullptr)
1886 : {
1887 2 : CPLString osULPath = pszSubdatasetName;
1888 1 : osULPath += "/Top Left East-North";
1889 :
1890 2 : CPLString osLineSpacingPath = pszSubdatasetName;
1891 1 : osLineSpacingPath += "/Line Spacing";
1892 :
1893 2 : CPLString osColumnSpacingPath = pszSubdatasetName;
1894 1 : osColumnSpacingPath += "/Column Spacing";
1895 :
1896 1 : double *pdOutUL = nullptr;
1897 1 : double *pdLineSpacing = nullptr;
1898 1 : double *pdColumnSpacing = nullptr;
1899 :
1900 : // If it could find the attributes on the metadata.
1901 1 : if (HDF5ReadDoubleAttr(osULPath.c_str(), &pdOutUL) == CE_Failure ||
1902 1 : HDF5ReadDoubleAttr(osLineSpacingPath.c_str(), &pdLineSpacing) ==
1903 2 : CE_Failure ||
1904 1 : HDF5ReadDoubleAttr(osColumnSpacingPath.c_str(),
1905 : &pdColumnSpacing) == CE_Failure)
1906 : {
1907 0 : bHasGeoTransform = false;
1908 : }
1909 : else
1910 : {
1911 : // geotransform[1] : width of pixel
1912 : // geotransform[4] : rotational coefficient, zero for north up
1913 : // images.
1914 : // geotransform[2] : rotational coefficient, zero for north up
1915 : // images.
1916 : // geotransform[5] : height of pixel (but negative)
1917 :
1918 1 : adfGeoTransform[0] = pdOutUL[0];
1919 1 : adfGeoTransform[1] = pdLineSpacing[0];
1920 1 : adfGeoTransform[2] = 0;
1921 1 : adfGeoTransform[3] = pdOutUL[1];
1922 1 : adfGeoTransform[4] = 0;
1923 1 : adfGeoTransform[5] = -pdColumnSpacing[0];
1924 :
1925 1 : CPLFree(pdOutUL);
1926 1 : CPLFree(pdLineSpacing);
1927 1 : CPLFree(pdColumnSpacing);
1928 :
1929 1 : bHasGeoTransform = true;
1930 : }
1931 : }
1932 : }
1933 2 : }
1934 :
1935 : /************************************************************************/
1936 : /* CaptureCSKGCPs() */
1937 : /************************************************************************/
1938 :
1939 : /**
1940 : * Retrieves and stores the GCPs from a COSMO-SKYMED dataset
1941 : * It only retrieves the GCPs for L0, L1A and L1B products
1942 : * for L1C and L1D products, geotransform is provided.
1943 : * The GCPs provided will be the Image's corners.
1944 : * @param iProductType type of CSK product @see HDF5CSKProductEnum
1945 : */
1946 2 : void HDF5ImageDataset::CaptureCSKGCPs(int iProductType)
1947 : {
1948 : // Only retrieve GCPs for L0,L1A and L1B products.
1949 2 : if (iProductType == PROD_CSK_L0 || iProductType == PROD_CSK_L1A ||
1950 : iProductType == PROD_CSK_L1B)
1951 : {
1952 10 : CPLString osCornerName[4];
1953 1 : double pdCornerPixel[4] = {0.0, 0.0, 0.0, 0.0};
1954 1 : double pdCornerLine[4] = {0.0, 0.0, 0.0, 0.0};
1955 :
1956 1 : const char *const pszSubdatasetName = GetSubdatasetName();
1957 :
1958 : // Load the subdataset name first.
1959 5 : for (int i = 0; i < 4; i++)
1960 4 : osCornerName[i] = pszSubdatasetName;
1961 :
1962 : // Load the attribute name, and raster coordinates for
1963 : // all the corners.
1964 1 : osCornerName[0] += "/Top Left Geodetic Coordinates";
1965 1 : pdCornerPixel[0] = 0;
1966 1 : pdCornerLine[0] = 0;
1967 :
1968 1 : osCornerName[1] += "/Top Right Geodetic Coordinates";
1969 1 : pdCornerPixel[1] = GetRasterXSize();
1970 1 : pdCornerLine[1] = 0;
1971 :
1972 1 : osCornerName[2] += "/Bottom Left Geodetic Coordinates";
1973 1 : pdCornerPixel[2] = 0;
1974 1 : pdCornerLine[2] = GetRasterYSize();
1975 :
1976 1 : osCornerName[3] += "/Bottom Right Geodetic Coordinates";
1977 1 : pdCornerPixel[3] = GetRasterXSize();
1978 1 : pdCornerLine[3] = GetRasterYSize();
1979 :
1980 : // For all the image's corners.
1981 5 : for (int i = 0; i < 4; i++)
1982 : {
1983 4 : double *pdCornerCoordinates = nullptr;
1984 :
1985 : // Retrieve the attributes.
1986 4 : if (HDF5ReadDoubleAttr(osCornerName[i].c_str(),
1987 4 : &pdCornerCoordinates) == CE_Failure)
1988 : {
1989 0 : CPLError(CE_Failure, CPLE_OpenFailed,
1990 : "Error retrieving CSK GCPs");
1991 0 : m_aoGCPs.clear();
1992 0 : break;
1993 : }
1994 :
1995 0 : m_aoGCPs.emplace_back(osCornerName[i].c_str(), "", pdCornerPixel[i],
1996 4 : pdCornerLine[i],
1997 4 : /* X = */ pdCornerCoordinates[1],
1998 : /* Y = */ pdCornerCoordinates[0],
1999 4 : /* Z = */ pdCornerCoordinates[2]);
2000 :
2001 : // Free the returned coordinates.
2002 4 : CPLFree(pdCornerCoordinates);
2003 : }
2004 : }
2005 2 : }
|