1 /******************************************************************************
2 * $Id: hdf5dataset.cpp 28822 2015-03-30 13:56:19Z rouault $
3 *
4 * Project: Hierarchical Data Format Release 5 (HDF5)
5 * Purpose: HDF5 Datasets. Open HDF5 file, fetch metadata and list of
6 * subdatasets.
7 * This driver initially based on code supplied by Markus Neteler
8 * Author: Denis Nadeau <denis.nadeau@gmail.com>
9 *
10 ******************************************************************************
11 * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
12 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at mines-paris dot org>
13 *
14 * Permission is hereby granted, free of charge, to any person obtaining a
15 * copy of this software and associated documentation files (the "Software"),
16 * to deal in the Software without restriction, including without limitation
17 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
18 * and/or sell copies of the Software, and to permit persons to whom the
19 * Software is furnished to do so, subject to the following conditions:
20 *
21 * The above copyright notice and this permission notice shall be included
22 * in all copies or substantial portions of the Software.
23 *
24 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
25 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
27 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
30 * DEALINGS IN THE SOFTWARE.
31 ****************************************************************************/
32
33 #define H5_USE_16_API
34
35 #define MAX_METADATA_LEN 32768
36
37 #include "hdf5.h"
38
39 #include "gdal_priv.h"
40 #include "cpl_string.h"
41 #include "hdf5dataset.h"
42
43 CPL_CVSID("$Id: hdf5dataset.cpp 28822 2015-03-30 13:56:19Z rouault $");
44
45 CPL_C_START
46 void GDALRegister_HDF5(void);
47 CPL_C_END
48
49
50
51 /************************************************************************/
52 /* ==================================================================== */
53 /* HDF5Dataset */
54 /* ==================================================================== */
55 /************************************************************************/
56
57 /************************************************************************/
58 /* GDALRegister_HDF5() */
59 /************************************************************************/
GDALRegister_HDF5()60 void GDALRegister_HDF5()
61
62 {
63 GDALDriver *poDriver;
64 if( GDALGetDriverByName("HDF5") == NULL )
65 {
66 poDriver = new GDALDriver();
67 poDriver->SetDescription("HDF5");
68 poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
69 poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
70 "Hierarchical Data Format Release 5");
71 poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
72 "frmt_hdf5.html");
73 poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hdf5");
74 poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
75 poDriver->pfnOpen = HDF5Dataset::Open;
76 poDriver->pfnIdentify = HDF5Dataset::Identify;
77 GetGDALDriverManager()->RegisterDriver(poDriver);
78 }
79
80 #ifdef HDF5_PLUGIN
81 GDALRegister_HDF5Image();
82 #endif
83
84 }
85
86 /************************************************************************/
87 /* HDF5Dataset() */
88 /************************************************************************/
HDF5Dataset()89 HDF5Dataset::HDF5Dataset()
90 {
91 papszSubDatasets = NULL;
92 papszMetadata = NULL;
93 poH5RootGroup = NULL;
94 nSubDataCount = 0;
95 hHDF5 = -1;
96 hGroupID = -1;
97 bIsHDFEOS = FALSE;
98 nDatasetType = -1;
99 }
100
101 /************************************************************************/
102 /* ~HDF5Dataset() */
103 /************************************************************************/
~HDF5Dataset()104 HDF5Dataset::~HDF5Dataset()
105 {
106 CSLDestroy( papszMetadata );
107 if( hGroupID > 0 )
108 H5Gclose( hGroupID );
109 if( hHDF5 > 0 )
110 H5Fclose( hHDF5 );
111 CSLDestroy( papszSubDatasets );
112 if( poH5RootGroup != NULL ) {
113 DestroyH5Objects( poH5RootGroup );
114 CPLFree( poH5RootGroup->pszName );
115 CPLFree( poH5RootGroup->pszPath );
116 CPLFree( poH5RootGroup->pszUnderscorePath );
117 CPLFree( poH5RootGroup->poHchild );
118 CPLFree( poH5RootGroup );
119 }
120 }
121
122 /************************************************************************/
123 /* GetDataType() */
124 /* */
125 /* Transform HDF5 datatype to GDAL datatype */
126 /************************************************************************/
GetDataType(hid_t TypeID)127 GDALDataType HDF5Dataset::GetDataType(hid_t TypeID)
128 {
129 if( H5Tequal( H5T_NATIVE_CHAR, TypeID ) )
130 return GDT_Byte;
131 else if( H5Tequal( H5T_NATIVE_SCHAR, TypeID ) )
132 return GDT_Byte;
133 else if( H5Tequal( H5T_NATIVE_UCHAR, TypeID ) )
134 return GDT_Byte;
135 else if( H5Tequal( H5T_NATIVE_SHORT, TypeID ) )
136 return GDT_Int16;
137 else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) )
138 return GDT_UInt16;
139 else if( H5Tequal( H5T_NATIVE_INT, TypeID ) )
140 return GDT_Int32;
141 else if( H5Tequal( H5T_NATIVE_UINT, TypeID ) )
142 return GDT_UInt32;
143 else if( H5Tequal( H5T_NATIVE_LONG, TypeID ) )
144 {
145 if( sizeof(long) == 4 )
146 return GDT_Int32;
147 else
148 return GDT_Unknown;
149 }
150 else if( H5Tequal( H5T_NATIVE_ULONG, TypeID ) )
151 {
152 if( sizeof(unsigned long) == 4 )
153 return GDT_UInt32;
154 else
155 return GDT_Unknown;
156 }
157 else if( H5Tequal( H5T_NATIVE_FLOAT, TypeID ) )
158 return GDT_Float32;
159 else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
160 return GDT_Float64;
161 else if( H5Tequal( H5T_NATIVE_LLONG, TypeID ) )
162 return GDT_Unknown;
163 else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) )
164 return GDT_Unknown;
165 else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
166 return GDT_Unknown;
167
168 return GDT_Unknown;
169 }
170
171 /************************************************************************/
172 /* GetDataTypeName() */
173 /* */
174 /* Return the human readable name of data type */
175 /************************************************************************/
GetDataTypeName(hid_t TypeID)176 const char *HDF5Dataset::GetDataTypeName(hid_t TypeID)
177 {
178 if( H5Tequal( H5T_NATIVE_CHAR, TypeID ) )
179 return "8-bit character";
180 else if( H5Tequal( H5T_NATIVE_SCHAR, TypeID ) )
181 return "8-bit signed character";
182 else if( H5Tequal( H5T_NATIVE_UCHAR, TypeID ) )
183 return "8-bit unsigned character";
184 else if( H5Tequal( H5T_NATIVE_SHORT, TypeID ) )
185 return "16-bit integer";
186 else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) )
187 return "16-bit unsigned integer";
188 else if( H5Tequal( H5T_NATIVE_INT, TypeID ) )
189 return "32-bit integer";
190 else if( H5Tequal( H5T_NATIVE_UINT, TypeID ) )
191 return "32-bit unsigned integer";
192 else if( H5Tequal( H5T_NATIVE_LONG, TypeID ) )
193 return "32/64-bit integer";
194 else if( H5Tequal( H5T_NATIVE_ULONG, TypeID ) )
195 return "32/64-bit unsigned integer";
196 else if( H5Tequal( H5T_NATIVE_FLOAT, TypeID ) )
197 return "32-bit floating-point";
198 else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
199 return "64-bit floating-point";
200 else if( H5Tequal( H5T_NATIVE_LLONG, TypeID ) )
201 return "64-bit integer";
202 else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) )
203 return "64-bit unsigned integer";
204 else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
205 return "64-bit floating-point";
206
207 return "Unknown";
208 }
209
210 /************************************************************************/
211 /* Identify() */
212 /************************************************************************/
213
Identify(GDALOpenInfo * poOpenInfo)214 int HDF5Dataset::Identify( GDALOpenInfo * poOpenInfo )
215
216 {
217 /* -------------------------------------------------------------------- */
218 /* Is it an HDF5 file? */
219 /* -------------------------------------------------------------------- */
220 static const char achSignature[] = "\211HDF\r\n\032\n";
221
222 if( poOpenInfo->pabyHeader )
223 {
224 if( memcmp(poOpenInfo->pabyHeader,achSignature,8) == 0 )
225 {
226 /* The tests to avoid opening KEA and BAG drivers are not */
227 /* necessary when drivers are built in the core lib, as they */
228 /* are registered after HDF5, but in the case of plugins, we */
229 /* cannot do assumptions about the registration order */
230
231 /* Avoid opening kea files if the kea driver is available */
232 if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "KEA") &&
233 GDALGetDriverByName("KEA") != NULL )
234 {
235 return FALSE;
236 }
237
238 /* Avoid opening kea files if the bag driver is available */
239 if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "BAG") &&
240 GDALGetDriverByName("BAG") != NULL )
241 {
242 return FALSE;
243 }
244
245 return TRUE;
246 }
247
248 if( memcmp(poOpenInfo->pabyHeader,"<HDF_UserBlock>",15) == 0)
249 {
250 if( H5Fis_hdf5(poOpenInfo->pszFilename) )
251 return TRUE;
252 }
253 }
254
255 return FALSE;
256 }
257
258 /************************************************************************/
259 /* Open() */
260 /************************************************************************/
Open(GDALOpenInfo * poOpenInfo)261 GDALDataset *HDF5Dataset::Open( GDALOpenInfo * poOpenInfo )
262 {
263 HDF5Dataset *poDS;
264
265 if( !Identify( poOpenInfo ) )
266 return NULL;
267
268 /* -------------------------------------------------------------------- */
269 /* Create datasource. */
270 /* -------------------------------------------------------------------- */
271 poDS = new HDF5Dataset();
272
273 poDS->SetDescription( poOpenInfo->pszFilename );
274
275 /* -------------------------------------------------------------------- */
276 /* Try opening the dataset. */
277 /* -------------------------------------------------------------------- */
278 poDS->hHDF5 = H5Fopen( poOpenInfo->pszFilename,
279 H5F_ACC_RDONLY,
280 H5P_DEFAULT );
281 if( poDS->hHDF5 < 0 ) {
282 delete poDS;
283 return NULL;
284 }
285
286 poDS->hGroupID = H5Gopen( poDS->hHDF5, "/" );
287 if( poDS->hGroupID < 0 ) {
288 poDS->bIsHDFEOS=false;
289 delete poDS;
290 return NULL;
291 }
292
293 poDS->bIsHDFEOS=true;
294 poDS->ReadGlobalAttributes( true );
295
296 poDS->SetMetadata( poDS->papszMetadata );
297
298 if ( CSLCount( poDS->papszSubDatasets ) / 2 >= 1 )
299 poDS->SetMetadata( poDS->papszSubDatasets, "SUBDATASETS" );
300
301 // Make sure we don't try to do any pam stuff with this dataset.
302 poDS->nPamFlags |= GPF_NOSAVE;
303
304 /* -------------------------------------------------------------------- */
305 /* If we have single subdataset only, open it immediately */
306 /* -------------------------------------------------------------------- */
307 int nSubDatasets = CSLCount( poDS->papszSubDatasets ) / 2;
308 if( nSubDatasets == 1 )
309 {
310 CPLString osDSName = CSLFetchNameValue( poDS->papszSubDatasets,
311 "SUBDATASET_1_NAME" );
312 delete poDS;
313 return (GDALDataset *) GDALOpen( osDSName, poOpenInfo->eAccess );
314 }
315 else
316 {
317 /* -------------------------------------------------------------------- */
318 /* Confirm the requested access is supported. */
319 /* -------------------------------------------------------------------- */
320 if( poOpenInfo->eAccess == GA_Update )
321 {
322 delete poDS;
323 CPLError( CE_Failure, CPLE_NotSupported,
324 "The HDF5 driver does not support update access to existing"
325 " datasets.\n" );
326 return NULL;
327 }
328 }
329 return( poDS );
330 }
331
332 /************************************************************************/
333 /* DestroyH5Objects() */
334 /* */
335 /* Erase all objects */
336 /************************************************************************/
DestroyH5Objects(HDF5GroupObjects * poH5Object)337 void HDF5Dataset::DestroyH5Objects( HDF5GroupObjects *poH5Object )
338 {
339 unsigned i;
340
341 /* -------------------------------------------------------------------- */
342 /* Visit all objects */
343 /* -------------------------------------------------------------------- */
344
345 for( i=0; i < poH5Object->nbObjs; i++ )
346 if( poH5Object->poHchild+i != NULL )
347 DestroyH5Objects( poH5Object->poHchild+i );
348
349 if( poH5Object->poHparent ==NULL )
350 return;
351
352 /* -------------------------------------------------------------------- */
353 /* Erase some data */
354 /* -------------------------------------------------------------------- */
355 CPLFree( poH5Object->paDims );
356 poH5Object->paDims = NULL;
357
358 CPLFree( poH5Object->pszPath );
359 poH5Object->pszPath = NULL;
360
361 CPLFree( poH5Object->pszName );
362 poH5Object->pszName = NULL;
363
364 CPLFree( poH5Object->pszUnderscorePath );
365 poH5Object->pszUnderscorePath = NULL;
366
367 if( poH5Object->native > 0 )
368 H5Tclose( poH5Object->native );
369 poH5Object->native = 0;
370
371 /* -------------------------------------------------------------------- */
372 /* All Children are visited and can be deleted. */
373 /* -------------------------------------------------------------------- */
374 if( ( i==poH5Object->nbObjs ) && ( poH5Object->nbObjs!=0 ) ) {
375 CPLFree( poH5Object->poHchild );
376 poH5Object->poHchild = NULL;
377 }
378
379 }
380
381 /************************************************************************/
382 /* CreatePath() */
383 /* */
384 /* Find Dataset path for HDopen */
385 /************************************************************************/
CreatePath(HDF5GroupObjects * poH5Object)386 char* CreatePath( HDF5GroupObjects *poH5Object )
387 {
388 char pszPath[8192];
389 char pszUnderscoreSpaceInName[8192];
390 char *popszPath;
391 int i;
392 char **papszPath;
393
394 /* -------------------------------------------------------------------- */
395 /* Recurse to the root path */
396 /* -------------------------------------------------------------------- */
397 pszPath[0]='\0';
398 if( poH5Object->poHparent !=NULL ) {
399 popszPath=CreatePath( poH5Object->poHparent );
400 strcpy( pszPath,popszPath );
401 }
402
403 /* -------------------------------------------------------------------- */
404 /* add name to the path */
405 /* -------------------------------------------------------------------- */
406 if( !EQUAL( poH5Object->pszName,"/" ) ){
407 strcat( pszPath,"/" );
408 strcat( pszPath,poH5Object->pszName );
409 }
410
411 /* -------------------------------------------------------------------- */
412 /* fill up path for each object */
413 /* -------------------------------------------------------------------- */
414 if( poH5Object->pszPath == NULL ) {
415
416 if( strlen( poH5Object->pszName ) == 1 ) {
417 strcat(pszPath, poH5Object->pszName );
418 strcpy(pszUnderscoreSpaceInName, poH5Object->pszName);
419 }
420 else {
421 /* -------------------------------------------------------------------- */
422 /* Change space for underscore */
423 /* -------------------------------------------------------------------- */
424 papszPath = CSLTokenizeString2( pszPath,
425 " ", CSLT_HONOURSTRINGS );
426
427 strcpy(pszUnderscoreSpaceInName,papszPath[0]);
428 for( i=1; i < CSLCount( papszPath ); i++ ) {
429 strcat( pszUnderscoreSpaceInName, "_" );
430 strcat( pszUnderscoreSpaceInName, papszPath[ i ] );
431 }
432 CSLDestroy(papszPath);
433
434 }
435 poH5Object->pszUnderscorePath =
436 CPLStrdup( pszUnderscoreSpaceInName );
437 poH5Object->pszPath = CPLStrdup( pszPath );
438 }
439
440 return( poH5Object->pszPath );
441 }
442
443 /************************************************************************/
444 /* HDF5GroupCheckDuplicate() */
445 /* */
446 /* Returns TRUE if an ancestor has the same objno[] as passed */
447 /* in - used to avoid looping in files with "links up" #(3218). */
448 /************************************************************************/
449
HDF5GroupCheckDuplicate(HDF5GroupObjects * poHparent,unsigned long * objno)450 static int HDF5GroupCheckDuplicate( HDF5GroupObjects *poHparent,
451 unsigned long *objno )
452
453 {
454 while( poHparent != NULL )
455 {
456 if( poHparent->objno[0] == objno[0]
457 && poHparent->objno[1] == objno[1] )
458 return TRUE;
459
460 poHparent = poHparent->poHparent;
461 }
462
463 return FALSE;
464 }
465
466 /************************************************************************/
467 /* HDF5CreateGroupObjs() */
468 /* */
469 /* Create HDF5 hierarchy into a linked list */
470 /************************************************************************/
HDF5CreateGroupObjs(hid_t hHDF5,const char * pszObjName,void * poHObjParent)471 herr_t HDF5CreateGroupObjs(hid_t hHDF5, const char *pszObjName,
472 void *poHObjParent)
473 {
474 hid_t hGroupID; /* identifier of group */
475 hid_t hDatasetID; /* identifier of dataset */
476 hsize_t nbObjs=0; /* number of objects in a group */
477 int nbAttrs=0; /* number of attributes in object */
478 unsigned idx;
479 int n_dims;
480 H5G_stat_t oStatbuf;
481 hsize_t *dims=NULL;
482 hsize_t *maxdims=NULL;
483 hid_t datatype;
484 hid_t dataspace;
485 hid_t native;
486
487 HDF5GroupObjects *poHchild;
488 HDF5GroupObjects *poHparent;
489
490 poHparent = ( HDF5GroupObjects * ) poHObjParent;
491 poHchild=poHparent->poHchild;
492
493 if( H5Gget_objinfo( hHDF5, pszObjName, FALSE, &oStatbuf ) < 0 )
494 return -1;
495
496
497 /* -------------------------------------------------------------------- */
498 /* Look for next child */
499 /* -------------------------------------------------------------------- */
500 for( idx=0; idx < poHparent->nbObjs; idx++ ) {
501 if( poHchild->pszName == NULL ) break;
502 poHchild++;
503 }
504
505 if( idx == poHparent->nbObjs )
506 return -1; // all children parsed
507
508 /* -------------------------------------------------------------------- */
509 /* Save child information */
510 /* -------------------------------------------------------------------- */
511 poHchild->pszName = CPLStrdup( pszObjName );
512
513 poHchild->nType = oStatbuf.type;
514 poHchild->nIndex = idx;
515 poHchild->poHparent = poHparent;
516 poHchild->nRank = 0;
517 poHchild->paDims = 0;
518 poHchild->HDatatype = 0;
519 poHchild->objno[0] = oStatbuf.objno[0];
520 poHchild->objno[1] = oStatbuf.objno[1];
521 if( poHchild->pszPath == NULL ) {
522 poHchild->pszPath = CreatePath( poHchild );
523 }
524 if( poHparent->pszPath == NULL ) {
525 poHparent->pszPath = CreatePath( poHparent );
526 }
527
528
529 switch ( oStatbuf.type )
530 {
531 case H5G_LINK:
532 poHchild->nbAttrs = 0;
533 poHchild->nbObjs = 0;
534 poHchild->poHchild = NULL;
535 poHchild->nRank = 0;
536 poHchild->paDims = 0;
537 poHchild->HDatatype = 0;
538 break;
539
540 case H5G_GROUP:
541 if( ( hGroupID = H5Gopen( hHDF5, pszObjName ) ) == -1 ) {
542 printf( "Error: unable to access \"%s\" group.\n",
543 pszObjName );
544 return -1;
545 }
546 nbAttrs = H5Aget_num_attrs( hGroupID );
547 H5Gget_num_objs( hGroupID, &nbObjs );
548 poHchild->nbAttrs= nbAttrs;
549 poHchild->nbObjs = (int) nbObjs;
550 poHchild->nRank = 0;
551 poHchild->paDims = 0;
552 poHchild->HDatatype = 0;
553
554 if( nbObjs > 0 ) {
555 poHchild->poHchild =( HDF5GroupObjects * )
556 CPLCalloc( (int)nbObjs, sizeof( HDF5GroupObjects ) );
557 memset( poHchild->poHchild, 0,
558 (size_t) (sizeof( HDF5GroupObjects ) * nbObjs) );
559 }
560 else
561 poHchild->poHchild = NULL;
562
563 if( !HDF5GroupCheckDuplicate( poHparent, oStatbuf.objno ) )
564 H5Giterate( hHDF5, pszObjName, NULL,
565 HDF5CreateGroupObjs, (void*) poHchild );
566 else
567 CPLDebug( "HDF5", "avoiding link looping on node '%s'.",
568 pszObjName );
569
570 H5Gclose( hGroupID );
571 break;
572
573 case H5G_DATASET:
574
575 if( ( hDatasetID = H5Dopen( hHDF5, pszObjName ) ) == -1 ) {
576 printf( "Error: unable to access \"%s\" dataset.\n",
577 pszObjName );
578 return -1;
579 }
580 nbAttrs = H5Aget_num_attrs( hDatasetID );
581 datatype = H5Dget_type( hDatasetID );
582 dataspace = H5Dget_space( hDatasetID );
583 n_dims = H5Sget_simple_extent_ndims( dataspace );
584 native = H5Tget_native_type( datatype, H5T_DIR_ASCEND );
585
586 if( n_dims > 0 ) {
587 dims = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) );
588 maxdims = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) );
589 }
590 H5Sget_simple_extent_dims( dataspace, dims, maxdims );
591 if( maxdims != NULL )
592 CPLFree( maxdims );
593
594 if( n_dims > 0 ) {
595 poHchild->nRank = n_dims; // rank of the array
596 poHchild->paDims = dims; // dimmension of the array.
597 poHchild->HDatatype = datatype; // HDF5 datatype
598 }
599 else {
600 poHchild->nRank = -1;
601 poHchild->paDims = NULL;
602 poHchild->HDatatype = 0;
603 }
604 poHchild->nbAttrs = nbAttrs;
605 poHchild->nbObjs = 0;
606 poHchild->poHchild = NULL;
607 poHchild->native = native;
608 H5Tclose( datatype );
609 H5Sclose( dataspace );
610 H5Dclose( hDatasetID );
611 break;
612
613 case H5G_TYPE:
614 poHchild->nbAttrs = 0;
615 poHchild->nbObjs = 0;
616 poHchild->poHchild = NULL;
617 poHchild->nRank = 0;
618 poHchild->paDims = 0;
619 poHchild->HDatatype = 0;
620 break;
621
622 default:
623 break;
624 }
625
626 return 0;
627 }
628
629
630 /************************************************************************/
631 /* HDF5AttrIterate() */
632 /************************************************************************/
633
HDF5AttrIterate(hid_t hH5ObjID,const char * pszAttrName,void * pDS)634 herr_t HDF5AttrIterate( hid_t hH5ObjID,
635 const char *pszAttrName,
636 void *pDS )
637 {
638 hid_t hAttrID;
639 hid_t hAttrTypeID;
640 hid_t hAttrNativeType;
641 hid_t hAttrSpace;
642
643 char *szData = NULL;
644 hsize_t nSize[64];
645 unsigned int nAttrElmts;
646 hsize_t nAttrSize;
647 hsize_t i;
648 void *buf = NULL;
649 unsigned int nAttrDims;
650
651 char **papszTokens;
652
653 HDF5Dataset *poDS;
654 CPLString osKey;
655 char *szValue = NULL;
656
657 poDS = (HDF5Dataset *) pDS;
658
659 // Convert "/" into "_" for the path component
660 const char* pszPath = poDS->poH5CurrentObject->pszUnderscorePath;
661 if(pszPath != NULL && strlen(pszPath) > 0)
662 {
663 papszTokens = CSLTokenizeString2( pszPath, "/", CSLT_HONOURSTRINGS );
664
665 for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i )
666 {
667 if( i != 0)
668 osKey += '_';
669 osKey += papszTokens[i];
670 }
671 CSLDestroy( papszTokens );
672 }
673
674 // Convert whitespaces into "_" for the attribute name component
675 papszTokens = CSLTokenizeString2( pszAttrName, " ",
676 CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES );
677 for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i )
678 {
679 if(!osKey.empty())
680 osKey += '_';
681 osKey += papszTokens[i];
682 }
683 CSLDestroy( papszTokens );
684
685 hAttrID = H5Aopen_name( hH5ObjID, pszAttrName );
686 hAttrTypeID = H5Aget_type( hAttrID );
687 hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT );
688 hAttrSpace = H5Aget_space( hAttrID );
689 nAttrDims = H5Sget_simple_extent_dims( hAttrSpace, nSize, NULL );
690
691 nAttrElmts = 1;
692 for( i=0; i < nAttrDims; i++ ) {
693 nAttrElmts *= (int) nSize[i];
694 }
695
696 if( H5Tget_class( hAttrNativeType ) == H5T_STRING )
697 {
698 if ( H5Tis_variable_str(hAttrNativeType) )
699 {
700 char** papszStrings;
701 papszStrings = (char**) CPLMalloc( nAttrElmts * sizeof(char*) );
702
703 // Read the values
704 H5Aread( hAttrID, hAttrNativeType, papszStrings );
705
706 // Concatenate all values as one string (separated by a space)
707 CPLString osVal = papszStrings[0];
708 for( i=1; i < nAttrElmts; i++ ) {
709 osVal += " ";
710 osVal += papszStrings[i];
711 }
712
713 szValue = (char*) CPLMalloc(osVal.length() + 1);
714 strcpy( szValue, osVal.c_str() );
715
716 H5Dvlen_reclaim( hAttrNativeType, hAttrSpace, H5P_DEFAULT,
717 papszStrings );
718 CPLFree( papszStrings );
719 }
720 else
721 {
722 nAttrSize = H5Aget_storage_size( hAttrID );
723 szValue = (char*) CPLMalloc((size_t) (nAttrSize+1));
724 H5Aread( hAttrID, hAttrNativeType, szValue );
725 szValue[nAttrSize] = '\0';
726 }
727 }
728 else {
729 if( nAttrElmts > 0 ) {
730 buf = (void *) CPLMalloc( nAttrElmts*
731 H5Tget_size( hAttrNativeType ));
732 szData = (char*) CPLMalloc( 8192 );
733 szValue = (char*) CPLMalloc( MAX_METADATA_LEN );
734 szData[0] = '\0';
735 szValue[0] ='\0';
736 H5Aread( hAttrID, hAttrNativeType, buf );
737 }
738 if( H5Tequal( H5T_NATIVE_CHAR, hAttrNativeType )
739 || H5Tequal( H5T_NATIVE_SCHAR, hAttrNativeType ) ) {
740 for( i=0; i < nAttrElmts; i++ ) {
741 sprintf( szData, "%c ", ((char *) buf)[i]);
742 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
743 MAX_METADATA_LEN )
744 CPLError( CE_Warning, CPLE_OutOfMemory,
745 "Header data too long. Truncated\n");
746 }
747 }
748 else if( H5Tequal( H5T_NATIVE_UCHAR, hAttrNativeType ) ) {
749 for( i=0; i < nAttrElmts; i++ ) {
750 sprintf( szData, "%c", ((char *) buf)[i] );
751 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
752 MAX_METADATA_LEN )
753 CPLError( CE_Warning, CPLE_OutOfMemory,
754 "Header data too long. Truncated\n");
755 }
756 }
757 else if( H5Tequal( H5T_NATIVE_SHORT, hAttrNativeType ) ) {
758 for( i=0; i < nAttrElmts; i++ ) {
759 sprintf( szData, "%d ", ((short *) buf)[i] );
760 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
761 MAX_METADATA_LEN )
762 CPLError( CE_Warning, CPLE_OutOfMemory,
763 "Header data too long. Truncated\n");
764 }
765 }
766 else if( H5Tequal( H5T_NATIVE_USHORT, hAttrNativeType ) ) {
767 for( i=0; i < nAttrElmts; i++ ) {
768 sprintf( szData, "%ud ", ((unsigned short *) buf)[i] );
769 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
770 MAX_METADATA_LEN )
771 CPLError( CE_Warning, CPLE_OutOfMemory,
772 "Header data too long. Truncated\n");
773 }
774 }
775 else if( H5Tequal( H5T_NATIVE_INT, hAttrNativeType ) ) {
776 for( i=0; i < nAttrElmts; i++ ) {
777 sprintf( szData, "%d ", ((int *) buf)[i] );
778 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
779 MAX_METADATA_LEN )
780 CPLError( CE_Warning, CPLE_OutOfMemory,
781 "Header data too long. Truncated\n");
782 }
783 }
784 else if( H5Tequal( H5T_NATIVE_UINT, hAttrNativeType ) ) {
785 for( i=0; i < nAttrElmts; i++ ) {
786 sprintf( szData, "%ud ", ((unsigned int *) buf)[i] );
787 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
788 MAX_METADATA_LEN )
789 CPLError( CE_Warning, CPLE_OutOfMemory,
790 "Header data too long. Truncated\n");
791 }
792 }
793 else if( H5Tequal( H5T_NATIVE_LONG, hAttrNativeType ) ) {
794 for( i=0; i < nAttrElmts; i++ ) {
795 sprintf( szData, "%ld ", ((long *)buf)[i] );
796 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
797 MAX_METADATA_LEN )
798 CPLError( CE_Warning, CPLE_OutOfMemory,
799 "Header data too long. Truncated\n");
800 }
801 }
802 else if( H5Tequal( H5T_NATIVE_ULONG, hAttrNativeType ) ) {
803 for( i=0; i < nAttrElmts; i++ ) {
804 sprintf( szData, "%ld ", ((unsigned long *)buf)[i] );
805 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
806 MAX_METADATA_LEN )
807 CPLError( CE_Warning, CPLE_OutOfMemory,
808 "Header data too long. Truncated\n");
809 }
810 }
811 else if( H5Tequal( H5T_NATIVE_FLOAT, hAttrNativeType ) ) {
812 for( i=0; i < nAttrElmts; i++ ) {
813 CPLsprintf( szData, "%.8g ", ((float *)buf)[i] );
814 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
815 MAX_METADATA_LEN )
816 CPLError( CE_Warning, CPLE_OutOfMemory,
817 "Header data too long. Truncated\n");
818 }
819 }
820 else if( H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) ) {
821 for( i=0; i < nAttrElmts; i++ ) {
822 CPLsprintf( szData, "%.15g ", ((double *)buf)[i] );
823 if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
824 MAX_METADATA_LEN )
825 CPLError( CE_Warning, CPLE_OutOfMemory,
826 "Header data too long. Truncated\n");
827 }
828 }
829 CPLFree( buf );
830
831 }
832 H5Sclose(hAttrSpace);
833 H5Tclose(hAttrNativeType);
834 H5Tclose(hAttrTypeID);
835 H5Aclose( hAttrID );
836 poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, osKey, szValue);
837
838 CPLFree( szData );
839 CPLFree( szValue );
840
841 return 0;
842 }
843
844 /************************************************************************/
845 /* CreateMetadata() */
846 /************************************************************************/
CreateMetadata(HDF5GroupObjects * poH5Object,int nType)847 CPLErr HDF5Dataset::CreateMetadata( HDF5GroupObjects *poH5Object, int nType)
848 {
849 hid_t hGroupID; /* identifier of group */
850 hid_t hDatasetID;
851 int nbAttrs;
852
853 HDF5Dataset *poDS;
854
855 if( !poH5Object->pszPath )
856 return CE_None;
857
858 poDS = this;
859
860 poH5CurrentObject = poH5Object;
861 nbAttrs = poH5Object->nbAttrs;
862
863 if( poH5Object->pszPath == NULL || EQUAL(poH5Object->pszPath, "" ) )
864 return CE_None;
865
866 switch( nType ) {
867
868 case H5G_GROUP:
869
870 if( nbAttrs > 0 ) {
871 hGroupID = H5Gopen( hHDF5, poH5Object->pszPath );
872 H5Aiterate( hGroupID, NULL, HDF5AttrIterate, (void *)poDS );
873 H5Gclose( hGroupID );
874 }
875
876 break;
877
878 case H5G_DATASET:
879
880 if( nbAttrs > 0 ) {
881 hDatasetID = H5Dopen(hHDF5, poH5Object->pszPath );
882 H5Aiterate( hDatasetID, NULL, HDF5AttrIterate, (void *)poDS );
883 H5Dclose( hDatasetID );
884 }
885 break;
886
887 default:
888 break;
889 }
890
891 return CE_None;
892 }
893
894
895 /************************************************************************/
896 /* HDF5FindDatasetObjectsbyPath() */
897 /* Find object by name */
898 /************************************************************************/
HDF5FindDatasetObjectsbyPath(HDF5GroupObjects * poH5Objects,const char * pszDatasetPath)899 HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjectsbyPath
900 ( HDF5GroupObjects *poH5Objects, const char* pszDatasetPath )
901 {
902 unsigned i;
903 HDF5Dataset *poDS;
904 HDF5GroupObjects *poObjectsFound;
905 poDS=this;
906
907 if( poH5Objects->nType == H5G_DATASET &&
908 EQUAL( poH5Objects->pszUnderscorePath,pszDatasetPath ) ) {
909
910 /* printf("found it! %ld\n",(long) poH5Objects);*/
911 return( poH5Objects );
912 }
913
914 if( poH5Objects->nbObjs >0 )
915 for( i=0; i <poH5Objects->nbObjs; i++ ) {
916 poObjectsFound=
917 poDS->HDF5FindDatasetObjectsbyPath( poH5Objects->poHchild+i,
918 pszDatasetPath );
919 /* -------------------------------------------------------------------- */
920 /* Is this our dataset?? */
921 /* -------------------------------------------------------------------- */
922 if( poObjectsFound != NULL ) return( poObjectsFound );
923 }
924 /* -------------------------------------------------------------------- */
925 /* Dataset has not been found! */
926 /* -------------------------------------------------------------------- */
927 return( NULL );
928
929 }
930
931
932 /************************************************************************/
933 /* HDF5FindDatasetObjects() */
934 /* Find object by name */
935 /************************************************************************/
HDF5FindDatasetObjects(HDF5GroupObjects * poH5Objects,const char * pszDatasetName)936 HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjects
937 ( HDF5GroupObjects *poH5Objects, const char* pszDatasetName )
938 {
939 unsigned i;
940 HDF5Dataset *poDS;
941 HDF5GroupObjects *poObjectsFound;
942 poDS=this;
943
944 if( poH5Objects->nType == H5G_DATASET &&
945 EQUAL( poH5Objects->pszName,pszDatasetName ) ) {
946
947 /* printf("found it! %ld\n",(long) poH5Objects);*/
948 return( poH5Objects );
949 }
950
951 if( poH5Objects->nbObjs >0 )
952 for( i=0; i <poH5Objects->nbObjs; i++ ) {
953 poObjectsFound=
954 poDS->HDF5FindDatasetObjects( poH5Objects->poHchild+i,
955 pszDatasetName );
956 /* -------------------------------------------------------------------- */
957 /* Is this our dataset?? */
958 /* -------------------------------------------------------------------- */
959 if( poObjectsFound != NULL ) return( poObjectsFound );
960
961 }
962 /* -------------------------------------------------------------------- */
963 /* Dataset has not been found! */
964 /* -------------------------------------------------------------------- */
965 return( NULL );
966
967 }
968
969
970 /************************************************************************/
971 /* HDF5ListGroupObjects() */
972 /* */
973 /* List all objects in HDF5 */
974 /************************************************************************/
HDF5ListGroupObjects(HDF5GroupObjects * poRootGroup,int bSUBDATASET)975 CPLErr HDF5Dataset::HDF5ListGroupObjects( HDF5GroupObjects *poRootGroup,
976 int bSUBDATASET )
977 {
978 char szTemp[8192];
979 char szDim[8192];
980 HDF5Dataset *poDS;
981 poDS=this;
982
983 if( poRootGroup->nbObjs >0 )
984 for( hsize_t i=0; i < poRootGroup->nbObjs; i++ ) {
985 poDS->HDF5ListGroupObjects( poRootGroup->poHchild+i, bSUBDATASET );
986 }
987
988
989 if( poRootGroup->nType == H5G_GROUP ) {
990 CreateMetadata( poRootGroup, H5G_GROUP );
991 }
992
993 /* -------------------------------------------------------------------- */
994 /* Create Sub dataset list */
995 /* -------------------------------------------------------------------- */
996 if( (poRootGroup->nType == H5G_DATASET ) && bSUBDATASET
997 && poDS->GetDataType( poRootGroup->native ) == GDT_Unknown )
998 {
999 CPLDebug( "HDF5", "Skipping unsupported %s of type %s",
1000 poRootGroup->pszUnderscorePath,
1001 poDS->GetDataTypeName( poRootGroup->native ) );
1002 }
1003 else if( (poRootGroup->nType == H5G_DATASET ) && bSUBDATASET )
1004 {
1005 CreateMetadata( poRootGroup, H5G_DATASET );
1006
1007 szDim[0]='\0';
1008 switch( poRootGroup->nRank ) {
1009 case 3:
1010 sprintf( szTemp,"%dx%dx%d",
1011 (int)poRootGroup->paDims[0],
1012 (int)poRootGroup->paDims[1],
1013 (int)poRootGroup->paDims[2] );
1014 break;
1015
1016 case 2:
1017 sprintf( szTemp,"%dx%d",
1018 (int)poRootGroup->paDims[0],
1019 (int)poRootGroup->paDims[1] );
1020 break;
1021
1022 default:
1023 return CE_None;
1024
1025 }
1026 strcat( szDim,szTemp );
1027
1028 sprintf( szTemp, "SUBDATASET_%d_NAME", ++(poDS->nSubDataCount) );
1029
1030 poDS->papszSubDatasets =
1031 CSLSetNameValue( poDS->papszSubDatasets, szTemp,
1032 CPLSPrintf( "HDF5:\"%s\":%s",
1033 poDS->GetDescription(),
1034 poRootGroup->pszUnderscorePath ) );
1035
1036 sprintf( szTemp, "SUBDATASET_%d_DESC", poDS->nSubDataCount );
1037
1038 poDS->papszSubDatasets =
1039 CSLSetNameValue( poDS->papszSubDatasets, szTemp,
1040 CPLSPrintf( "[%s] %s (%s)",
1041 szDim,
1042 poRootGroup->pszUnderscorePath,
1043 poDS->GetDataTypeName
1044 ( poRootGroup->native ) ) );
1045
1046 }
1047
1048 return CE_None;
1049 }
1050
1051
1052 /************************************************************************/
1053 /* ReadGlobalAttributes() */
1054 /************************************************************************/
ReadGlobalAttributes(int bSUBDATASET)1055 CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET)
1056 {
1057
1058 HDF5GroupObjects *poRootGroup;
1059
1060 poRootGroup = (HDF5GroupObjects*) CPLCalloc(sizeof(HDF5GroupObjects), 1);
1061
1062 poH5RootGroup=poRootGroup;
1063 poRootGroup->pszName = CPLStrdup( "/" );
1064 poRootGroup->nType = H5G_GROUP;
1065 poRootGroup->poHparent = NULL;
1066 poRootGroup->pszPath = NULL;
1067 poRootGroup->pszUnderscorePath = NULL;
1068
1069 if( hHDF5 < 0 ) {
1070 printf( "hHDF5 <0!!\n" );
1071 return CE_None;
1072 }
1073
1074 H5G_stat_t oStatbuf;
1075 if( H5Gget_objinfo( hHDF5, "/", FALSE, &oStatbuf ) < 0 )
1076 return CE_Failure;
1077 poRootGroup->objno[0] = oStatbuf.objno[0];
1078 poRootGroup->objno[1] = oStatbuf.objno[1];
1079
1080 if( hGroupID > 0 )
1081 H5Gclose( hGroupID );
1082 hGroupID = H5Gopen( hHDF5, "/" );
1083 if( hGroupID < 0 ){
1084 printf( "hGroupID <0!!\n" );
1085 return CE_None;
1086 }
1087
1088 poRootGroup->nbAttrs = H5Aget_num_attrs( hGroupID );
1089
1090 H5Gget_num_objs( hGroupID, &( poRootGroup->nbObjs ) );
1091
1092 if( poRootGroup->nbObjs > 0 ) {
1093 poRootGroup->poHchild = ( HDF5GroupObjects * )
1094 CPLCalloc( poRootGroup->nbObjs,
1095 sizeof( HDF5GroupObjects ) );
1096 H5Giterate( hGroupID, "/", NULL,
1097 HDF5CreateGroupObjs, (void *)poRootGroup );
1098 }
1099 else poRootGroup->poHchild = NULL;
1100
1101 HDF5ListGroupObjects( poRootGroup, bSUBDATASET );
1102 return CE_None;
1103 }
1104
1105
1106 /**
1107 * Reads an array of double attributes from the HDF5 metadata.
1108 * It reads the attributes directly on it's binary form directly,
1109 * thus avoiding string conversions.
1110 *
1111 * Important: It allocates the memory for the attributes internally,
1112 * so the caller must free the returned array after using it.
1113 * @param pszAttrName Name of the attribute to be read.
1114 * the attribute name must be the form:
1115 * root attribute name
1116 * SUBDATASET/subdataset attribute name
1117 * @param pdfValues pointer wich will store the array of doubles read.
1118 * @param nLen it stores the length of the array read. If NULL it doesn't
1119 * inform the lenght of the array.
1120 * @return CPLErr CE_None in case of success, CE_Failure in case of failure
1121 */
HDF5ReadDoubleAttr(const char * pszAttrFullPath,double ** pdfValues,int * nLen)1122 CPLErr HDF5Dataset::HDF5ReadDoubleAttr(const char* pszAttrFullPath,
1123 double **pdfValues,int *nLen)
1124 {
1125 CPLErr retVal = CE_Failure;
1126 hid_t hAttrID=-1;
1127 hid_t hAttrTypeID=-1;
1128 hid_t hAttrNativeType=-1;
1129 hid_t hAttrSpace=-1;
1130 hid_t hObjAttrID=-1;
1131
1132 hsize_t nSize[64];
1133 unsigned int nAttrElmts;
1134 hsize_t i;
1135 unsigned int nAttrDims;
1136
1137 size_t nSlashPos;
1138
1139 CPLString osAttrFullPath(pszAttrFullPath);
1140 CPLString osObjName;
1141 CPLString osAttrName;
1142
1143 //Search for the last "/" in order to get the
1144 //Path to the attribute
1145 nSlashPos = osAttrFullPath.find_last_of("/");
1146
1147 //If objects name have been found
1148 if(nSlashPos != CPLString::npos )
1149 {
1150 //Split Object name (dataset, group)
1151 osObjName = osAttrFullPath.substr(0,nSlashPos);
1152 //Split attribute name
1153 osAttrName = osAttrFullPath.substr(nSlashPos+1);
1154 }
1155 else
1156 {
1157 //By default the group is root, and
1158 //the attribute is the full path
1159 osObjName = "/";
1160 osAttrName = pszAttrFullPath;
1161 }
1162
1163 hObjAttrID = H5Oopen( hHDF5, osObjName.c_str(),H5P_DEFAULT);
1164
1165 if(hObjAttrID < 0)
1166 {
1167 CPLError( CE_Failure, CPLE_OpenFailed,
1168 "Object %s could not be opened\n", pszAttrFullPath);
1169 retVal = CE_Failure;
1170 }
1171 else
1172 {
1173 //Open attribute handler by name, from the object handler opened
1174 //earlier
1175 hAttrID = H5Aopen_name( hObjAttrID, osAttrName.c_str());
1176
1177 //Check for errors opening the attribute
1178 if(hAttrID <0)
1179 {
1180 CPLError( CE_Failure, CPLE_OpenFailed,
1181 "Attribute %s could not be opened\n", pszAttrFullPath);
1182 retVal = CE_Failure;
1183 }
1184 else
1185 {
1186 hAttrTypeID = H5Aget_type( hAttrID );
1187 hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT );
1188 hAttrSpace = H5Aget_space( hAttrID );
1189 nAttrDims = H5Sget_simple_extent_dims( hAttrSpace, nSize, NULL );
1190
1191 if( !H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) )
1192 {
1193 CPLError( CE_Failure, CPLE_OpenFailed,
1194 "Attribute %s is not of type double\n", pszAttrFullPath);
1195 retVal = CE_Failure;
1196 }
1197 else
1198 {
1199 //Get the ammount of elements
1200 nAttrElmts = 1;
1201 for( i=0; i < nAttrDims; i++ )
1202 {
1203 //For multidimensional attributes
1204 nAttrElmts *= nSize[i];
1205 }
1206
1207 if(nLen != NULL)
1208 *nLen = nAttrElmts;
1209
1210 (*pdfValues) = (double *) CPLMalloc(nAttrElmts*sizeof(double));
1211
1212 //Read the attribute contents
1213 if(H5Aread( hAttrID, hAttrNativeType, *pdfValues )<0)
1214 {
1215 CPLError( CE_Failure, CPLE_OpenFailed,
1216 "Attribute %s could not be opened\n",
1217 pszAttrFullPath);
1218 retVal = CE_Failure;
1219 }
1220 else
1221 {
1222 retVal = CE_None;
1223 }
1224 }
1225
1226 H5Tclose(hAttrNativeType);
1227 H5Tclose(hAttrTypeID);
1228 H5Sclose(hAttrSpace);
1229 H5Aclose(hAttrID);
1230 }
1231 H5Oclose(hObjAttrID);
1232 }
1233
1234 return retVal;
1235 }
1236