How to read a MODIS HDF4 file using fortran ?

Published: June 02, 2016

DMCA.com Protection Status

Following previous article on How to read a MODIS HDF file using python let's see how to do the same thing in fortran:
Note: HDF libraries have to be installed in your system first (go to the link: obtaining the hdf software )

For example, let's consider the following MODIS HDF file : "MYD06_L2.A2008001.2040.006.2013341205855.hdf".

How to print all SDS names ?

Example of fortran source code (called here how_to_read_modis_hdf4.f90) to get all Scientific Datasets (SDS) names stored in the HDF file (see HDF user guide for more details):

program how_to_read_modis_hdf4

!----------------------------------------------------------------------------------------!

implicit none

integer i

character*300 file_name

integer DFACC_READ, DFNT_INT32

parameter ( DFACC_READ = 1, DFNT_INT32 = 24 )

integer sfstart, sfselect, sfrdata, sfendacc, sfend

integer sffinfo , sfginfo

integer sd_id, sds_id, sds_index

integer status, n_attrs

integer n_datasets, n_file_attrs , index

integer rank, data_type

integer MAX_NC_NAME , MAX_VAR_DIMS

parameter ( MAX_NC_NAME = 256 , MAX_VAR_DIMS = 32 )

character*( MAX_NC_NAME ) name

integer dim_sizes( MAX_VAR_DIMS )

!----------------------------------------------------------------------------------------!

call getarg(1,file_name)

write(6,*) 'file_name : ' , file_name

!----------------------------------------------------------------------------------------!
! Print all SDS names

sd_id = sfstart(file_name, DFACC_READ)

status = sffinfo( sd_id , n_datasets , n_file_attrs )

write(6,*) '! Number of data sets in the file and Number of file attributes :'
write(6,*) 'sffinfo :', status , n_datasets , n_file_attrs
write(6,*)

do index = 0, n_datasets - 1

    sds_id = sfselect(sd_id, index)
    status = sfginfo(sds_id, name, rank, dim_sizes, data_type,n_attrs)

    write(6,*) "name : ", name(1:100), "rank : ", rank, &
             & "index : ", index, "dimension sizes are : ", (dim_sizes(i), i=1, rank), & 
             &  "data type is : ", data_type, "number of attributes is : ", n_attrs

end do

!----------------------------------------------------------------------------------------!
! Close hdf file

status = sfendacc(sds_id)
status = sfend(sd_id)

!----------------------------------------------------------------------------------------!

end program how_to_read_modis_hdf4

!----------------------------------------------------------------------------------------!

Explanations: sfstart function open the HDF file; sffinfo function gives the number of SDS (n_datasets) and attributes (n_file_attrs) associated with the HDF file; sfselect function select a SDS using its index; sfginfo function returns information about the selected SDS and finally sfend close the file. To compile the code:

gfortran how_to_read_modis_hdf4.f90 -I/path_to_hdf/include/ -L/path_to_hdf/lib/ -lmfhdf -ldf -ljpeg -lz -o how_to_read_modis_hdf4

(replace "path_to_hdf" by the full path where HDF libraries are installed on your system) and to execute the code

how_to_read_modis_hdf4 MYD06_L2.A2008001.2040.006.2013341205855.hdf

The above code returns:

 ! Number of data sets in the file and Number of file attributes :
 sffinfo :           0         127          14

 name : Latitude                                                                                            rank :            2 index :            0 dimension sizes are :          270         408 data type is :            5 number of attributes is :           10
 -------------------------
 name : Longitude                                                                                           rank :            2 index :            1 dimension sizes are :          270         408 data type is :            5 number of attributes is :           10
 -------------------------
 name : Scan_Start_Time                                                                                     rank :            2 index :            2 dimension sizes are :          270         408 data type is :            6 number of attributes is :           10
 -------------------------
 name : Solar_Zenith                                                                                        rank :            2 index :            3 dimension sizes are :          270         408 data type is :           22 number of attributes is :           10
 -------------------------
 name : Solar_Zenith_Day                                                                                    rank :            2 index :            4 dimension sizes are :          270         408 data type is :           22 number of attributes is :           10
 -------------------------
 name : Solar_Zenith_Night   
