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