ENVI IDL: How to reproject MODIS GRID data?

01 Description

It contains three pro processes, of which modis_grid_warp is the main program, modis_grid_geo_transform and img_warp are regular pro processes, and read_h4 is a custom function.

modis_grid_geo_transform generates a projection coordinate data set based on corner information and converts it into a longitude and latitude data set, and img_warp performs reprojection (GLT correction) based on the longitude and latitude data set

02 complete code

2.1 modis_grid_geo_transform process

; +
; Function usage:
; This program is used to read the corner information of MODIS GRID products and return the latitude and longitude data set
; Function parameters:
; h4_path: path of MODIS GRID file
; extract_lon: the returned longitude data set
; extract_lat: returned latitude data set
; ds_size: (keyword parameter) binary array containing column number and row number, if not passed in, read from file
; helpme: View function usage
;-
pro modis_grid_geo_transform, h4_path, extract_lon, extract_lat, ds_size=ds_size

    ; Get metadata
    h4_id = hdf_sd_start(h4_path, /read)
    metadata_ix = hdf_sd_attrfind(h4_id, 'StructMetadata.0')
    hdf_sd_attrinfo, h4_id, metadata_ix, data=metadata
    
    ; Get the row and column number
    if ~keyword_set(ds_size) then begin
        ds_size = make_array(2, /double)
        ix_one = strpos(metadata, 'XDim')
        ix_two = strpos(metadata, 'YDim')
        ix_three = strpos(metadata, 'UpperLeftPointMtrs')
        ds_size[0] = (strsplit(strmid(metadata, ix_one, ix_two - ix_one), '=', /extract))[1]
        ds_size[1] = (strsplit(strmid(metadata, ix_two, ix_three - ix_two), '=', /extract))[1]
    endif else begin
        ds_size = double(ds_size); Convert to double precision to avoid subsequent out-of-range
    endelse
    
    ; Get corner point information
    ix_one = strpos(metadata, 'UpperLeftPointMtrs')
    ix_two = strpos(metadata, 'LowerRightMtrs')
    ix_three = strpos(metadata, 'Projection')
    ul_coor = strsplit(strmid(metadata, ix_one, ix_two - ix_one), '=(,)', /extract)
    dr_coor = strsplit(strmid(metadata, ix_two, ix_three - ix_two), '=(,)', /extract)
    ul_coor = double(ul_coor[1:2])
    dr_coor = double(dr_coor[1:2])
    x_res = (dr_coor[0] - ul_coor[0]) / ds_size[0]
    y_res = (ul_coor[1] - dr_coor[1]) / ds_size[1]
    
    ; Calculation-projected coordinate data set
    x_prj_coor = make_array(ds_size, /double)
    y_prj_coor = make_array(ds_size, /double)
    for ix=0, ds_size[0] - 1 do x_prj_coor[ix, *] = ul_coor[0] + x_res * ix + x_res / 2.0; represents the center position of the pixel
    for ix=0, ds_size[1] - 1 do y_prj_coor[*, ix] = ul_coor[1] - y_res * ix - y_res / 2.0
    x_prj_coor = reform(x_prj_coor, ds_size[0] * ds_size[1], /overwrite); /overwrite avoids copying and speeds up copying
    y_prj_coor = reform(y_prj_coor, ds_size[0] * ds_size[1], /overwrite)
    ; The reform is performed to adapt to the input of the map_proj_inverse function: An n-element vector containing the x values.
    
    ; Projected coordinate data set ==> Latitude and longitude data set
    prj_info = map_proj_init('Sinusoidal', /gctp, sphere_radius=6371007.181, $
        center_longitude=0.0, false_easting=0.0, false_northing=0.0)
    ; It should be noted that gctp represents U.S. Geological Survey's General Cartographic Transformation Package (GCTP) for projection rather than IDL's own projection library
    geo_coor = map_proj_inverse(x_prj_coor, y_prj_coor, map_structure=prj_info)
    extract_lon = geo_coor[0, *].reform(ds_size)
    extract_lat = geo_coor[1, *].reform(ds_size)
end

2.2 img_warp process

