#include "h5augjpss.h" /* #include "h5aj_error.h" */ #include "h5aj_private.h" #include #include #include /* There are a number of 1x1 dataspaces in this file. */ #define RANK 1 /* * Function: h5aj_add_string_attribute * * Purpose: Add attribute of type string * * Return: 0 if successful, non-zero otherwise */ herr_t h5aj_add_string_attribute(hid_t field_dataset, char *value, char *attr_name) { herr_t status; hid_t dataset, datatype, dataspace, dsid; hid_t grpid, spaceid, typeid, attid; hsize_t dims; dims = 1; spaceid = H5Screate(H5S_SCALAR); typeid = H5Tcopy(H5T_C_S1); H5Tset_size(typeid, strlen(value) + 1); attid = H5Acreate2(field_dataset, attr_name, typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT); status = H5Awrite(attid, typeid, value); H5Sclose(spaceid); H5Tclose(typeid); H5Aclose(attid); return status; } /* * Function: h5aj_add_fillval_attribute * * Purpose: Add attribute for Fill Value. Fill Value is type double in the * xml file, but will be converted to the correct type for the * dataset for which it is an attribute. * * Return: 0 if successful, non-zero otherwise */ herr_t h5aj_add_fillval_attribute(hid_t field_dataset, hid_t attr_typeid, double value, char *attr_name) { herr_t status; hid_t dataset, datatype, dataspace, dsid; hid_t grpid, spaceid, typeid, attid; hsize_t dims = 1; spaceid = H5Screate_simple(RANK, &dims, NULL); typeid = H5Tcopy(H5T_NATIVE_DOUBLE); attid = H5Acreate2(field_dataset, attr_name, attr_typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT); status = H5Awrite(attid, typeid, &(value)); H5Sclose(spaceid); H5Tclose(typeid); H5Aclose(attid); return status; } /* * Function: h5aj_add_double_attribute * * Purpose: Add attribute of type double * * Return: 0 if successful, non-zero otherwise */ herr_t h5aj_add_double_attribute(hid_t field_dataset, double value, char *attr_name) { herr_t status; hid_t dataset, datatype, dataspace, dsid; hid_t grpid, spaceid, typeid, attid; hsize_t dims = 1; spaceid = H5Screate_simple(RANK, &dims, NULL); typeid = H5Tcopy(H5T_NATIVE_DOUBLE); attid = H5Acreate2(field_dataset, attr_name, typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT); status = H5Awrite(attid, typeid, &(value)); H5Sclose(spaceid); H5Tclose(typeid); H5Aclose(attid); return status; } /* * Function: h5aj_add_int_attribute * * Purpose: Add attribute of type int * * Return: 0 if successful, non-zero otherwise */ herr_t h5aj_add_int_attribute(hid_t field_dataset, int value, char *attr_name) { herr_t status; hid_t dataset, datatype, dataspace, dsid; hid_t grpid, spaceid, typeid, attid; hsize_t dims = 1; spaceid = H5Screate_simple(RANK, &dims, NULL); typeid = H5Tcopy(H5T_NATIVE_INT); attid = H5Acreate2(field_dataset, attr_name, typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT); status = H5Awrite(attid, typeid, &(value)); H5Sclose(spaceid); H5Tclose(typeid); H5Aclose(attid); return status; } /* * Function: h5aj_add_attributes_for_datums * * Purpose: Add attributes for the information in the datum tags for a field * in the xml file to the dataset corresponding to the field provided * the dataset exists. Attribute names are the same as the tag names * for fields with only one datum. The fields with multiple datums * are all QF fields, and to make the names of tha attributes unique, * Flag_ is used as a prefix for the attribute name. * Fill values and Legend Entries are treated similarly. * * Return: 0 if successful, non-zero otherwise */ int h5aj_add_attributes_for_datums(hid_t group, H5aj_field_t *field) { herr_t status; hid_t field_dataset, datatype, dataspace, dsid; hid_t grpid, spaceid, typeid, attid, attr_typeid; hsize_t dims; int value; int i, j, size; if (! H5Lexists(group, field->name, H5P_DEFAULT)) { printf("\nThere is no dataset in the file named %s! No ", field->name); printf("attributes will be added for Field %s.\n", field->name); return 0; } field_dataset = H5Dopen2(group, field->name, H5P_DEFAULT); if (field_dataset < 0) { printf("Failed to open dataset for %s\n", field->name); return field_dataset; } /* get the type for checking the size of the datatype and for creating fill value attributes. */ attr_typeid = H5Dget_type(field_dataset); size = H5Tget_size(attr_typeid); if (size != field->ds.count) printf("\nSize of dataset (%d) for field %s does not match xml file (%d)!\n", size, field->name, field->ds.count); if (field->ds.type) { if (strstr("byte", field->ds.type) || strstr("bit", field->ds.type) ) { printf("Type of DataSize in xml file was expected to be byte(s) or bit(s) "); printf(" but %s is specified.\n", field->ds.type); } } else { printf("field->ds.type is null.\n"); } /* TODO: verify that the dataset type matches the field type from the xml file. This will require a map of types of the form "32-bit floating point" in the xml file to types of the form "H5T_IEEE_F32LE" from the datasets. */ for (i = 0; i < field->n_datum; ++i) { char attr_prefix[128]; char attr_name[256]; memset( attr_prefix, '\0', 128); memset( attr_name, '\0', 256); if (field->n_datum > 1) { char digit_str[8]; sprintf(attr_prefix, "Datum"); sprintf(digit_str, "%d", i); strcat(attr_prefix, digit_str); strcat(attr_prefix, "_"); } if (field->datum[i].description) { strncpy(attr_name, attr_prefix, strlen(attr_prefix)); strcat(attr_name, "Description"); h5aj_add_string_attribute(field_dataset, field->datum[i].description, attr_name); } memset( attr_name, '\0', 256); strncpy(attr_name, attr_prefix, strlen(attr_prefix)); strcat(attr_name, "DatumOffset"); h5aj_add_int_attribute(field_dataset, field->datum[i].datumoffset, attr_name); memset( attr_name, '\0', 256); strncpy(attr_name, attr_prefix, strlen(attr_prefix)); strcat(attr_name, "Scaled"); h5aj_add_int_attribute(field_dataset, field->datum[i].scaled, attr_name); if (field->datum[i].sf_name) { memset( attr_name, '\0', 256); strncpy(attr_name, attr_prefix, strlen(attr_prefix)); strcat(attr_name, "ScaleFactorName"); h5aj_add_string_attribute(field_dataset, field->datum[i].sf_name, attr_name); } if (field->datum[i].m_units) { memset( attr_name, '\0', 256); strncpy(attr_name, attr_prefix, strlen(attr_prefix)); strcat(attr_name, "MeasurementUnits"); h5aj_add_string_attribute(field_dataset, field->datum[i].m_units, attr_name); } for (j = 0; j < field->datum[i].n_fill_val; ++j) { memset( attr_name, '\0', 256); strncpy(attr_name, attr_prefix, strlen(attr_prefix)); strcat(attr_name, "FillValue_"); strcat(attr_name, field->datum[i].fv[j].name); h5aj_add_fillval_attribute(field_dataset, attr_typeid, field->datum[i].fv[j].value, attr_name); } for (j = 0; j < field->datum[i].n_legent; ++j) { memset( attr_name, '\0', 256); strncpy(attr_name, attr_prefix, strlen(attr_prefix)); strcat(attr_name, "LegendEntry_"); strcat(attr_name, field->datum[i].le[j].name); h5aj_add_double_attribute(field_dataset, field->datum[i].le[j].value, attr_name); } } H5Dclose(field_dataset); return 0; } /* * Function: h5aj_add_dimension_scales * * Purpose: add the appropriate dimension scales to the dataset for each field. * If a dataset with the correct name and size is found, attach * and set_label for it. * If no dataset for the name of the dimension exists, create, * attach and set_label for it. * If the existing dataset has the wrong size, create a dataset * named dimension_size, attach it and set_label to the original * dimension name * * Return: 0 if successful, non-zero otherwise */ int h5aj_add_dimension_scales(hid_t group, H5aj_field_t *field) { herr_t status; hid_t field_dataset, datatype, dataspace, dsid, ds_dataset; htri_t dimscale_exists; int i; hsize_t dims, dimsize; if (! H5Lexists(group, field->name, H5P_DEFAULT)) { printf("\nThere is no dataset in the file named %s! No dimension ", field->name); printf("scales will be added for Field %s.\n", field->name); return 0; } field_dataset = H5Dopen2(group, field->name, H5P_DEFAULT); if (field_dataset < 0) printf("Failed to open dataset for %s\n", field->name); for (i = 0; i < field->n_dims; ++i) { dimsize = field->dims[i].maxidx; dimscale_exists = H5Lexists(group, field->dims[i].name, H5P_DEFAULT); if (dimscale_exists) { dsid = H5Dopen2(group, field->dims[i].name, H5P_DEFAULT); if (dsid < 0) printf("Failed to open dataset for %s\n", field->dims[i].name); dataspace = H5Dget_space(dsid); status = H5Sget_simple_extent_dims(dataspace, &dims, NULL); if (dims == field->dims[i].maxidx) { status = H5DSset_scale(dsid, field->dims[i].name); status = H5DSattach_scale(field_dataset, dsid, i); } else { /* create unique name dimension_size */ char digit_str[128]; char alt_dim[1024]; sprintf(digit_str, "%d", dimsize); strcpy(alt_dim, field->dims[i].name); strcat(alt_dim, "_"); strcat(alt_dim, digit_str); /*printf("Create dataset %s with size %d.\n", alt_dim, dimsize); */ dataspace = H5Screate_simple(RANK, &dimsize, NULL); datatype = H5Tcopy(H5T_NATIVE_INT); ds_dataset = H5Dcreate2(group, alt_dim, datatype, dataspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); H5Sclose(dataspace); H5Tclose(datatype); status = H5DSset_scale(ds_dataset, alt_dim); status = H5DSattach_scale(field_dataset, ds_dataset, i); H5Dclose(ds_dataset); } H5Dclose(dsid); } else { /* printf("Create dataset %s with size %d.\n", field->dims[i].name, dimsize); */ dataspace = H5Screate_simple(RANK, &dimsize, NULL); datatype = H5Tcopy(H5T_NATIVE_INT); ds_dataset = H5Dcreate2(group, field->dims[i].name, datatype, dataspace, H5P_DEFAULT, H5P_DEFAULT, H5P_DEFAULT); H5Sclose(dataspace); H5Tclose(datatype); status = H5DSset_scale(ds_dataset, field->dims[i].name); status = H5DSattach_scale(field_dataset, ds_dataset, i); H5Dclose(ds_dataset); } status = H5DSset_label(field_dataset, i, field->dims[i].name); } /* end for */ H5Dclose(field_dataset); return 0; } /* * Function: h5aj_disconnect_group * * Purpose: The NPOESS files generally have 2 groups, one containing data * and the other containing references to the data. NetCDF-4 won't * open a file with reference types, but we can temporarily hide the * group with the reference types by saving its address and deleting * the link to it. The link can be recreated later from the saved * address. * * Return: 0 if successful, non-zero otherwise */ herr_t h5aj_disconnect_group(hid_t file_id, char *group_name) { herr_t status; H5O_info_t object_info; hid_t grpid, spaceid, typeid, attid; hsize_t dims; unsigned long long value; char *prefix1 = "HDF5_internal_address_of_"; char *prefix2 = "HDF5_internal_name_of_"; char attr1name[1024]; char attr2name[1024]; grpid = H5Oopen( file_id, group_name, H5P_DEFAULT); status = H5Oincr_refcount(grpid); if (status < 0) printf("Failed to increment refcount.\n"); status = H5Oget_info(grpid, &object_info); if (status < 0) printf("Failed to get object info for group %s.\n", group_name); value = object_info.addr; strcpy(attr1name, prefix1); strcat(attr1name, group_name); strcpy(attr2name, prefix2); strcat(attr2name, group_name); dims = 1; spaceid = H5Screate_simple(RANK, &dims, &dims); typeid = H5Tcopy(H5T_NATIVE_ULLONG); /* Create or open attributes to store the name and address of the disconnected group. */ if (H5Aexists_by_name(file_id, "/", attr1name, H5P_DEFAULT)>0) { attid = H5Aopen_by_name(file_id, "/", attr1name, H5P_DEFAULT, H5P_DEFAULT); } else { attid = H5Acreate2(file_id, attr1name, typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT); } if (attid > 0) status = H5Awrite(attid, typeid, &value); else printf("Failed to open or create attribute %s\n", attr1name); if (status < 0) printf("Failed to write %d to attribute %s.\n", value, attr1name); H5Sclose(spaceid); H5Tclose(typeid); H5Aclose(attid); spaceid = H5Screate(H5S_SCALAR); typeid = H5Tcopy(H5T_C_S1); H5Tset_size(typeid, strlen(group_name) + 1); if (H5Aexists_by_name(file_id, "/", attr2name, H5P_DEFAULT)>0) { attid = H5Aopen_by_name(file_id, "/", attr2name, H5P_DEFAULT, H5P_DEFAULT); } else { attid = H5Acreate2(file_id, attr2name, typeid, spaceid, H5P_DEFAULT, H5P_DEFAULT); } if (attid > 0) status = H5Awrite(attid, typeid, &value); else printf("Failed to open or create attribute %s\n", attr2name); if (status < 0) printf("Failed to write %s to attribute %s.\n", value, attr2name); H5Sclose(spaceid); H5Tclose(typeid); H5Aclose(attid); /* Delete link to group with datasets that has reference types */ H5Ldelete(file_id, group_name, H5P_DEFAULT); return status; } /* * Function: h5aj_get_All_Data_sole_subgroup * * Purpose: Get the name of the only subgroup in the "/All_Data" group. * This is where the datasets containing the actual data are * expected to be. It is assumed that the "/All_Data" group has * only one subgroup and that the data will be "complete" with only * the datasets from the subgroup. * */ herr_t h5aj_get_All_Data_sole_subgroup(hid_t file, hid_t *subgroup, char *subgroup_name) { herr_t status; char *group_name = "All_Data"; hid_t all_data_group; ssize_t link_size = 0; /* Get the name of the subgroup of /All_Data, which is assumed to always exist in the file named by N_GEO_Ref, along with one subgroup containing datasets Height, Longitude and Latitude. If we can't count on that arrangement we'll need to search for those three datasets somewhere in the file. */ all_data_group = H5Gopen(file, group_name, H5P_DEFAULT); /* First get the size of the string and make sure it will fit in the buffer. */ link_size = H5Lget_name_by_idx(all_data_group, ".", H5_INDEX_NAME, H5_ITER_NATIVE, 0, NULL, 0, H5P_DEFAULT); if (link_size <= 0) { printf("Failed to get length of name (or length is 0) of object 0 in %s.\n", group_name); return -1; } if (link_size > 1024) { printf("Size of object name is %d; larger than buffer subgroup_name.\n", (int)link_size); return -1; } /* Get the name of the subgroup. */ link_size = H5Lget_name_by_idx(all_data_group, ".", H5_INDEX_NAME, H5_ITER_NATIVE, 0, subgroup_name, 1024, H5P_DEFAULT); if (link_size <= 0) { /* couldn't find "/All_Data/subgroup". */ /*H5GOTO_ERROR("Expected group not found in N_GEO_Ref file.");*/ printf("Expected group not found in N_GEO_Ref file."); return -1; } *subgroup = H5Gopen(all_data_group, subgroup_name, H5P_DEFAULT); return status; } /* * Function: h5aj_check_geolocation_dimensions * * Purpose: Check for dimension scales to match the dimensions of the Latitude, * */ herr_t h5aj_get_ngeoref_file_name(hid_t file, char *attrname, char *filename) { hid_t attrtype, memtype, attr; /* Handles */ herr_t status; size_t sdim; /* We assume that this attribute contains a single string, even though it's a two dimensional array. Otherwise we would have to get the dimensions and sizes and allocate memory for the whole array. */ attr = H5Aopen (file, attrname, H5P_DEFAULT); attrtype = H5Aget_type (attr); sdim = H5Tget_size (attrtype); sdim++; /* Make room for null terminator */ memtype = H5Tcopy (H5T_C_S1); status = H5Tset_size (memtype, sdim); status = H5Aread (attr, memtype, filename); status = H5Aclose (attr); status = H5Tclose (attrtype); status = H5Tclose (memtype); return status; } /* This function was useful when dimension scales were always AlongTrack and * CrossTrack. It may still be useful to look for actual dimension scales * somehow, but for now to see if level 2 has been run we'll set a flag. */ /* htri_t h5aj_check_dimscales(hid_t group) { htri_t alongTrack_exists; htri_t crossTrack_exists; char *dim0name = "AlongTrack"; char *dim1name = "CrossTrack"; alongTrack_exists = H5Lexists(group, dim0name, H5P_DEFAULT); crossTrack_exists = H5Lexists(group, dim1name, H5P_DEFAULT); return (alongTrack_exists && crossTrack_exists); } */ /* * Function: h5aj_check_geolocation_dimensions * * Purpose: Check for dimension scales to match the dimensions of the Latitude, * Longitude, or Height datasets. Look for dimscales "AlongTrack" * and "CrossTrack". Attach them if they exist. * * Return: 0 if matching dimension scales are found and attached, non-zero * otherwise. */ herr_t h5aj_check_geolocation_dimensions(hid_t group, char *dset_name ) { herr_t status; hid_t dataset, datatype, dataspace, dsid, ds_dataset; htri_t dimscale_exists; int ndims, i; hsize_t dset_dims[2]; hsize_t ds_dims; /* dims for the dimension scales. Would need an array if the dataset weren't something x 1. */ char *dim0name = "AlongTrack"; char *dim1name = "CrossTrack"; dataset = H5Dopen2(group, dset_name, H5P_DEFAULT); if (dataset < 0) printf("Failed to open dataset: %s\n", dset_name); /* get the dimensions */ dataspace = H5Dget_space(dataset); ndims = H5Sget_simple_extent_dims(dataspace, dset_dims, NULL); if (ndims != 2) printf("2 dimensions expected, %d dimensions found.\n", ndims); /* printf("The %d dimensions found for %s were %d and %d.\n", ndims, dset_name, (int)dset_dims[0], (int)dset_dims[1]); */ H5Sclose(dataspace); /* Should probably make this a function and call it twice. */ dimscale_exists = H5Lexists(group, dim0name, H5P_DEFAULT); if (dimscale_exists) { dsid = H5Dopen2(group, dim0name, H5P_DEFAULT); if (dsid < 0) printf("Failed to open dataset for %s\n", dim0name); dataspace = H5Dget_space(dsid); status = H5Sget_simple_extent_dims(dataspace, &ds_dims, NULL); if (dset_dims[0] == ds_dims) { status = H5DSset_scale(dsid, dim0name); status = H5DSattach_scale(dataset, dsid, 0); } else { printf("Error: dimension size %d differs from %s dimension %s size %d.\n", (int)ds_dims, dset_name, dim0name, (int)dset_dims[0]); status = -1; } H5Sclose(dataspace); H5Dclose(dsid); } dimscale_exists = H5Lexists(group, dim1name, H5P_DEFAULT); if (dimscale_exists) { dsid = H5Dopen2(group, dim1name, H5P_DEFAULT); if (dsid < 0) printf("Failed to open dataset for %s\n", dim1name); dataspace = H5Dget_space(dsid); status = H5Sget_simple_extent_dims(dataspace, &ds_dims, NULL); if (dset_dims[1] == ds_dims) { status = H5DSset_scale(dsid, dim1name); status = H5DSattach_scale(dataset, dsid, 1); } else { printf("Error: dimension size %d differs from %s dimension %s size %d.\n", (int)ds_dims, dset_name, dim1name, (int)dset_dims[1]); status = -1; } H5Sclose(dataspace); H5Dclose(dsid); } H5Dclose(dataset); } /* * Function: h5aj_add_geolocation_datasets * * Purpose: The NPOESS files have an attribute named N_GEO_Ref whose value in * some cases is the name of a file having datasets named "Latitude", * "Longitude", and "Height" that correspond to the datasets in the * file itself (i.e. "Radiance" and "Reflectance"). In other cases * these datasets may already be in the file. * * This function will need further development for the second case * as of 2/23/11. * * Return: 0 if successful, non-zero otherwise */ herr_t h5aj_add_geolocation_datasets(hid_t file, hid_t group, const char *datafilename) { hid_t ngeoref_file; herr_t status; char filename[1024]; char subgroup_name[1024]; hid_t ngeoref_coordinates_group; /* Check to see if the group already has datasets named "Height", "Latitude", and "Longitude". If it does, skip to calling check_geolocation_dimensions. */ if (!(H5Lexists(group, "Height", H5P_DEFAULT) && H5Lexists(group, "Latitude", H5P_DEFAULT) && H5Lexists(group, "Longitude", H5P_DEFAULT))) { status = h5aj_get_ngeoref_file_name(file, "N_GEO_Ref", filename); ngeoref_file = H5Fopen(filename, H5F_ACC_RDONLY, H5P_DEFAULT); if (ngeoref_file < 0) { fprintf(stderr, "Unable to open geolocation data file. %s must be in the same directory as %s.\n", filename, datafilename); status = (-1); return status; } status = h5aj_get_All_Data_sole_subgroup(ngeoref_file, &ngeoref_coordinates_group, subgroup_name); status = H5Ocopy(ngeoref_coordinates_group, "Height", group, "Height", H5P_DEFAULT, H5P_DEFAULT); status = H5Ocopy(ngeoref_coordinates_group, "Latitude", group, "Latitude", H5P_DEFAULT, H5P_DEFAULT); status = H5Ocopy(ngeoref_coordinates_group, "Longitude", group, "Longitude", H5P_DEFAULT, H5P_DEFAULT); status = H5Gclose(ngeoref_coordinates_group); status = H5Fclose(ngeoref_file); } status = h5aj_check_geolocation_dimensions(group, "Height"); status = h5aj_check_geolocation_dimensions(group, "Latitude"); status = h5aj_check_geolocation_dimensions(group, "Longitude"); return status; } /* * * Function: link_op_func * * Purpose: Operator function for function h5aj_link_group_objects. Creates a * link in the root group to each of the datasets. * */ herr_t link_op_func (hid_t loc_id, const char *name, const H5L_info_t *info, void *operator_data) { herr_t status; H5O_info_t infobuf; /* * Get type of the object and display its name and type. * The name of the object is passed to this function by * the Library. */ status = H5Lcreate_hard(loc_id, name, *(hid_t *)operator_data, name, H5P_DEFAULT, H5P_DEFAULT); return status; } /* * Function: h5aj_link_group_objects * * Purpose: Iterate over all the datasets in the group and create links to them * in the root group. Then the group structure can be hidden for * visualization tools that will only handle netCDF Classic data files. * This function assumes all the datasets are in the same group, that * there are no subgroups in the group and that anything needed for the * visualization tool will be otherwise provided in the expected manner. */ herr_t h5aj_link_group_objects(hid_t file, hid_t group) { herr_t status; hid_t root_group = H5Gopen(file, "/", H5P_DEFAULT); status = H5Literate (group, H5_INDEX_NAME, H5_ITER_NATIVE, NULL, link_op_func, &root_group); h5aj_disconnect_group(file, "/All_Data"); status = H5Gclose(root_group); return status; }