next up previous index
Next: HDF Summary Up: Working with Scientific Data Previous: Annotating an HDF File

Reading File and Data Set Attributes

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.


next up previous index
Next: HDF Summary Up: Working with Scientific Data Previous: Annotating an HDF File
Zdzislaw Meglicki
2001-02-26