; +
; Function usage:
; Geometrically correct the target dataset based on the latitude and longitude dataset (reprojection-WGS84)
; Function parameters:
; target: target data set to be corrected
; lon: Longitude data set corresponding to the target data set
; lat: latitude data set corresponding to the target data set
; out_res: output resolution (°)
; target_warped: corrected target data set
; geo_info: the geographical structure corresponding to the corrected target data set
; degree<keyword argument: 5>: degree of polynomial
; interp<keyword parameter: nearest>: interpolation algorithm (including: nearest, linear, cublic)
; sub_percent<keyword parameter: 0.1>: By default, 10% of the points are used for geometric correction
;-
pro img_warp, target, lon, lat, out_res, target_warped, geo_info, degree=degree, interp=interp, $
    sub_percent=sub_percent

    ; Get basic information
    ds_size = size(target)
    lon_lat_corner = hash($; considering that min()max() is the center of the corner pixel rather than the boundaries of the four corner points
        'min_lon', min(lon) - out_res / 2.0d, $
        'max_lon', max(lon) + out_res / 2.0d, $
        'min_lat', min(lat) - out_res / 2.0d, $
        'max_lat', max(lat) + out_res / 2.0d)
    col_count_out = ceil((lon_lat_corner['max_lon'] - lon_lat_corner['min_lon']) / out_res)
    row_count_out = ceil((lon_lat_corner['max_lat'] - lon_lat_corner['min_lat']) / out_res)
    interp_code = hash($
        'nearest', 0, $
        'linear', 1, $
        'cublic', 2)
    if ~keyword_set(interp) then interp='nearest'
    if ~keyword_set(degree) then degree=5
    if ~keyword_set(sub_percent) then sub_percent = 0.1

    ; Original row and column number matrix
    row_ori = make_array(ds_size[1:2], /integer)
    col_ori = make_array(ds_size[1:2], /integer)
    for ix=0, ds_size[1]-1 do col_ori[ix, *] = ix
    for ix=0, ds_size[2]-1 do row_ori[*, ix] = ix
    
    ; Corrected row and column number matrix
    col_warp = floor((lon - lon_lat_corner['min_lon']) / out_res)
    row_warp = floor((lon_lat_corner['max_lat'] - lat) / out_res)
    
    ; Get the uniform sampling point index of sub_percent
    all_count = ds_size[1] * ds_size[2]
    sample_count = floor(all_count * sub_percent)
    sample_ix = floor((findgen(sample_count) / double(sample_count)) * all_count)
    
    polywarp, col_ori[sample_ix], row_ori[sample_ix], col_warp[sample_ix], row_warp[sample_ix], $
        degree, k_col, k_row
    target_warped = poly_2d(target, k_col, k_row, interp_code[interp], $
        col_count_out, row_count_out, missing=!values.F_NAN)
    
    geo_info={<!-- -->$
        MODELPIXELSCALETAG: [out_res, out_res, 0.0], $ ; resolution
        MODELTIEPOINTTAG: [0.0, 0.0, 0.0, lon_lat_corner['min_lon'], lon_lat_corner['max_lat'], 0.0], $ ; Corner information
        GTMODELTYPEGEOKEY: 2, $ ; Set to geographic coordinate system
        GTRASTERTYPEGEOKEY: 1, $ ; pixel representation type, North-Up image (North-Up)
        GEOGRAPHICTYPEGEOKEY: 4326, $ ; The geographical coordinate system is WGS84
        GEOGCITATIONGEOKEY: 'GCS_WGS_1984', $
        GEOGANGULARUNITSGEOKEY: 9102} ; Units are degrees
end

2.3 read_h4 function

; +
; Function usage:
; Dataset and attributes used to read HDF4 files
; Function parameters:
; h4_path: The path of the HDF4 file to be read
; ds_name: name of the data set to be read
; attr_name<keyword parameter>: If the attribute name is passed in, the attribute information under the data set will be returned.
; double<keyword argument, /double>: if specified, converts the returned data set to double precision floating point type
;-
function read_h4, h4_path, ds_name, attr_name=attr_name, double=double
    ; This function is used to read HDF4 file data sets or attributes

    h4_id = hdf_sd_start(h4_path, /read)
    ds_index = hdf_sd_nametoindex(h4_id, ds_name)
    ds_id = hdf_sd_select(h4_id, ds_index)
    if keyword_set(attr_name) then begin
        attr_index = hdf_sd_attrfind(ds_id, attr_name)
        hdf_sd_attrinfo, ds_id, attr_index, data=data
    endif else begin
        hdf_sd_getdata, ds_id, data
        if keyword_set(double) then data = double(data)
    endelse

    hdf_sd_endaccess, ds_id
    hdf_sd_end, h4_id

    return, data
end

2.4 modis_grid_warp process (main program)

; @Author : ChaoQiezi
; @Time : October 19, 2023 - 4:26:55 pm
; @Email: [email protected]

; This program is used to reproject the MODIS GRID data set
pro modis_grid_warp
    ; Prepare
    in_dir = 'D:\Objects\JuniorFallTerm\IDLProgram\Experiments\ExperimentalData\chapter_3\MYD11A1\'
    out_dir = 'D:\Objects\JuniorFallTerm\IDLProgram\Experiments\ExperimentalData\chapter_3\IDL_out_grid_warp_me\'
    if ~file_test(out_dir, /directory) then file_mkdir, out_dir
    lst_name = 'LST_Day_1km'
    range_name = 'valid_range'
    sf_name = 'scale_factor'
    out_res = 0.01 ; degree
    
    h4_paths = file_search(in_dir, '*.hdf', count=h4_paths_count)
    foreach h4_path, h4_paths do begin
        start_time = systime(1)
        ; Get data sets and attributes, etc.
        lst = read_h4(h4_path, lst_name, /double)
        valid_range = read_h4(h4_path, lst_name, attr_name=range_name)
        scale_factor = read_h4(h4_path, lst_name, attr_name=sf_name)
        lst_size = size(lst)
        
        ; LST data preprocessing
        invalid_ix = where((lst lt valid_range[0]) or (lst gt valid_range[1]))
        lst[invalid_ix] = !values.F_NAN
        lst = lst * scale_factor[0]
        
        ; Get the latitude and longitude data set
        modis_grid_geo_transform, h4_path, lon, lat, ds_size=lst_size[1:2]
        
        ; Perform geometric correction (reprojection)
        img_warp, lst, lon, lat, out_res, lst_warped, geo_info

        ; Output
        out_path = out_dir + file_basename(h4_path, '.hdf') + '.tiff'
        write_tiff, out_path, lst_warped, geotiff=geo_info, /float
        print, file_basename(h4_path, '.hdf'), systime(1) - start_time, format='%s: %0.2f s'
    endforeach
    
end

Bye~