ultimatepp/bazaar/plugin/gdal/frmts/hdf5/hdf5dataset.cpp
cxl 23ff1e7e82 .gdal moved to bazaar
git-svn-id: svn://ultimatepp.org/upp/trunk@9273 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2015-12-07 13:36:24 +00:00

1235 lines
45 KiB
C++

/******************************************************************************
* $Id: hdf5dataset.cpp 28822 2015-03-30 13:56:19Z rouault $
*
* Project: Hierarchical Data Format Release 5 (HDF5)
* Purpose: HDF5 Datasets. Open HDF5 file, fetch metadata and list of
* subdatasets.
* This driver initially based on code supplied by Markus Neteler
* Author: Denis Nadeau <denis.nadeau@gmail.com>
*
******************************************************************************
* Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
* Copyright (c) 2008-2013, Even Rouault <even dot rouault at mines-paris dot org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#define H5_USE_16_API
#define MAX_METADATA_LEN 32768
#include "hdf5.h"
#include "gdal_priv.h"
#include "cpl_string.h"
#include "hdf5dataset.h"
CPL_CVSID("$Id: hdf5dataset.cpp 28822 2015-03-30 13:56:19Z rouault $");
CPL_C_START
void GDALRegister_HDF5(void);
CPL_C_END
/************************************************************************/
/* ==================================================================== */
/* HDF5Dataset */
/* ==================================================================== */
/************************************************************************/
/************************************************************************/
/* GDALRegister_HDF5() */
/************************************************************************/
void GDALRegister_HDF5()
{
GDALDriver *poDriver;
if( GDALGetDriverByName("HDF5") == NULL )
{
poDriver = new GDALDriver();
poDriver->SetDescription("HDF5");
poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
poDriver->SetMetadataItem(GDAL_DMD_LONGNAME,
"Hierarchical Data Format Release 5");
poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
"frmt_hdf5.html");
poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "hdf5");
poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
poDriver->pfnOpen = HDF5Dataset::Open;
poDriver->pfnIdentify = HDF5Dataset::Identify;
GetGDALDriverManager()->RegisterDriver(poDriver);
}
#ifdef HDF5_PLUGIN
GDALRegister_HDF5Image();
#endif
}
/************************************************************************/
/* HDF5Dataset() */
/************************************************************************/
HDF5Dataset::HDF5Dataset()
{
papszSubDatasets = NULL;
papszMetadata = NULL;
poH5RootGroup = NULL;
nSubDataCount = 0;
hHDF5 = -1;
hGroupID = -1;
bIsHDFEOS = FALSE;
nDatasetType = -1;
}
/************************************************************************/
/* ~HDF5Dataset() */
/************************************************************************/
HDF5Dataset::~HDF5Dataset()
{
CSLDestroy( papszMetadata );
if( hGroupID > 0 )
H5Gclose( hGroupID );
if( hHDF5 > 0 )
H5Fclose( hHDF5 );
CSLDestroy( papszSubDatasets );
if( poH5RootGroup != NULL ) {
DestroyH5Objects( poH5RootGroup );
CPLFree( poH5RootGroup->pszName );
CPLFree( poH5RootGroup->pszPath );
CPLFree( poH5RootGroup->pszUnderscorePath );
CPLFree( poH5RootGroup->poHchild );
CPLFree( poH5RootGroup );
}
}
/************************************************************************/
/* GetDataType() */
/* */
/* Transform HDF5 datatype to GDAL datatype */
/************************************************************************/
GDALDataType HDF5Dataset::GetDataType(hid_t TypeID)
{
if( H5Tequal( H5T_NATIVE_CHAR, TypeID ) )
return GDT_Byte;
else if( H5Tequal( H5T_NATIVE_SCHAR, TypeID ) )
return GDT_Byte;
else if( H5Tequal( H5T_NATIVE_UCHAR, TypeID ) )
return GDT_Byte;
else if( H5Tequal( H5T_NATIVE_SHORT, TypeID ) )
return GDT_Int16;
else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) )
return GDT_UInt16;
else if( H5Tequal( H5T_NATIVE_INT, TypeID ) )
return GDT_Int32;
else if( H5Tequal( H5T_NATIVE_UINT, TypeID ) )
return GDT_UInt32;
else if( H5Tequal( H5T_NATIVE_LONG, TypeID ) )
{
if( sizeof(long) == 4 )
return GDT_Int32;
else
return GDT_Unknown;
}
else if( H5Tequal( H5T_NATIVE_ULONG, TypeID ) )
{
if( sizeof(unsigned long) == 4 )
return GDT_UInt32;
else
return GDT_Unknown;
}
else if( H5Tequal( H5T_NATIVE_FLOAT, TypeID ) )
return GDT_Float32;
else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
return GDT_Float64;
else if( H5Tequal( H5T_NATIVE_LLONG, TypeID ) )
return GDT_Unknown;
else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) )
return GDT_Unknown;
else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
return GDT_Unknown;
return GDT_Unknown;
}
/************************************************************************/
/* GetDataTypeName() */
/* */
/* Return the human readable name of data type */
/************************************************************************/
const char *HDF5Dataset::GetDataTypeName(hid_t TypeID)
{
if( H5Tequal( H5T_NATIVE_CHAR, TypeID ) )
return "8-bit character";
else if( H5Tequal( H5T_NATIVE_SCHAR, TypeID ) )
return "8-bit signed character";
else if( H5Tequal( H5T_NATIVE_UCHAR, TypeID ) )
return "8-bit unsigned character";
else if( H5Tequal( H5T_NATIVE_SHORT, TypeID ) )
return "16-bit integer";
else if( H5Tequal( H5T_NATIVE_USHORT, TypeID ) )
return "16-bit unsigned integer";
else if( H5Tequal( H5T_NATIVE_INT, TypeID ) )
return "32-bit integer";
else if( H5Tequal( H5T_NATIVE_UINT, TypeID ) )
return "32-bit unsigned integer";
else if( H5Tequal( H5T_NATIVE_LONG, TypeID ) )
return "32/64-bit integer";
else if( H5Tequal( H5T_NATIVE_ULONG, TypeID ) )
return "32/64-bit unsigned integer";
else if( H5Tequal( H5T_NATIVE_FLOAT, TypeID ) )
return "32-bit floating-point";
else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
return "64-bit floating-point";
else if( H5Tequal( H5T_NATIVE_LLONG, TypeID ) )
return "64-bit integer";
else if( H5Tequal( H5T_NATIVE_ULLONG, TypeID ) )
return "64-bit unsigned integer";
else if( H5Tequal( H5T_NATIVE_DOUBLE, TypeID ) )
return "64-bit floating-point";
return "Unknown";
}
/************************************************************************/
/* Identify() */
/************************************************************************/
int HDF5Dataset::Identify( GDALOpenInfo * poOpenInfo )
{
/* -------------------------------------------------------------------- */
/* Is it an HDF5 file? */
/* -------------------------------------------------------------------- */
static const char achSignature[] = "\211HDF\r\n\032\n";
if( poOpenInfo->pabyHeader )
{
if( memcmp(poOpenInfo->pabyHeader,achSignature,8) == 0 )
{
/* The tests to avoid opening KEA and BAG drivers are not */
/* necessary when drivers are built in the core lib, as they */
/* are registered after HDF5, but in the case of plugins, we */
/* cannot do assumptions about the registration order */
/* Avoid opening kea files if the kea driver is available */
if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "KEA") &&
GDALGetDriverByName("KEA") != NULL )
{
return FALSE;
}
/* Avoid opening kea files if the bag driver is available */
if( EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "BAG") &&
GDALGetDriverByName("BAG") != NULL )
{
return FALSE;
}
return TRUE;
}
if( memcmp(poOpenInfo->pabyHeader,"<HDF_UserBlock>",15) == 0)
{
if( H5Fis_hdf5(poOpenInfo->pszFilename) )
return TRUE;
}
}
return FALSE;
}
/************************************************************************/
/* Open() */
/************************************************************************/
GDALDataset *HDF5Dataset::Open( GDALOpenInfo * poOpenInfo )
{
HDF5Dataset *poDS;
if( !Identify( poOpenInfo ) )
return NULL;
/* -------------------------------------------------------------------- */
/* Create datasource. */
/* -------------------------------------------------------------------- */
poDS = new HDF5Dataset();
poDS->SetDescription( poOpenInfo->pszFilename );
/* -------------------------------------------------------------------- */
/* Try opening the dataset. */
/* -------------------------------------------------------------------- */
poDS->hHDF5 = H5Fopen( poOpenInfo->pszFilename,
H5F_ACC_RDONLY,
H5P_DEFAULT );
if( poDS->hHDF5 < 0 ) {
delete poDS;
return NULL;
}
poDS->hGroupID = H5Gopen( poDS->hHDF5, "/" );
if( poDS->hGroupID < 0 ) {
poDS->bIsHDFEOS=false;
delete poDS;
return NULL;
}
poDS->bIsHDFEOS=true;
poDS->ReadGlobalAttributes( true );
poDS->SetMetadata( poDS->papszMetadata );
if ( CSLCount( poDS->papszSubDatasets ) / 2 >= 1 )
poDS->SetMetadata( poDS->papszSubDatasets, "SUBDATASETS" );
// Make sure we don't try to do any pam stuff with this dataset.
poDS->nPamFlags |= GPF_NOSAVE;
/* -------------------------------------------------------------------- */
/* If we have single subdataset only, open it immediately */
/* -------------------------------------------------------------------- */
int nSubDatasets = CSLCount( poDS->papszSubDatasets ) / 2;
if( nSubDatasets == 1 )
{
CPLString osDSName = CSLFetchNameValue( poDS->papszSubDatasets,
"SUBDATASET_1_NAME" );
delete poDS;
return (GDALDataset *) GDALOpen( osDSName, poOpenInfo->eAccess );
}
else
{
/* -------------------------------------------------------------------- */
/* Confirm the requested access is supported. */
/* -------------------------------------------------------------------- */
if( poOpenInfo->eAccess == GA_Update )
{
delete poDS;
CPLError( CE_Failure, CPLE_NotSupported,
"The HDF5 driver does not support update access to existing"
" datasets.\n" );
return NULL;
}
}
return( poDS );
}
/************************************************************************/
/* DestroyH5Objects() */
/* */
/* Erase all objects */
/************************************************************************/
void HDF5Dataset::DestroyH5Objects( HDF5GroupObjects *poH5Object )
{
unsigned i;
/* -------------------------------------------------------------------- */
/* Visit all objects */
/* -------------------------------------------------------------------- */
for( i=0; i < poH5Object->nbObjs; i++ )
if( poH5Object->poHchild+i != NULL )
DestroyH5Objects( poH5Object->poHchild+i );
if( poH5Object->poHparent ==NULL )
return;
/* -------------------------------------------------------------------- */
/* Erase some data */
/* -------------------------------------------------------------------- */
CPLFree( poH5Object->paDims );
poH5Object->paDims = NULL;
CPLFree( poH5Object->pszPath );
poH5Object->pszPath = NULL;
CPLFree( poH5Object->pszName );
poH5Object->pszName = NULL;
CPLFree( poH5Object->pszUnderscorePath );
poH5Object->pszUnderscorePath = NULL;
if( poH5Object->native > 0 )
H5Tclose( poH5Object->native );
poH5Object->native = 0;
/* -------------------------------------------------------------------- */
/* All Children are visited and can be deleted. */
/* -------------------------------------------------------------------- */
if( ( i==poH5Object->nbObjs ) && ( poH5Object->nbObjs!=0 ) ) {
CPLFree( poH5Object->poHchild );
poH5Object->poHchild = NULL;
}
}
/************************************************************************/
/* CreatePath() */
/* */
/* Find Dataset path for HDopen */
/************************************************************************/
char* CreatePath( HDF5GroupObjects *poH5Object )
{
char pszPath[8192];
char pszUnderscoreSpaceInName[8192];
char *popszPath;
int i;
char **papszPath;
/* -------------------------------------------------------------------- */
/* Recurse to the root path */
/* -------------------------------------------------------------------- */
pszPath[0]='\0';
if( poH5Object->poHparent !=NULL ) {
popszPath=CreatePath( poH5Object->poHparent );
strcpy( pszPath,popszPath );
}
/* -------------------------------------------------------------------- */
/* add name to the path */
/* -------------------------------------------------------------------- */
if( !EQUAL( poH5Object->pszName,"/" ) ){
strcat( pszPath,"/" );
strcat( pszPath,poH5Object->pszName );
}
/* -------------------------------------------------------------------- */
/* fill up path for each object */
/* -------------------------------------------------------------------- */
if( poH5Object->pszPath == NULL ) {
if( strlen( poH5Object->pszName ) == 1 ) {
strcat(pszPath, poH5Object->pszName );
strcpy(pszUnderscoreSpaceInName, poH5Object->pszName);
}
else {
/* -------------------------------------------------------------------- */
/* Change space for underscore */
/* -------------------------------------------------------------------- */
papszPath = CSLTokenizeString2( pszPath,
" ", CSLT_HONOURSTRINGS );
strcpy(pszUnderscoreSpaceInName,papszPath[0]);
for( i=1; i < CSLCount( papszPath ); i++ ) {
strcat( pszUnderscoreSpaceInName, "_" );
strcat( pszUnderscoreSpaceInName, papszPath[ i ] );
}
CSLDestroy(papszPath);
}
poH5Object->pszUnderscorePath =
CPLStrdup( pszUnderscoreSpaceInName );
poH5Object->pszPath = CPLStrdup( pszPath );
}
return( poH5Object->pszPath );
}
/************************************************************************/
/* HDF5GroupCheckDuplicate() */
/* */
/* Returns TRUE if an ancestor has the same objno[] as passed */
/* in - used to avoid looping in files with "links up" #(3218). */
/************************************************************************/
static int HDF5GroupCheckDuplicate( HDF5GroupObjects *poHparent,
unsigned long *objno )
{
while( poHparent != NULL )
{
if( poHparent->objno[0] == objno[0]
&& poHparent->objno[1] == objno[1] )
return TRUE;
poHparent = poHparent->poHparent;
}
return FALSE;
}
/************************************************************************/
/* HDF5CreateGroupObjs() */
/* */
/* Create HDF5 hierarchy into a linked list */
/************************************************************************/
herr_t HDF5CreateGroupObjs(hid_t hHDF5, const char *pszObjName,
void *poHObjParent)
{
hid_t hGroupID; /* identifier of group */
hid_t hDatasetID; /* identifier of dataset */
hsize_t nbObjs=0; /* number of objects in a group */
int nbAttrs=0; /* number of attributes in object */
unsigned idx;
int n_dims;
H5G_stat_t oStatbuf;
hsize_t *dims=NULL;
hsize_t *maxdims=NULL;
hid_t datatype;
hid_t dataspace;
hid_t native;
HDF5GroupObjects *poHchild;
HDF5GroupObjects *poHparent;
poHparent = ( HDF5GroupObjects * ) poHObjParent;
poHchild=poHparent->poHchild;
if( H5Gget_objinfo( hHDF5, pszObjName, FALSE, &oStatbuf ) < 0 )
return -1;
/* -------------------------------------------------------------------- */
/* Look for next child */
/* -------------------------------------------------------------------- */
for( idx=0; idx < poHparent->nbObjs; idx++ ) {
if( poHchild->pszName == NULL ) break;
poHchild++;
}
if( idx == poHparent->nbObjs )
return -1; // all children parsed
/* -------------------------------------------------------------------- */
/* Save child information */
/* -------------------------------------------------------------------- */
poHchild->pszName = CPLStrdup( pszObjName );
poHchild->nType = oStatbuf.type;
poHchild->nIndex = idx;
poHchild->poHparent = poHparent;
poHchild->nRank = 0;
poHchild->paDims = 0;
poHchild->HDatatype = 0;
poHchild->objno[0] = oStatbuf.objno[0];
poHchild->objno[1] = oStatbuf.objno[1];
if( poHchild->pszPath == NULL ) {
poHchild->pszPath = CreatePath( poHchild );
}
if( poHparent->pszPath == NULL ) {
poHparent->pszPath = CreatePath( poHparent );
}
switch ( oStatbuf.type )
{
case H5G_LINK:
poHchild->nbAttrs = 0;
poHchild->nbObjs = 0;
poHchild->poHchild = NULL;
poHchild->nRank = 0;
poHchild->paDims = 0;
poHchild->HDatatype = 0;
break;
case H5G_GROUP:
if( ( hGroupID = H5Gopen( hHDF5, pszObjName ) ) == -1 ) {
printf( "Error: unable to access \"%s\" group.\n",
pszObjName );
return -1;
}
nbAttrs = H5Aget_num_attrs( hGroupID );
H5Gget_num_objs( hGroupID, &nbObjs );
poHchild->nbAttrs= nbAttrs;
poHchild->nbObjs = (int) nbObjs;
poHchild->nRank = 0;
poHchild->paDims = 0;
poHchild->HDatatype = 0;
if( nbObjs > 0 ) {
poHchild->poHchild =( HDF5GroupObjects * )
CPLCalloc( (int)nbObjs, sizeof( HDF5GroupObjects ) );
memset( poHchild->poHchild, 0,
(size_t) (sizeof( HDF5GroupObjects ) * nbObjs) );
}
else
poHchild->poHchild = NULL;
if( !HDF5GroupCheckDuplicate( poHparent, oStatbuf.objno ) )
H5Giterate( hHDF5, pszObjName, NULL,
HDF5CreateGroupObjs, (void*) poHchild );
else
CPLDebug( "HDF5", "avoiding link looping on node '%s'.",
pszObjName );
H5Gclose( hGroupID );
break;
case H5G_DATASET:
if( ( hDatasetID = H5Dopen( hHDF5, pszObjName ) ) == -1 ) {
printf( "Error: unable to access \"%s\" dataset.\n",
pszObjName );
return -1;
}
nbAttrs = H5Aget_num_attrs( hDatasetID );
datatype = H5Dget_type( hDatasetID );
dataspace = H5Dget_space( hDatasetID );
n_dims = H5Sget_simple_extent_ndims( dataspace );
native = H5Tget_native_type( datatype, H5T_DIR_ASCEND );
if( n_dims > 0 ) {
dims = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) );
maxdims = (hsize_t *) CPLCalloc( n_dims,sizeof( hsize_t ) );
}
H5Sget_simple_extent_dims( dataspace, dims, maxdims );
if( maxdims != NULL )
CPLFree( maxdims );
if( n_dims > 0 ) {
poHchild->nRank = n_dims; // rank of the array
poHchild->paDims = dims; // dimmension of the array.
poHchild->HDatatype = datatype; // HDF5 datatype
}
else {
poHchild->nRank = -1;
poHchild->paDims = NULL;
poHchild->HDatatype = 0;
}
poHchild->nbAttrs = nbAttrs;
poHchild->nbObjs = 0;
poHchild->poHchild = NULL;
poHchild->native = native;
H5Tclose( datatype );
H5Sclose( dataspace );
H5Dclose( hDatasetID );
break;
case H5G_TYPE:
poHchild->nbAttrs = 0;
poHchild->nbObjs = 0;
poHchild->poHchild = NULL;
poHchild->nRank = 0;
poHchild->paDims = 0;
poHchild->HDatatype = 0;
break;
default:
break;
}
return 0;
}
/************************************************************************/
/* HDF5AttrIterate() */
/************************************************************************/
herr_t HDF5AttrIterate( hid_t hH5ObjID,
const char *pszAttrName,
void *pDS )
{
hid_t hAttrID;
hid_t hAttrTypeID;
hid_t hAttrNativeType;
hid_t hAttrSpace;
char *szData = NULL;
hsize_t nSize[64];
unsigned int nAttrElmts;
hsize_t nAttrSize;
hsize_t i;
void *buf = NULL;
unsigned int nAttrDims;
char **papszTokens;
HDF5Dataset *poDS;
CPLString osKey;
char *szValue = NULL;
poDS = (HDF5Dataset *) pDS;
// Convert "/" into "_" for the path component
const char* pszPath = poDS->poH5CurrentObject->pszUnderscorePath;
if(pszPath != NULL && strlen(pszPath) > 0)
{
papszTokens = CSLTokenizeString2( pszPath, "/", CSLT_HONOURSTRINGS );
for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i )
{
if( i != 0)
osKey += '_';
osKey += papszTokens[i];
}
CSLDestroy( papszTokens );
}
// Convert whitespaces into "_" for the attribute name component
papszTokens = CSLTokenizeString2( pszAttrName, " ",
CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES );
for( i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i )
{
if(!osKey.empty())
osKey += '_';
osKey += papszTokens[i];
}
CSLDestroy( papszTokens );
hAttrID = H5Aopen_name( hH5ObjID, pszAttrName );
hAttrTypeID = H5Aget_type( hAttrID );
hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT );
hAttrSpace = H5Aget_space( hAttrID );
nAttrDims = H5Sget_simple_extent_dims( hAttrSpace, nSize, NULL );
nAttrElmts = 1;
for( i=0; i < nAttrDims; i++ ) {
nAttrElmts *= (int) nSize[i];
}
if( H5Tget_class( hAttrNativeType ) == H5T_STRING )
{
if ( H5Tis_variable_str(hAttrNativeType) )
{
char** papszStrings;
papszStrings = (char**) CPLMalloc( nAttrElmts * sizeof(char*) );
// Read the values
H5Aread( hAttrID, hAttrNativeType, papszStrings );
// Concatenate all values as one string (separated by a space)
CPLString osVal = papszStrings[0];
for( i=1; i < nAttrElmts; i++ ) {
osVal += " ";
osVal += papszStrings[i];
}
szValue = (char*) CPLMalloc(osVal.length() + 1);
strcpy( szValue, osVal.c_str() );
H5Dvlen_reclaim( hAttrNativeType, hAttrSpace, H5P_DEFAULT,
papszStrings );
CPLFree( papszStrings );
}
else
{
nAttrSize = H5Aget_storage_size( hAttrID );
szValue = (char*) CPLMalloc((size_t) (nAttrSize+1));
H5Aread( hAttrID, hAttrNativeType, szValue );
szValue[nAttrSize] = '\0';
}
}
else {
if( nAttrElmts > 0 ) {
buf = (void *) CPLMalloc( nAttrElmts*
H5Tget_size( hAttrNativeType ));
szData = (char*) CPLMalloc( 8192 );
szValue = (char*) CPLMalloc( MAX_METADATA_LEN );
szData[0] = '\0';
szValue[0] ='\0';
H5Aread( hAttrID, hAttrNativeType, buf );
}
if( H5Tequal( H5T_NATIVE_CHAR, hAttrNativeType )
|| H5Tequal( H5T_NATIVE_SCHAR, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%c ", ((char *) buf)[i]);
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_UCHAR, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%c", ((char *) buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_SHORT, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%d ", ((short *) buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_USHORT, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%ud ", ((unsigned short *) buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_INT, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%d ", ((int *) buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_UINT, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%ud ", ((unsigned int *) buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_LONG, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%ld ", ((long *)buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_ULONG, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
sprintf( szData, "%ld ", ((unsigned long *)buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_FLOAT, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
CPLsprintf( szData, "%.8g ", ((float *)buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
else if( H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) ) {
for( i=0; i < nAttrElmts; i++ ) {
CPLsprintf( szData, "%.15g ", ((double *)buf)[i] );
if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >=
MAX_METADATA_LEN )
CPLError( CE_Warning, CPLE_OutOfMemory,
"Header data too long. Truncated\n");
}
}
CPLFree( buf );
}
H5Sclose(hAttrSpace);
H5Tclose(hAttrNativeType);
H5Tclose(hAttrTypeID);
H5Aclose( hAttrID );
poDS->papszMetadata = CSLSetNameValue( poDS->papszMetadata, osKey, szValue);
CPLFree( szData );
CPLFree( szValue );
return 0;
}
/************************************************************************/
/* CreateMetadata() */
/************************************************************************/
CPLErr HDF5Dataset::CreateMetadata( HDF5GroupObjects *poH5Object, int nType)
{
hid_t hGroupID; /* identifier of group */
hid_t hDatasetID;
int nbAttrs;
HDF5Dataset *poDS;
if( !poH5Object->pszPath )
return CE_None;
poDS = this;
poH5CurrentObject = poH5Object;
nbAttrs = poH5Object->nbAttrs;
if( poH5Object->pszPath == NULL || EQUAL(poH5Object->pszPath, "" ) )
return CE_None;
switch( nType ) {
case H5G_GROUP:
if( nbAttrs > 0 ) {
hGroupID = H5Gopen( hHDF5, poH5Object->pszPath );
H5Aiterate( hGroupID, NULL, HDF5AttrIterate, (void *)poDS );
H5Gclose( hGroupID );
}
break;
case H5G_DATASET:
if( nbAttrs > 0 ) {
hDatasetID = H5Dopen(hHDF5, poH5Object->pszPath );
H5Aiterate( hDatasetID, NULL, HDF5AttrIterate, (void *)poDS );
H5Dclose( hDatasetID );
}
break;
default:
break;
}
return CE_None;
}
/************************************************************************/
/* HDF5FindDatasetObjectsbyPath() */
/* Find object by name */
/************************************************************************/
HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjectsbyPath
( HDF5GroupObjects *poH5Objects, const char* pszDatasetPath )
{
unsigned i;
HDF5Dataset *poDS;
HDF5GroupObjects *poObjectsFound;
poDS=this;
if( poH5Objects->nType == H5G_DATASET &&
EQUAL( poH5Objects->pszUnderscorePath,pszDatasetPath ) ) {
/* printf("found it! %ld\n",(long) poH5Objects);*/
return( poH5Objects );
}
if( poH5Objects->nbObjs >0 )
for( i=0; i <poH5Objects->nbObjs; i++ ) {
poObjectsFound=
poDS->HDF5FindDatasetObjectsbyPath( poH5Objects->poHchild+i,
pszDatasetPath );
/* -------------------------------------------------------------------- */
/* Is this our dataset?? */
/* -------------------------------------------------------------------- */
if( poObjectsFound != NULL ) return( poObjectsFound );
}
/* -------------------------------------------------------------------- */
/* Dataset has not been found! */
/* -------------------------------------------------------------------- */
return( NULL );
}
/************************************************************************/
/* HDF5FindDatasetObjects() */
/* Find object by name */
/************************************************************************/
HDF5GroupObjects* HDF5Dataset::HDF5FindDatasetObjects
( HDF5GroupObjects *poH5Objects, const char* pszDatasetName )
{
unsigned i;
HDF5Dataset *poDS;
HDF5GroupObjects *poObjectsFound;
poDS=this;
if( poH5Objects->nType == H5G_DATASET &&
EQUAL( poH5Objects->pszName,pszDatasetName ) ) {
/* printf("found it! %ld\n",(long) poH5Objects);*/
return( poH5Objects );
}
if( poH5Objects->nbObjs >0 )
for( i=0; i <poH5Objects->nbObjs; i++ ) {
poObjectsFound=
poDS->HDF5FindDatasetObjects( poH5Objects->poHchild+i,
pszDatasetName );
/* -------------------------------------------------------------------- */
/* Is this our dataset?? */
/* -------------------------------------------------------------------- */
if( poObjectsFound != NULL ) return( poObjectsFound );
}
/* -------------------------------------------------------------------- */
/* Dataset has not been found! */
/* -------------------------------------------------------------------- */
return( NULL );
}
/************************************************************************/
/* HDF5ListGroupObjects() */
/* */
/* List all objects in HDF5 */
/************************************************************************/
CPLErr HDF5Dataset::HDF5ListGroupObjects( HDF5GroupObjects *poRootGroup,
int bSUBDATASET )
{
char szTemp[8192];
char szDim[8192];
HDF5Dataset *poDS;
poDS=this;
if( poRootGroup->nbObjs >0 )
for( hsize_t i=0; i < poRootGroup->nbObjs; i++ ) {
poDS->HDF5ListGroupObjects( poRootGroup->poHchild+i, bSUBDATASET );
}
if( poRootGroup->nType == H5G_GROUP ) {
CreateMetadata( poRootGroup, H5G_GROUP );
}
/* -------------------------------------------------------------------- */
/* Create Sub dataset list */
/* -------------------------------------------------------------------- */
if( (poRootGroup->nType == H5G_DATASET ) && bSUBDATASET
&& poDS->GetDataType( poRootGroup->native ) == GDT_Unknown )
{
CPLDebug( "HDF5", "Skipping unsupported %s of type %s",
poRootGroup->pszUnderscorePath,
poDS->GetDataTypeName( poRootGroup->native ) );
}
else if( (poRootGroup->nType == H5G_DATASET ) && bSUBDATASET )
{
CreateMetadata( poRootGroup, H5G_DATASET );
szDim[0]='\0';
switch( poRootGroup->nRank ) {
case 3:
sprintf( szTemp,"%dx%dx%d",
(int)poRootGroup->paDims[0],
(int)poRootGroup->paDims[1],
(int)poRootGroup->paDims[2] );
break;
case 2:
sprintf( szTemp,"%dx%d",
(int)poRootGroup->paDims[0],
(int)poRootGroup->paDims[1] );
break;
default:
return CE_None;
}
strcat( szDim,szTemp );
sprintf( szTemp, "SUBDATASET_%d_NAME", ++(poDS->nSubDataCount) );
poDS->papszSubDatasets =
CSLSetNameValue( poDS->papszSubDatasets, szTemp,
CPLSPrintf( "HDF5:\"%s\":%s",
poDS->GetDescription(),
poRootGroup->pszUnderscorePath ) );
sprintf( szTemp, "SUBDATASET_%d_DESC", poDS->nSubDataCount );
poDS->papszSubDatasets =
CSLSetNameValue( poDS->papszSubDatasets, szTemp,
CPLSPrintf( "[%s] %s (%s)",
szDim,
poRootGroup->pszUnderscorePath,
poDS->GetDataTypeName
( poRootGroup->native ) ) );
}
return CE_None;
}
/************************************************************************/
/* ReadGlobalAttributes() */
/************************************************************************/
CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET)
{
HDF5GroupObjects *poRootGroup;
poRootGroup = (HDF5GroupObjects*) CPLCalloc(sizeof(HDF5GroupObjects), 1);
poH5RootGroup=poRootGroup;
poRootGroup->pszName = CPLStrdup( "/" );
poRootGroup->nType = H5G_GROUP;
poRootGroup->poHparent = NULL;
poRootGroup->pszPath = NULL;
poRootGroup->pszUnderscorePath = NULL;
if( hHDF5 < 0 ) {
printf( "hHDF5 <0!!\n" );
return CE_None;
}
H5G_stat_t oStatbuf;
if( H5Gget_objinfo( hHDF5, "/", FALSE, &oStatbuf ) < 0 )
return CE_Failure;
poRootGroup->objno[0] = oStatbuf.objno[0];
poRootGroup->objno[1] = oStatbuf.objno[1];
if( hGroupID > 0 )
H5Gclose( hGroupID );
hGroupID = H5Gopen( hHDF5, "/" );
if( hGroupID < 0 ){
printf( "hGroupID <0!!\n" );
return CE_None;
}
poRootGroup->nbAttrs = H5Aget_num_attrs( hGroupID );
H5Gget_num_objs( hGroupID, &( poRootGroup->nbObjs ) );
if( poRootGroup->nbObjs > 0 ) {
poRootGroup->poHchild = ( HDF5GroupObjects * )
CPLCalloc( poRootGroup->nbObjs,
sizeof( HDF5GroupObjects ) );
H5Giterate( hGroupID, "/", NULL,
HDF5CreateGroupObjs, (void *)poRootGroup );
}
else poRootGroup->poHchild = NULL;
HDF5ListGroupObjects( poRootGroup, bSUBDATASET );
return CE_None;
}
/**
* Reads an array of double attributes from the HDF5 metadata.
* It reads the attributes directly on it's binary form directly,
* thus avoiding string conversions.
*
* Important: It allocates the memory for the attributes internally,
* so the caller must free the returned array after using it.
* @param pszAttrName Name of the attribute to be read.
* the attribute name must be the form:
* root attribute name
* SUBDATASET/subdataset attribute name
* @param pdfValues pointer wich will store the array of doubles read.
* @param nLen it stores the length of the array read. If NULL it doesn't
* inform the lenght of the array.
* @return CPLErr CE_None in case of success, CE_Failure in case of failure
*/
CPLErr HDF5Dataset::HDF5ReadDoubleAttr(const char* pszAttrFullPath,
double **pdfValues,int *nLen)
{
CPLErr retVal = CE_Failure;
hid_t hAttrID=-1;
hid_t hAttrTypeID=-1;
hid_t hAttrNativeType=-1;
hid_t hAttrSpace=-1;
hid_t hObjAttrID=-1;
hsize_t nSize[64];
unsigned int nAttrElmts;
hsize_t i;
unsigned int nAttrDims;
size_t nSlashPos;
CPLString osAttrFullPath(pszAttrFullPath);
CPLString osObjName;
CPLString osAttrName;
//Search for the last "/" in order to get the
//Path to the attribute
nSlashPos = osAttrFullPath.find_last_of("/");
//If objects name have been found
if(nSlashPos != CPLString::npos )
{
//Split Object name (dataset, group)
osObjName = osAttrFullPath.substr(0,nSlashPos);
//Split attribute name
osAttrName = osAttrFullPath.substr(nSlashPos+1);
}
else
{
//By default the group is root, and
//the attribute is the full path
osObjName = "/";
osAttrName = pszAttrFullPath;
}
hObjAttrID = H5Oopen( hHDF5, osObjName.c_str(),H5P_DEFAULT);
if(hObjAttrID < 0)
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Object %s could not be opened\n", pszAttrFullPath);
retVal = CE_Failure;
}
else
{
//Open attribute handler by name, from the object handler opened
//earlier
hAttrID = H5Aopen_name( hObjAttrID, osAttrName.c_str());
//Check for errors opening the attribute
if(hAttrID <0)
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Attribute %s could not be opened\n", pszAttrFullPath);
retVal = CE_Failure;
}
else
{
hAttrTypeID = H5Aget_type( hAttrID );
hAttrNativeType = H5Tget_native_type( hAttrTypeID, H5T_DIR_DEFAULT );
hAttrSpace = H5Aget_space( hAttrID );
nAttrDims = H5Sget_simple_extent_dims( hAttrSpace, nSize, NULL );
if( !H5Tequal( H5T_NATIVE_DOUBLE, hAttrNativeType ) )
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Attribute %s is not of type double\n", pszAttrFullPath);
retVal = CE_Failure;
}
else
{
//Get the ammount of elements
nAttrElmts = 1;
for( i=0; i < nAttrDims; i++ )
{
//For multidimensional attributes
nAttrElmts *= nSize[i];
}
if(nLen != NULL)
*nLen = nAttrElmts;
(*pdfValues) = (double *) CPLMalloc(nAttrElmts*sizeof(double));
//Read the attribute contents
if(H5Aread( hAttrID, hAttrNativeType, *pdfValues )<0)
{
CPLError( CE_Failure, CPLE_OpenFailed,
"Attribute %s could not be opened\n",
pszAttrFullPath);
retVal = CE_Failure;
}
else
{
retVal = CE_None;
}
}
H5Tclose(hAttrNativeType);
H5Tclose(hAttrTypeID);
H5Sclose(hAttrSpace);
H5Aclose(hAttrID);
}
H5Oclose(hObjAttrID);
}
return retVal;
}