Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: Hierarchical Data Format Release 4 (HDF4)
4 : * Purpose: HDF4 Datasets. Open HDF4 file, fetch metadata and list of
5 : * subdatasets.
6 : * This driver initially based on code supplied by Markus Neteler
7 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2002, Andrey Kiselev <dron@ak4719.spb.edu>
11 : * Copyright (c) 2007-2013, Even Rouault <even dot rouault at spatialys.com>
12 : *
13 : * Permission is hereby granted, free of charge, to any person obtaining a
14 : * copy of this software and associated documentation files (the "Software"),
15 : * to deal in the Software without restriction, including without limitation
16 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 : * and/or sell copies of the Software, and to permit persons to whom the
18 : * Software is furnished to do so, subject to the following conditions:
19 : *
20 : * The above copyright notice and this permission notice shall be included
21 : * in all copies or substantial portions of the Software.
22 : *
23 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 : * DEALINGS IN THE SOFTWARE.
30 : ****************************************************************************/
31 :
32 : #include "cpl_multiproc.h"
33 : #include "cpl_string.h"
34 : #include "gdal_frmts.h"
35 : #include "gdal_priv.h"
36 :
37 : #include "hdf.h"
38 : #include "mfhdf.h"
39 :
40 : #include "HdfEosDef.h"
41 :
42 : #include "hdf4compat.h"
43 : #include "hdf4dataset.h"
44 : #include <cctype>
45 :
46 : #include "hdf4drivercore.h"
47 :
48 : extern const char *const pszGDALSignature;
49 :
50 : CPLMutex *hHDF4Mutex = nullptr;
51 :
52 : /************************************************************************/
53 : /* ==================================================================== */
54 : /* HDF4Dataset */
55 : /* ==================================================================== */
56 : /************************************************************************/
57 :
58 : /************************************************************************/
59 : /* HDF4Dataset() */
60 : /************************************************************************/
61 :
62 785 : HDF4Dataset::HDF4Dataset()
63 : : bIsHDFEOS(false), hGR(0), hSD(0), nImages(0),
64 : iSubdatasetType(H4ST_UNKNOWN), pszSubdatasetType(nullptr),
65 785 : papszGlobalMetadata(nullptr), papszSubDatasets(nullptr)
66 : {
67 785 : }
68 :
69 : /************************************************************************/
70 : /* ~HDF4Dataset() */
71 : /************************************************************************/
72 :
73 1097 : HDF4Dataset::~HDF4Dataset()
74 :
75 : {
76 1570 : CPLMutexHolderD(&hHDF4Mutex);
77 :
78 785 : if (hSD)
79 6 : SDend(hSD);
80 785 : if (hGR)
81 0 : GRend(hGR);
82 785 : if (papszSubDatasets)
83 302 : CSLDestroy(papszSubDatasets);
84 785 : if (papszGlobalMetadata)
85 599 : CSLDestroy(papszGlobalMetadata);
86 1097 : }
87 :
88 : /************************************************************************/
89 : /* GetMetadataDomainList() */
90 : /************************************************************************/
91 :
92 0 : char **HDF4Dataset::GetMetadataDomainList()
93 : {
94 0 : return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
95 0 : TRUE, "SUBDATASETS", nullptr);
96 : }
97 :
98 : /************************************************************************/
99 : /* GetMetadata() */
100 : /************************************************************************/
101 :
102 248 : char **HDF4Dataset::GetMetadata(const char *pszDomain)
103 :
104 : {
105 248 : if (pszDomain != nullptr && STARTS_WITH_CI(pszDomain, "SUBDATASETS"))
106 0 : return papszSubDatasets;
107 :
108 248 : return GDALDataset::GetMetadata(pszDomain);
109 : }
110 :
111 : /************************************************************************/
112 : /* SPrintArray() */
113 : /* Prints numerical arrays in string buffer. */
114 : /* This function takes pfaDataArray as a pointer to printed array, */
115 : /* nValues as a number of values to print and pszDelimiter as a */
116 : /* field delimiting strings. */
117 : /* Pointer to filled buffer will be returned. */
118 : /************************************************************************/
119 :
120 304 : char *SPrintArray(GDALDataType eDataType, const void *paDataArray, int nValues,
121 : const char *pszDelimiter)
122 : {
123 304 : const int iFieldSize = 32 + static_cast<int>(strlen(pszDelimiter));
124 304 : char *pszField = static_cast<char *>(CPLMalloc(iFieldSize + 1));
125 304 : const int iStringSize = nValues * iFieldSize + 1;
126 304 : char *pszString = static_cast<char *>(CPLMalloc(iStringSize));
127 304 : memset(pszString, 0, iStringSize);
128 1076 : for (int i = 0; i < nValues; i++)
129 : {
130 772 : switch (eDataType)
131 : {
132 0 : case GDT_Byte:
133 0 : snprintf(pszField, iFieldSize + 1, "%d%s",
134 : reinterpret_cast<GByte *>(
135 0 : const_cast<void *>(paDataArray))[i],
136 0 : (i < nValues - 1) ? pszDelimiter : "");
137 0 : break;
138 0 : case GDT_Int8:
139 0 : snprintf(pszField, iFieldSize + 1, "%d%s",
140 : reinterpret_cast<GInt8 *>(
141 0 : const_cast<void *>(paDataArray))[i],
142 0 : (i < nValues - 1) ? pszDelimiter : "");
143 0 : break;
144 0 : case GDT_UInt16:
145 0 : snprintf(pszField, iFieldSize + 1, "%u%s",
146 : reinterpret_cast<GUInt16 *>(
147 0 : const_cast<void *>(paDataArray))[i],
148 0 : (i < nValues - 1) ? pszDelimiter : "");
149 0 : break;
150 0 : case GDT_Int16:
151 : default:
152 0 : snprintf(pszField, iFieldSize + 1, "%d%s",
153 : reinterpret_cast<GInt16 *>(
154 0 : const_cast<void *>(paDataArray))[i],
155 0 : (i < nValues - 1) ? pszDelimiter : "");
156 0 : break;
157 772 : case GDT_UInt32:
158 772 : snprintf(pszField, iFieldSize + 1, "%u%s",
159 : reinterpret_cast<GUInt32 *>(
160 772 : const_cast<void *>(paDataArray))[i],
161 772 : (i < nValues - 1) ? pszDelimiter : "");
162 772 : break;
163 0 : case GDT_Int32:
164 0 : snprintf(pszField, iFieldSize + 1, "%d%s",
165 : reinterpret_cast<GInt32 *>(
166 0 : const_cast<void *>(paDataArray))[i],
167 0 : (i < nValues - 1) ? pszDelimiter : "");
168 0 : break;
169 0 : case GDT_Float32:
170 0 : CPLsnprintf(pszField, iFieldSize + 1, "%.10g%s",
171 : reinterpret_cast<float *>(
172 0 : const_cast<void *>(paDataArray))[i],
173 0 : (i < nValues - 1) ? pszDelimiter : "");
174 0 : break;
175 0 : case GDT_Float64:
176 0 : CPLsnprintf(pszField, iFieldSize + 1, "%.15g%s",
177 : reinterpret_cast<double *>(
178 0 : const_cast<void *>(paDataArray))[i],
179 0 : (i < nValues - 1) ? pszDelimiter : "");
180 0 : break;
181 : }
182 772 : strcat(pszString, pszField);
183 : }
184 :
185 304 : CPLFree(pszField);
186 304 : return pszString;
187 : }
188 :
189 : /************************************************************************/
190 : /* Translate HDF4 data type into GDAL data type */
191 : /************************************************************************/
192 362 : GDALDataType HDF4Dataset::GetDataType(int32 iNumType)
193 : {
194 362 : switch (iNumType)
195 : {
196 101 : case DFNT_CHAR8: // The same as DFNT_CHAR
197 : case DFNT_UCHAR8: // The same as DFNT_UCHAR
198 : case DFNT_UINT8:
199 101 : return GDT_Byte;
200 32 : case DFNT_INT8:
201 32 : return GDT_Int8;
202 52 : case DFNT_INT16:
203 52 : return GDT_Int16;
204 34 : case DFNT_UINT16:
205 34 : return GDT_UInt16;
206 36 : case DFNT_INT32:
207 36 : return GDT_Int32;
208 34 : case DFNT_UINT32:
209 34 : return GDT_UInt32;
210 0 : case DFNT_INT64:
211 0 : return GDT_Int64;
212 0 : case DFNT_UINT64:
213 0 : return GDT_UInt64;
214 37 : case DFNT_FLOAT32:
215 37 : return GDT_Float32;
216 36 : case DFNT_FLOAT64:
217 36 : return GDT_Float64;
218 0 : default:
219 0 : return GDT_Unknown;
220 : }
221 : }
222 :
223 : /************************************************************************/
224 : /* Return the human readable name of data type */
225 : /************************************************************************/
226 :
227 304 : const char *HDF4Dataset::GetDataTypeName(int32 iNumType)
228 : {
229 304 : switch (iNumType)
230 : {
231 0 : case DFNT_CHAR8: // The same as DFNT_CHAR
232 0 : return "8-bit character";
233 0 : case DFNT_UCHAR8: // The same as DFNT_UCHAR
234 0 : return "8-bit unsigned character";
235 30 : case DFNT_INT8:
236 30 : return "8-bit integer";
237 78 : case DFNT_UINT8:
238 78 : return "8-bit unsigned integer";
239 33 : case DFNT_INT16:
240 33 : return "16-bit integer";
241 32 : case DFNT_UINT16:
242 32 : return "16-bit unsigned integer";
243 35 : case DFNT_INT32:
244 35 : return "32-bit integer";
245 32 : case DFNT_UINT32:
246 32 : return "32-bit unsigned integer";
247 0 : case DFNT_INT64:
248 0 : return "64-bit integer";
249 0 : case DFNT_UINT64:
250 0 : return "64-bit unsigned integer";
251 32 : case DFNT_FLOAT32:
252 32 : return "32-bit floating-point";
253 32 : case DFNT_FLOAT64:
254 32 : return "64-bit floating-point";
255 0 : default:
256 : {
257 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown type %d",
258 : static_cast<int>(iNumType));
259 :
260 0 : return "unknown type";
261 : }
262 : }
263 : }
264 :
265 : /************************************************************************/
266 : /* Return the size of data type in bytes */
267 : /************************************************************************/
268 :
269 1723 : int HDF4Dataset::GetDataTypeSize(int32 iNumType)
270 : {
271 1723 : switch (iNumType)
272 : {
273 1723 : case DFNT_CHAR8: // The same as DFNT_CHAR
274 : case DFNT_UCHAR8: // The same as DFNT_UCHAR
275 : case DFNT_INT8:
276 : case DFNT_UINT8:
277 1723 : return 1;
278 0 : case DFNT_INT16:
279 : case DFNT_UINT16:
280 0 : return 2;
281 0 : case DFNT_INT32:
282 : case DFNT_UINT32:
283 : case DFNT_FLOAT32:
284 0 : return 4;
285 0 : case DFNT_INT64:
286 : case DFNT_UINT64:
287 : case DFNT_FLOAT64:
288 0 : return 8;
289 0 : default:
290 : {
291 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown type %d",
292 : static_cast<int>(iNumType));
293 0 : return 0;
294 : }
295 : }
296 : }
297 :
298 : /************************************************************************/
299 : /* Convert value stored in the input buffer to double value. */
300 : /************************************************************************/
301 :
302 0 : double HDF4Dataset::AnyTypeToDouble(int32 iNumType, void *pData)
303 : {
304 0 : switch (iNumType)
305 : {
306 0 : case DFNT_INT8:
307 0 : return static_cast<double>(*reinterpret_cast<signed char *>(pData));
308 0 : case DFNT_UINT8:
309 0 : return static_cast<double>(*reinterpret_cast<GByte *>(pData));
310 0 : case DFNT_INT16:
311 0 : return static_cast<double>(*reinterpret_cast<GInt16 *>(pData));
312 0 : case DFNT_UINT16:
313 0 : return static_cast<double>(*reinterpret_cast<GUInt16 *>(pData));
314 0 : case DFNT_INT32:
315 0 : return static_cast<double>(*reinterpret_cast<GInt32 *>(pData));
316 0 : case DFNT_UINT32:
317 0 : return static_cast<double>(*reinterpret_cast<GUInt32 *>(pData));
318 0 : case DFNT_INT64:
319 0 : return static_cast<double>(*reinterpret_cast<GInt64 *>(pData));
320 0 : case DFNT_UINT64:
321 0 : return static_cast<double>(*reinterpret_cast<GUInt64 *>(pData));
322 0 : case DFNT_FLOAT32:
323 0 : return static_cast<double>(*reinterpret_cast<float *>(pData));
324 0 : case DFNT_FLOAT64:
325 0 : return static_cast<double>(*reinterpret_cast<double *>(pData));
326 0 : default:
327 : {
328 0 : CPLError(CE_Warning, CPLE_AppDefined, "Unknown type %d",
329 : static_cast<int>(iNumType));
330 0 : return 0.0;
331 : }
332 : }
333 : }
334 :
335 : /************************************************************************/
336 : /* Tokenize HDF-EOS attributes. */
337 : /************************************************************************/
338 :
339 0 : char **HDF4Dataset::HDF4EOSTokenizeAttrs(const char *pszString)
340 :
341 : {
342 0 : const char *const pszDelimiters = " \t\n\r";
343 0 : char **papszRetList = nullptr;
344 :
345 0 : char *pszToken = static_cast<char *>(CPLCalloc(10, 1));
346 0 : int nTokenMax = 10;
347 :
348 0 : while (pszString != nullptr && *pszString != '\0')
349 : {
350 0 : bool bInString = false;
351 0 : bool bInBracket = false;
352 :
353 0 : int nTokenLen = 0;
354 :
355 : // Try to find the next delimiter, marking end of token.
356 0 : for (; *pszString != '\0'; pszString++)
357 : {
358 :
359 : // End if this is a delimiter skip it and break.
360 0 : if (!bInBracket && !bInString &&
361 0 : strchr(pszDelimiters, *pszString) != nullptr)
362 : {
363 0 : pszString++;
364 0 : break;
365 : }
366 :
367 : // Sometimes in bracketed tokens we may found a sort of
368 : // paragraph formatting. We will remove unneeded spaces and new
369 : // lines.
370 0 : if (bInBracket)
371 0 : if (strchr("\r\n", *pszString) != nullptr ||
372 0 : (*pszString == ' ' &&
373 0 : strchr(" \r\n", *(pszString - 1)) != nullptr))
374 0 : continue;
375 :
376 0 : if (*pszString == '"')
377 : {
378 0 : bInString = !bInString;
379 0 : continue;
380 : }
381 0 : else if (*pszString == '(')
382 : {
383 0 : bInBracket = true;
384 0 : continue;
385 : }
386 0 : else if (*pszString == ')')
387 : {
388 0 : bInBracket = false;
389 0 : continue;
390 : }
391 :
392 0 : if (nTokenLen >= nTokenMax - 2)
393 : {
394 0 : nTokenMax = nTokenMax * 2 + 10;
395 0 : pszToken = static_cast<char *>(CPLRealloc(pszToken, nTokenMax));
396 : }
397 :
398 0 : pszToken[nTokenLen] = *pszString;
399 0 : nTokenLen++;
400 : }
401 :
402 0 : pszToken[nTokenLen] = '\0';
403 :
404 0 : if (pszToken[0] != '\0')
405 : {
406 0 : papszRetList = CSLAddString(papszRetList, pszToken);
407 : }
408 :
409 : // If the last token is an empty token, then we have to catch
410 : // it now, otherwise we won't reenter the loop and it will be lost.
411 0 : if (*pszString == '\0' && strchr(pszDelimiters, *(pszString - 1)))
412 : {
413 0 : papszRetList = CSLAddString(papszRetList, "");
414 : }
415 : }
416 :
417 0 : if (papszRetList == nullptr)
418 0 : papszRetList = static_cast<char **>(CPLCalloc(sizeof(char *), 1));
419 :
420 0 : CPLFree(pszToken);
421 :
422 0 : return papszRetList;
423 : }
424 :
425 : /************************************************************************/
426 : /* Find object name, class value in HDF-EOS attributes. */
427 : /* Function returns pointer to the string in list next behind */
428 : /* recognized object. */
429 : /************************************************************************/
430 :
431 0 : char **HDF4Dataset::HDF4EOSGetObject(char **papszAttrList, char **ppszAttrName,
432 : char **ppszAttrClass, char **ppszAttrValue)
433 : {
434 0 : *ppszAttrName = nullptr;
435 0 : *ppszAttrClass = nullptr;
436 0 : *ppszAttrValue = nullptr;
437 :
438 0 : const int iCount = CSLCount(papszAttrList);
439 0 : for (int i = 0; i < iCount - 2; i++)
440 : {
441 0 : if (EQUAL(papszAttrList[i], "OBJECT"))
442 : {
443 0 : i += 2;
444 0 : for (int j = 1; i + j < iCount - 2; j++)
445 : {
446 0 : if (EQUAL(papszAttrList[i + j], "END_OBJECT") ||
447 0 : EQUAL(papszAttrList[i + j], "OBJECT"))
448 0 : return &papszAttrList[i + j];
449 0 : else if (EQUAL(papszAttrList[i + j], "CLASS"))
450 : {
451 0 : *ppszAttrClass = papszAttrList[i + j + 2];
452 0 : continue;
453 : }
454 0 : else if (EQUAL(papszAttrList[i + j], "VALUE"))
455 : {
456 0 : *ppszAttrName = papszAttrList[i];
457 0 : *ppszAttrValue = papszAttrList[i + j + 2];
458 0 : continue;
459 : }
460 : }
461 : }
462 : }
463 :
464 0 : return nullptr;
465 : }
466 :
467 : /************************************************************************/
468 : /* Translate HDF4-EOS attributes in GDAL metadata items */
469 : /************************************************************************/
470 :
471 0 : char **HDF4Dataset::TranslateHDF4EOSAttributes(int32 iHandle, int32 iAttribute,
472 : int32 nValues,
473 : char **papszMetadata)
474 : {
475 : char *pszData =
476 0 : static_cast<char *>(CPLMalloc((nValues + 1) * sizeof(char)));
477 0 : pszData[nValues] = '\0';
478 0 : SDreadattr(iHandle, iAttribute, pszData);
479 : // HDF4-EOS attributes has followed structure:
480 : //
481 : // GROUP = <name>
482 : // GROUPTYPE = <name>
483 : //
484 : // GROUP = <name>
485 : //
486 : // OBJECT = <name>
487 : // CLASS = <string>
488 : // NUM_VAL = <number>
489 : // VALUE = <string> or <number>
490 : // END_OBJECT = <name>
491 : //
492 : // .......
493 : // .......
494 : // .......
495 : //
496 : // END_GROUP = <name>
497 : //
498 : // .......
499 : // .......
500 : // .......
501 : //
502 : // END_GROUP = <name>
503 : // END
504 : //
505 : // Used data types:
506 : // <name> --- unquoted character strings
507 : // <string> --- quoted character strings
508 : // <number> --- numerical value
509 : // If NUM_VAL != 1 then values combined in lists:
510 : // (<string>,<string>,...)
511 : // or
512 : // (<number>,<number>,...)
513 : //
514 : // Records within objects could come in any order, objects could contain
515 : // other objects (and lack VALUE record), groups could contain other groups
516 : // and objects. Names of groups and objects are not unique and may repeat.
517 : // Objects may contains other types of records.
518 : //
519 : // We are interested in OBJECTS structures only. To avoid multiple items
520 : // with the same name, names will be suffixed with the class values, e.g.
521 : //
522 : // OBJECT = PARAMETERNAME
523 : // CLASS = "9"
524 : // NUM_VAL = 1
525 : // VALUE = "Spectral IR Surf Bidirect Reflectivity"
526 : // END_OBJECT = PARAMETERNAME
527 : //
528 : // will be translated into metadata record:
529 : //
530 : // PARAMETERNAME.9 = "Spectral IR Surf Bidirect Reflectivity"
531 :
532 0 : char *pszAttrName = nullptr;
533 0 : char *pszAttrClass = nullptr;
534 0 : char *pszAttrValue = nullptr;
535 0 : char *pszAddAttrName = nullptr;
536 :
537 0 : char **const papszAttrList = HDF4EOSTokenizeAttrs(pszData);
538 0 : char **papszAttrs = papszAttrList;
539 0 : while (papszAttrs)
540 : {
541 0 : papszAttrs = HDF4EOSGetObject(papszAttrs, &pszAttrName, &pszAttrClass,
542 : &pszAttrValue);
543 0 : if (pszAttrName && pszAttrValue)
544 : {
545 : // Now we should recognize special type of HDF EOS metastructures:
546 : // ADDITIONALATTRIBUTENAME = <name>
547 : // PARAMETERVALUE = <value>
548 0 : if (EQUAL(pszAttrName, "ADDITIONALATTRIBUTENAME"))
549 : {
550 0 : pszAddAttrName = pszAttrValue;
551 : }
552 0 : else if (pszAddAttrName && EQUAL(pszAttrName, "PARAMETERVALUE"))
553 : {
554 0 : papszMetadata = CSLAddNameValue(papszMetadata, pszAddAttrName,
555 : pszAttrValue);
556 0 : pszAddAttrName = nullptr;
557 : }
558 : else
559 : {
560 : // Add class suffix to the key name if applicable.
561 0 : papszMetadata = CSLAddNameValue(
562 : papszMetadata,
563 : pszAttrClass
564 0 : ? CPLSPrintf("%s.%s", pszAttrName, pszAttrClass)
565 0 : : pszAttrName,
566 : pszAttrValue);
567 : }
568 : }
569 : }
570 :
571 0 : CSLDestroy(papszAttrList);
572 0 : CPLFree(pszData);
573 :
574 0 : return papszMetadata;
575 : }
576 :
577 : /************************************************************************/
578 : /* Translate HDF4 attributes in GDAL metadata items */
579 : /************************************************************************/
580 :
581 1723 : char **HDF4Dataset::TranslateHDF4Attributes(int32 iHandle, int32 iAttribute,
582 : char *pszAttrName, int32 iNumType,
583 : int32 nValues, char **papszMetadata)
584 : {
585 :
586 : /* -------------------------------------------------------------------- */
587 : /* Allocate a buffer to hold the attribute data. */
588 : /* -------------------------------------------------------------------- */
589 1723 : void *pData = nullptr;
590 1723 : if (iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8)
591 1723 : pData = CPLMalloc((nValues + 1) * GetDataTypeSize(iNumType));
592 : else
593 0 : pData = CPLMalloc(nValues * GetDataTypeSize(iNumType));
594 :
595 : /* -------------------------------------------------------------------- */
596 : /* Read the attribute data. */
597 : /* -------------------------------------------------------------------- */
598 1723 : SDreadattr(iHandle, iAttribute, pData);
599 1723 : if (iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8)
600 : {
601 1723 : reinterpret_cast<char *>(pData)[nValues] = '\0';
602 1723 : papszMetadata = CSLAddNameValue(
603 : papszMetadata, pszAttrName,
604 : const_cast<const char *>(reinterpret_cast<char *>(pData)));
605 : }
606 : else
607 : {
608 : char *pszTemp =
609 0 : SPrintArray(GetDataType(iNumType), pData, nValues, ", ");
610 0 : papszMetadata = CSLAddNameValue(papszMetadata, pszAttrName, pszTemp);
611 0 : CPLFree(pszTemp);
612 : }
613 :
614 1723 : CPLFree(pData);
615 :
616 1723 : return papszMetadata;
617 : }
618 :
619 : /************************************************************************/
620 : /* ReadGlobalAttributes() */
621 : /************************************************************************/
622 :
623 605 : CPLErr HDF4Dataset::ReadGlobalAttributes(int32 iHandler)
624 : {
625 : /* -------------------------------------------------------------------- */
626 : /* Obtain number of SDSs and global attributes in input file. */
627 : /* -------------------------------------------------------------------- */
628 605 : int32 nDatasets = 0;
629 605 : int32 nAttributes = 0;
630 605 : if (SDfileinfo(iHandler, &nDatasets, &nAttributes) != 0)
631 0 : return CE_Failure;
632 :
633 605 : char szAttrName[H4_MAX_NC_NAME] = {}; // TODO: Get this off the stack.
634 :
635 : // Loop through the all attributes
636 2328 : for (int32 iAttribute = 0; iAttribute < nAttributes; iAttribute++)
637 : {
638 1723 : int32 iNumType = 0;
639 1723 : int32 nValues = 0;
640 :
641 : // Get information about the attribute. Note that the first
642 : // parameter is an SD interface identifier.
643 1723 : SDattrinfo(iHandler, iAttribute, szAttrName, &iNumType, &nValues);
644 :
645 1723 : if (STARTS_WITH_CI(szAttrName, "coremetadata") ||
646 1723 : STARTS_WITH_CI(szAttrName, "archivemetadata.") ||
647 1723 : STARTS_WITH_CI(szAttrName, "productmetadata.") ||
648 1723 : STARTS_WITH_CI(szAttrName, "badpixelinformation") ||
649 1723 : STARTS_WITH_CI(szAttrName, "product_summary") ||
650 1723 : STARTS_WITH_CI(szAttrName, "dem_specific") ||
651 1723 : STARTS_WITH_CI(szAttrName, "bts_specific") ||
652 1723 : STARTS_WITH_CI(szAttrName, "etse_specific") ||
653 1723 : STARTS_WITH_CI(szAttrName, "dst_specific") ||
654 1723 : STARTS_WITH_CI(szAttrName, "acv_specific") ||
655 1723 : STARTS_WITH_CI(szAttrName, "act_specific") ||
656 1723 : STARTS_WITH_CI(szAttrName, "etst_specific") ||
657 1723 : STARTS_WITH_CI(szAttrName, "level_1_carryover"))
658 : {
659 0 : bIsHDFEOS = true;
660 0 : papszGlobalMetadata = TranslateHDF4EOSAttributes(
661 : iHandler, iAttribute, nValues, papszGlobalMetadata);
662 : }
663 :
664 : // Skip "StructMetadata.N" records. We will fetch information
665 : // from them using HDF-EOS API
666 1723 : else if (STARTS_WITH_CI(szAttrName, "structmetadata."))
667 : {
668 0 : bIsHDFEOS = true;
669 0 : continue;
670 : }
671 :
672 : else
673 : {
674 1723 : papszGlobalMetadata =
675 1723 : TranslateHDF4Attributes(iHandler, iAttribute, szAttrName,
676 : iNumType, nValues, papszGlobalMetadata);
677 : }
678 : }
679 :
680 605 : return CE_None;
681 : }
682 :
683 : /************************************************************************/
684 : /* QuoteIfNeeded() */
685 : /************************************************************************/
686 :
687 0 : static CPLString QuoteIfNeeded(const CPLString &osStr)
688 : {
689 0 : if (osStr.find(' ') != std::string::npos ||
690 0 : osStr.find(':') != std::string::npos ||
691 0 : osStr.find('"') != std::string::npos ||
692 0 : osStr.find('\\') != std::string::npos)
693 : {
694 0 : CPLString osRet;
695 0 : for (size_t i = 0; i < osStr.size(); i++)
696 : {
697 0 : if (osStr[i] == '"')
698 0 : osRet += "\\\"";
699 0 : else if (osStr[i] == '\\')
700 0 : osRet += "\\\\";
701 : else
702 0 : osRet += osStr[i];
703 : }
704 0 : return '"' + osRet + '"';
705 : }
706 0 : return osStr;
707 : }
708 :
709 : /************************************************************************/
710 : /* Open() */
711 : /************************************************************************/
712 :
713 312 : GDALDataset *HDF4Dataset::Open(GDALOpenInfo *poOpenInfo)
714 :
715 : {
716 : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
717 : // During fuzzing, do not use Identify to reject crazy content.
718 312 : if (!HDF4DatasetIdentify(poOpenInfo))
719 0 : return nullptr;
720 : #endif
721 :
722 624 : CPLMutexHolderD(&hHDF4Mutex);
723 :
724 : /* -------------------------------------------------------------------- */
725 : /* Try opening the dataset. */
726 : /* -------------------------------------------------------------------- */
727 :
728 : // Attempt to increase maximum number of opened HDF files.
729 : #ifdef HDF4_HAS_MAXOPENFILES
730 312 : intn nCurrMax = 0;
731 312 : intn nSysLimit = 0;
732 :
733 312 : if (SDget_maxopenfiles(&nCurrMax, &nSysLimit) >= 0 && nCurrMax < nSysLimit)
734 : {
735 2 : /*intn res = */ SDreset_maxopenfiles(nSysLimit);
736 : }
737 : #endif /* HDF4_HAS_MAXOPENFILES */
738 :
739 312 : int32 hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0);
740 :
741 312 : if (hHDF4 <= 0)
742 0 : return nullptr;
743 :
744 312 : Hclose(hHDF4);
745 :
746 : /* -------------------------------------------------------------------- */
747 : /* Create a corresponding GDALDataset. */
748 : /* -------------------------------------------------------------------- */
749 : // Release mutex otherwise we will deadlock with GDALDataset own mutex.
750 312 : CPLReleaseMutex(hHDF4Mutex);
751 312 : HDF4Dataset *poDS = new HDF4Dataset();
752 312 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
753 :
754 312 : if (poOpenInfo->fpL != nullptr)
755 : {
756 312 : VSIFCloseL(poOpenInfo->fpL);
757 312 : poOpenInfo->fpL = nullptr;
758 : }
759 :
760 : /* -------------------------------------------------------------------- */
761 : /* Open HDF SDS Interface. */
762 : /* -------------------------------------------------------------------- */
763 312 : poDS->hSD = SDstart(poOpenInfo->pszFilename, DFACC_READ);
764 :
765 312 : if (poDS->hSD == -1)
766 : {
767 : // Release mutex otherwise we will deadlock with GDALDataset own mutex.
768 0 : CPLReleaseMutex(hHDF4Mutex);
769 0 : delete poDS;
770 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
771 0 : CPLError(CE_Failure, CPLE_OpenFailed,
772 : "Failed to open HDF4 file \"%s\" for SDS reading.",
773 : poOpenInfo->pszFilename);
774 0 : return nullptr;
775 : }
776 :
777 312 : if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
778 : {
779 6 : poDS->OpenMultiDim(poOpenInfo->pszFilename,
780 6 : poOpenInfo->papszOpenOptions);
781 6 : return poDS;
782 : }
783 :
784 : /* -------------------------------------------------------------------- */
785 : /* Now read Global Attributes. */
786 : /* -------------------------------------------------------------------- */
787 306 : if (poDS->ReadGlobalAttributes(poDS->hSD) != CE_None)
788 : {
789 : // Release mutex otherwise we will deadlock with GDALDataset own mutex.
790 0 : CPLReleaseMutex(hHDF4Mutex);
791 0 : delete poDS;
792 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
793 0 : CPLError(CE_Failure, CPLE_OpenFailed,
794 : "Failed to read global attributes from HDF4 file \"%s\".",
795 : poOpenInfo->pszFilename);
796 0 : return nullptr;
797 : }
798 :
799 306 : poDS->SetMetadata(poDS->papszGlobalMetadata, "");
800 :
801 : /* -------------------------------------------------------------------- */
802 : /* Determine type of file we read. */
803 : /* -------------------------------------------------------------------- */
804 : const char *pszValue =
805 306 : CSLFetchNameValue(poDS->papszGlobalMetadata, "Signature");
806 :
807 306 : if (pszValue != nullptr && EQUAL(pszValue, pszGDALSignature))
808 : {
809 299 : poDS->iSubdatasetType = H4ST_GDAL;
810 299 : poDS->pszSubdatasetType = "GDAL_HDF4";
811 : }
812 :
813 7 : else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
814 7 : "Title")) != nullptr &&
815 0 : EQUAL(pszValue, "SeaWiFS Level-1A Data"))
816 : {
817 0 : poDS->iSubdatasetType = H4ST_SEAWIFS_L1A;
818 0 : poDS->pszSubdatasetType = "SEAWIFS_L1A";
819 : }
820 :
821 7 : else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
822 7 : "Title")) != nullptr &&
823 0 : EQUAL(pszValue, "SeaWiFS Level-2 Data"))
824 : {
825 0 : poDS->iSubdatasetType = H4ST_SEAWIFS_L2;
826 0 : poDS->pszSubdatasetType = "SEAWIFS_L2";
827 : }
828 :
829 7 : else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
830 7 : "Title")) != nullptr &&
831 0 : EQUAL(pszValue, "SeaWiFS Level-3 Standard Mapped Image"))
832 : {
833 0 : poDS->iSubdatasetType = H4ST_SEAWIFS_L3;
834 0 : poDS->pszSubdatasetType = "SEAWIFS_L3";
835 : }
836 :
837 7 : else if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
838 : "L1 File Generated By")) !=
839 7 : nullptr &&
840 0 : STARTS_WITH_CI(pszValue, "HYP version "))
841 : {
842 0 : poDS->iSubdatasetType = H4ST_HYPERION_L1;
843 0 : poDS->pszSubdatasetType = "HYPERION_L1";
844 : }
845 :
846 : else
847 : {
848 7 : poDS->iSubdatasetType = H4ST_UNKNOWN;
849 7 : poDS->pszSubdatasetType = "UNKNOWN";
850 : }
851 :
852 : /* -------------------------------------------------------------------- */
853 : /* If we have HDF-EOS dataset, process it here. */
854 : /* -------------------------------------------------------------------- */
855 306 : int32 aiDimSizes[H4_MAX_VAR_DIMS] = {}; // TODO: Get this off of the stack.
856 306 : int32 iRank = 0;
857 306 : int32 iNumType = 0;
858 306 : int32 nAttrs = 0;
859 :
860 : // Sometimes "HDFEOSVersion" attribute is not defined and we will
861 : // determine HDF-EOS datasets using other records
862 : // (see ReadGlobalAttributes() method).
863 612 : if (poDS->bIsHDFEOS ||
864 306 : CSLFetchNameValue(poDS->papszGlobalMetadata, "HDFEOSVersion"))
865 : {
866 : /* --------------------------------------------------------------------
867 : */
868 : /* Process swath layers. */
869 : /* --------------------------------------------------------------------
870 : */
871 0 : hHDF4 = SWopen(poOpenInfo->pszFilename, DFACC_READ);
872 0 : if (hHDF4 < 0)
873 : {
874 : // Release mutex otherwise we will deadlock with GDALDataset own
875 : // mutex.
876 0 : CPLReleaseMutex(hHDF4Mutex);
877 0 : delete poDS;
878 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
879 0 : CPLError(CE_Failure, CPLE_OpenFailed,
880 : "Failed to open HDF-EOS file \"%s\" for swath reading.",
881 : poOpenInfo->pszFilename);
882 0 : return nullptr;
883 : }
884 0 : int32 nStrBufSize = 0;
885 : int32 nSubDatasets =
886 0 : SWinqswath(poOpenInfo->pszFilename, nullptr, &nStrBufSize);
887 :
888 : #ifdef DEBUG
889 0 : CPLDebug("HDF4", "Number of HDF-EOS swaths: %d",
890 : static_cast<int>(nSubDatasets));
891 : #endif
892 :
893 0 : if (nSubDatasets > 0 && nStrBufSize > 0)
894 : {
895 : char *pszSwathList =
896 0 : static_cast<char *>(CPLMalloc(nStrBufSize + 1));
897 0 : SWinqswath(poOpenInfo->pszFilename, pszSwathList, &nStrBufSize);
898 0 : pszSwathList[nStrBufSize] = '\0';
899 :
900 : #ifdef DEBUG
901 0 : CPLDebug("HDF4", "List of HDF-EOS swaths: %s", pszSwathList);
902 : #endif
903 :
904 : char **papszSwaths =
905 0 : CSLTokenizeString2(pszSwathList, ",", CSLT_HONOURSTRINGS);
906 0 : CPLFree(pszSwathList);
907 :
908 0 : if (nSubDatasets != CSLCount(papszSwaths))
909 : {
910 0 : CSLDestroy(papszSwaths);
911 : // Release mutex otherwise we will deadlock with GDALDataset own
912 : // mutex.
913 0 : CPLReleaseMutex(hHDF4Mutex);
914 0 : delete poDS;
915 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
916 0 : CPLDebug("HDF4", "Cannot parse list of HDF-EOS grids.");
917 0 : return nullptr;
918 : }
919 :
920 0 : for (int32 i = 0; i < nSubDatasets; i++)
921 : {
922 0 : const int32 hSW = SWattach(hHDF4, papszSwaths[i]);
923 :
924 : const int32 nFields =
925 0 : SWnentries(hSW, HDFE_NENTDFLD, &nStrBufSize);
926 : char *pszFieldList =
927 0 : static_cast<char *>(CPLMalloc(nStrBufSize + 1));
928 : int32 *paiRank =
929 0 : static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
930 : int32 *paiNumType =
931 0 : static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
932 :
933 0 : SWinqdatafields(hSW, pszFieldList, paiRank, paiNumType);
934 :
935 : #ifdef DEBUG
936 : {
937 : char *const pszTmp =
938 0 : SPrintArray(GDT_UInt32, paiRank, nFields, ",");
939 :
940 0 : CPLDebug("HDF4", "Number of data fields in swath %d: %d",
941 : static_cast<int>(i), static_cast<int>(nFields));
942 0 : CPLDebug("HDF4", "List of data fields in swath %d: %s",
943 : static_cast<int>(i), pszFieldList);
944 0 : CPLDebug("HDF4", "Data fields ranks: %s", pszTmp);
945 :
946 0 : CPLFree(pszTmp);
947 : }
948 : #endif
949 :
950 : char **papszFields =
951 0 : CSLTokenizeString2(pszFieldList, ",", CSLT_HONOURSTRINGS);
952 :
953 0 : char szTemp[256] = {'\0'}; // TODO: Get this off the stack.
954 0 : for (int32 j = 0; j < nFields; j++)
955 : {
956 0 : SWfieldinfo(hSW, papszFields[j], &iRank, aiDimSizes,
957 : &iNumType, nullptr);
958 :
959 0 : if (iRank < 2)
960 0 : continue;
961 :
962 : // Add field to the list of GDAL subdatasets.
963 0 : const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
964 0 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME",
965 : nCount + 1);
966 : // We will use the field index as an identificator.
967 0 : poDS->papszSubDatasets = CSLSetNameValue(
968 : poDS->papszSubDatasets, szTemp,
969 : CPLSPrintf("HDF4_EOS:EOS_SWATH:\"%s\":%s:%s",
970 : poOpenInfo->pszFilename,
971 0 : QuoteIfNeeded(papszSwaths[i]).c_str(),
972 0 : QuoteIfNeeded(papszFields[j]).c_str()));
973 :
974 0 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC",
975 : nCount + 1);
976 : char *pszString =
977 0 : SPrintArray(GDT_UInt32, aiDimSizes, iRank, "x");
978 0 : poDS->papszSubDatasets = CSLSetNameValue(
979 : poDS->papszSubDatasets, szTemp,
980 0 : CPLSPrintf("[%s] %s %s (%s)", pszString, papszFields[j],
981 0 : papszSwaths[i],
982 : poDS->GetDataTypeName(iNumType)));
983 0 : CPLFree(pszString);
984 0 : szTemp[0] = '\0';
985 : }
986 :
987 0 : CSLDestroy(papszFields);
988 0 : CPLFree(paiNumType);
989 0 : CPLFree(paiRank);
990 0 : CPLFree(pszFieldList);
991 0 : SWdetach(hSW);
992 : }
993 :
994 0 : CSLDestroy(papszSwaths);
995 : }
996 0 : SWclose(hHDF4);
997 :
998 : /* --------------------------------------------------------------------
999 : */
1000 : /* Process grid layers. */
1001 : /* --------------------------------------------------------------------
1002 : */
1003 0 : hHDF4 = GDopen(poOpenInfo->pszFilename, DFACC_READ);
1004 : nSubDatasets =
1005 0 : GDinqgrid(poOpenInfo->pszFilename, nullptr, &nStrBufSize);
1006 :
1007 : #ifdef DEBUG
1008 0 : CPLDebug("HDF4", "Number of HDF-EOS grids: %d",
1009 : static_cast<int>(nSubDatasets));
1010 : #endif
1011 :
1012 0 : if (nSubDatasets > 0 && nStrBufSize > 0)
1013 : {
1014 0 : char *pszGridList = static_cast<char *>(CPLMalloc(nStrBufSize + 1));
1015 0 : GDinqgrid(poOpenInfo->pszFilename, pszGridList, &nStrBufSize);
1016 :
1017 : #ifdef DEBUG
1018 0 : CPLDebug("HDF4", "List of HDF-EOS grids: %s", pszGridList);
1019 : #endif
1020 :
1021 : char **papszGrids =
1022 0 : CSLTokenizeString2(pszGridList, ",", CSLT_HONOURSTRINGS);
1023 0 : CPLFree(pszGridList);
1024 :
1025 0 : if (nSubDatasets != CSLCount(papszGrids))
1026 : {
1027 0 : CSLDestroy(papszGrids);
1028 0 : GDclose(hHDF4);
1029 : // Release mutex otherwise we will deadlock with GDALDataset own
1030 : // mutex.
1031 0 : CPLReleaseMutex(hHDF4Mutex);
1032 0 : delete poDS;
1033 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
1034 0 : CPLDebug("HDF4", "Cannot parse list of HDF-EOS grids.");
1035 0 : return nullptr;
1036 : }
1037 :
1038 0 : for (int32 i = 0; i < nSubDatasets; i++)
1039 : {
1040 0 : const int32 hGD = GDattach(hHDF4, papszGrids[i]);
1041 :
1042 : const int32 nFields =
1043 0 : GDnentries(hGD, HDFE_NENTDFLD, &nStrBufSize);
1044 : char *pszFieldList =
1045 0 : static_cast<char *>(CPLMalloc(nStrBufSize + 1));
1046 : int32 *paiRank =
1047 0 : static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
1048 : int32 *paiNumType =
1049 0 : static_cast<int32 *>(CPLMalloc(nFields * sizeof(int32)));
1050 :
1051 0 : GDinqfields(hGD, pszFieldList, paiRank, paiNumType);
1052 :
1053 : #ifdef DEBUG
1054 : {
1055 : char *pszTmp =
1056 0 : SPrintArray(GDT_UInt32, paiRank, nFields, ",");
1057 0 : CPLDebug("HDF4", "Number of fields in grid %d: %d",
1058 : static_cast<int>(i), static_cast<int>(nFields));
1059 0 : CPLDebug("HDF4", "List of fields in grid %d: %s",
1060 : static_cast<int>(i), pszFieldList);
1061 0 : CPLDebug("HDF4", "Fields ranks: %s", pszTmp);
1062 0 : CPLFree(pszTmp);
1063 : }
1064 : #endif
1065 :
1066 : char **papszFields =
1067 0 : CSLTokenizeString2(pszFieldList, ",", CSLT_HONOURSTRINGS);
1068 :
1069 : char szTemp[256];
1070 0 : for (int32 j = 0; j < nFields; j++)
1071 : {
1072 0 : GDfieldinfo(hGD, papszFields[j], &iRank, aiDimSizes,
1073 : &iNumType, nullptr);
1074 :
1075 0 : if (iRank < 2)
1076 0 : continue;
1077 :
1078 : // Add field to the list of GDAL subdatasets
1079 0 : const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
1080 0 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME",
1081 : nCount + 1);
1082 : // We will use the field index as an identificator.
1083 0 : poDS->papszSubDatasets = CSLSetNameValue(
1084 : poDS->papszSubDatasets, szTemp,
1085 : CPLSPrintf("HDF4_EOS:EOS_GRID:\"%s\":%s:%s",
1086 : poOpenInfo->pszFilename,
1087 0 : QuoteIfNeeded(papszGrids[i]).c_str(),
1088 0 : QuoteIfNeeded(papszFields[j]).c_str()));
1089 :
1090 0 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC",
1091 : nCount + 1);
1092 : char *pszString =
1093 0 : SPrintArray(GDT_UInt32, aiDimSizes, iRank, "x");
1094 0 : poDS->papszSubDatasets = CSLSetNameValue(
1095 : poDS->papszSubDatasets, szTemp,
1096 0 : CPLSPrintf("[%s] %s %s (%s)", pszString, papszFields[j],
1097 0 : papszGrids[i],
1098 : poDS->GetDataTypeName(iNumType)));
1099 0 : CPLFree(pszString);
1100 : }
1101 :
1102 0 : CSLDestroy(papszFields);
1103 0 : CPLFree(paiNumType);
1104 0 : CPLFree(paiRank);
1105 0 : CPLFree(pszFieldList);
1106 0 : GDdetach(hGD);
1107 : }
1108 :
1109 0 : CSLDestroy(papszGrids);
1110 : }
1111 0 : GDclose(hHDF4);
1112 : }
1113 :
1114 : char szName[VSNAMELENMAX + 1];
1115 :
1116 : const char *pszListSDS =
1117 306 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "LIST_SDS", "AUTO");
1118 306 : if ((poDS->papszSubDatasets == nullptr && EQUAL(pszListSDS, "AUTO")) ||
1119 0 : (!EQUAL(pszListSDS, "AUTO") && CPLTestBool(pszListSDS)))
1120 : {
1121 :
1122 : /* --------------------------------------------------------------------
1123 : */
1124 : /* Make a list of subdatasets from SDSs contained in input HDF file. */
1125 : /* --------------------------------------------------------------------
1126 : */
1127 306 : int32 nDatasets = 0;
1128 :
1129 306 : if (SDfileinfo(poDS->hSD, &nDatasets, &nAttrs) != 0)
1130 0 : return nullptr;
1131 :
1132 306 : char szTemp[256] = {'\0'}; // TODO: Get this off the stack.
1133 306 : const char *pszName = nullptr;
1134 :
1135 608 : for (int32 i = 0; i < nDatasets; i++)
1136 : {
1137 302 : const int32 iSDS = SDselect(poDS->hSD, i);
1138 302 : if (SDgetinfo(iSDS, szName, &iRank, aiDimSizes, &iNumType,
1139 302 : &nAttrs) != 0)
1140 0 : return nullptr;
1141 :
1142 302 : if (iRank == 1) // Skip 1D datasets
1143 0 : continue;
1144 :
1145 : // Do sort of known datasets. We will display only image bands
1146 302 : if ((poDS->iSubdatasetType == H4ST_SEAWIFS_L1A) &&
1147 0 : !STARTS_WITH_CI(szName, "l1a_data"))
1148 0 : continue;
1149 : else
1150 302 : pszName = szName;
1151 :
1152 : // Add datasets with multiple dimensions to the list of GDAL
1153 : // subdatasets.
1154 302 : const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
1155 302 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME", nCount + 1);
1156 : // We will use SDS index as an identificator, because SDS names
1157 : // are not unique. Filename also needed for further file opening
1158 302 : poDS->papszSubDatasets = CSLSetNameValue(
1159 : poDS->papszSubDatasets, szTemp,
1160 : CPLSPrintf("HDF4_SDS:%s:\"%s\":%ld", poDS->pszSubdatasetType,
1161 : poOpenInfo->pszFilename, static_cast<long>(i)));
1162 302 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC", nCount + 1);
1163 302 : char *pszString = SPrintArray(GDT_UInt32, aiDimSizes, iRank, "x");
1164 302 : poDS->papszSubDatasets =
1165 302 : CSLSetNameValue(poDS->papszSubDatasets, szTemp,
1166 : CPLSPrintf("[%s] %s (%s)", pszString, pszName,
1167 : poDS->GetDataTypeName(iNumType)));
1168 302 : CPLFree(pszString);
1169 :
1170 302 : SDendaccess(iSDS);
1171 302 : szTemp[0] = '\0';
1172 : }
1173 :
1174 306 : SDend(poDS->hSD);
1175 306 : poDS->hSD = 0;
1176 : }
1177 :
1178 : /* -------------------------------------------------------------------- */
1179 : /* Build a list of raster images. Note, that HDF-EOS dataset may */
1180 : /* contain a raster image as well. */
1181 : /* -------------------------------------------------------------------- */
1182 :
1183 306 : hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0);
1184 306 : poDS->hGR = GRstart(hHDF4);
1185 :
1186 306 : if (poDS->hGR != -1)
1187 : {
1188 306 : if (GRfileinfo(poDS->hGR, &poDS->nImages, &nAttrs) == -1)
1189 : {
1190 : // Release mutex otherwise we will deadlock with GDALDataset own
1191 : // mutex.
1192 0 : CPLReleaseMutex(hHDF4Mutex);
1193 0 : GRend(poDS->hGR);
1194 0 : poDS->hGR = 0;
1195 0 : Hclose(hHDF4);
1196 0 : delete poDS;
1197 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
1198 0 : return nullptr;
1199 : }
1200 :
1201 306 : char szTemp[256] = {'\0'}; // TODO: Get this off the stack.
1202 308 : for (int32 i = 0; i < poDS->nImages; i++)
1203 : {
1204 2 : const int32 iGR = GRselect(poDS->hGR, i);
1205 :
1206 : // iRank in GR interface has another meaning. It represents number
1207 : // of samples per pixel. aiDimSizes has only two dimensions.
1208 2 : int32 iInterlaceMode = 0;
1209 2 : if (GRgetiminfo(iGR, szName, &iRank, &iNumType, &iInterlaceMode,
1210 2 : aiDimSizes, &nAttrs) != 0)
1211 : {
1212 : // Release mutex otherwise we will deadlock with GDALDataset
1213 : // own mutex.
1214 0 : CPLReleaseMutex(hHDF4Mutex);
1215 0 : GRend(poDS->hGR);
1216 0 : poDS->hGR = 0;
1217 0 : Hclose(hHDF4);
1218 0 : delete poDS;
1219 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
1220 0 : return nullptr;
1221 : }
1222 2 : const int nCount = CSLCount(poDS->papszSubDatasets) / 2;
1223 2 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME", nCount + 1);
1224 2 : poDS->papszSubDatasets = CSLSetNameValue(
1225 : poDS->papszSubDatasets, szTemp,
1226 : CPLSPrintf("HDF4_GR:UNKNOWN:\"%s\":%ld",
1227 : poOpenInfo->pszFilename, static_cast<long>(i)));
1228 2 : snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC", nCount + 1);
1229 2 : char *pszString = SPrintArray(GDT_UInt32, aiDimSizes, 2, "x");
1230 2 : poDS->papszSubDatasets =
1231 2 : CSLSetNameValue(poDS->papszSubDatasets, szTemp,
1232 : CPLSPrintf("[%sx%ld] %s (%s)", pszString,
1233 : static_cast<long>(iRank), szName,
1234 : poDS->GetDataTypeName(iNumType)));
1235 2 : CPLFree(pszString);
1236 :
1237 2 : GRendaccess(iGR);
1238 2 : szTemp[0] = '\0';
1239 : }
1240 :
1241 306 : GRend(poDS->hGR);
1242 306 : poDS->hGR = 0;
1243 : }
1244 :
1245 306 : Hclose(hHDF4);
1246 :
1247 306 : poDS->nRasterXSize = 512; // XXX: bogus value
1248 306 : poDS->nRasterYSize = 512; // XXX: bogus value
1249 :
1250 : // Make sure we don't try to do any pam stuff with this dataset.
1251 306 : poDS->nPamFlags |= GPF_NOSAVE;
1252 :
1253 : /* -------------------------------------------------------------------- */
1254 : /* If we have single subdataset only, open it immediately */
1255 : /* -------------------------------------------------------------------- */
1256 306 : if (CSLCount(poDS->papszSubDatasets) / 2 == 1)
1257 : {
1258 301 : char *pszSDSName = CPLStrdup(
1259 301 : CSLFetchNameValue(poDS->papszSubDatasets, "SUBDATASET_1_NAME"));
1260 : // Release mutex otherwise we will deadlock with GDALDataset own mutex.
1261 301 : CPLReleaseMutex(hHDF4Mutex);
1262 301 : delete poDS;
1263 301 : poDS = nullptr;
1264 :
1265 : GDALDataset *poRetDS =
1266 301 : GDALDataset::FromHandle(GDALOpen(pszSDSName, poOpenInfo->eAccess));
1267 301 : CPLFree(pszSDSName);
1268 :
1269 301 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
1270 :
1271 301 : if (poRetDS)
1272 : {
1273 301 : poRetDS->SetDescription(poOpenInfo->pszFilename);
1274 : }
1275 :
1276 301 : return poRetDS;
1277 : }
1278 : else
1279 : {
1280 : /* --------------------------------------------------------------------
1281 : */
1282 : /* Confirm the requested access is supported. */
1283 : /* --------------------------------------------------------------------
1284 : */
1285 5 : if (poOpenInfo->eAccess == GA_Update)
1286 : {
1287 : // Release mutex otherwise we will deadlock with GDALDataset own
1288 : // mutex.
1289 0 : CPLReleaseMutex(hHDF4Mutex);
1290 0 : delete poDS;
1291 0 : CPLAcquireMutex(hHDF4Mutex, 1000.0);
1292 :
1293 0 : CPLError(CE_Failure, CPLE_NotSupported,
1294 : "The HDF4 driver does not support update access to "
1295 : "existing datasets.");
1296 0 : return nullptr;
1297 : }
1298 : }
1299 :
1300 5 : return poDS;
1301 : }
1302 :
1303 : /************************************************************************/
1304 : /* HDF4UnloadDriver() */
1305 : /************************************************************************/
1306 :
1307 4 : static void HDF4UnloadDriver(GDALDriver * /* poDriver */)
1308 : {
1309 4 : if (hHDF4Mutex != nullptr)
1310 0 : CPLDestroyMutex(hHDF4Mutex);
1311 4 : hHDF4Mutex = nullptr;
1312 4 : }
1313 :
1314 : /************************************************************************/
1315 : /* GDALRegister_HDF4() */
1316 : /************************************************************************/
1317 :
1318 7 : void GDALRegister_HDF4()
1319 :
1320 : {
1321 7 : if (!GDAL_CHECK_VERSION("HDF4 driver"))
1322 0 : return;
1323 :
1324 7 : if (GDALGetDriverByName(HDF4_DRIVER_NAME) != nullptr)
1325 0 : return;
1326 :
1327 7 : GDALDriver *poDriver = new GDALDriver();
1328 7 : HDF4DriverSetCommonMetadata(poDriver);
1329 7 : poDriver->pfnOpen = HDF4Dataset::Open;
1330 7 : poDriver->pfnUnloadDriver = HDF4UnloadDriver;
1331 :
1332 7 : GetGDALDriverManager()->RegisterDriver(poDriver);
1333 :
1334 : #ifdef HDF4_HAS_MAXOPENFILES
1335 7 : poDriver->SetMetadataItem("HDF4_HAS_MAXOPENFILES", "YES");
1336 : #endif
1337 :
1338 : #ifdef HDF4_PLUGIN
1339 7 : GDALRegister_HDF4Image();
1340 : #endif
1341 : }
|