So far we've been reading HDF file and data set attributes using hdfed and inspecting various records more or less at random. In this section I'll show you how to do that from within your Fortran program, so that you can, for example, pass it on to a Gnuplot command file and, ultimately, print on your data plot.
Before going any further, let us run our program hdf-5 that reads an HDF file first. When we do so this time, we get the following output:
gustav@blanc:../src 12:51:28 !551 $ ./hdf-5 File SDS.hdf opened sd_file_id = 393216 Obtained information about the file file_info_status = 0 n_datasets = 1 n_file_attributes = 1 First scientific data set selected sd_set_id = 262144 Obtained information about the data set sd_set_name = SDStmpl rank = 1 dim_sizes = 10 data_type = 24 n_set_attributes = 1 data (before) = 10*0 sd_read_status = 0 data (after) = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 sd_set_status = 0 sd_file_status = 0 gustav@blanc:../src 12:51:30 !552 $This time the program finds one file attribute and one set attribute associated with the first data set.
Now we will modify program hdf-5, renaming it to hdf-8, so that it will read both file and data set attributes.
The following listing gets quite long, but there is little in it that is earth-shaking. It's long, because it has a lot of write statements. I have indented those so as to make them a little less prominent
program read_SDS
use hdf; use dffunc; use netcdf; implicit none
integer, parameter :: sd_set_index = 0, file_attr_index = 0, &
set_attr_index = 0
integer, dimension(1), parameter :: start = (/ 0 /), stride = (/ 1 /)
integer :: sd_file_id, sd_set_id, sd_read_status, sd_set_status, &
sd_file_status, n_datasets, n_file_attributes, file_info_status, &
rank, data_type, n_set_attributes, i, set_info_status, &
file_attr_status, file_attr_type, file_attr_length, read_attr_status, &
set_attr_status, set_attr_type, set_attr_length
integer(kind=4), dimension(:), allocatable :: data
integer, dimension(MAXVDIMS) :: dim_sizes
integer, dimension(1) :: edges
character(len=MAXNCNAM) :: sd_set_name = " ", file_attr_name = " ", &
file_attribute = " ", set_attr_name = " ", set_attribute = " "
sd_file_id = sfstart("SDS.hdf", DFACC_READ)
write (*, *) "File SDS.hdf opened"
write (*, *) "sd_file_id = ", sd_file_id
file_info_status = sffinfo(sd_file_id, n_datasets, n_file_attributes)
write (*, *) "Obtained information about the file"
write (*, *) "file_info_status = ", file_info_status
write (*, *) "n_datasets = ", n_datasets
write (*, *) "n_file_attributes = ", n_file_attributes
file_attr_status = sfgainfo(sd_file_id, file_attr_index, file_attr_name, &
file_attr_type, file_attr_length)
write (*, *) "file_attr_name = ", &
file_attr_name(:len_trim(file_attr_name))
write (*, *) "file_attr_type = ", file_attr_type
write (*, *) "file_attr_length = ", file_attr_length
if (file_attr_length .le. MAXNCNAM &
.and. file_attr_type .eq. DFNT_CHAR8) then
read_attr_status = sfrcatt(sd_file_id, file_attr_index, file_attribute)
write (*, *) "file_attribute = ", &
file_attribute(:len_trim(file_attribute))
end if
sd_set_id = sfselect(sd_file_id, sd_set_index)
write (*, *) "First scientific data set selected"
write (*, *) "sd_set_id = ", sd_set_id
set_info_status = sfginfo(sd_set_id, sd_set_name, rank, dim_sizes, &
data_type, n_set_attributes)
write (*, *) "Obtained information about the data set"
write (*, *) "sd_set_name = ", sd_set_name(:len_trim(sd_set_name))
write (*, *) "rank = ", rank
write (*, *) "dim_sizes = ", (/ (dim_sizes(i), i = 1, rank) /)
write (*, *) "data_type = ", data_type
write (*, *) "n_set_attributes = ", n_set_attributes
set_attr_status = sfgainfo(sd_set_id, set_attr_index, set_attr_name, &
set_attr_type, set_attr_length)
write (*, *) "set_attr_name = ", &
set_attr_name(:len_trim(set_attr_name))
write (*, *) "set_attr_type = ", set_attr_type
write (*, *) "set_attr_length = ", set_attr_length
if (set_attr_length .le. MAXNCNAM &
.and. set_attr_type .eq. DFNT_CHAR8) then
read_attr_status = sfrcatt(sd_set_id, set_attr_index, set_attribute)
write (*, *) "set_attribute = ", &
set_attribute(:len_trim(set_attribute))
end if
if (rank .eq. 1) then
allocate (data(dim_sizes(1))); data = 0; edges = dim_sizes(1)
write (*, *) "data (before) = ", data
sd_read_status = sfrdata(sd_set_id, start, stride, edges, data)
write (*, *) "sd_read_status = ", sd_read_status
write (*, *) "data (after) = ", data
deallocate(data)
else
write (*, *) "rank different from 1, skipping the data reading part"
end if
sd_set_status = sfendacc(sd_set_id)
write (*, *) "sd_set_status = ", sd_set_status
sd_file_status = sfend(sd_file_id)
write (*, *) "sd_file_status = ", sd_file_status
end program read_SDS
Before analysing the program let us compile and run it first:
gustav@blanc:../src 13:52:38 !582 $ make hdf-8
f90 -c -M/afs/ovpit.indiana.edu/@sys/HDF/modules hdf-8.f90
f90 -o hdf-8 hdf-8.o -L/afs/ovpit.indiana.edu/@sys/HDF/lib \
-lmfhdf -lnsl -ldf -ljpeg -lz -lm
gustav@blanc:../src 13:52:43 !583 $ ./hdf-8
File SDS.hdf opened
sd_file_id = 393216
Obtained information about the file
file_info_status = 0
n_datasets = 1
n_file_attributes = 1
file_attr_name = File Contents
file_attr_type = 4
file_attr_length = 30
file_attribute = Data for the chi^2-fit Program
First scientific data set selected
sd_set_id = 262144
Obtained information about the data set
sd_set_name = SDStmpl
rank = 1
dim_sizes = 10
data_type = 24
n_set_attributes = 1
set_attr_name = First Set Annotation
set_attr_type = 4
set_attr_length = 25
set_attribute = redshift velocity in km/h
data (before) = 10*0
sd_read_status = 0
data (after) = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
sd_set_status = 0
sd_file_status = 0
gustav@blanc:../src 13:52:46 !584 $
As you see it all works just fine. We get information about the file,
its scientific data sets, annotations and all that.
How did we get here? Let us now have a look at the program itself.
It begins the same way as before, until we get to the following:
file_attr_status = sfgainfo(sd_file_id, file_attr_index, file_attr_name, &
file_attr_type, file_attr_length)
write (*, *) "file_attr_name = ", &
file_attr_name(:len_trim(file_attr_name))
write (*, *) "file_attr_type = ", file_attr_type
write (*, *) "file_attr_length = ", file_attr_length
Function sfgainfo
takes two arguments in: sd_file_id and
file_attr_index, which correspond, in this case, to the file
object and to the number of the first file attribute (here it
is zero). In general any HDF file object, including the file itself,
can have more than one attribute.
The function returns the name of
the attribute in file_attr_name. We must reserve an appropriate
amount of space for the name, but we know that it can't be longer
than MAXNCNAM, and so file_attr_name is defined to
be of that length. When we declare that variable we also fill it
entirely with spaces (this is like filling C strings with nulls).
You must not assume that Fortran run-time
system will do that for you. It doesn't have to!
When we write the value of file_attr_name on standard output
we trim it by calling function len_trim .
This function returns
the real length of the string, but it works on space padded strings
only. So this is a yet another reason why we have pre-padded
the string with spaces.
The next value returned by function sfgainfo
is file_attr_type. This is the type of data that constitutes
the attribute. As you can see the number returned is 4, which
corresponds to DFNT_CHAR8.
The last type is the length of the attribute, in other words the length of the string. We could use it to allocate a required amount of space dynamically, but for teaching purposes alone it would make this program too messy. So all that I do at this stage is merely to check that I have enough space to read the data and then read it by calling function sfrcatt:
if (file_attr_length .le. MAXNCNAM &
.and. file_attr_type .eq. DFNT_CHAR8) then
read_attr_status = sfrcatt(sd_file_id, file_attr_index, file_attribute)
write (*, *) "file_attribute = ", &
file_attribute(:len_trim(file_attribute))
end if
Observe that I check if the type of data is indeed a character
too.
It doesn't have to be. For example, an attribute can be an array of
numbers. But in that case you would have to use a different function
to read it. That function is sfrnatt .
Function sfrcatt has a very simple synopsis. It takes an object
ID as its first argument. It can be a file object or a data set object.
The second argument is an attribute indes, i.e., its number. Again,
we have only one attribute here, so its number is zero. The last
argument is what sfrcatt writes the data on. In our case
this is a string of a predefined length MAXNCNAM. That is
why I had to check that the string is long enough to take in all
the data. If it wasn't I would have to forgo reading the attribute,
or I would have to allocate required amount of memory dynamically.
Observe that when we write the content of file_attribute on
standard output, we trim it by calling function len_trim.
Having obtained the file attribute we proceed as before to select
the first (and only) scientific data set and to read information
about it, including, this time, information about its attributes
and the attributes themselves. This is done in the same way as
with the file attributes, the only difference being the use of
sd_set_id in place of sd_file_id.