EVOLUTION-MANAGER
Edit File: hdf5dataset.cpp
/****************************************************************************** * * 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. ****************************************************************************/ #include "cpl_port.h" #define H5_USE_16_API #ifdef _MSC_VER #pragma warning(push) // Warning C4005: '_HDF5USEDLL_' : macro redefinition. #pragma warning(disable : 4005) #endif #include "hdf5.h" #include "hdf5dataset.h" #ifdef _MSC_VER #pragma warning(pop) #endif #include <stdio.h> #include <string.h> #include <string> #include "cpl_conv.h" #include "cpl_error.h" #include "cpl_string.h" #include "gdal.h" #include "gdal_frmts.h" #include "gdal_priv.h" # CPL_CVSID("$Id: hdf5dataset.cpp 37930 2017-04-09 06:15:28Z goatbar $"); static const size_t MAX_METADATA_LEN = 32768; /************************************************************************/ /* ==================================================================== */ /* HDF5Dataset */ /* ==================================================================== */ /************************************************************************/ /************************************************************************/ /* GDALRegister_HDF5() */ /************************************************************************/ void GDALRegister_HDF5() { if( GDALGetDriverByName("HDF5") != NULL ) return; GDALDriver *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() : hHDF5(-1), hGroupID(-1), papszSubDatasets(NULL), bIsHDFEOS(FALSE), nDatasetType(-1), nSubDataCount(0), poH5RootGroup(NULL), papszMetadata(NULL), poH5CurrentObject(NULL) {} /************************************************************************/ /* ~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 ) return FALSE; if( memcmp(poOpenInfo->pabyHeader, achSignature, 8) == 0 ) { CPLString osExt(CPLGetExtension(poOpenInfo->pszFilename)); // 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(osExt, "KEA") && GDALGetDriverByName("KEA") != NULL ) { return FALSE; } // Avoid opening BAG files if the bag driver is available. if( EQUAL(osExt, "BAG") && GDALGetDriverByName("BAG") != NULL ) { return FALSE; } // Avoid opening NC files if the netCDF driver is available and // they are recognized by it. if( (EQUAL(osExt, "NC") || EQUAL(osExt, "CDF") || EQUAL(osExt, "NC4")) && GDALGetDriverByName("netCDF") != NULL ) { const char *const apszAllowedDriver[] = { "netCDF", NULL }; CPLPushErrorHandler(CPLQuietErrorHandler); GDALDatasetH hDS = GDALOpenEx(poOpenInfo->pszFilename, GDAL_OF_RASTER | GDAL_OF_VECTOR, apszAllowedDriver, NULL, NULL); CPLPopErrorHandler(); if( hDS ) { GDALClose(hDS); 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 ) { if( !Identify(poOpenInfo) ) return NULL; // Create datasource. HDF5Dataset *const 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."); return NULL; } } return poDS; } /************************************************************************/ /* DestroyH5Objects() */ /* */ /* Erase all objects */ /************************************************************************/ void HDF5Dataset::DestroyH5Objects( HDF5GroupObjects *poH5Object ) { // Visit all objects. unsigned i = 0; // i is used after the for loop. for( ; 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 */ /************************************************************************/ static void CreatePath( HDF5GroupObjects *poH5Object ) { // Recurse to the root path. CPLString osPath; if( poH5Object->poHparent != NULL ) { CreatePath(poH5Object->poHparent); osPath = poH5Object->poHparent->pszPath; } // Add name to the path. if( !EQUAL(poH5Object->pszName, "/") ) { osPath.append("/"); osPath.append(poH5Object->pszName); } // Fill up path for each object. CPLString osUnderscoreSpaceInName; if( poH5Object->pszPath == NULL ) { if( strlen(poH5Object->pszName) == 1 ) { osPath.append(poH5Object->pszName); osUnderscoreSpaceInName = poH5Object->pszName; } else { // Change space for underscore. char **papszPath = CSLTokenizeString2(osPath.c_str(), " ", CSLT_HONOURSTRINGS); for( int i = 0; papszPath[i] != NULL ; i++ ) { if( i > 0 ) osUnderscoreSpaceInName.append("_"); osUnderscoreSpaceInName.append(papszPath[i]); } CSLDestroy(papszPath); } // -1 to give room for NUL in C strings. static const size_t MAX_PATH = 8192 - 1; // TODO(schwehr): Is it an issue if the results are longer than 8192? // It appears that the output can never be longer than the source. if( osUnderscoreSpaceInName.size() > MAX_PATH ) CPLError(CE_Fatal, CPLE_AppDefined, "osUnderscoreSpaceInName longer than MAX_PATH: " "%u > %u", static_cast<unsigned int>(osUnderscoreSpaceInName.size()), static_cast<unsigned int>(MAX_PATH)); if( osPath.size() > MAX_PATH ) CPLError(CE_Fatal, CPLE_AppDefined, "osPath longer than MAX_PATH: %u > %u", static_cast<unsigned int>(osPath.size()), static_cast<unsigned int>(MAX_PATH)); poH5Object->pszUnderscorePath = CPLStrdup(osUnderscoreSpaceInName.c_str()); poH5Object->pszPath = CPLStrdup(osPath.c_str()); } } /************************************************************************/ /* 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) { HDF5GroupObjects *const poHparent = static_cast<HDF5GroupObjects *>(poHObjParent); HDF5GroupObjects *poHchild = poHparent->poHchild; H5G_stat_t oStatbuf; if( H5Gget_objinfo(hHDF5, pszObjName, FALSE, &oStatbuf) < 0 ) return -1; // Look for next child. unsigned idx = 0; // idx is used after the for loop. for( ; 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 = NULL; poHchild->HDatatype = 0; poHchild->objno[0] = oStatbuf.objno[0]; poHchild->objno[1] = oStatbuf.objno[1]; if( poHchild->pszPath == NULL ) { CreatePath(poHchild); } if( poHparent->pszPath == NULL ) { CreatePath(poHparent); } switch( oStatbuf.type ) { case H5G_LINK: { poHchild->nbAttrs = 0; poHchild->nbObjs = 0; poHchild->poHchild = NULL; poHchild->nRank = 0; poHchild->paDims = NULL; poHchild->HDatatype = 0; break; } case H5G_GROUP: { hid_t hGroupID = H5I_INVALID_HID; // Identifier of group. if( (hGroupID = H5Gopen(hHDF5, pszObjName)) == -1) { CPLError(CE_Failure, CPLE_AppDefined, "unable to access \"%s\" group.", pszObjName); return -1; } // Number of attributes in object. const int nbAttrs = H5Aget_num_attrs(hGroupID); hsize_t nbObjs = 0; // Number of objects in a group. H5Gget_num_objs(hGroupID, &nbObjs); poHchild->nbAttrs = nbAttrs; poHchild->nbObjs = static_cast<int>(nbObjs); poHchild->nRank = 0; poHchild->paDims = NULL; poHchild->HDatatype = 0; if( nbObjs > 0 ) { poHchild->poHchild = static_cast<HDF5GroupObjects *>( CPLCalloc(static_cast<int>(nbObjs), sizeof(HDF5GroupObjects))); memset(poHchild->poHchild, 0, static_cast<size_t>(sizeof(HDF5GroupObjects) * nbObjs)); } else { poHchild->poHchild = NULL; } if( !HDF5GroupCheckDuplicate(poHparent, oStatbuf.objno) ) H5Giterate(hHDF5, pszObjName, NULL, HDF5CreateGroupObjs, poHchild); else CPLDebug("HDF5", "avoiding link looping on node '%s'.", pszObjName); H5Gclose(hGroupID); break; } case H5G_DATASET: { hid_t hDatasetID = H5I_INVALID_HID; // Identifier of dataset. if( (hDatasetID = H5Dopen(hHDF5, pszObjName)) == -1) { CPLError(CE_Failure, CPLE_AppDefined, "unable to access \"%s\" dataset.", pszObjName); return -1; } const int nbAttrs = H5Aget_num_attrs(hDatasetID); const hid_t datatype = H5Dget_type(hDatasetID); const hid_t dataspace = H5Dget_space(hDatasetID); const int n_dims = H5Sget_simple_extent_ndims(dataspace); const hid_t native = H5Tget_native_type(datatype, H5T_DIR_ASCEND); hsize_t *maxdims = NULL; hsize_t *dims = NULL; if( n_dims > 0 ) { dims = static_cast<hsize_t *>(CPLCalloc(n_dims, sizeof(hsize_t))); maxdims = static_cast<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; // dimension 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 = NULL; poHchild->HDatatype = 0; break; } default: break; } return 0; } /************************************************************************/ /* HDF5AttrIterate() */ /************************************************************************/ static herr_t HDF5AttrIterate( hid_t hH5ObjID, const char *pszAttrName, // TODO(schwehr): void * -> HDF5Dataset * void *pDS ) { char **papszTokens = NULL; CPLString osKey; HDF5Dataset *const poDS = static_cast<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( hsize_t 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( hsize_t i = 0; papszTokens != NULL && papszTokens[i] != NULL; ++i ) { if(!osKey.empty()) osKey += '_'; osKey += papszTokens[i]; } CSLDestroy(papszTokens); const hid_t hAttrID = H5Aopen_name(hH5ObjID, pszAttrName); const hid_t hAttrTypeID = H5Aget_type(hAttrID); const hid_t hAttrNativeType = H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT); const hid_t hAttrSpace = H5Aget_space(hAttrID); if( H5Tget_class(hAttrNativeType) == H5T_VLEN ) return 0; hsize_t nSize[64] = {}; const unsigned int nAttrDims = H5Sget_simple_extent_dims(hAttrSpace, nSize, NULL); unsigned int nAttrElmts = 1; for( hsize_t i = 0; i < nAttrDims; i++ ) { nAttrElmts *= static_cast<int>(nSize[i]); } char *szData = NULL; hsize_t nAttrSize = 0; char *szValue = NULL; if( H5Tget_class(hAttrNativeType) == H5T_STRING ) { if ( H5Tis_variable_str(hAttrNativeType) ) { char **papszStrings = static_cast<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( hsize_t i = 1; i < nAttrElmts; i++ ) { osVal += " "; osVal += papszStrings[i]; } szValue = static_cast<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 = static_cast<char *>(CPLMalloc((size_t)(nAttrSize + 1))); H5Aread(hAttrID, hAttrNativeType, szValue); szValue[nAttrSize] = '\0'; } } else { const size_t nDataLen = 8192; void *buf = NULL; if( nAttrElmts > 0 ) { buf = CPLMalloc(nAttrElmts * H5Tget_size(hAttrNativeType)); szData = static_cast<char *>(CPLMalloc(nDataLen)); szValue = static_cast<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( hsize_t i = 0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%c ", static_cast<char *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_UCHAR, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%c", static_cast<char *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_SHORT, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%d ", static_cast<short *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_USHORT, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%ud ", static_cast<unsigned short *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_INT, hAttrNativeType) ) { for( hsize_t i=0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%d ", static_cast<int *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_UINT, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%ud ", static_cast<unsigned int *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_LONG, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%ld ", static_cast<long *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_ULONG, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { snprintf(szData, nDataLen, "%ld ", static_cast<unsigned long *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_FLOAT, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { CPLsnprintf(szData, nDataLen, "%.8g ", static_cast<float *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } else if( H5Tequal(H5T_NATIVE_DOUBLE, hAttrNativeType) ) { for( hsize_t i = 0; i < nAttrElmts; i++ ) { CPLsnprintf(szData, nDataLen, "%.15g ", static_cast<double *>(buf)[i]); if( CPLStrlcat(szValue, szData, MAX_METADATA_LEN) >= MAX_METADATA_LEN ) CPLError(CE_Warning, CPLE_OutOfMemory, "Header data too long. Truncated"); } } 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) { if( !poH5Object->pszPath ) return CE_None; poH5CurrentObject = poH5Object; if( poH5Object->pszPath == NULL || EQUAL(poH5Object->pszPath, "") ) return CE_None; HDF5Dataset *const poDS = this; const int nbAttrs = poH5Object->nbAttrs; switch( nType ) { case H5G_GROUP: if( nbAttrs > 0 ) { // Identifier of group. const hid_t l_hGroupID = H5Gopen(hHDF5, poH5Object->pszPath); H5Aiterate(l_hGroupID, NULL, HDF5AttrIterate, poDS); H5Gclose(l_hGroupID); } break; case H5G_DATASET: if( nbAttrs > 0 ) { const hid_t hDatasetID = H5Dopen(hHDF5, poH5Object->pszPath); H5Aiterate(hDatasetID, NULL, HDF5AttrIterate, poDS); H5Dclose(hDatasetID); } break; default: break; } return CE_None; } /************************************************************************/ /* HDF5FindDatasetObjectsbyPath() */ /* Find object by name */ /************************************************************************/ HDF5GroupObjects * HDF5Dataset::HDF5FindDatasetObjectsbyPath( HDF5GroupObjects *poH5Objects, const char *pszDatasetPath ) { if( poH5Objects->nType == H5G_DATASET && EQUAL(poH5Objects->pszUnderscorePath, pszDatasetPath) ) { #ifdef DEBUG_VERBOSE printf("found it! %p\n", poH5Objects); /*ok*/ #endif return poH5Objects; } HDF5Dataset *const poDS = this; if( poH5Objects->nbObjs > 0 ) { for( unsigned int i = 0; i <poH5Objects->nbObjs; i++ ) { HDF5GroupObjects *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 ) { if( poH5Objects->nType == H5G_DATASET && EQUAL(poH5Objects->pszName, pszDatasetName) ) { #ifdef DEBUG_VERBOSE printf("found it! %p\n", poH5Objects); /*ok*/ #endif return poH5Objects; } HDF5Dataset *poDS = this; if( poH5Objects->nbObjs > 0 ) { for( unsigned int i = 0; i <poH5Objects->nbObjs; i++ ) { HDF5GroupObjects *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 ) { HDF5Dataset *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); char szTemp[8192]; // TODO(schwehr): Get this off of the stack. switch( poRootGroup->nRank ) { case 2: snprintf(szTemp, sizeof(szTemp), "%dx%d", static_cast<int>(poRootGroup->paDims[0]), static_cast<int>(poRootGroup->paDims[1])); break; case 3: snprintf(szTemp, sizeof(szTemp), "%dx%dx%d", static_cast<int>(poRootGroup->paDims[0]), static_cast<int>(poRootGroup->paDims[1]), static_cast<int>(poRootGroup->paDims[2])); break; default: return CE_None; } const std::string osDim = szTemp; snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_NAME", ++(poDS->nSubDataCount)); poDS->papszSubDatasets = CSLSetNameValue(poDS->papszSubDatasets, szTemp, CPLSPrintf("HDF5:\"%s\":%s", poDS->GetDescription(), poRootGroup->pszUnderscorePath)); snprintf(szTemp, sizeof(szTemp), "SUBDATASET_%d_DESC", poDS->nSubDataCount); poDS->papszSubDatasets = CSLSetNameValue( poDS->papszSubDatasets, szTemp, CPLSPrintf("[%s] %s (%s)", osDim.c_str(), poRootGroup->pszUnderscorePath, poDS->GetDataTypeName(poRootGroup->native))); } return CE_None; } /************************************************************************/ /* ReadGlobalAttributes() */ /************************************************************************/ CPLErr HDF5Dataset::ReadGlobalAttributes(int bSUBDATASET) { HDF5GroupObjects *poRootGroup = static_cast<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 ) { CPLError(CE_Failure, CPLE_AppDefined, "hHDF5 < 0!"); return CE_None; } H5G_stat_t oStatbuf = {{0, 0}, {0, 0}, 0, H5G_UNKNOWN, 0, 0, {0, 0, 0, 0}}; 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 ) { CPLError(CE_Failure, CPLE_AppDefined, "hGroupId <0!"); return CE_None; } poRootGroup->nbAttrs = H5Aget_num_attrs(hGroupID); H5Gget_num_objs(hGroupID, &(poRootGroup->nbObjs)); if( poRootGroup->nbObjs > 0 ) { poRootGroup->poHchild = static_cast<HDF5GroupObjects *>( CPLCalloc(static_cast<size_t>(poRootGroup->nbObjs), sizeof(HDF5GroupObjects))); H5Giterate(hGroupID, "/", NULL, HDF5CreateGroupObjs, 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 its 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 which will store the array of doubles read. * @param nLen it stores the length of the array read. If NULL it doesn't * inform the length 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) { CPLString osAttrFullPath(pszAttrFullPath); // Search for the last "/" in order to get the path to the attribute. const size_t nSlashPos = osAttrFullPath.find_last_of("/"); CPLString osObjName; CPLString osAttrName; // 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; } const hid_t hObjAttrID = H5Oopen(hHDF5, osObjName.c_str(), H5P_DEFAULT); CPLErr retVal = CE_Failure; if(hObjAttrID < 0) { CPLError(CE_Failure, CPLE_OpenFailed, "Object %s could not be opened", pszAttrFullPath); retVal = CE_Failure; } else { // Open attribute handler by name, from the object handler opened // earlier. const hid_t 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", pszAttrFullPath); retVal = CE_Failure; } else { const hid_t hAttrTypeID = H5Aget_type(hAttrID); const hid_t hAttrNativeType = H5Tget_native_type(hAttrTypeID, H5T_DIR_DEFAULT); const hid_t hAttrSpace = H5Aget_space(hAttrID); hsize_t nSize[64] = {}; const unsigned int 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", pszAttrFullPath); retVal = CE_Failure; } else { // Get the amount of elements. unsigned int nAttrElmts = 1; for( hsize_t i = 0; i < nAttrDims; i++ ) { // For multidimensional attributes nAttrElmts *= static_cast<unsigned int>(nSize[i]); } if(nLen != NULL) *nLen = nAttrElmts; *pdfValues = static_cast<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", pszAttrFullPath); retVal = CE_Failure; } else { retVal = CE_None; } } H5Tclose(hAttrNativeType); H5Tclose(hAttrTypeID); H5Sclose(hAttrSpace); H5Aclose(hAttrID); } H5Oclose(hObjAttrID); } return retVal; }