1 : /******************************************************************************
2 : * $Id: hdf4dataset.cpp 22838 2011-07-30 17:55:35Z rouault $
3 : *
4 : * Project: Hierarchical Data Format Release 4 (HDF4)
5 : * Purpose: HDF4 Datasets. Open HDF4 file, fetch metadata and list of
6 : * subdatasets.
7 : * This driver initially based on code supplied by Markus Neteler
8 : * Author: Andrey Kiselev, dron@ak4719.spb.edu
9 : *
10 : ******************************************************************************
11 : * Copyright (c) 2002, Andrey Kiselev <dron@ak4719.spb.edu>
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 "hdf.h"
33 : #include "mfhdf.h"
34 :
35 : #include "HdfEosDef.h"
36 :
37 : #include "gdal_priv.h"
38 : #include "cpl_string.h"
39 : #include "hdf4compat.h"
40 : #include "hdf4dataset.h"
41 :
42 : CPL_CVSID("$Id: hdf4dataset.cpp 22838 2011-07-30 17:55:35Z rouault $");
43 :
44 : CPL_C_START
45 : void GDALRegister_HDF4(void);
46 : CPL_C_END
47 :
48 : extern const char *pszGDALSignature;
49 :
50 : /************************************************************************/
51 : /* ==================================================================== */
52 : /* HDF4Dataset */
53 : /* ==================================================================== */
54 : /************************************************************************/
55 :
56 : /************************************************************************/
57 : /* HDF4Dataset() */
58 : /************************************************************************/
59 :
60 666 : HDF4Dataset::HDF4Dataset()
61 :
62 : {
63 666 : fp = NULL;
64 666 : hSD = 0;
65 666 : hGR = 0;
66 666 : nImages = 0;
67 666 : iSubdatasetType = H4ST_UNKNOWN;
68 666 : pszSubdatasetType = NULL;
69 666 : papszGlobalMetadata = NULL;
70 666 : papszSubDatasets = NULL;
71 666 : bIsHDFEOS = 0;
72 666 : }
73 :
74 : /************************************************************************/
75 : /* ~HDF4Dataset() */
76 : /************************************************************************/
77 :
78 666 : HDF4Dataset::~HDF4Dataset()
79 :
80 : {
81 666 : if ( hSD )
82 2 : SDend( hSD );
83 666 : if ( hGR )
84 0 : GRend( hGR );
85 666 : if ( papszSubDatasets )
86 256 : CSLDestroy( papszSubDatasets );
87 666 : if ( papszGlobalMetadata )
88 519 : CSLDestroy( papszGlobalMetadata );
89 666 : if( fp != NULL )
90 256 : VSIFClose( fp );
91 666 : }
92 :
93 : /************************************************************************/
94 : /* GetMetadata() */
95 : /************************************************************************/
96 :
97 208 : char **HDF4Dataset::GetMetadata( const char *pszDomain )
98 :
99 : {
100 208 : if( pszDomain != NULL && EQUALN( pszDomain, "SUBDATASETS", 11 ) )
101 0 : return papszSubDatasets;
102 : else
103 208 : return GDALDataset::GetMetadata( pszDomain );
104 : }
105 :
106 : /************************************************************************/
107 : /* SPrintArray() */
108 : /* Prints numerical arrays in string buffer. */
109 : /* This function takes pfaDataArray as a pointer to printed array, */
110 : /* nValues as a number of values to print and pszDelimiter as a */
111 : /* field delimiting strings. */
112 : /* Pointer to filled buffer will be returned. */
113 : /************************************************************************/
114 :
115 900 : char *SPrintArray( GDALDataType eDataType, const void *paDataArray,
116 : int nValues, const char *pszDelimiter )
117 : {
118 : char *pszString, *pszField;
119 : int i, iFieldSize, iStringSize;
120 :
121 900 : iFieldSize = 32 + strlen( pszDelimiter );
122 900 : pszField = (char *)CPLMalloc( iFieldSize + 1 );
123 900 : iStringSize = nValues * iFieldSize + 1;
124 900 : pszString = (char *)CPLMalloc( iStringSize );
125 900 : memset( pszString, 0, iStringSize );
126 3285 : for ( i = 0; i < nValues; i++ )
127 : {
128 2385 : switch ( eDataType )
129 : {
130 : case GDT_Byte:
131 14 : sprintf( pszField, "%d%s", ((GByte *)paDataArray)[i],
132 28 : (i < nValues - 1)?pszDelimiter:"" );
133 14 : break;
134 : case GDT_UInt16:
135 184 : sprintf( pszField, "%u%s", ((GUInt16 *)paDataArray)[i],
136 368 : (i < nValues - 1)?pszDelimiter:"" );
137 184 : break;
138 : case GDT_Int16:
139 : default:
140 54 : sprintf( pszField, "%d%s", ((GInt16 *)paDataArray)[i],
141 108 : (i < nValues - 1)?pszDelimiter:"" );
142 54 : break;
143 : case GDT_UInt32:
144 : sprintf( pszField, "%u%s", ((GUInt32 *)paDataArray)[i],
145 720 : (i < nValues - 1)?pszDelimiter:"" );
146 720 : break;
147 : case GDT_Int32:
148 : sprintf( pszField, "%d%s", ((GInt32 *)paDataArray)[i],
149 325 : (i < nValues - 1)?pszDelimiter:"" );
150 325 : break;
151 : case GDT_Float32:
152 1066 : sprintf( pszField, "%.7g%s", ((float *)paDataArray)[i],
153 2132 : (i < nValues - 1)?pszDelimiter:"" );
154 1066 : break;
155 : case GDT_Float64:
156 : sprintf( pszField, "%.15g%s", ((double *)paDataArray)[i],
157 22 : (i < nValues - 1)?pszDelimiter:"" );
158 : break;
159 : }
160 2385 : strcat( pszString, pszField );
161 : }
162 :
163 900 : CPLFree( pszField );
164 900 : return pszString;
165 : }
166 :
167 : /************************************************************************/
168 : /* Translate HDF4 data type into GDAL data type */
169 : /************************************************************************/
170 948 : GDALDataType HDF4Dataset::GetDataType( int32 iNumType )
171 : {
172 948 : switch (iNumType)
173 : {
174 : case DFNT_CHAR8: // The same as DFNT_CHAR
175 : case DFNT_UCHAR8: // The same as DFNT_UCHAR
176 : case DFNT_INT8:
177 : case DFNT_UINT8:
178 102 : return GDT_Byte;
179 : case DFNT_INT16:
180 103 : return GDT_Int16;
181 : case DFNT_UINT16:
182 111 : return GDT_UInt16;
183 : case DFNT_INT32:
184 185 : return GDT_Int32;
185 : case DFNT_UINT32:
186 88 : return GDT_UInt32;
187 : case DFNT_INT64:
188 0 : return GDT_Unknown;
189 : case DFNT_UINT64:
190 0 : return GDT_Unknown;
191 : case DFNT_FLOAT32:
192 305 : return GDT_Float32;
193 : case DFNT_FLOAT64:
194 54 : return GDT_Float64;
195 : default:
196 0 : return GDT_Unknown;
197 : }
198 : }
199 :
200 : /************************************************************************/
201 : /* Return the human readable name of data type */
202 : /************************************************************************/
203 :
204 258 : const char *HDF4Dataset::GetDataTypeName( int32 iNumType )
205 : {
206 258 : switch (iNumType)
207 : {
208 : case DFNT_CHAR8: // The same as DFNT_CHAR
209 0 : return "8-bit character";
210 : case DFNT_UCHAR8: // The same as DFNT_UCHAR
211 0 : return "8-bit unsigned character";
212 : case DFNT_INT8:
213 0 : return "8-bit integer";
214 : case DFNT_UINT8:
215 69 : return "8-bit unsigned integer";
216 : case DFNT_INT16:
217 33 : return "16-bit integer";
218 : case DFNT_UINT16:
219 33 : return "16-bit unsigned integer";
220 : case DFNT_INT32:
221 33 : return "32-bit integer";
222 : case DFNT_UINT32:
223 30 : return "32-bit unsigned integer";
224 : case DFNT_INT64:
225 0 : return "64-bit integer";
226 : case DFNT_UINT64:
227 0 : return "64-bit unsigned integer";
228 : case DFNT_FLOAT32:
229 30 : return "32-bit floating-point";
230 : case DFNT_FLOAT64:
231 30 : return "64-bit floating-point";
232 : default:
233 0 : return "unknown type";
234 : }
235 : }
236 :
237 : /************************************************************************/
238 : /* Return the size of data type in bytes */
239 : /************************************************************************/
240 :
241 2396 : int HDF4Dataset::GetDataTypeSize( int32 iNumType )
242 : {
243 2396 : switch (iNumType)
244 : {
245 : case DFNT_CHAR8: // The same as DFNT_CHAR
246 : case DFNT_UCHAR8: // The same as DFNT_UCHAR
247 : case DFNT_INT8:
248 : case DFNT_UINT8:
249 1762 : return 1;
250 : case DFNT_INT16:
251 : case DFNT_UINT16:
252 130 : return 2;
253 : case DFNT_INT32:
254 : case DFNT_UINT32:
255 : case DFNT_FLOAT32:
256 482 : return 4;
257 : case DFNT_INT64:
258 : case DFNT_UINT64:
259 : case DFNT_FLOAT64:
260 22 : return 8;
261 : default:
262 0 : return 0;
263 : }
264 : }
265 :
266 : /************************************************************************/
267 : /* Convert value stored in the input buffer to double value. */
268 : /************************************************************************/
269 :
270 0 : double HDF4Dataset::AnyTypeToDouble( int32 iNumType, void *pData )
271 : {
272 0 : switch ( iNumType )
273 : {
274 : case DFNT_INT8:
275 0 : return (double)*(char *)pData;
276 : case DFNT_UINT8:
277 0 : return (double)*(unsigned char *)pData;
278 : case DFNT_INT16:
279 0 : return (double)*(short *)pData;
280 : case DFNT_UINT16:
281 0 : return (double)*(unsigned short *)pData;
282 : case DFNT_INT32:
283 0 : return (double)*(long *)pData;
284 : case DFNT_UINT32:
285 0 : return (double)*(unsigned long *)pData;
286 : case DFNT_INT64:
287 0 : return (double)*(char *)pData;
288 : case DFNT_UINT64:
289 0 : return (double)*(GIntBig *)pData;
290 : case DFNT_FLOAT32:
291 0 : return (double)*(float *)pData;
292 : case DFNT_FLOAT64:
293 0 : return (double)*(double *)pData;
294 : default:
295 0 : return 0.0;
296 : }
297 : }
298 :
299 : /************************************************************************/
300 : /* Tokenize HDF-EOS attributes. */
301 : /************************************************************************/
302 :
303 20 : char **HDF4Dataset::HDF4EOSTokenizeAttrs( const char * pszString )
304 :
305 : {
306 20 : const char *pszDelimiters = " \t\n\r";
307 20 : char **papszRetList = NULL;
308 : char *pszToken;
309 : int nTokenMax, nTokenLen;
310 :
311 20 : pszToken = (char *) CPLCalloc( 10, 1 );
312 20 : nTokenMax = 10;
313 :
314 129522 : while( pszString != NULL && *pszString != '\0' )
315 : {
316 129482 : int bInString = FALSE, bInBracket = FALSE;
317 :
318 129482 : nTokenLen = 0;
319 :
320 : // Try to find the next delimeter, marking end of token
321 287644 : for( ; *pszString != '\0'; pszString++ )
322 : {
323 :
324 : // End if this is a delimeter skip it and break.
325 287644 : if ( !bInBracket && !bInString
326 : && strchr(pszDelimiters, *pszString) != NULL )
327 : {
328 129482 : pszString++;
329 129482 : break;
330 : }
331 :
332 : // Sometimes in bracketed tokens we may found a sort of
333 : // paragraph formatting. We will remove unneeded spaces and new
334 : // lines.
335 158162 : if ( bInBracket )
336 41728 : if ( strchr("\r\n", *pszString) != NULL
337 : || ( *pszString == ' '
338 : && strchr(" \r\n", *(pszString - 1)) != NULL ) )
339 2214 : continue;
340 :
341 155948 : if ( *pszString == '"' )
342 : {
343 4608 : if ( bInString )
344 : {
345 2304 : bInString = FALSE;
346 2304 : continue;
347 : }
348 : else
349 : {
350 2304 : bInString = TRUE;
351 2304 : continue;
352 : }
353 : }
354 151340 : else if ( *pszString == '(' )
355 : {
356 72 : bInBracket = TRUE;
357 72 : continue;
358 : }
359 151268 : else if ( *pszString == ')' )
360 : {
361 72 : bInBracket = FALSE;
362 72 : continue;
363 : }
364 :
365 151196 : if( nTokenLen >= nTokenMax - 2 )
366 : {
367 112 : nTokenMax = nTokenMax * 2 + 10;
368 112 : pszToken = (char *) CPLRealloc( pszToken, nTokenMax );
369 : }
370 :
371 151196 : pszToken[nTokenLen] = *pszString;
372 151196 : nTokenLen++;
373 : }
374 :
375 129482 : pszToken[nTokenLen] = '\0';
376 :
377 129482 : if( pszToken[0] != '\0' )
378 : {
379 16538 : papszRetList = CSLAddString( papszRetList, pszToken );
380 : }
381 :
382 : // If the last token is an empty token, then we have to catch
383 : // it now, otherwise we won't reenter the loop and it will be lost.
384 129482 : if ( *pszString == '\0' && strchr(pszDelimiters, *(pszString-1)) )
385 : {
386 20 : papszRetList = CSLAddString( papszRetList, "" );
387 : }
388 : }
389 :
390 20 : if( papszRetList == NULL )
391 0 : papszRetList = (char **) CPLCalloc( sizeof(char *), 1 );
392 :
393 20 : CPLFree( pszToken );
394 :
395 20 : return papszRetList;
396 : }
397 :
398 : /************************************************************************/
399 : /* Find object name and its value in HDF-EOS attributes. */
400 : /* Function returns pointer to the string in list next behind */
401 : /* recognized object. */
402 : /************************************************************************/
403 :
404 1106 : char **HDF4Dataset::HDF4EOSGetObject( char **papszAttrList, char **ppszAttrName,
405 : char **ppszAttrValue )
406 : {
407 : int iCount, i, j;
408 1106 : *ppszAttrName = NULL;
409 1106 : *ppszAttrValue = NULL;
410 :
411 1106 : iCount = CSLCount( papszAttrList );
412 7326 : for ( i = 0; i < iCount - 2; i++ )
413 : {
414 7306 : if ( EQUAL( papszAttrList[i], "OBJECT" ) )
415 : {
416 1086 : i += 2;
417 6258 : for ( j = 1; i + j < iCount - 2; j++ )
418 : {
419 12516 : if ( EQUAL( papszAttrList[i + j], "END_OBJECT" ) ||
420 6258 : EQUAL( papszAttrList[i + j], "OBJECT" ) )
421 152 : return &papszAttrList[i + j];
422 6106 : else if ( EQUAL( papszAttrList[i + j], "VALUE" ) )
423 : {
424 934 : *ppszAttrName = papszAttrList[i];
425 934 : *ppszAttrValue = papszAttrList[i + j + 2];
426 :
427 934 : return &papszAttrList[i + j + 2];
428 : }
429 : }
430 : }
431 : }
432 :
433 20 : return NULL;
434 : }
435 :
436 : /************************************************************************/
437 : /* Translate HDF4-EOS attributes in GDAL metadata items */
438 : /************************************************************************/
439 :
440 20 : char** HDF4Dataset::TranslateHDF4EOSAttributes( int32 iHandle,
441 : int32 iAttribute, int32 nValues, char **papszMetadata )
442 : {
443 : char *pszData;
444 :
445 20 : pszData = (char *)CPLMalloc( (nValues + 1) * sizeof(char) );
446 20 : pszData[nValues] = '\0';
447 20 : SDreadattr( iHandle, iAttribute, pszData );
448 : // HDF4-EOS attributes has followed structure:
449 : //
450 : // GROUP = <name>
451 : // GROUPTYPE = <name>
452 : //
453 : // GROUP = <name>
454 : //
455 : // OBJECT = <name>
456 : // CLASS = <string>
457 : // NUM_VAL = <number>
458 : // VALUE = <string> or <number>
459 : // END_OBJECT = <name>
460 : //
461 : // .......
462 : // .......
463 : // .......
464 : //
465 : // END_GROUP = <name>
466 : //
467 : // .......
468 : // .......
469 : // .......
470 : //
471 : // END_GROUP = <name>
472 : // END
473 : //
474 : // Used data types:
475 : // <name> --- unquoted character strings
476 : // <string> --- quoted character strings
477 : // <number> --- numerical value
478 : // If NUM_VAL != 1 then values combined in lists:
479 : // (<string>,<string>,...)
480 : // or
481 : // (<number>,<number>,...)
482 : //
483 : // Records within objects may follows in any order, objects may contains
484 : // other objects (and lacks VALUE record), groups contains other groups
485 : // and objects. Names of groups and objects are not unique and may repeat.
486 : // Objects may contains other types of records.
487 : //
488 : // We are interested in OBJECTS structures only.
489 :
490 : char *pszAttrName, *pszAttrValue;
491 20 : char *pszAddAttrName = NULL;
492 : char **papszAttrList, **papszAttrs;
493 :
494 20 : papszAttrList = HDF4EOSTokenizeAttrs( pszData );
495 20 : papszAttrs = papszAttrList;
496 1146 : while ( papszAttrs )
497 : {
498 : papszAttrs =
499 1106 : HDF4EOSGetObject( papszAttrs, &pszAttrName, &pszAttrValue );
500 1106 : if ( pszAttrName && pszAttrValue )
501 : {
502 : // Now we should recognize special type of HDF EOS metastructures:
503 : // ADDITIONALATTRIBUTENAME = <name>
504 : // PARAMETERVALUE = <value>
505 934 : if ( EQUAL( pszAttrName, "ADDITIONALATTRIBUTENAME" ) )
506 74 : pszAddAttrName = pszAttrValue;
507 934 : else if ( pszAddAttrName && EQUAL( pszAttrName, "PARAMETERVALUE" ) )
508 : {
509 : papszMetadata =
510 74 : CSLAddNameValue( papszMetadata, pszAddAttrName, pszAttrValue );
511 74 : pszAddAttrName = NULL;
512 : }
513 : else
514 : {
515 : papszMetadata =
516 786 : CSLAddNameValue( papszMetadata, pszAttrName, pszAttrValue );
517 : }
518 : }
519 : }
520 :
521 20 : CSLDestroy( papszAttrList );
522 20 : CPLFree( pszData );
523 :
524 20 : return papszMetadata;
525 : }
526 :
527 : /************************************************************************/
528 : /* Translate HDF4 attributes in GDAL metadata items */
529 : /************************************************************************/
530 :
531 2388 : char** HDF4Dataset::TranslateHDF4Attributes( int32 iHandle,
532 : int32 iAttribute, char *pszAttrName, int32 iNumType, int32 nValues,
533 : char **papszMetadata )
534 : {
535 2388 : void *pData = NULL;
536 2388 : char *pszTemp = NULL;
537 :
538 : /* -------------------------------------------------------------------- */
539 : /* Allocate a buffer to hold the attribute data. */
540 : /* -------------------------------------------------------------------- */
541 4136 : if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 )
542 1748 : pData = CPLMalloc( (nValues + 1) * GetDataTypeSize(iNumType) );
543 : else
544 640 : pData = CPLMalloc( nValues * GetDataTypeSize(iNumType) );
545 :
546 : /* -------------------------------------------------------------------- */
547 : /* Read the attribute data. */
548 : /* -------------------------------------------------------------------- */
549 2388 : SDreadattr( iHandle, iAttribute, pData );
550 4136 : if ( iNumType == DFNT_CHAR8 || iNumType == DFNT_UCHAR8 )
551 : {
552 1748 : ((char *)pData)[nValues] = '\0';
553 : papszMetadata = CSLAddNameValue( papszMetadata, pszAttrName,
554 1748 : (const char *) pData );
555 : }
556 : else
557 : {
558 640 : pszTemp = SPrintArray( GetDataType(iNumType), pData, nValues, ", " );
559 640 : papszMetadata = CSLAddNameValue( papszMetadata, pszAttrName, pszTemp );
560 640 : if ( pszTemp )
561 640 : CPLFree( pszTemp );
562 : }
563 :
564 2388 : if ( pData )
565 2388 : CPLFree( pData );
566 :
567 2388 : return papszMetadata;
568 : }
569 :
570 : /************************************************************************/
571 : /* ReadGlobalAttributes() */
572 : /************************************************************************/
573 :
574 520 : CPLErr HDF4Dataset::ReadGlobalAttributes( int32 iHandler )
575 : {
576 : int32 iAttribute, nValues, iNumType, nDatasets, nAttributes;
577 : char szAttrName[H4_MAX_NC_NAME];
578 :
579 : /* -------------------------------------------------------------------- */
580 : /* Obtain number of SDSs and global attributes in input file. */
581 : /* -------------------------------------------------------------------- */
582 520 : if ( SDfileinfo( iHandler, &nDatasets, &nAttributes ) != 0 )
583 0 : return CE_Failure;
584 :
585 : // Loop through the all attributes
586 2851 : for ( iAttribute = 0; iAttribute < nAttributes; iAttribute++ )
587 : {
588 : // Get information about the attribute. Note that the first
589 : // parameter is an SD interface identifier.
590 2331 : SDattrinfo( iHandler, iAttribute, szAttrName, &iNumType, &nValues );
591 :
592 2351 : if ( EQUALN( szAttrName, "coremetadata.", 13 ) ||
593 : EQUALN( szAttrName, "archivemetadata.", 16 ) ||
594 : EQUALN( szAttrName, "productmetadata.", 16 ) ||
595 : EQUALN( szAttrName, "badpixelinformation", 19 ) ||
596 : EQUALN( szAttrName, "product_summary", 15 ) ||
597 : EQUALN( szAttrName, "dem_specific", 12 ) ||
598 : EQUALN( szAttrName, "bts_specific", 12 ) ||
599 : EQUALN( szAttrName, "etse_specific", 13 ) ||
600 : EQUALN( szAttrName, "dst_specific", 12 ) ||
601 : EQUALN( szAttrName, "acv_specific", 12 ) ||
602 : EQUALN( szAttrName, "act_specific", 12 ) ||
603 : EQUALN( szAttrName, "etst_specific", 13 ) ||
604 : EQUALN( szAttrName, "level_1_carryover", 17 ) )
605 : {
606 20 : bIsHDFEOS = 1;
607 : papszGlobalMetadata = TranslateHDF4EOSAttributes( iHandler,
608 20 : iAttribute, nValues, papszGlobalMetadata );
609 : }
610 :
611 : // Skip "StructMetadata.N" records. We will fetch information
612 : // from them using HDF-EOS API
613 2311 : else if ( EQUALN( szAttrName, "structmetadata.", 15 ) )
614 : {
615 10 : bIsHDFEOS = 1;
616 10 : continue;
617 : }
618 :
619 : else
620 : {
621 : papszGlobalMetadata = TranslateHDF4Attributes( iHandler,
622 2301 : iAttribute, szAttrName, iNumType, nValues, papszGlobalMetadata );
623 : }
624 : }
625 :
626 520 : return CE_None;
627 : }
628 :
629 : /************************************************************************/
630 : /* Identify() */
631 : /************************************************************************/
632 :
633 12063 : int HDF4Dataset::Identify( GDALOpenInfo * poOpenInfo )
634 :
635 : {
636 12063 : if( poOpenInfo->nHeaderBytes < 4 )
637 10860 : return FALSE;
638 :
639 1203 : if( memcmp(poOpenInfo->pabyHeader,"\016\003\023\001",4) != 0 )
640 946 : return FALSE;
641 :
642 257 : return TRUE;
643 : }
644 :
645 : /************************************************************************/
646 : /* Open() */
647 : /************************************************************************/
648 :
649 2817 : GDALDataset *HDF4Dataset::Open( GDALOpenInfo * poOpenInfo )
650 :
651 : {
652 : int32 i;
653 :
654 2817 : if( !Identify( poOpenInfo ) )
655 2561 : return NULL;
656 :
657 : /* -------------------------------------------------------------------- */
658 : /* Try opening the dataset. */
659 : /* -------------------------------------------------------------------- */
660 : int32 hHDF4;
661 :
662 256 : hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0);
663 :
664 256 : if( hHDF4 <= 0 )
665 0 : return( NULL );
666 :
667 256 : Hclose( hHDF4 );
668 :
669 : /* -------------------------------------------------------------------- */
670 : /* Create a corresponding GDALDataset. */
671 : /* -------------------------------------------------------------------- */
672 : HDF4Dataset *poDS;
673 :
674 256 : poDS = new HDF4Dataset();
675 :
676 256 : poDS->fp = poOpenInfo->fp;
677 256 : poOpenInfo->fp = NULL;
678 :
679 : /* -------------------------------------------------------------------- */
680 : /* Open HDF SDS Interface. */
681 : /* -------------------------------------------------------------------- */
682 256 : poDS->hSD = SDstart( poOpenInfo->pszFilename, DFACC_READ );
683 :
684 256 : if ( poDS->hSD == -1 )
685 : {
686 0 : delete poDS;
687 0 : return NULL;
688 : }
689 :
690 : /* -------------------------------------------------------------------- */
691 : /* Now read Global Attributes. */
692 : /* -------------------------------------------------------------------- */
693 256 : if ( poDS->ReadGlobalAttributes( poDS->hSD ) != CE_None )
694 : {
695 0 : delete poDS;
696 0 : return NULL;
697 : }
698 :
699 256 : poDS->SetMetadata( poDS->papszGlobalMetadata, "" );
700 :
701 : /* -------------------------------------------------------------------- */
702 : /* Determine type of file we read. */
703 : /* -------------------------------------------------------------------- */
704 : const char *pszValue;
705 :
706 256 : if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
707 : "Signature"))
708 : && EQUAL( pszValue, pszGDALSignature ) )
709 : {
710 249 : poDS->iSubdatasetType = H4ST_GDAL;
711 249 : poDS->pszSubdatasetType = "GDAL_HDF4";
712 : }
713 :
714 7 : else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, "Title"))
715 : && EQUAL( pszValue, "SeaWiFS Level-1A Data" ) )
716 : {
717 2 : poDS->iSubdatasetType = H4ST_SEAWIFS_L1A;
718 2 : poDS->pszSubdatasetType = "SEAWIFS_L1A";
719 : }
720 :
721 5 : else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, "Title"))
722 : && EQUAL( pszValue, "SeaWiFS Level-2 Data" ) )
723 : {
724 0 : poDS->iSubdatasetType = H4ST_SEAWIFS_L2;
725 0 : poDS->pszSubdatasetType = "SEAWIFS_L2";
726 : }
727 :
728 5 : else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata, "Title"))
729 : && EQUAL( pszValue, "SeaWiFS Level-3 Standard Mapped Image" ) )
730 : {
731 0 : poDS->iSubdatasetType = H4ST_SEAWIFS_L3;
732 0 : poDS->pszSubdatasetType = "SEAWIFS_L3";
733 : }
734 :
735 5 : else if ( (pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
736 : "L1 File Generated By"))
737 : && EQUALN( pszValue, "HYP version ", 12 ) )
738 : {
739 0 : poDS->iSubdatasetType = H4ST_HYPERION_L1;
740 0 : poDS->pszSubdatasetType = "HYPERION_L1";
741 : }
742 :
743 : else
744 : {
745 5 : poDS->iSubdatasetType = H4ST_UNKNOWN;
746 5 : poDS->pszSubdatasetType = "UNKNOWN";
747 : }
748 :
749 : /* -------------------------------------------------------------------- */
750 : /* If we have HDF-EOS dataset, process it here. */
751 : /* -------------------------------------------------------------------- */
752 : char szName[VSNAMELENMAX + 1], szTemp[8192];
753 : char *pszString;
754 : const char *pszName;
755 : int nCount;
756 : int32 aiDimSizes[H4_MAX_VAR_DIMS];
757 : int32 iRank, iNumType, nAttrs;
758 256 : bool bIsHDF = true;
759 :
760 : // Sometimes "HDFEOSVersion" attribute is not defined and we will
761 : // determine HDF-EOS datasets using other records
762 : // (see ReadGlobalAttributes() method).
763 256 : if ( poDS->bIsHDFEOS
764 : || CSLFetchNameValue(poDS->papszGlobalMetadata, "HDFEOSVersion") )
765 : {
766 2 : bIsHDF = false;
767 :
768 : int32 nSubDatasets, nStrBufSize;
769 :
770 : /* -------------------------------------------------------------------- */
771 : /* Process swath layers. */
772 : /* -------------------------------------------------------------------- */
773 2 : hHDF4 = SWopen( poOpenInfo->pszFilename, DFACC_READ );
774 2 : if( hHDF4 < 0)
775 : {
776 0 : delete poDS;
777 0 : CPLError( CE_Failure, CPLE_OpenFailed, "Failed to open HDF4 `%s'.\n", poOpenInfo->pszFilename );
778 0 : return NULL;
779 : }
780 2 : nSubDatasets = SWinqswath(poOpenInfo->pszFilename, NULL, &nStrBufSize);
781 : #if DEBUG
782 2 : CPLDebug( "HDF4", "Number of HDF-EOS swaths: %d", (int)nSubDatasets );
783 : #endif
784 2 : if ( nSubDatasets > 0 && nStrBufSize > 0 )
785 : {
786 : char *pszSwathList;
787 : char **papszSwaths;
788 :
789 0 : pszSwathList = (char *)CPLMalloc( nStrBufSize + 1 );
790 0 : SWinqswath( poOpenInfo->pszFilename, pszSwathList, &nStrBufSize );
791 0 : pszSwathList[nStrBufSize] = '\0';
792 :
793 : #if DEBUG
794 0 : CPLDebug( "HDF4", "List of HDF-EOS swaths: %s", pszSwathList );
795 : #endif
796 :
797 : papszSwaths =
798 0 : CSLTokenizeString2( pszSwathList, ",", CSLT_HONOURSTRINGS );
799 0 : CPLFree( pszSwathList );
800 :
801 0 : if ( nSubDatasets != CSLCount(papszSwaths) )
802 : {
803 0 : CSLDestroy( papszSwaths );
804 0 : delete poDS;
805 0 : CPLDebug( "HDF4", "Can not parse list of HDF-EOS grids." );
806 0 : return NULL;
807 : }
808 :
809 0 : for ( i = 0; i < nSubDatasets; i++)
810 : {
811 : char *pszFieldList;
812 : char **papszFields;
813 : int32 *paiRank, *paiNumType;
814 : int32 hSW, nFields, j;
815 :
816 0 : hSW = SWattach( hHDF4, papszSwaths[i] );
817 :
818 0 : nFields = SWnentries( hSW, HDFE_NENTDFLD, &nStrBufSize );
819 0 : pszFieldList = (char *)CPLMalloc( nStrBufSize + 1 );
820 0 : paiRank = (int32 *)CPLMalloc( nFields * sizeof(int32) );
821 0 : paiNumType = (int32 *)CPLMalloc( nFields * sizeof(int32) );
822 :
823 0 : SWinqdatafields( hSW, pszFieldList, paiRank, paiNumType );
824 :
825 : #if DEBUG
826 : {
827 : char *pszTmp =
828 0 : SPrintArray( GDT_UInt32, paiRank, nFields, "," );
829 :
830 : CPLDebug( "HDF4", "Number of data fields in swath %d: %d",
831 0 : (int) i, (int) nFields );
832 : CPLDebug( "HDF4", "List of data fields in swath %d: %s",
833 0 : (int) i, pszFieldList );
834 0 : CPLDebug( "HDF4", "Data fields ranks: %s", pszTmp );
835 :
836 0 : CPLFree( pszTmp );
837 : }
838 : #endif
839 :
840 : papszFields = CSLTokenizeString2( pszFieldList, ",",
841 0 : CSLT_HONOURSTRINGS );
842 :
843 0 : for ( j = 0; j < nFields; j++ )
844 : {
845 0 : SWfieldinfo( hSW, papszFields[j], &iRank, aiDimSizes,
846 0 : &iNumType, NULL );
847 :
848 0 : if ( iRank < 2 )
849 0 : continue;
850 :
851 : // Add field to the list of GDAL subdatasets
852 0 : nCount = CSLCount( poDS->papszSubDatasets ) / 2;
853 0 : sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 );
854 : // We will use the field index as an identificator.
855 : poDS->papszSubDatasets =
856 : CSLSetNameValue( poDS->papszSubDatasets, szTemp,
857 : CPLSPrintf("HDF4_EOS:EOS_SWATH:\"%s\":%s:%s",
858 : poOpenInfo->pszFilename,
859 0 : papszSwaths[i], papszFields[j]) );
860 :
861 0 : sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 );
862 : pszString = SPrintArray( GDT_UInt32, aiDimSizes,
863 0 : iRank, "x" );
864 : poDS->papszSubDatasets =
865 : CSLSetNameValue( poDS->papszSubDatasets, szTemp,
866 : CPLSPrintf( "[%s] %s %s (%s)", pszString,
867 : papszFields[j],
868 : papszSwaths[i],
869 0 : poDS->GetDataTypeName(iNumType) ) );
870 0 : CPLFree( pszString );
871 : }
872 :
873 0 : CSLDestroy( papszFields );
874 0 : CPLFree( paiNumType );
875 0 : CPLFree( paiRank );
876 0 : CPLFree( pszFieldList );
877 0 : SWdetach( hSW );
878 : }
879 :
880 0 : CSLDestroy( papszSwaths );
881 : }
882 2 : SWclose( hHDF4 );
883 :
884 : /* -------------------------------------------------------------------- */
885 : /* Process grid layers. */
886 : /* -------------------------------------------------------------------- */
887 2 : hHDF4 = GDopen( poOpenInfo->pszFilename, DFACC_READ );
888 2 : nSubDatasets = GDinqgrid( poOpenInfo->pszFilename, NULL, &nStrBufSize );
889 : #if DEBUG
890 2 : CPLDebug( "HDF4", "Number of HDF-EOS grids: %d", (int)nSubDatasets );
891 : #endif
892 2 : if ( nSubDatasets > 0 && nStrBufSize > 0 )
893 : {
894 : char *pszGridList;
895 : char **papszGrids;
896 :
897 2 : pszGridList = (char *)CPLMalloc( nStrBufSize + 1 );
898 2 : GDinqgrid( poOpenInfo->pszFilename, pszGridList, &nStrBufSize );
899 :
900 : #if DEBUG
901 2 : CPLDebug( "HDF4", "List of HDF-EOS grids: %s", pszGridList );
902 : #endif
903 :
904 : papszGrids =
905 2 : CSLTokenizeString2( pszGridList, ",", CSLT_HONOURSTRINGS );
906 2 : CPLFree( pszGridList );
907 :
908 2 : if ( nSubDatasets != CSLCount(papszGrids) )
909 : {
910 0 : CSLDestroy( papszGrids );
911 0 : delete poDS;
912 0 : CPLDebug( "HDF4", "Can not parse list of HDF-EOS grids." );
913 0 : return NULL;
914 : }
915 :
916 4 : for ( i = 0; i < nSubDatasets; i++)
917 : {
918 : char *pszFieldList;
919 : char **papszFields;
920 : int32 *paiRank, *paiNumType;
921 : int32 hGD, nFields, j;
922 :
923 2 : hGD = GDattach( hHDF4, papszGrids[i] );
924 :
925 2 : nFields = GDnentries( hGD, HDFE_NENTDFLD, &nStrBufSize );
926 2 : pszFieldList = (char *)CPLMalloc( nStrBufSize + 1 );
927 2 : paiRank = (int32 *)CPLMalloc( nFields * sizeof(int32) );
928 2 : paiNumType = (int32 *)CPLMalloc( nFields * sizeof(int32) );
929 :
930 2 : GDinqfields( hGD, pszFieldList, paiRank, paiNumType );
931 :
932 : #if DEBUG
933 : {
934 : char* pszTmp =
935 2 : SPrintArray( GDT_UInt32, paiRank, nFields, "," );
936 : CPLDebug( "HDF4", "Number of fields in grid %d: %d",
937 2 : (int) i, (int) nFields );
938 : CPLDebug( "HDF4", "List of fields in grid %d: %s",
939 2 : (int) i, pszFieldList );
940 : CPLDebug( "HDF4", "Fields ranks: %s",
941 2 : pszTmp );
942 2 : CPLFree( pszTmp );
943 : }
944 : #endif
945 :
946 : papszFields = CSLTokenizeString2( pszFieldList, ",",
947 2 : CSLT_HONOURSTRINGS );
948 :
949 4 : for ( j = 0; j < nFields; j++ )
950 : {
951 : GDfieldinfo( hGD, papszFields[j], &iRank, aiDimSizes,
952 2 : &iNumType, NULL );
953 :
954 2 : if ( iRank < 2 )
955 0 : continue;
956 :
957 : // Add field to the list of GDAL subdatasets
958 2 : nCount = CSLCount( poDS->papszSubDatasets ) / 2;
959 2 : sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 );
960 : // We will use the field index as an identificator.
961 : poDS->papszSubDatasets =
962 : CSLSetNameValue(poDS->papszSubDatasets, szTemp,
963 : CPLSPrintf( "HDF4_EOS:EOS_GRID:\"%s\":%s:%s",
964 : poOpenInfo->pszFilename,
965 2 : papszGrids[i], papszFields[j]));
966 :
967 2 : sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 );
968 : pszString = SPrintArray( GDT_UInt32, aiDimSizes,
969 2 : iRank, "x" );
970 : poDS->papszSubDatasets =
971 : CSLSetNameValue( poDS->papszSubDatasets, szTemp,
972 : CPLSPrintf("[%s] %s %s (%s)", pszString,
973 : papszFields[j],
974 : papszGrids[i],
975 2 : poDS->GetDataTypeName(iNumType)) );
976 2 : CPLFree( pszString );
977 : }
978 :
979 2 : CSLDestroy( papszFields );
980 2 : CPLFree( paiNumType );
981 2 : CPLFree( paiRank );
982 2 : CPLFree( pszFieldList );
983 2 : GDdetach( hGD );
984 : }
985 :
986 2 : CSLDestroy( papszGrids );
987 2 : GDclose( hHDF4 );
988 : }
989 2 : GDclose( hHDF4 );
990 :
991 2 : bIsHDF = ( nSubDatasets == 0 ); // Try to read as HDF
992 : }
993 :
994 256 : if( bIsHDF )
995 : {
996 :
997 : /* -------------------------------------------------------------------- */
998 : /* Make a list of subdatasets from SDSs contained in input HDF file. */
999 : /* -------------------------------------------------------------------- */
1000 : int32 nDatasets;
1001 :
1002 254 : if ( SDfileinfo( poDS->hSD, &nDatasets, &nAttrs ) != 0 )
1003 0 : return NULL;
1004 :
1005 616 : for ( i = 0; i < nDatasets; i++ )
1006 : {
1007 : int32 iSDS;
1008 :
1009 362 : iSDS = SDselect( poDS->hSD, i );
1010 362 : if ( SDgetinfo( iSDS, szName, &iRank, aiDimSizes, &iNumType, &nAttrs) != 0 )
1011 0 : return NULL;
1012 :
1013 362 : if ( iRank == 1 ) // Skip 1D datsets
1014 44 : continue;
1015 :
1016 : // Do sort of known datasets. We will display only image bands
1017 318 : if ( (poDS->iSubdatasetType == H4ST_SEAWIFS_L1A ) &&
1018 : !EQUALN( szName, "l1a_data", 8 ) )
1019 62 : continue;
1020 : else
1021 256 : pszName = szName;
1022 :
1023 : // Add datasets with multiple dimensions to the list of GDAL subdatasets
1024 256 : nCount = CSLCount( poDS->papszSubDatasets ) / 2;
1025 256 : sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 );
1026 : // We will use SDS index as an identificator, because SDS names
1027 : // are not unique. Filename also needed for further file opening
1028 : poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, szTemp,
1029 : CPLSPrintf( "HDF4_SDS:%s:\"%s\":%ld", poDS->pszSubdatasetType,
1030 256 : poOpenInfo->pszFilename, (long)i) );
1031 256 : sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 );
1032 256 : pszString = SPrintArray( GDT_UInt32, aiDimSizes, iRank, "x" );
1033 : poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, szTemp,
1034 : CPLSPrintf( "[%s] %s (%s)", pszString,
1035 256 : pszName, poDS->GetDataTypeName(iNumType)) );
1036 256 : CPLFree( pszString );
1037 :
1038 256 : SDendaccess( iSDS );
1039 : }
1040 :
1041 254 : SDend( poDS->hSD );
1042 254 : poDS->hSD = 0;
1043 : }
1044 :
1045 : /* -------------------------------------------------------------------- */
1046 : /* Build a list of raster images. Note, that HDF-EOS dataset may */
1047 : /* contain a raster image as well. */
1048 : /* -------------------------------------------------------------------- */
1049 256 : hHDF4 = Hopen(poOpenInfo->pszFilename, DFACC_READ, 0);
1050 256 : poDS->hGR = GRstart( hHDF4 );
1051 :
1052 256 : if ( poDS->hGR != -1 )
1053 : {
1054 256 : if ( GRfileinfo( poDS->hGR, &poDS->nImages, &nAttrs ) == -1 )
1055 0 : return NULL;
1056 :
1057 256 : for ( i = 0; i < poDS->nImages; i++ )
1058 : {
1059 : int32 iInterlaceMode;
1060 0 : int32 iGR = GRselect( poDS->hGR, i );
1061 :
1062 : // iRank in GR interface has another meaning. It represents number
1063 : // of samples per pixel. aiDimSizes has only two dimensions.
1064 0 : if ( GRgetiminfo( iGR, szName, &iRank, &iNumType, &iInterlaceMode,
1065 : aiDimSizes, &nAttrs ) != 0 )
1066 0 : return NULL;
1067 0 : nCount = CSLCount( poDS->papszSubDatasets ) / 2;
1068 0 : sprintf( szTemp, "SUBDATASET_%d_NAME", nCount + 1 );
1069 : poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
1070 : szTemp,CPLSPrintf( "HDF4_GR:UNKNOWN:\"%s\":%ld",
1071 0 : poOpenInfo->pszFilename, (long)i));
1072 0 : sprintf( szTemp, "SUBDATASET_%d_DESC", nCount + 1 );
1073 0 : pszString = SPrintArray( GDT_UInt32, aiDimSizes, 2, "x" );
1074 : poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets,
1075 : szTemp, CPLSPrintf( "[%sx%ld] %s (%s)", pszString, (long)iRank,
1076 0 : szName, poDS->GetDataTypeName(iNumType)) );
1077 0 : CPLFree( pszString );
1078 :
1079 0 : GRendaccess( iGR );
1080 : }
1081 :
1082 256 : GRend( poDS->hGR );
1083 256 : poDS->hGR = 0;
1084 : }
1085 :
1086 256 : Hclose( hHDF4 );
1087 :
1088 256 : poDS->nRasterXSize = poDS->nRasterYSize = 512; // XXX: bogus values
1089 :
1090 : // Make sure we don't try to do any pam stuff with this dataset.
1091 256 : poDS->nPamFlags |= GPF_NOSAVE;
1092 :
1093 : /* -------------------------------------------------------------------- */
1094 : /* If we have single subdataset only, open it immediately */
1095 : /* -------------------------------------------------------------------- */
1096 256 : if ( CSLCount( poDS->papszSubDatasets ) / 2 == 1 )
1097 : {
1098 : char *pszSDSName;
1099 : pszSDSName = CPLStrdup( CSLFetchNameValue( poDS->papszSubDatasets,
1100 255 : "SUBDATASET_1_NAME" ));
1101 255 : delete poDS;
1102 255 : poDS = NULL;
1103 :
1104 255 : GDALDataset* poRetDS = (GDALDataset*) GDALOpen( pszSDSName, poOpenInfo->eAccess );
1105 255 : CPLFree( pszSDSName );
1106 :
1107 255 : if (poRetDS)
1108 : {
1109 255 : poRetDS->SetDescription(poOpenInfo->pszFilename);
1110 : }
1111 :
1112 255 : return poRetDS;
1113 : }
1114 : else
1115 : {
1116 : /* -------------------------------------------------------------------- */
1117 : /* Confirm the requested access is supported. */
1118 : /* -------------------------------------------------------------------- */
1119 1 : if( poOpenInfo->eAccess == GA_Update )
1120 : {
1121 0 : delete poDS;
1122 : CPLError( CE_Failure, CPLE_NotSupported,
1123 : "The HDF4 driver does not support update access to existing"
1124 0 : " datasets.\n" );
1125 0 : return NULL;
1126 : }
1127 :
1128 : }
1129 :
1130 1 : return( poDS );
1131 : }
1132 :
1133 : /************************************************************************/
1134 : /* GDALRegister_HDF4() */
1135 : /************************************************************************/
1136 :
1137 558 : void GDALRegister_HDF4()
1138 :
1139 : {
1140 : GDALDriver *poDriver;
1141 :
1142 558 : if (! GDAL_CHECK_VERSION("HDF4 driver"))
1143 0 : return;
1144 :
1145 558 : if( GDALGetDriverByName( "HDF4" ) == NULL )
1146 : {
1147 537 : poDriver = new GDALDriver();
1148 :
1149 537 : poDriver->SetDescription( "HDF4" );
1150 : poDriver->SetMetadataItem( GDAL_DMD_LONGNAME,
1151 537 : "Hierarchical Data Format Release 4" );
1152 : poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC,
1153 537 : "frmt_hdf4.html" );
1154 :
1155 537 : poDriver->pfnOpen = HDF4Dataset::Open;
1156 537 : poDriver->pfnIdentify = HDF4Dataset::Identify;
1157 :
1158 537 : GetGDALDriverManager()->RegisterDriver( poDriver );
1159 : }
1160 : }
1161 :
|