/* ReadNetCDF.c Written by Evans A Criswell, ITSC, UAH, April 23, 2012 Modified by Evans A Criswell, ITSC, UAH, June 28, 2012 Bug fixes (dimension querying problem for variable dimensions and improper handling of variable arrays with less than 10 elements when printing the value summary). The purpose of this program is to read the contents of a NetCDF file's "root group" and report the contents. It is written to report the contents of SSMIS and SSM/I netCDF files created at the GHRC, although it can easily be augmented to handle other netCDF files by checking for types other than short (16-bit signed integer) and float (32-bit floating point) variable types. Since these are the only two types used for SSMIS and SSM/I variables, there was no need in making the program longer to check for types that would not be used in the files. If run on another type of netCDF file, it will not print the summary of array values for unknown types and report type is unknown. It prints the following information: 1. Global Attributes Each is displayed on a separate line in the form AttributeName : "AttributeValue" 2. Dimensions Each is displayed on a separate line in the form DimensionName : IntegerSize 3. Variables A section is displayed for each variable, including its name, type code, number of dimensions, and number of attributes. Variables can have attributes, so each attrribute is displayed in the same manner as the global attributes above. The dimensions are then listed. Finally, if the variable is signed short int or 32-bit float (the only variable types in SSMIS and SSM/I files), a value-summary is printed, consisting of the first few and last few array values. The variable information section looks like the following: VariableName : type = TypeCode, dimensions = NumDimensions, number of attributes = NumAttributes variable attributes : attribute_name_0 : "attribute_value_0" attribute_name_1 : "attribute_value_1" attribute_name_2 : "attribute_value_2" ... attribute_name_N-1 : "attribute_value_N-1" dimensions : dim_size_0 x dim_size_1 x ... x dim_size_N-1 (NNN elements total) value_summary : [if applicable] raw TYPE values (value0 value1 value2 value3 value4 ... valueN-3 valueN-2 valueN-1) values scaled/offsetted [if applicable] (SValue0 SValue1 SValue2 SValue3 SValue4 ... SValueN-3 SValueN-2 SValueN-1) This program can be easily transformed into a program that makes use of the variable arrays by modifying the print_variables function and using the arrays as needed rather than simply printing a summary of the values. To use this program with a wider range of netCDF files containing values of other types, simply add if blocks in the print_variables to check for more type ids than NC_SHORT and NC_FLOAT and handle them appropriately. This was not done here to keep the program shorter. Since the "unlimited dimension" is not used in SSMIS and SSM/I files, it is not handled. netCDF files with nested groups are not handled. Only the "root group" is examined since SSMIS and SSM/I files use the root group only. */ #include #include /* The netcdf.h file is required. You will need to have this in your include path. Under UNIX-type environments, this is done by providing the -Ipath_to_netcdf_include option to the C compiler if your sysadmin hasn't installed it on the system in a "standard place" for you. */ #include /* Handy function for printing errors returned by netCDF calls. */ #define NC_Error(e) { printf ("Error: %s\n", nc_strerror(e)); return (1); } /* Function prototypes for functions defined in this program. */ int get_variable_scale_and_offset (int nc_id, int var_id, float *scale, float *offset); int print_attributes (int nc_id, int var_id, int num_attributes); int print_dimensions (int nc_id, int num_dimensions); int print_variables (int nc_id, int num_variables); int print_short_array_summary (short *values, int num_values, double scale, double offset); int print_float_array_summary (float *values, int num_values); int main (int argc, char *argv[]) { int rc; int nc_id; int num_dimensions, num_variables; int num_global_attributes, unlimited_dimension_id; /* Check for valid invocation with correct arguments. */ if (argc != 2) { fprintf (stderr, "Usage : %s filename\n", argv[0]); exit (1); } /* Open the netCDF file for reading, getting the id of the root group. */ rc = nc_open (argv[1], NC_NOWRITE, &nc_id); if (rc != 0) NC_Error (rc); /* Inquire about the contents of the root group. (Dimensions, Variables, Attributes). Note that this program does not check the unlimited_dimension_id since it isn't used in SSMIS and SSM/I */ rc = nc_inq (nc_id, &num_dimensions, &num_variables, &num_global_attributes, &unlimited_dimension_id); if (rc != 0) NC_Error (rc); printf ("Number of global attributes = %d\n", num_global_attributes); printf ("Number of dimensions = %d\n", num_dimensions); printf ("Number of variables = %d\n", num_variables); /* Report the findings. See individual functions' comments for details */ print_attributes (nc_id, NC_GLOBAL, num_global_attributes); print_dimensions (nc_id, num_dimensions); print_variables (nc_id, num_variables); return (0); } int get_variable_scale_and_offset (int nc_id, int var_id, float *scale, float *offset) { /* For a variable, gets the scale_factor and add_offset attributes and puts them in more-usable float variables. */ int rc; rc = nc_get_att_float (nc_id, var_id, "scale_factor", scale); if (rc != 0) NC_Error (rc); rc = nc_get_att_float (nc_id, var_id, "add_offset", offset); if (rc != 0) NC_Error (rc); return (0); } int print_attributes (int nc_id, int var_id, int num_attributes) { /* Print attributes. It is assumed that the group id, variable id, and number of attributes are set correctly and are valid. */ char name[NC_MAX_NAME + 1]; char *char_value; float *float_values; nc_type att_type; size_t att_length; int rc; int i, j; if (var_id == NC_GLOBAL) printf ("\nGlobal attributes :\n\n"); /* For each attribute to inquire about... */ for (i = 0; i < num_attributes; i++) { /* What is the name of the attribute? */ rc = nc_inq_attname (nc_id, var_id, i, name); if (rc != 0) NC_Error (rc); printf (" %s : ", name); /* What is the type of the attribute? */ rc = nc_inq_atttype (nc_id, var_id, name, &att_type); if (rc != 0) NC_Error (rc); /* What is the length of the attribute? */ rc = nc_inq_attlen (nc_id, var_id, name, &att_length); if (rc != 0) NC_Error (rc); if (att_type == NC_CHAR) { /* If it's a string, make a string and get the attribute value into it. */ char_value = (char *) malloc ((att_length + 1) * sizeof (char)); rc = nc_get_att_text (nc_id, var_id, name, char_value); if (rc != 0) NC_Error (rc); char_value[att_length] = '\0'; printf ("\"%s\"", char_value); } else if (att_type == NC_FLOAT) { /* If it's 32-bit floating point, make an array of floats and get the values into it. */ float_values = (float *) malloc ((att_length + 1) * sizeof (float)); rc = nc_get_att_float (nc_id, var_id, name, float_values); if (rc != 0) NC_Error (rc); for (j = 0; j < att_length; j++) printf ("%g ", float_values[j]); } else { /* If it's another type, say so and skip printing the value. */ /* This block can be extended to handle other types if needed. */ printf ("unexpected type. value not printed."); } printf ("\n"); } return (0); } int print_dimensions (int nc_id, int num_dimensions) { /* Print dimensions. It is assumed that nc_id and num_dimensions are set properly and are valid. */ char name[NC_MAX_NAME + 1]; size_t length; int rc; int i; printf ("\nDimensions :\n\n"); /* For each dimension to inquire about.... */ for (i = 0; i < num_dimensions; i++) { /* What is the length of this dimension? */ rc = nc_inq_dim (nc_id, i, name, &length); if (rc != 0) NC_Error (rc); printf ("%s : %d\n", name, (int) length); } printf ("\n"); return (0); } int print_variables (int nc_id, int num_variables) { /* Print detailed information about each variable. It is assumed that nc_id and num_variables are set correctly and are valid. */ char name[NC_MAX_NAME + 1]; nc_type type_id; int num_dimensions, dimension_ids[NC_MAX_VAR_DIMS], num_attributes; size_t dim_length, attr_length; int variable_size; int rc; int var_i, dim_i, attr_i, var_dim_id; char attr_name[NC_MAX_NAME + 1]; nc_type attr_type; char *attr_value; float *var_float_values; short *var_short_values; float scale = 1.0f, offset = 0.0f; if (num_variables < 1) { printf ("No variables.\n"); return (0); } printf ("Variables :\n\n"); /* For each variable to inquire about... */ for (var_i = 0; var_i < num_variables; var_i++) { /* Get information about this variable. */ rc = nc_inq_var (nc_id, var_i, name, &type_id, &num_dimensions, dimension_ids, &num_attributes); if (rc != 0) NC_Error (rc); printf ("%s : type = %d, dimensions = %d, number of attributes = %d\n", name, type_id, num_dimensions, num_attributes); /* Print the attributes associated with this variable */ printf (" variable attributes :\n"); print_attributes (nc_id, var_i, num_attributes); /* Inquite about this variable's dimensions and compute the total array size in the process. */ variable_size = 1; printf (" dimensions : "); /* For each variable dimension to inquire about.... */ for (dim_i = 0; dim_i < num_dimensions; dim_i++) { /* What is this dimension's ID? */ var_dim_id = dimension_ids[dim_i]; /* What is the size of the dimension with that ID? */ rc = nc_inq_dim (nc_id, var_dim_id, 0, &dim_length); if (rc != 0) NC_Error (rc); printf ("%d ", (int) dim_length); if (dim_i < (num_dimensions - 1)) printf ("x "); /* Keep a running product of the variable dimensions to have the total size when done. */ variable_size *= dim_length; } printf (" (%d elements total)\n", variable_size); if (variable_size < 1) { printf (" Warning : number of elements less than 1 !\n"); continue; } /* Check for type and handle the ones cared about. */ if (type_id == NC_FLOAT) { /* If 32-bit float, make an array of floats and get the values. */ var_float_values = (float *) malloc (variable_size * sizeof (float)); rc = nc_get_var_float (nc_id, var_i, var_float_values); if (rc != 0) NC_Error (rc); /* Print a summary. To use this array in another manner, insert code here. */ printf (" value summary : "); print_float_array_summary (var_float_values, variable_size); printf ("\n"); free (var_float_values); } else if (type_id == NC_SHORT) { /* If 16-bit signed short int, make an array of shorts and get the values. */ var_short_values = (short *) malloc (variable_size * sizeof (short)); get_variable_scale_and_offset (nc_id, var_i, &scale, &offset); rc = nc_get_var_short (nc_id, var_i, var_short_values); if (rc != 0) NC_Error (rc); /* Print a summary. To use this array in another manner, insert code here. */ printf (" value summary :\n"); print_short_array_summary (var_short_values, variable_size, scale, offset); printf ("\n"); free (var_short_values); } else { /* If unknown type, say so. This code block can be easily extended to check for and handle other types. */ printf (" unexpected type ... value summary not printed.\n"); } printf ("\n"); } printf ("\n"); return (0); } int print_short_array_summary (short *values, int num_values, double scale, double offset) { /* Print a summary of values of an array of shorts, both without scaling and offset, and with. Scaled and offset values are computed as floating point numbers by calculating short_value * scale + offset The routine prints arrays of size 10 or less in their entirety and prints the first few and last few values otherwise. num_values must be consistent with the array passed in. */ int i; int max_whole_print = 10; int number_to_print; float *scaled = (float *) malloc (num_values * sizeof (float)); for (i = 0; i < num_values; i++) scaled[i] = values[i] * scale + offset; printf (" raw short values ("); if (num_values >= 1) { if (num_values <= max_whole_print) { number_to_print = max_whole_print; if (num_values < number_to_print) number_to_print = num_values; for (i = 0; i < number_to_print; i++) printf ("%hd ", values[i]); } else printf ("%hd %hd %hd %hd %hd ... %hd %hd %hd", values[0], values[1], values[2], values[3], values[4], values[num_values - 3], values[num_values - 2], values[num_values - 1]); } printf (")\n"); printf (" values scaled/offsetted "); print_float_array_summary (scaled, num_values); free (scaled); return (0); } int print_float_array_summary (float *values, int num_values) { /* Print a summary of values of an array of floats. The routine prints arrays of size 10 or less in their entirety and prints the first few and last few values otherwise. num_values must be consistent with the array passed in. */ int i; int max_whole_print = 10; int number_to_print; printf ("("); if (num_values >= 1) { if (num_values <= max_whole_print) { number_to_print = max_whole_print; if (num_values < number_to_print) number_to_print = num_values; for (i = 0; i < number_to_print; i++) printf ("%g ", values[i]); } else printf ("%g %g %g %g %g ... %g %g %g", values[0], values[1], values[2], values[3], values[4], values[num_values - 3], values[num_values - 2], values[num_values - 1]); } printf (")"); return (0); }