GEE:批量处理和下载指定时间段的MODIS-GPP产品(MOD17A3HGF)

01 说明

  1. 任务分成两个:第一是指定时间范围,提取该范围内的所有GPP影像求取均值;第二是指定时间范围,按年尺度提取每一年中的GPP影像求取均值(一年一景)
  2. 要求包括:全球尺度、输出地理坐标系WGS84且分辨率为0.05°、无效值处理;

02 代码说明

2.1 输出指定时间段的影像均值

完整代码:

javascript 复制代码
// 选择需要输出GPP产品的时间范围
var start_date = ee.Date.fromYMD(2023, 1, 1);  // 开始日期
var end_date = ee.Date.fromYMD(2023, 12, 31);  // 结束日期
var gpp_name = 'Gpp';  // 需要的波段名称, MOD17A3HGF包括Gpp、Npp以及Npp_QC三个波段
var apply_land_mask = true;  // 是否掩膜只要陆地地区, 若为true则掩膜;若为false则不进行掩膜
var min_value = 0;  // 有效值的范围
var max_value = 65500;  // 同理
var out_res = 0.05  // 输出分辨率(单位为°)


// 加载MODIS-GPP产品(MOD17A3HGF)
var gpp_collection = ee.ImageCollection("MODIS/061/MOD17A3HGF");  // 从云计算平台加载指定数据集
// 筛选指定年份的影像
var gpp_img = gpp_collection.filterDate(start_date, end_date).select(gpp_name).mean();
// 获取并应用有效值范围的掩膜
var valid_range_mask = gpp_img.gte(min_value).and(gpp_img.lte(max_value));
gpp_img = gpp_img.updateMask(valid_range_mask);
// 应用缩放因子(缩放后单位为:kg*C/m^2, 由于MOD17A3是年尺度,因此这也可以视为kg*C/m^2/year)
gpp_img = gpp_img.multiply(0.0001);
/*
为什么要应用缩放因子?
因为GEE上存储的数据量非常大, 整数存储相比于小数存储在大数据量面前将会有非常大的性价比减少
更多的存储空间, 因此存储时会先将小数放大n倍转化为小数,在需要时乘以缩放因子即可恢复原来的数值.
*/
// 掩膜(只需要陆地地区)
if (apply_land_mask === true){
  var water_mask = ee.Image('MODIS/MOD44W/MOD44W_005_2000_02_24').select('water_mask');
  var land_mask = water_mask.eq(0);
  gpp_img = gpp_img.updateMask(land_mask);
}
// 定义地理范围
var global_region = ee.Geometry.Rectangle([-180, -90, 180, 90], 'EPSG:4326', false);


// 可视化参数
var gpp_vis_param = {
  min: 0.0,
  max: 3.0,
  palette: ['#bbe029', '#0a9501', '#074b03']
};
// 可视化
Map.setCenter(0, 0, 2);  // 显示的中心位置和缩放大小, 参数依次为(经度: 0, 纬度: 0, 缩放级别:2)


// 导出影像到Google Drive
var cur_year = start_date.get('year');
cur_year.evaluate(function(cur_year){
  Map.addLayer(gpp_img, gpp_vis_param, 'Annual_GPP_' + cur_year);
  
  Export.image.toDrive({
  image: gpp_img.resample('bilinear'),  // 输出使用双线性插值, 若不需要删除.resample('bilinear')
  description: 'Global_GPP_' + cur_year,
  folder: 'Global_GPP',
  fileNamePrefix: 'Global_GPP_' + cur_year,
  region: global_region,
  crs: 'EPSG:4326',  // 坐标系为WGS84
  crsTransform: [out_res, 0, -180, 0, -out_res, 90],  // 
  maxPixels: 1e13,  // 防止像元个数过多拒绝导出, 增加导出上限
  formatOptions: {'noData': -9999}  // NoData值为-9999
  });
});

可能需要说明的是evaluate方法(这段代码使用evaluate方法有点多余,但是后续批量输出下载等是很难避免使用这个方法,不如在这里将这个方法稍微理一理):