.
.
.

sffinfo function returns 127 (meaning that MODIS C6 cloud optical products HDF files MYD/MOD06 contain 127 Scientific Datasets (SDS)

How to get the data ?

Suppose we know the name of the SDS we are interested in (for exemple "cloud_top_temperature_1km"). To find the index of the SDS "cloud_top_temperature_1km":

index = sfn2index(sd_id, 'cloud_top_temperature_1km')

To select the SDS

sds_id = sfselect(sd_id, index)

To get info about the selected SDS:

status = sfginfo( sds_id , name , rank , dim_sizes , data_type , n_attrs )

write(6,*) "index : ", index, "dimension sizes are : ", (dim_sizes(i), i=1, rank), & 
    &   "data type is : ", data_type, "number of attributes is : ", n_attrs

we got:

index :           60 dimension sizes are :         1354        2040 data type is :           22 number of attributes is :           10

meaning than cloud_top_temperature_1km SDS is a (1354,2040) array, data type is 22 corresponding to a 16-bit integer type in fortran (to see how to translate data type HDF Data Types and Fortran 90 kind parameter ) and has 10 attributes (which contain additional information about the SDS such as the valid range, the unit, etc).

Now to read the SDS, using sfrdata function:

integer*2, dimension(:,:), allocatable :: cloud_top_temperature_1km

allocate(cloud_top_temperature_1km(dim_sizes(1),dim_sizes(2)),STAT=AllocateStatus)
IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"
status = sfrdata( sds_id, start, stride, edges, cloud_top_temperature_1km )

Print cloud_top_temperature_1km values:

do i = 1, 1354
do j = 1, 2040
write(6,*) cloud_top_temperature_1km(i,j)
end do
end do

gives

9246
9246
9444
9444
9444
9444
9444
9444
9444
.
.
.

Note: Don't forget to deallocate the array if you don't want to go out of memory pretty quickly:

deallocate(cloud_top_temperature_1km, STAT = DeAllocateStatus)
if(DeAllocateStatus/=0) STOP

How to get the SDS attributes ?

cloud_top_temperature_1km SDS has 10 attributes, to get information about each attribute, there is the sfgainfo function:

do attr_index = 0, n_attrs - 1
status = sfgainfo(sds_id, attr_index, attr_name, data_type, n_values)
write(6,*) attr_name, data_type, n_values
end do

results

 valid_range                   22           2
 _FillValue                    22           1
 long_name                      4         123
 units                          4           1
 scale_factor                   6           1
 add_offset                     6           1
 Parameter_Type                 4           6
 Cell_Along_Swath_Sam          24           3
 Cell_Across_Swath_Sa          24           3
 Geolocation_Pointer            4          34

Let's say we want the 'scale_factor' and 'add_offset', the data type is 6 for both corresponding to a 64-bit floating-point type in fortran (see hdf data types and Fortran 90 kind parameter ):

real(KIND=8) scale_factor
real(KIND=8) add_offset

to select the attribute use the sffattr function and sfrattr to get selected attribute value. Example for 'scale_factor' attribute:

attr_index = sffattr(sds_id, 'scale_factor')
write(6,*) attr_index
status = sfrattr(sds_id, attr_index, scale_factor)
write(6,*) scale_factor

same for 'add_offset':

attr_index = sffattr(sds_id, 'add_offset')
write(6,*) attr_index
status = sfrattr(sds_id, attr_index, add_offset)
write(6,*) add_offset

Print cloud top temperature at 1km:

do i = 1, 1354
do j = 1, 2040
write(6,*) ( cloud_top_temperature_1km(i,j) - add_offset) * scale_factor 
end do
end do

results

242.45999458059669     
242.45999458059669     
242.45999458059669     
244.43999453634024     
244.43999453634024     
244.43999453634024     
244.43999453634024     
244.43999453634024

How to extract info from a byte ?

To extract info from a byte there is the fortran function ibits. Example: how to read Cloud_Mask_1km SDS stored in MOD/MYD06 cloud optical products (see MODIS C6 cloud optical products user guide page 115). The Cloud Mask 1km SDS has two bytes (1byte = 8 bits, so 16 bits for Cloud Mask 1km). Lets consider we want to get the "Cloud Mask Status Flag" and "Cloud Mask Cloudiness Flag". Both are stored in the first byte (see page 115 of MODIS C6 cloud optical products user guide ):

Cloud_Mask_1km(1,i,j)

where i,j are the pixel indexes of along track and cross track respectively. To extract "Cloud Mask Status Flag" and "Cloud Mask Cloudiness Flag" using ibits function:

cloud_mask_status_flag =  ibits(Cloud_Mask_1km(1,i,j),0,1)
cloud_mask_cloudiness_flag = ibits(Cloud_Mask_1km(1,i,j),1,2)

ibits function needs the position of the first bit and the number of bits used to code the information (0-based indexing):

Byte 1 ( Cloud_Mask_1km(1,i,j) )

Flag Name First bit index Number of Bits
Cloud Mask Status Flag 0 1
Cloud Mask Cloudiness Flag 1 2
Day/Night Flag 3 1
Sunglint Flag 4 1
Snow/Ice Flag 5 1
Surface Type Flag 6 2

Byte 2 ( Cloud_Mask_1km(2,i,j) )

Flag Name First bit index Number of Bits
Heavy Aerosol Flag 0 1
Thin Cirrus Flag 1 1
Shadow Flag 2 1

In summary: Cloud Mask Cloudiness Flag is stored in the first byte of Cloud_Mask_1km SDS (Cloud_Mask_1km(1,i,j)), first bit index = 1 and number of bits = 2: ibits(Cloud_Mask_1km(1,i,j),1,2).

See full code below.

Example of source code :

program how_to_read_modis_hdf4

!----------------------------------------------------------------------------------------!

implicit none

integer i

character*300 file_name

integer DFACC_READ, DFNT_INT32

parameter ( DFACC_READ = 1, DFNT_INT32 = 24 )

integer sfstart, sfselect, sfrdata, sfendacc, sfend

integer sffinfo , sfginfo

integer sd_id, sds_id, sds_index

integer status, n_attrs

integer n_datasets, n_file_attrs , index

integer rank, data_type

integer MAX_NC_NAME , MAX_VAR_DIMS

parameter ( MAX_NC_NAME = 256 , MAX_VAR_DIMS = 32 )

character*( MAX_NC_NAME ) name

integer dim_sizes( MAX_VAR_DIMS )

character * 100 :: sds_name

integer start(3), edges(3), stride(3)

integer sfn2index

integer :: AllocateStatus, DeAllocateStatus

! data type 22 => integer 16 bits

integer*2, dimension(:,:), allocatable :: cloud_top_temperature_1km

integer sffattr, sfrattr, sfgainfo

integer      attr_index, n_values

character*20 attr_name

real(KIND=8) scale_factor

real(KIND=8) add_offset

! data type 20 => integer 8 bits

integer*1, dimension(:,:,:), allocatable :: Cloud_Mask_1km

integer cloud_mask_status_flag, cloud_mask

!----------------------------------------------------------------------------------------!

call getarg(1,file_name)

write(6,*) 'file_name : ' , file_name

!----------------------------------------------------------------------------------------!
! Get all SDS names

sd_id = sfstart(file_name, DFACC_READ)

status = sffinfo( sd_id , n_datasets , n_file_attrs )

write(6,*) '! Number of data sets in the file and Number of file attributes :'
write(6,*) 'sffinfo :', status , n_datasets , n_file_attrs
write(6,*)

do index = 0, n_datasets - 1

    sds_id = sfselect(sd_id, index)
    status = sfginfo(sds_id, name, rank, dim_sizes, data_type,n_attrs)

    write(6,*) "name : ", name(1:100), "rank : ", rank, &
             & "index : ", index, "dimension sizes are : ", (dim_sizes(i), i=1, rank), & 
             &  "data type is : ", data_type, "number of attributes is : ", n_attrs

end do

!----------------------------------------------------------------------------------------!
! Example of how to extract the data

index = sfn2index(sd_id, 'cloud_top_temperature_1km')

write(6,*) 'index', index

sds_id = sfselect(sd_id, index)
status = sfginfo( sds_id , name , rank , dim_sizes , data_type , n_attrs )

sds_name = name(1:len(name))

write(6,*) "index : ", index, "dimension sizes are : ", (dim_sizes(i), i=1, rank), & 
        &   "data type is : ", data_type, "number of attributes is : ", n_attrs

do i = 1 , rank
start(i) = 0
edges(i) = dim_sizes(i)
stride(i) = 1
end do

allocate(cloud_top_temperature_1km(dim_sizes(1),dim_sizes(2)),STAT=AllocateStatus)
IF (AllocateStatus /= 0) STOP "*** Not enough memory ***"
status = sfrdata( sds_id, start, stride, edges, cloud_top_temperature_1km )

do i = 1, 100
    write(6,*) cloud_top_temperature_1km(i,100)
end do

!----------------------------------------------------------------------------------------!
! Example of how to get SDS attributes

do attr_index = 0, n_attrs - 1
    status = sfgainfo(sds_id, attr_index, attr_name, data_type, n_values)
    write(6,*) attr_name, data_type, n_values
end do

attr_index = sffattr(sds_id, 'scale_factor')
write(6,*) attr_index
status = sfrattr(sds_id, attr_index, scale_factor)
write(6,*) scale_factor

attr_index = sffattr(sds_id, 'add_offset')
write(6,*) attr_index
status = sfrattr(sds_id, attr_index, add_offset)
write(6,*) add_offset

do i = 1, 10
    write(6,*) ( cloud_top_temperature_1km(i,100) - add_offset) * scale_factor
end do

!----------------------------------------------------------------------------------------!
! Example of how to extract information from a Byte

index = sfn2index(sd_id, 'Cloud_Mask_1km')

write(6,*) 'Cloud_Mask_1km index', index

sds_id = sfselect(sd_id, index)
status = sfginfo( sds_id , name , rank , dim_sizes , data_type , n_attrs )

sds_name = name(1:len(name))

do i = 1 , rank
start(i) = 0
edges(i) = dim_sizes(i)
stride(i) = 1
end do

allocate(Cloud_Mask_1km(dim_sizes(1),dim_sizes(2),dim_sizes(3)),STAT=AllocateStatus)
IF (AllocateStatus /= 0) STOP "*** Not enough memory Cloud_Mask_1km***"
status = sfrdata( sds_id, start, stride, edges, Cloud_Mask_1km )

write(6,*) 'READ MODIS Cloud Mask Example : '

do i = 1, 100

    cloud_mask_status_flag = ibits(Cloud_Mask_1km(1,i,1),0,1)
    cloud_mask = ibits(Cloud_Mask_1km(1,i,1),1,2)

    write(6,*) cloud_mask_status_flag, cloud_mask

end do

!----------------------------------------------------------------------------------------!
! deallocate

deallocate(cloud_top_temperature_1km, STAT = DeAllocateStatus)
if(DeAllocateStatus/=0) STOP

deallocate(Cloud_Mask_1km, STAT = DeAllocateStatus)
if(DeAllocateStatus/=0) STOP

!----------------------------------------------------------------------------------------!
! Close hdf file

status = sfendacc(sds_id)
status = sfend(sd_id)

!----------------------------------------------------------------------------------------!

end program how_to_read_modis_hdf4

!----------------------------------------------------------------------------------------!

References