• ENVI_IDL: 如何读取HDF5文件再优美地做个均值处理?(以OMI数据为例)


    目录

    01 阅读提示

    02 HDF5函数说明

    2.1 常用的一些函数

    2.2 使用频率不高的函数 

    03 读取HDF5文件数据集输出为TIFF格式

    04 读取HDF5文件数据集并进行均值处理而后输出为GEOTIFF格式 


    01 阅读提示

    我将会以两个案例作为分析,第一个案例即是单纯地读取HDF5文件然后输出为Tiff格式,第二个案例即是读取HDF5文件并进行月、季、年地均值处理并输出为GeoTiff格式。(使用的数据是OMI Level 2G(Grid)数据, 是全球范围的关于NO2的数据)

    当然,如果你觉得阅读案例比较繁琐,你可以选择直接阅读下方关于HDF5文件的相关操作函数的说明。

    02 HDF5函数说明

    2.1 常用的一些函数

    前提说明:

    1. IDL对大小写是不敏感的,所以下面的函数既可以大写也可以小写,我个人比较偏好小写。

    2. 以下说明的函数中h5f(file)表示对hdf5文件进行的相关操作的函数;h5d(dataset)表示对hdf5文件中的数据集进行的相关操作的函数;h5a(attribute)表示对hdf5文件中的数据集的属性或者对hdf5文件中的全局属性进行的相关操作的函数。h5g(Group)表示对hdf5文件中的组进行的相关操作的函数。

    file_id = h5f_open(file_path,/write)

    功能:获取已经存在的hdf5文件,返回该hdf5文件的内存地址/id。

    解释:

    传入hdf5文件在硬盘上的路径;目前还传入了/write参数表示打开hdf5文件的方式是写入,如果不传入/write默认是以只读方式打开hdf5文件。

     dataset_id = h5d_open(file_id,dataset_path)

    功能:获取hdf5文件中的某一个数据集的id

    解释:

    如果你已经学习了如何打开hdf4文件,那么你可能存有疑虑:

    第一个参数传入数据集所在hdf5文件的id这个可以理解,但是第二个参数为什么是传入数据集的路径而不是传入数据集的名称呢?

    这个怎么说呢,我只能说这是人家的规定,如果你觉得不好,你可以自己尝试写一个idl,没有办法。

    这里解释一下第二个参数传入的数据集路径是什么?

    在hdf5文件中也有类似像硬盘上的一个个文件夹的东西,不过IDL将其称之为Geoup(组),不过大同小异,我们的数据集路径就是传入形如Group/Group/Group.../数据集名称 的格式。

    这里有两点需要注意:

    第一个Group你不能传入数据集所在hdf5文件的名称,因为它不是Group。

    第二个在IDL中,硬盘上的路径既可以以反斜杠<\>作为分隔符,也可以以斜杆作为分隔符,但是像Group这种路径则必须使用斜杆作为分隔符。

    dataset_data = h5d_read(dataset_id) 

    功能:获取数据集内的主体数据。

    解释:传入数据集的id号即可。这里的主体数据即是字面意思,它不包含属性内容。

     attr_id = h5a_open_name(dataset_id,attr_name)

    功能:获取属性的id/内存地址。

    解释:第一个参数传入属性所在数据集的id;如果属性是全局属性,那么传入文件id。第二个参数传入属性的名称。

    attr_data = h5a_read(attr_id)

    功能:获取属性内容。

    解释:传入属性的id号即可。这里需要说明一下,一般返回的属性内容比较简短,但是它大部分仍是以数组(array)的形式返回,所以利用属性进行一些数组之间的运算时需要特别注意,否则你会得到意想不到的结果。譬如全部都是数字2的3乘3矩A,我想让矩阵中的每一个数字都乘以属性b值,但是我们直接用获得到的数组形式的属性[a],那么是[a] * A,最终得到的值是只含有一个值的数组[2a];如果需要实现上述要求,那么应该是将属性值从数组中取出:[a][0] * A。这里不再赘述,可以自行敲代码验证。

     h5f_close,file_id

    功能:关闭已经打开的hdf5文件。

    解释:传入hdf5文件的id。但是这里需要说明该文件关闭之后,你将无法对该hdf5文件中任何资源进行访问,包括全局属性,数据集属性,数据集内容,组(Group)等等,即使你已经获取到了它们的内存地址(id)。

    其实对于数据集也有对应的关闭函数,但是这里我们一般都是把文件中所有需要获取的内容全部获取到后在直接关闭hdf5文件,而不会操作完某一个数据集关闭该数据集,操作完一个属性关闭该属性。总之一句话,一般我们只需要执行文件关闭的命令而累赘地执行数据集和属性地关闭命令。

    该函数会将hdf5文件在内存中占用的资源全部回收,连同内部的数据集等等,一旦资源被回收,那么你将无法通过id等去操作该文件内部的任何数据。除非你将该文件重新打开,将其从硬盘中重新加载到内存中。

    另外,其它的诸如h5d_close,h5a_close函数也是类似的使用方法,这里不再赘述。

     

    2.2 使用频率不高的函数 

    接下来的仍然是hdf5文件的相关操作函数,但是使用的频率低,只是略提一下。

    h5f_list,file_path,OUTPUT=变量

    功能:获取hdf5文件的相关基本信息。(看下图)

    解释:第一个参数传入hdf5文件的路径;后面的OUTPUT可传可不传,区别在于不传那么结果输出在屏幕上,传入一个变量,那么输出结果赋值给变量而不会输出到屏幕上。 

    attr_count = h5a_get_num_attrs(dataset_id) 

    功能:获取数据集下的属性个数,或者获取hdf5文件中的全局属性个数。

    解释:目前传入的是数据集的id,那么返回数据集下的属性个数,如果传入hdf5文件的id,那么返回hdf5文件下的全局属性的个数。

     attr_id = h5a_open_idx(dataset_id,index)

    功能:获取数据集下索引(index)为index的属性的id,或者获取hdf5文件中索引为index的全局属性的id。

    解释:目前第一个参数传入属性所在数据集的id,第二个参数传入属性的索引 ==> 获取的数据集的索引为index的属性的id;如果需要获取全局属性的id,那么需要第一个参数传入hdf5文件的id,第二个参数仍是传入索引。

    这个需要说明一下,索引和id是不一样的,索引是从0开始。如果有三个属性a,b,c,那么一次索引是0,1,2。但是这种函数一般使用的比较少,因为获取属性的名字我们可以通过软件HDF Explorer查询。

    attr_name = h5a_get_name(attr_id) 

    功能:获取属性的名称

    解释:传入属性的id,函数返回属性的名称。

     dataset_count = h5g_get_members(file_id,group_path)

    功能:获取某一个组下面所有数据集的个数。

    解释:传入hdf5文件的id,那么group_path就需要从最初的group开始写。如果传入的是Group的id,那么group_path可以从该Group之后往下写。

    dataset_name = h5g_get_obj_name_by_idx(group_id,dataset_index)

    功能:获取数据集的名称

    解释:传入数据集所在组的id,传入数据集的index。

    我所说明的函数只是常见或者较为常见的,还有其它函数大家可以自行查看书籍或者官方文档并敲代码实践 ==> 实践出真知。

    03 读取HDF5文件数据集输出为TIFF格式

    1. ; 本函数用于打开HDF5文件并获取数据集
    2. function get_hdf5_ds, file_path, ds_path ; 输入数据集所在文件的路径以及 --数据集的路径(关于这个看主程序的注释)
    3. ; 获取文件的id————》通过h5f_open()函数获取
    4. file_id = h5f_open(file_path)
    5. ; 传入文件的路径
    6. ; 获取数据集的id————》通过h5d_open()函数获取(另外需要说一下, h5d表示hdf(简称h)5(简称5)文件中的data set(简称d)的相关操作)
    7. ds_id = h5d_open(file_id, ds_path)
    8. ; 传入数据集所在文件的id,传入数据集的名称
    9. ; 获取数据集的数据————》通过h5d_read()函数获取
    10. data = h5d_read(ds_id)
    11. ; 传入数据集的id
    12. ; 打开的文件以及数据集需要全部关闭当不用的时候,这是习惯和态度问题(其实是为了后续你再使用这些文件避免一些错误和问题)
    13. h5d_close, ds_id ; 这里是h5d, 下面是h5f,两个函数是不一样的,需要注意一下
    14. h5f_close, file_id
    15. ; 返回数据集的数据
    16. return, data
    17. ; 销毁数据变量(避免空占内存——但是我是存疑的,return之后函数内部的所有变量不都会被销毁吗?还需要自己操作吗)
    18. ; 现在不需要存疑了,确实不会销毁,需要自己销毁,当然就算你不销毁,只要该程序整个运行完毕后,这些资源会被回收,除非你将其保存在硬盘中。
    19. ; 但是IDL中似乎对于程序运行占用的内存好像是由限制的,但是具体多少我忘了,只要你过多的几个变量存储同样大量的数据那么会有内存溢出的错误,导致程序运行失败,不过现在你还没有遇到。
    20. ; 但是养成这样子的习惯不好吗。未雨绸缪嘛
    21. data = !null ; 其实这个我感觉就是类似于重新赋值,那么原来的变量对应的数据就会被销毁。
    22. end
    23. pro week_three_study1
    24. ; 本程序解决如何读取OMI数据以及它的输出(OMI数据这里是HDF5格式,实际上是学习如何读取和输出HDF5文件)
    25. ; 获取文件路径以及数据集的路径(通过hdf explorer软件轻松获取)
    26. file_path = 'D:\IDL_program\experiment_data\chapter_2\NO2\2017\OMI-Aura_L3-OMNO2d_2017m0101_v003-2018m0627t042221.he5'
    27. ds_path = '/HDFEOS/GRIDS/ColumnAmountNO2/Data Fields/ColumnAmountNO2TropCloudScreened'
    28. ; 这里说明一下,hdf4不需要数据集在文件里面的目录,但是hdf5需要输入数据集在文件里面的目录
    29. ; 获取输出的文件夹路径
    30. out_dir_path = 'D:\IDL_program\experiment_data\chapter_2\output'
    31. ; 这里假定你不知道路径中output文件夹是否存在,且你并不愿意亲自查看,可以进行如下操作
    32. if file_test(out_dir_path, /directory) eq 0 then begin
    33. ; 这里有一个函数需要认识一下————》file_test()——》检查文件是否存在(默认检查文件,需要检查文件夹需要加上参数/directory)
    34. ; 其实上面的/directory说的不够准确,应该说是file_test()用来检查文件或者文件夹是否是否存在,加上/directory表示检查文件是否存在并且该文件是否是文件夹 ==> 也就是说即使一个文件存在但是他不是文件夹那么也会返回0
    35. ; 该函数会返回0或者10表示不存在,1表示存在
    36. ; 创建文件夹
    37. file_mkdir, out_dir_path
    38. endif
    39. ; 获取输出的文件的名称
    40. out_name = file_basename(file_path, '.he5') + '.tiff' ; 输出为tiff格式可以进行查看
    41. ; 这里file_basename()传入file_path已经可以将文件名称输出,但是我们不需要后缀名.he5
    42. ; 所以传入字符串'.he5'函数会将已经获取的文件名称中这个字符串删除并返回
    43. ; 输出的路径 = 输出文件夹的路径 + 输出文件的名称
    44. out_path = out_dir_path + '\' + out_name
    45. ; 获取数据集的数据
    46. data = get_hdf5_ds(file_path, ds_path)
    47. ; 输出
    48. write_tiff, out_path, data, /float
    49. ;传入输出的路径、输出的数据、另外这里默认是输出int型,但是我们通过hdf explorer发现该数据集的type是float,所需要指定一下(这很重要)
    50. end

     

    04 读取HDF5文件数据集并进行均值处理而后输出为GEOTIFF格式 

    这个案例举个可能不够好,因为它并不适合刚学完上述函数的学习者。但是相信你仍可以从中获取一些有用的信息。

    1. ; 获取数据集内容
    2. function get_he5_dataset, file_path, dataset_path
    3. ; 获取he5文件的id
    4. file_id = H5F_OPEN(file_path) ; 传入需要打开的hdf5文件的绝对路径, 不传入打开方式默认以只读方式打开, 传入/write表示可读可写
    5. ; 获取数据集的id
    6. dataset_id = H5D_OPEN(file_id, dataset_path) ; 传入参数: 数据集所在文件的id 数据集的名称
    7. ; 获取数据集的内容
    8. dataset_data = H5D_READ(dataset_id)
    9. ; 关闭打开的hdf5文件
    10. H5F_CLOSE, file_id
    11. ; 返回数据集内容
    12. return, dataset_data
    13. end
    14. ; 获取数据集属性
    15. function get_he5_attr, file_path, dataset_path, attr_name
    16. ; 获取he5文件的id
    17. file_id = H5F_OPEN(file_path) ; 传入需要打开的hdf5文件的绝对路径, 不传入打开方式默认以只读方式打开, 传入/write表示可读可写
    18. ; 获取数据集的id
    19. dataset_id = H5D_OPEN(file_id, dataset_path) ; 传入参数: 数据集所在文件的id 数据集的名称
    20. ; 获取数据集的属性的id
    21. attr_id = H5A_OPEN_NAME(dataset_id, attr_name)
    22. ; 获取数据集属性的内容
    23. attr_data = H5A_READ(attr_id)
    24. h5a_
    25. ; 关闭打开的hdf5文件
    26. H5F_CLOSE, file_id
    27. ; 返回数据集属性的内容
    28. return, attr_data
    29. end
    30. ; 判断目标数据集是否存在
    31. function exist_dataset, file_path, group_path, dataset_name
    32. ; 标记是否存在, 1为存在, 0为不存在
    33. exit_falg = 0
    34. ; 获取hdf5文件的id
    35. file_id = h5f_open(file_path) ; 不传入读写方式默认只读
    36. ; 获取目标数据集所在组的数据集个数
    37. dataset_num = H5G_GET_NMEMBERS(file_id, group_path) ; 传入文件的id, 传入组的路径
    38. ; 获取组id
    39. group_id = H5G_OPEN(file_id, group_path) ; 传入hdf5文件的id, 传入组的路径
    40. ; 循环搜查, 查看是否存在目标数据集
    41. for dataset_i = 0, dataset_num - 1 do begin
    42. ; 获取当前循环下的数据集名称
    43. dataset_i_name = H5G_GET_OBJ_NAME_BY_IDX(group_id, dataset_i)
    44. ; 查看是否为传入的dataset_name一致
    45. if dataset_name eq dataset_i_name then begin
    46. ; 标记赋值
    47. exit_falg = 1
    48. ; 结束循环
    49. break
    50. endif
    51. endfor
    52. ; 关闭刚刚打开的hdf5文件 ==> 注意, 这里你关闭了整个hdf5文件,就不需要再去关闭什么数据集,组什么的了,因为它们都在hdf5文件中,文件都关闭了,里面的数据集,组都无法访问,即便你已经得到了它们的id
    53. ; 所以,只有再获取到了文件里面你所需要的所有数据,你才可以去关闭hdf5文件,关闭之后内存中该hdf5文件的资源被回收,如果还需要访问该文件,你需要重新打开该文件将其从硬盘中重新加载到内存中才可以继续访问
    54. H5F_CLOSE, file_id
    55. ; 返回值
    56. return, exit_falg
    57. end
    58. pro omi_no2_average
    59. ; 记录一下开始时间
    60. start_time = SYSTIME(1)
    61. ; 所有he5文件所在的总路径
    62. files_path = 'D:\IDL_program\experiment_data\chapter_2\NO2'
    63. ; 获取所有he5文件的路径列表
    64. file_path_list = FILE_SEARCH(files_path, '*NO2*.he5') ; 限制一下, 需要取文件名称里面含有NO2以及后缀名是.he5的文件, 否则该文件不是我们寻找的对象
    65. ; 总共的he5文件的数量
    66. file_path_num = N_ELEMENTS(file_path_list)
    67. ; 需要获取的数据集的路径 --> 这个路径指的是在hdf5里面group/group/数据集名称这样子写, 而不能像hdf4那样子只写数据集名称
    68. ; 数据集所在的组的路径 ==> 这里可能需要解释一下, 就是反斜杠\和斜杠/, 在上面这种正常的文件路径里面你可以使用\或者/, 但是在hdf5文件里面的路径是只能\的, 否则报错
    69. group_path = 'HDFEOS/GRIDS/ColumnAmountNO2/Data Fields'
    70. ; 数据集名称
    71. no2_name = 'ColumnAmountNO2TropCloudScreened'
    72. ; 组合得到数据集在hdf5文件内的路径
    73. no2_path = group_path + '/' + no2_name
    74. ; 获取ColumnAmountNO2TropCloudScreened数据集的_FillValue属性内容
    75. no2_fv_name = '_FillValue'
    76. ; 获取年的最大值和最小值以及年数
    77. ; 获取所有文件的名称列表
    78. file_name_list = file_basename(file_path_list)
    79. ; 获取所有年的列表
    80. years_list = strmid(file_name_list, 19, 4)
    81. ; 获取最大年(str)
    82. max_year = max(years_list)
    83. ; 获取最小年(str)
    84. min_year = min(years_list)
    85. ; 获取最大年(int)
    86. max_year = fix(max_year)
    87. ; 获取最小年(int)
    88. min_year = fix(min_year)
    89. ; 获取年数
    90. year_num = max_year - min_year + 1 ; 这里注意要加一, 自己也要算进去
    91. ; 所有月数据的事先存储箱子
    92. months_total_box = fltarr(1440, 720, 12) ; 1440列, 720行, 12个月
    93. ; 所有月数据的有效次数的事先存储箱子
    94. months_valid_box = fltarr(1440, 720, 12)
    95. ; 月均值的事先存储箱子
    96. months_aver_box = fltarr(1440, 720, 12)
    97. ; 所有季数据的事先存储箱子
    98. seasons_total_box = fltarr(1440, 720, 4) ; 14407204
    99. ; 所有季数据的有效次数的事先存储箱子
    100. seasons_valid_box = fltarr(1440, 720, 4)
    101. ; 季均值的事先存储箱子
    102. seasons_aver_box = fltarr(1440, 720, 4)
    103. ; 所有年数据的事先存储箱子
    104. years_total_box = fltarr(1440, 720, year_num)
    105. ; 所有年数据的有效次数的事先存储箱子
    106. years_valid_box = fltarr(1440, 720, year_num)
    107. ; 年均值的事先存储箱子
    108. years_aver_box = fltarr(1440, 720, year_num)
    109. ; 由于获取的数据集的全球数据集, 但是hdf5文件中没有经纬度数据集, 所以需要我们自己构造
    110. ; 创建经纬度数据的空箱子
    111. latitude_box = fltarr(1440, 720)
    112. longitude_box = fltarr(1440, 720)
    113. ; 往箱子里面存放数据
    114. for column_i = 0, 1439 do begin
    115. for row_i = 0, 719 do begin
    116. latitude_box[column_i, row_i] = 89.875 - row_i * 0.25 ; 为什么是89.875而不是90呢?因为我们想要取每一个像素的最中间的经纬度, 下面的-179.875也是这个原因
    117. longitude_box[column_i, row_i] = -179.875 + column_i * 0.25
    118. endfor
    119. endfor
    120. ; 循环每一个路径,对每一个he5文件进行读取获取其中NO2云柱量数据集的数据并进行均值处理
    121. for file_path_i = 0, file_path_num - 1 do begin
    122. ; 获取当前循环下的路径
    123. file_path = file_path_list[file_path_i]
    124. ; 这里需要说明一下, 就是可能有的hdf5文件中目标数据集可能缺失没有, 所以需要判断一下, 如果没有那么结束本次循环跳转到下一次循环中
    125. if ~exist_dataset(file_path, group_path, no2_name) then begin
    126. ; 注意这里我加了一个~表示否定, 和python的not, C语言的!是一个意思. 这里的用法和C语言类似, 在这里 <> 可以使用and, 也可以使用&&; <> 可以使用or, 也可以使用||
    127. ; 结束当前循环
    128. print, '>>> 文件 ' + file_path + '不存在目标数据集'
    129. continue
    130. endif
    131. ; 获取数据集ColumnAmountNO2TropCloudScreened数据集的数据(数组)
    132. no2_data = get_he5_dataset(file_path, no2_path)
    133. ; 将数据进行颠倒 ==> 因为我们的OMI数据是全球数据, 而且上面是南极, 下面是北极, 所以需要颠倒
    134. no2_data = rotate(no2_data, 7) ; 7表示现在的x等于原来的x, 现在的y等于原来的-y, 也就是上下颠倒, 左右不颠倒
    135. ; 获取数据集no2_data中的属性_FillValue(array)
    136. no2_fv = get_he5_attr(file_path, no2_path, no2_fv_name)
    137. ; 获取数据集no2_data中的属性_FillValue(float)
    138. no2_fv = no2_fv[0]
    139. ; 通过he5文件的路径获取该文件的日期
    140. ; 获取he5文件的名称
    141. file_name = file_basename(file_path, '.he5') ; '.he5'表示获取最后一个斜杆之后的字符串之后,再将获取到的字符串中的字符串.he5删除(实际上是为了去除后缀名)
    142. ; 获取日期(str)
    143. file_year = strmid(file_name, 19, 4)
    144. file_month = strmid(file_name, 24, 2)
    145. file_day = strmid(file_name, 26, 2)
    146. ; 获取日期(int)
    147. file_year = fix(file_year) ; 没有int()这个函数, 就是fix()表示转化为int型
    148. file_month = fix(file_month)
    149. file_day = fix(file_day)
    150. ; 单位换算: 由于数据的单位是molc(分子数)/cm平方, 需要转化为mol/km平方, 1mol=6.02*10^23=!const.NA
    151. no2_data = ((no2_data ne no2_fv) * no2_data / !CONST.NA) * (10.0 ^ 10.0)
    152. ; 严重注意上面的10.0 ^ 10.0, 这里如果是10 ^ 10, 那么你的均值运算一定会出现问题, 就是10 ^ 10 是整数相乘, 但是需要注意, int的范围是比较小的, 所以计算会失败, 得出结果为-7168, 所以换成小数进行^是正确的
    153. ; 上面的式子让数值为no2_fv变为0, 数值不为该值的值进行单位换算.
    154. ; 存储月数据
    155. months_total_box[*, *, file_month - 1] += no2_data
    156. ; 存储月数据的有效次数
    157. months_valid_box[*, *, file_month - 1] += (no2_data ne 0.0)
    158. ; 存储季数据和季数据的有效次数
    159. season_list = [3, 3, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3]
    160. season_i = season_list[file_month - 1]
    161. ; 开始存储季数据和有效次数
    162. seasons_total_box[*, *, season_i] += no2_data
    163. seasons_valid_box[*, *, season_i] += no2_data ne 0.0
    164. ; 存储年数据
    165. years_total_box[*, *, file_year - min_year] += no2_data
    166. ; 存储年数据的有效次数
    167. years_valid_box[*, *, file_year - min_year] += no2_data ne 0.0
    168. endfor
    169. ; 这里假使有的数据的有效次数为0, 那么待会儿total / valid(求均值)就会出现除数为0的情况, 但是这种计算也是有结果的, 结果为NA(表示无效值)
    170. months_valid_box = (months_valid_box eq 0.0) + (months_valid_box ne 0.0) * months_valid_box
    171. seasons_valid_box = (seasons_valid_box eq 0.0) + (seasons_valid_box ne 0.0) * seasons_valid_box
    172. years_valid_box = (years_valid_box eq 0.0) + (years_valid_box ne 0.0) * years_valid_box
    173. ; 计算月均值
    174. for month_i = 0, 11 do begin
    175. months_aver_box[*, *, month_i] = months_total_box[*, *, month_i] / months_valid_box[*, *, month_i]
    176. endfor
    177. ; 计算季均值
    178. for season_i = 0, 3 do begin
    179. seasons_aver_box[*, *, season_i] = seasons_total_box[*, *, season_i] / seasons_valid_box[*, *, season_i]
    180. endfor
    181. ; 计算年均值
    182. for year_i = 0, year_num - 1 do begin
    183. years_aver_box[*, *, year_i] = years_total_box[*, *, year_i] / years_valid_box[*, *, year_i]
    184. endfor
    185. ; Geotiff的设置
    186. geo_info={$
    187. MODELPIXELSCALETAG:[0.25,0.25,0.0],$
    188. MODELTIEPOINTTAG:[0.0,0.0,0.0,-180.0,90.0,0.0],$
    189. GTMODELTYPEGEOKEY:2,$
    190. GTRASTERTYPEGEOKEY:1,$
    191. GEOGRAPHICTYPEGEOKEY:4326,$
    192. GEOGCITATIONGEOKEY:'GCS_WGS_1984',$
    193. GEOGANGULARUNITSGEOKEY:9102,$
    194. GEOGSEMIMAJORAXISGEOKEY:6378137.0,$
    195. GEOGINVFLATTENINGGEOKEY:298.25722}
    196. ; 输出路径
    197. out_path = files_path + '\average'
    198. ; 判断该路径是否存在, 如果不存在就创建
    199. if FILE_TEST(out_path, /DIRECTORY) eq 0 then begin
    200. ; 创建该目录
    201. file_mkdir, out_path
    202. endif
    203. ; 输出月均值
    204. for month_i = 0, 11 do begin
    205. out_month_path = out_path + '\month_aver_' + STRCOMPRESS(string(month_i + 1), /REMOVE_ALL) + '.tiff'
    206. WRITE_TIFF, out_month_path, months_aver_box[*, *, month_i], GEOTIFF=geo_info, /float
    207. ; 注意/float不要忘记了, 因为默认按照浮点数写入tiff文件, 但是我们的数据是浮点的, 所以需要指定/float告诉write_tiff函数要以float形式写入数据, 下面/float同理
    208. endfor
    209. print, '>>> 输出月均值完毕'
    210. ; 输出季均值
    211. season_name = ['spring', 'summer', 'autumn', 'winter']
    212. for season_i = 0, 3 do begin
    213. out_season_path = out_path + '\season_aver_' + STRCOMPRESS(season_name[season_i], /REMOVE_ALL) + '.tiff'
    214. WRITE_TIFF, out_season_path, seasons_aver_box[*, *, season_i], GEOTIFF=geo_info, /float
    215. endfor
    216. print, '>>> 输出季均值完毕'
    217. ; 输出年均值
    218. for year_i = 0, year_num - 1 do begin
    219. out_year_path = out_path + '\year_aver_' + STRCOMPRESS(string(year_i + min_year), /REMOVE_ALL) + '.tiff'
    220. WRITE_TIFF, out_year_path, years_aver_box[*, *, year_i], GEOTIFF=geo_info, /float
    221. endfor
    222. print, '>>> 输出年均值完毕'
    223. ; 记录一下结束时间
    224. end_time = SYSTIME(1)
    225. ; 程序总计用时
    226. spend_time = strcompress(string(end_time - start_time), /REMOVE_ALL) ; 里面是为了str化, 外边这个函数是为了将多余的空格删除清空
    227. ; 提示
    228. print, '>>> 用时 ' + spend_time + ' s'
    229. end

    我是炒茄子

  • 相关阅读:
    python——GIL锁详解
    防御第六次作业-笔记整理
    ssm基于Springboot的大学生竞赛辅导网站系统java-maven项目
    主机加固如何应对数据世界的绑匪
    OpenGL入门(一)之认识OpenGL和创建Window
    征途服务端安装
    ubuntu16.04安装驱动记录
    SpringCloud | 单体商城项目拆分(微服务)
    软考知识点之需求管理
    【MAPBOX基础功能】21、mapbox绘制自动旋转的图标
  • 原文地址:https://blog.csdn.net/m0_63001937/article/details/126382993