原文为:Asynchronously retrieves the value of this object from the server and passes it to the provided callback function. 即异步地将该变量(谁调用的evaluate方法谁就是此处所指代的the value,例如year_list.evaluate(),则the value指代year_list)传递给服务器,让服务器计算它的值,服务器计算好该值之后再将其提供给回调函数。 所以含义是什么? 不管异步这一操作,就是在客户端(你的浏览器)上存在一个变量,这个变量具体的值还不清楚没有计算但是你现在又需要这里面的值来进行后续处理,那么你就可以通过evaluate方法将其传递给服务器(GEE的远程云计算平台),让服务器计算好之后,再传送回给客户端浏览器,再继续在客户端本地进行后续处理,这里的后续处理就是前面提及的回调函数,服务器计算好的结果应该怎么传送回给本地浏览器呢?你用一个变量去接?接住了还需要进行处理? ⇒ 那么方法就是定义一个回调函数以及一个函数的参数,服务器计算好之后将这个计算好的变量值传递给这个回调函数参数,然后运行这个回调函数。 那么异步是什么意思呢?就是year_list.evaluate执行之后,变量到了服务器就好,接着往后运行后续代码,而至于.evaluate()内部的回调函数的定义、运行回调函数它慢慢运行就好了,我的主程序是不会等待的。举一个不恰当的例子就是我在去银行取号排队,中间排队这个过程就是evaluate函数内部做的事情(变量上传给服务器、服务器计算结果、结果返回给回调函数,执行回调函数),但是排队过程我无需排队一直等待(即不用等前面括号内的这一系列事情做完),我可以去做我想做的事情(执行后续代码)例如买零食买玩具。

上述的evaluate方法因为时间关系并没有解释特别到位,但是这里列举一点示例稍微说明一下这个函数

javascript 复制代码
// 情况1
var year_list = [2010, 2011, 2012, 2013, 2014];
year_list.map(function(cur_year){
  print(cur_year);
});

// 情况2
var year_list = ee.List.sequence(2010, 2014)
year_list.map(function(cur_year){
  print(cur_year);
});

// 情况3
var year_list = ee.List.sequence(2010, 2014)
year_list.evaluate(function(year_list){
  year_list.map(function(cur_year){
    print(cur_year);
  });
})

对于上面三种情况,只有情况2无法运行会报错,为什么呢?这里就需要理解为什么要使用.evaluate()方法了。由于year_list中的各个元素值是需要print输出的。而通过ee.List.sequence定义的year_list使用的map方法是需要在服务端上运行的(这里就要提及为什么情况1的map就不需要,因为通过[]定义的是具体array数组,其map方法不需要在服务器中运行,此外这两个map方法也有一定的差别,如果对于情况2的报错内容仔细查看发现首先是报错map方法没有返回值),而map方法中的print是在本地进行运行的(因为print输出肯定是在浏览器中展示结果自然是需要在本地浏览器中运行),因此map在服务器中运行时,遇到了print,服务器不知道打印到哪里去,因此报错了。但是对于情况3,year_list.evaluate将变量传递给服务器计算之后传回本地浏览器,执行回调函数,由于变量已经传回本地了因此print输出自然没有问题。

至于异步这一问题,查看下方代码和输出自行理解即可,这里不再赘述:

javascript 复制代码
// 情况4
print('1. 代码开始运行')
var year_list = ee.List.sequence(2010, 2014)
year_list.evaluate(function(year_list){
  print('3. 获取得到服务器传递的变量值, 批量输出中···')
  year_list.map(function(cur_year){
    print(cur_year);
  });
  print('4. 回调函数执行完毕')
})
print('2. 主程序运行完毕')

输出结果:

2.2 年尺度上批量输出指定时间段的一年影像均值(一年一景)

完整代码:

javascript 复制代码
// 选择需要输出GPP产品的时间范围(一年一景)
var start_date = ee.Date.fromYMD(2001, 1, 1);  // 开始日期, MOD17A3的生产范围是2001-1-1至今
var end_date = ee.Date.fromYMD(2020, 12, 31);  // 结束日期
var gpp_name = 'Gpp';  // 需要的波段名称, MOD17A3HGF包括Gpp、Npp以及Npp_QC三个波段
var apply_land_mask = true;  // 是否掩膜只要陆地地区, 若为true则掩膜;若为false则不进行掩膜
var is_vis = true  // 是否在下方地图中显示每一年的GPP产品(耗时)
var min_value = 0;  // 有效值的范围
var max_value = 65500;  // 同理
var out_res = 0.05;  // 输出分辨率(单位为°)


// 获取年列表-用于批量输出
var start_year = start_date.get('year');
var end_year = end_date.get('year');
var year_list = ee.List.sequence(start_year, end_year)

