以EarthData提供的MOD16A2数据集(8-day,500m)为基础得到逐月的蒸散量(ET)栅格影像。该数据集的数据编号对应的日序如下图所示。以平年为例,3月份被包含在057、065、073、081、089这五幅影像中。
在之前的文章中,我提供了一种简单的思路利用重镶嵌功能,将ET栅格的时间分辨率从8-day转换为monthly,详情可参考[MODIS数据处理#5]例二:将ET栅格的时间分辨率从8-day转换为monthly。
本文在此基础上提供了一种批量处理的处理流程,希望能对各位有所帮助。
下载MOD16A2数据集,详细操作可参考:MOD16A2数据集下载教程。
这里下载了2000~2019年h25v04、h25v05、h26v04、h26v05、h27v05五个区块的MOD16A2数据(.hdf),如下:
这一步可以用MRT或HEG工具的批处理方法完成,可参考:
利用MRT工具预处理MODIS数据
或者基于ArcGIS的自定义脚本分步完成,可参考:
【ArcGIS自定义脚本工具】批量提取子数据集
【ArcGIS自定义脚本工具】按区块批量镶嵌MODIS影像
【ArcGIS自定义脚本工具】批量重投影栅格脚本
也可以利用LAADS DAAC(https://ladsweb.modaps.eosdis.nasa.gov/search/)的在线处理功能对HDF文件进行提取、拼接、投影等操作。
按选定的矢量边界文件对ET栅格进行裁剪,这里以黄土高原为例,具体步骤可以参考:
【ArcGIS自定义脚本工具】批量裁剪栅格脚本
裁剪后得到的文件如下所示:
由于MOD16数据集对于城市、冻土冰雪、裸地、水体这些区域仅用特殊值标记,不计算ET值,在此对这些区域进行排除。所用到的工具是设为空函数。具体的操作步骤可以参考:
批量设为空函数脚本
将包含X月初至X月末时间长度的8-day分辨率的ET影像利用镶嵌至新栅格工具合成到月分辨率。但需要注意以下两点:
为了方便批处理,在合成1~11月的影像时,镶嵌运算符选择MEAN;在合成12月的影响时,镶嵌运算符选择SUM。这两个镶嵌运算符的说明见下图:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import os
import time
import arcpy
def show_files(path, out_files, suffix=".tif", out_type="path"):
file_list = os.listdir(path)
for file in file_list:
cur_path = os.path.join(path, file)
if os.path.isdir(cur_path):
show_files(cur_path, out_files, out_type=out_type)
else:
if file.endswith(suffix):
if out_type == "path":
out_files.append(cur_path)
elif out_type == "name":
out_files.append(file)
else:
raise Exception("please select correct out_type value:path ;name")
months_ping = {
1: [1, 9, 17, 25],
2: [25, 33, 41, 49, 57],
3: [57, 65, 73, 81, 89],
4: [89, 97, 105, 113],
5: [121, 129, 137, 145],
6: [145, 153, 161, 169, 177],
7: [177, 185, 193, 201, 209],
8: [209, 217, 225, 233, 241],
9: [241, 249, 257, 265, 273],
10: [273, 281, 289, 297],
11: [305, 313, 321, 329],
12: [329, 337, 345, 353, 361]}
months_run = {
1: [1, 9, 17, 25],
2: [25, 33, 41, 49, 57],
3: [57, 65, 73, 81, 89],
4: [89, 97, 105, 113, 121],
5: [121, 129, 137, 145],
6: [153, 161, 169, 177],
7: [177, 185, 193, 201, 209],
8: [209, 217, 225, 233, 241],
9: [241, 249, 257, 265, 273],
10: [273, 281, 289, 297, 305],
11: [305, 313, 321, 329],
12: [329, 337, 345, 353, 361]}
in_path = arcpy.GetParameterAsText(0) # r"H:\taobao\suisdio\loess"
out_path = arcpy.GetParameterAsText(1)
pixel_type = arcpy.GetParameterAsText(2)
colormap_mode = arcpy.GetParameterAsText(3)
arcpy.env.workspace = in_path
all_tifs = []
show_files(in_path, all_tifs, out_type="name")
name_map = {
}
avail_years = []
for fname in all_tifs:
y = int(fname.split(".")[1][1:5])
d = int(fname.split(".")[1][-3:])
keyname = "{0}.{1}".format(y, d)
name_map[keyname] = fname
if y not in avail_years:
avail_years.append(y)
base = all_tifs[0]
out_coor_system = arcpy.Describe(base).spatialReference
cell_width = arcpy.Describe(base).meanCellWidth
band_count = arcpy.Describe(base).bandCount
groups = {
}
for year in avail_years:
months_temp = months_ping
if year % 4 == 0:
months_temp = months_run
for month in months_temp.keys():
new_name = "A{0}M{1}.tif".format(year, month)
target_days = months_temp[month]
groups[new_name] = []
for i in target_days:
fkey = "{0}.{1}".format(year, i)
if fkey in name_map:
groups[new_name].append(name_map[fkey])
nums = len(groups)
num = 1
for i in groups:
s = time.time()
month = int(i.split(".")[0][6:]) # A2000M2.tif
if len(groups[i]) == 0:
arcpy.AddMessage("{0}/{1} | {2} is None".format(num, nums, i))
num = num + 1
continue
groups[i] = ';'.join(groups[i])
if not os.path.exists(os.path.join(out_path, i)):
if month != 12:
arcpy.MosaicToNewRaster_management(groups[i], out_path, i, out_coor_system, pixel_type, cell_width, band_count,
"MEAN", colormap_mode)
else:
arcpy.MosaicToNewRaster_management(groups[i], out_path, i, out_coor_system, pixel_type, cell_width, band_count,
"SUM", colormap_mode)
e = time.time()
arcpy.AddMessage("{0}/{1} | {2} Completed, time used {3}s".format(num, nums, i, e - s))
else:
e = time.time()
arcpy.AddMessage("{0}/{1} | {2} existed, , time used {3}s".format(num, nums, i, e - s))
num = num + 1
注意:
上述代码中对栅格文件的命名有要求,栅格命名必须满足"xxxxx.AYYYYDDD.xxxxx"的形式,即以 . 为分隔符对文件名进行分组后,第二组满足形式AYYYYDDD,其中YYYY为年份,DDD为日序,例如A2000117。脚本从栅格的文件名中获取该栅格对应的年份和日序,可通过修改下面的代码块解决。
for fname in all_tifs:
y = int(fname.split(".")[1][1:5]) # 年份在文件名中的位置
d = int(fname.split(".")[1][-3:]) # 日序在文件名中的位置
keyname = "{0}.{1}".format(y, d)
name_map[keyname] = fname
if y not in avail_years:
avail_years.append(y)
经过上一步的操作,已经初步得到了月分辨率的ET栅格影像,对于1~11月份来说,此时的单位为mm/8day,缩放因子为0.1。以下图为例,黄土高原地区2000年1月份的ET范围为2.375mm/8day至9.5mm/8day。
而经过上一步脚本的操作后,由于对于12月份采用的镶嵌运算符为SUM,故此时的单位及缩放因子为:
以下图为例,黄土高原地区2000年12月份的ET范围为0mm/38day至47.4mm/38day。
因此,为了方便对比,利用乘工具,将单位统一换算为mm/day。
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import arcpy
from arcpy import env
from arcpy.sa import *
import os
raster_path = arcpy.GetParameterAsText(0)
new_dir_name = arcpy.GetParameterAsText(1)
def multiply_batch(new_dir_name, new_prefix="united_"):
env.workspace = raster_path
raster_list = arcpy.ListRasters()
for inraster in raster_list:
year = int(inraster.split(".")[0][1:5]) # 年份在文件名中的位置
month = int(inraster.split(".")[0][6:]) # 日序在文件名中的位置
if month == 12:
if year % 4 == 0:
scale_factor = 0.1 / 38
else:
scale_factor = 0.1 / 37
else:
scale_factor = 0.1 / 8
outraster = "\\" + new_dir_name + "\\" + new_prefix + inraster
arcpy.gp.Times_sa(inraster, scale_factor, outraster)
arcpy.AddMessage("Step2:" + str(new_prefix) + inraster[:] + " has done.")
arcpy.SetProgressorPosition()
arcpy.AddMessage("Step2:Completed")
arcpy.ResetProgressor()
dir_name = new_dir_name
out_path = raster_path + "\\" + dir_name
os.makedirs(out_path)
arcpy.AddMessage("Step1:Creating new folder named " + str(dir_name))
arcpy.AddMessage("Step1:Completed")
if arcpy.CheckExtension("Spatial") == "Available":
multiply_batch(dir_name)
else:
arcpy.AddMessage("Error!!! Spatial Analyst is unavailable")