function file_extension, filename ; ; Determine the length of the filename string ; string_length = strlen(filename) ; ; Find the position of the last period of the filename. ; If there is no period, then set variable to the length ; of the filename. ; period_position = strpos( filename, '.', /reverse_search ) if period_position eq -1 then period_position = string_length ; ; Extract the extension ; answer=strmid( filename, period_position, string_length ) return, answer end function read_tape7scn, file_name ; ; Read in the tape7 file using read_ascii ; ; Keep track of header and number of records read ; n_header_lines = 11 data=read_ascii( file_name, header=tape5, $ data_start=n_header_lines, count=n_records) ; ; Check to make sure this tape7.scn file ran to completion, ; i.e., last record is -9999 ; last_record_index = n_records-1 last_record = data.(0)[0,last_record_index] last_valid_record = last_record_index-1 if (last_record ne -9999) then message, file_name+$ ' tape7.scn file not complete' ; ; The following is a retrofit after we adopted the use of ; the read_ascii routine. As much as read_ascii has a lot ; of utility, it might have been simpler to bypass it in ; light of the following problem. ; ; We need to figure out where all the NaNs are and properly ; reformat those records because for some unfortunate reason, ; when the NaN values are written to the tape7.scn file, the ; values get concatenated to the previous value, e.g., ; "2.8132E-04NaN" instead of "2.8132E-04 NaN". This causes ; the read_ascii routine to read fewer columns than usual. ; ; ; Count the number of columns ; n_columns = n_elements( data.(0)[*,0] ) ; ; Figure out where all the NaNs are in the data structure ; both in columns and records ; nan_indices=where(~finite(data.(0)[*,0:last_valid_record]), $ nan_count ) if ( nan_count gt 0 ) then begin ; ; Translate these vector dimensions into array dimensions ; so we know what columns and records they occur ; nan_array_indices=$ array_indices(data.(0)[*,0:last_valid_record], nan_indices) ; ; Find all the records in which the NaNs occur ; nan_records = nan_array_indices[1,*] ; ; Make sure those records are unique ; unique_nan_records = nan_records(uniq(nan_records)) ; ; Note how many of these records we have ; n_unique_nan_records = n_elements( unique_nan_records ) ; ; At this point, we know which records have NaNs in them. ; We now need to loop through each of these records ; and reread the information correcly so that the NaN ; values are located in the proper columns and the correct ; number of columns are set in these records ; input_string = '' new_columns = fltarr( n_columns ) for i = 0, n_unique_nan_records - 1 do begin ; ; Figure out the record number to the line contatining ; the NaN values. ; n_records_to_nan = unique_nan_records[i] + n_header_lines ; ; Create an array that will read up to the record containing ; the NaN values ; input_string_array = replicate(input_string, n_records_to_nan+1) ; ; Open the file ; openr,lun, file_name, /get_lun ; ; Read up to the location of the NaN ; readf,lun, input_string_array ; ; Close the file ; free_lun, lun ; ; Extract the line containing the NaN into a string variable ; target_string = input_string_array[n_records_to_nan] ; ; Separate out the string into an array of strings using ; 'NaN' as a delimiter ; target_string_array = $ strsplit( target_string, 'NaN', /extract ) ; ; Recombine the string array, but this time pad the NaN with ; spaces on both sides ; new_target_string = $ strjoin( target_string_array, ' NaN ' ) ; ; Read the newly formed string into an array of floats ; reads, new_target_string, new_columns ; ; Deposit the new array of floats back into the structure ; data.(0)[*,unique_nan_records[i]] = new_columns endfor endif ; ; The file appears to be OK, so we can proceed with ; determining the version of MODTRAN. ; ;-------------------------------------------------------- ; ; This is where we also need to add logic to figure out ; the wavelength and radiance units and send that ; information back to the calling routine. ; ; Maybe an augmented structure to what we currently ; return ; ;-------------------------------------------------------- ; ; First we setup a baseline structure that is common to ; Mod4v1r1, Mod5v1r1, and Modv2r4 so that we can ; translate the generic field records generated by read_ascii ; into a more meaningful structure ; baseline_tape7_columns= { TRAN: 0.0, $ PTH_THRML: 0.0, $ THRML_SCT: 0.0, $ SURF_EMIS: 0.0, $ SOL_SCAT: 0.0, $ SING_SCAT: 0.0, $ GRND_RFLT: 0.0, $ DRCT_RFLT: 0.0, $ TOTAL_RAD: 0.0, $ REF_SOL: 0.0, $ SOL_OBS: 0.0 } ; ; Next we need to figure out which version of MODTRAN ; we are dealing with. ; ; We will do this by counting the number of words ; in the column titles. ; ; Once we know which version we are working with, ; the baseline_tape7_columns can be augmented ; appropriately. ; column_titles = strsplit( tape5[ n_header_lines - 1], $ /extract, count=n_column_title_words ) case n_column_title_words of 23: begin ; print, "The tape7.scn file is Mod4v1r1 format" ; ; I DON'T LIKE THE FOLLOWING METHOD, BUT UNTIL WE KNOW ; THE RHYME OR REASON FOR THE EMPTY "DEPTH" COLUMN IN ; Mod4v3r1, WE WILL MAKE DO WITH THE FOLLOWING SCHEME ; OF KEYING OFF THE "FREQ" VS. "WAVLEN" DIFFERENCE ; BETWEEN Mod4v1r1 AND Mod4v3r1, RESPECTIVELY - RVR case column_titles[0] of 'FREQ' : begin ;print, "The tape7.scn file is Mod4v1r1 format" tape7_columns=create_struct('FREQ', 0.0, $ baseline_tape7_columns, 'DEPTH', 0.0 ) end 'WAVLEN' : begin ;print, "The tape7.scn file is Mod4v3r1 format" tape7_columns=create_struct('WAVELEN', 0.0, $ baseline_tape7_columns ) end ELSE: message, "Cannot determine MODTRAN version of tape7.scn" endcase end 15: begin ; print, "The tape7.scn file is Mod5v2r4 format" tape7_columns=create_struct('WAVELEN', 0.0, $ baseline_tape7_columns, 'DEPTH', 0.0, $ 'DIR_EM', 0.0, 'TOA_SUN', 0.0 ) end 22: begin ; print, " The tape7.scn file is Mod5v1r1 format" tape7_columns=create_struct('WAVELEN', 0.0, $ baseline_tape7_columns ) end ELSE: message, "Cannot determine MODTRAN version of tape7.scn" endcase ; ; Create a new array of structures to deposit the data ; tape7_columns_array = replicate( tape7_columns, n_records-1 ) ; ; In cases where DISORT is specified in the MODTRAN runs, ; the column THRML_SCT is left blank which causes problems ; in terms of the number of columns read in by the IDL ; READ_ASCII routine. ; We now need to keep track of the number of columns read in ; by READ_ASCII to make sure that we don't shift the spectra ; into the incorrect column titles ; n_columns_read = n_elements( data.(0)[*,0] ) n_column_titles = n_tags( tape7_columns ) thrml_sct_column = 3 ; ; Transfer data from read_ascii generic data structure to ; more meaningful MODTRAN data structure ; for i = 0, n_column_titles - 1 do begin ; ; In the number of columns read and the number of ; column titles are the same, then it is a ; straightforward transfer of data ; if ( n_columns_read eq n_column_titles ) then begin tape7_columns_array.(i) = $ reform(data.(0)[i,0:last_valid_record]) endif else begin ; ; If they are not equal, then that tells us that ; one of the columns (THRML_SCT) was blank so we ; have to make sure that subsequent data is ; assigned to the correct column ; if (i lt thrml_sct_column ) then tape7_columns_array.(i) = $ reform(data.(0)[i,0:last_valid_record]) if (i gt thrml_sct_column ) then tape7_columns_array.(i) = $ reform(data.(0)[i-1,0:last_valid_record]) end endfor ; ; Return filled data structure to calling routine ; return, tape7_columns_array end pro make_modtran_envi_spectral_library, tape7scn_filename modtran_spectral_data = read_tape7scn( tape7scn_filename ) ; ; Create a header for each MODTRAN radiometric parameter entry in the spectral library ; by pulling out all the structure names (except for the wavelengths) and depositing it ; into a string array ; all_names = tag_names( modtran_spectral_data ) n_all_names = n_elements( all_names ) names = all_names[1:*] ; ; Figure out how many spectra will be written out to the file ; n_spectra = n_elements( names ) ; ; Save the wavelength values into a variable ; wavelengths = modtran_spectral_data.(0) ; ; Figure out how many spectral points are in each spectra ; n_points = n_elements( wavelengths ) ; ; Open an ENVI spectral library with the appropriate file name and write out the different ; MODTRAN radiometric parameters ; openw, lun, tape7scn_filename+'.sli', /get_lun for i = 1, n_all_names - 1 do begin writeu, lun, modtran_spectral_data.(i) endfor ; ; Close the spectral library file ; free_lun, lun ; ; Register the ENVI file ; envi_setup_head, fname=tape7scn_filename+'.sli', data_type=4 , file_type=4, $ wl=wavelengths, wavelength_unit=0 , spec_names=names, /open, nb=1, ns=n_points, $ nl=n_spectra, interleave=0, r_fid=f_fid, /write ; ; Check the extensions of the tape7.scn file because this will ; determine if how we look for the input tape5 file ; ; First we need to check out what the extension of the tape7.scn ; file might be ; tape7_extension = file_extension( tape7scn_filename ) tape7_basename = file_basename(tape7scn_filename ) tape7_head = file_dirname(tape7scn_filename ) ; ; Once we have this all figured out, we should be able to get ; the correct tape5 file ; if (tape7_extension eq '.scn') then tape5_filename = tape7_head+'/'+'tape5' if (tape7_extension eq '.7sc') then tape5_filename = tape7_basename+'.tp5' ; ; Count how many lines are in the tape5 file and read in the data ; into a string array in order to put it into the spectral library ; header file. ; tape5 = read_ascii(tape5_filename,count=n_lines) carddeck = strarr( n_lines ) openr,lun, tape5_filename, /get_lun readf, lun, carddeck free_lun, lun ; ; Assign the tape5 data into the spectral library header ; envi_assign_header_value, fid=f_fid, keyword='tape5', value=carddeck ; ; Assign the current working directory into the spectral library ; header ; envi_assign_header_value, fid=f_fid, keyword='original_directory', $ value=tape7_head ; ; Make sure to write out the header file again ; envi_write_file_header, f_fid end