// 加载MODIS-GPP产品(MOD17A3HGF)
var gpp_collection = ee.ImageCollection("MODIS/061/MOD17A3HGF");  // 从云计算平台加载指定数据集
// 获取陆地掩膜
var water_mask = ee.Image('MODIS/MOD44W/MOD44W_005_2000_02_24').select('water_mask');
var land_mask = water_mask.eq(0);
// 定义地理范围(默认全球)
var global_region = ee.Geometry.Rectangle([-180, -90, 180, 90], 'EPSG:4326', false)

// 可视化参数
var gpp_vis_param = {
  min: 0.0,
  max: 3.0,
  palette: ['#bbe029', '#0a9501', '#074b03']
};
// 可视化
Map.setCenter(0, 0, 2);  // 显示的中心位置和缩放大小, 参数依次为(经度: 0, 纬度: 0, 缩放级别:2)


// 导出影像到Google Drive(ps: 一年一景是理论, 实际上由于全球区域太大且分辨率过高,因此一年的tiff会分块输出成多个tiff文件)
year_list.evaluate(function(year_list){
  year_list.map(function(cur_year){
    var cur_start_date = ee.Date.fromYMD(cur_year, 1, 1);
    var cur_end_date = ee.Date.fromYMD(cur_year, 12, 31);
    
    // 筛选指定年份的影像
    var gpp_img = gpp_collection.filterDate(cur_start_date, cur_end_date).select(gpp_name).first();
    // 获取并应用有效值范围的掩膜
    var valid_range_mask = gpp_img.gte(min_value).and(gpp_img.lte(max_value));
    gpp_img = gpp_img.updateMask(valid_range_mask);
    // 应用缩放因子(缩放后单位为:kg*C/m^2, 由于MOD17A3是年尺度,因此这也可以视为kg*C/m^2/year)
    gpp_img = gpp_img.multiply(0.0001);
    /*
    为什么要应用缩放因子?
    因为GEE上存储的数据量非常大, 整数存储相比于小数存储在大数据量面前将会有非常大的性价比减少
    更多的存储空间, 因此存储时会先将小数放大n倍转化为小数,在需要时乘以缩放因子即可恢复原来的数值.
    */
    // 掩膜(只需要陆地地区)
    if (apply_land_mask === true){
      gpp_img = gpp_img.updateMask(land_mask);
    }
    // 是否可视化
    if (is_vis === true){
      Map.addLayer(gpp_img, gpp_vis_param, 'Annual_GPP_' + cur_year);
    }
    
    Export.image.toDrive({
    image: gpp_img.resample('bilinear'),  // 若需要最近邻插值, 删除.resample('bilinear')即可
    description: 'Global_GPP_' + cur_year,
    folder: 'Global_GPP',
    fileNamePrefix: 'Global_GPP_' + cur_year,
    crs: 'EPSG:4326',  // 坐标系为WGS84
    crsTransform: [out_res, 0, -180, 0, -out_res, 90],  // 仿射系数
    region: global_region,  // 输出的地理范围
    maxPixels: 1e13,  // 防止像元个数过多拒绝导出, 增加导出上限
    formatOptions: {'noData': -9999}  // NoData值为-9999
    });
  })
});

这里关于代码说明不在过多赘述,原理类似,只是多加了一层循环。

本文由博客一文多发平台 OpenWrite 发布!

相关推荐
算法_小学生28 分钟前
Huber Loss(胡贝损失)详解:稳健回归的秘密武器 + Python实现
人工智能·python·深度学习·机器学习
陌上倾城落蝶雨1 小时前
python爬虫
python·scrapy·pycharm
木子杳衫1 小时前
【Python】LEGB作用域 + re模块 + 正则表达式
数据库·python·正则表达式
寄思~1 小时前
PyQt5—Qt QDialog 学习笔记
开发语言·笔记·python·qt·学习
熊猫钓鱼>_>2 小时前
编程实现Word自动排版:从理论到实践的全面指南
人工智能·python·算法
婪苏4 小时前
Python 异常机制详解:从 Error 类型到 raise 与 assert 的对比
后端·python
别在内卷了4 小时前
测试学习之——Pytest Day3
python·学习·pytest
MediaTea4 小时前
Python:简易的 TCP + SSL 服务端与客户端示例
开发语言·python·网络协议·tcp/ip·ssl
墨尘游子4 小时前
3-大语言模型—理论基础:生成式预训练语言模型GPT(代码“活起来”)
人工智能·python·gpt·深度学习·神经网络·语言模型·自然语言处理