想使用现有的蒸散发产品,发现MOD16A2是8天累计的,但是我需要月度数据,有大佬用Python实现了这个功能,也有大佬直接在GEE上将同一个月的聚合,根据这两位的思路和代码拼凑了一下,尝试在GEE平台上进行MOD16A2产品月合成。
参考:
Python GDAL MODIS ET(MOD16A2GF)8天合成月尺度_gis风轻云淡的博客-CSDN博客
GEE开发之Modis_ET数据分析和获取_gee计算et_等待着冬天的风的博客-CSDN博客
跨月数据的处理
参考用Python处理的大佬的思路,将跨月的日期进行处理。按照这一景在两个月内天数的不同比值进行分割(比较简单粗暴的分割,如果追求精细可以在这里修改权重)。在尝试的过程中,了解到尽可能少用toList进行转换,但碍于水平有限只能用这个了。如果有大神有更好的办法可以交流一下。
//定义了每个月第一张影像的处理办法
var Firstdate = function(imagecol){
var first = imagecol.first()
var firstdate = ee.Date(first.get('system:time_start'))
//获取上一景影像时间
var lastdate = firstdate.advance(-8,'day')
var year = firstdate.get('year')
var month = firstdate.get('month')
var firstday = ee.Date.fromYMD(year,month,1)
//获取在本月部分的影像(*)
if (firstdate.get('day').neq(1)){
var image1 = ee.ImageCollection("MODIS/006/MOD16A2").select('ET').filterDate(lastdate,firstdate.advance(-1,'day')).first()
var dif = ee.Number(firstdate.difference(firstday,'day'))
var delta = (dif.subtract(8)).multiply(-1)
var image2 = image1.multiply(dif).divide(8).toInt16().copyProperties(first,
['system:time_start','system:time_end']).set('system:time_end',firstdate.millis()).set('system:time_start',firstday.millis())
.set('system:id',firstday.format('yyyy-MM-dd'))
//获取在上个月部分的影像
var image3 = image1.multiply(delta).divide(8).toInt16().copyProperties(first,
['system:time_start','system:time_end']).set('system:time_end',lastdate.advance(delta,'day').millis()).set('system:time_start',lastdate.millis())
.set('system:id',lastdate.format('yyyy-MM-dd'))
var list = imagecol.toList(imagecol.size()).add(image2).add(image3)
return list//ee.ImageCollection(list)
}//else if (firstdate.get('day') == 1){
//return imagecol
//}
}
思路就是找到每月第一张影像,如果第一张影像的天数(.get('day'))不是1,那么将上一张影像(上个月)按照在不同月份天数的比例进行分割。这里值得注意的是,在代码段注释(*)我定义了firstdate.get('day').neq(1)的判断规则,但是firstdate.get('day')的结果是object,不能直接和数字1进行比较,所以如果保持原样,这个判断条件始终为true,这是后面我才意识到的。当时发现这个问题之后我直接修改了后面的代码(见代码段注释**),所以这里的判断实际是无效的,也就是始终执行。图示参考:
Python GDAL MODIS ET(MOD16A2GF)8天合成月尺度_gis风轻云淡的博客-CSDN博客
逐月执行
var dataset = ee.ImageCollection("MODIS/006/MOD16A2").select('ET').filterBounds(table)
//按月分离,没有处理跨月数据
var year = ee.List([])
for(var i=2001; i<=2022;i++){
var data_collection = null;
if (i%4 !==0){
for(var j=1;j<=12;j++){
switch(j){
case 1:
var data_collection1 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection = data_collection1.toList(data_collection1.size()).slice(0,-1)
break;
case 3:
var data_collection3 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection3 = ee.ImageCollection(data_collection3.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection3);
break;
case 5:
var data_collection5 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection = data_collection5.toList(data_collection5.size()).slice(0,-1);
break;
case 7:
var data_collection7 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection7 = ee.ImageCollection(data_collection7.toList(col.size()).slice(0,-1))
var imagecon = ee.Date(data_collection7.first().get('system:time_start'))
var chazhi = imagecon.difference(june,'day').getInfo()
switch(chazhi){
case 8:
var data_collectionb = Firstdate(data_collection7);
var data_collection = data_collectionb
break;
case 16:
var data_collectiona = data_collection7.toList(data_collection7.size());
var data_collection = data_collectiona
break;
}
break;
case 8:
var data_collection8 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection8 = ee.ImageCollection(data_collection8.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection8);
break;
case 10:
var data_collection10 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection = Firstdate(data_collection10);
break;
case 12:
var data_collection12 = dataset.filterDate(i+'-'+j+'-01',i+'-'+j+'-31')
var data_collection = Firstdate(data_collection12);
break;
case 4:
var data_collection4 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection = Firstdate(data_collection4);
break;
case 6:
var data_collection6 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var junelastimage = data_collection6.toList(data_collection6.size())
var june = ee.Date(ee.Image(junelastimage.get(-1)).get('system:time_start'));
var junelast = ee.Date(i+'-'+'06-30')
var chazhi2 = junelast.difference(june,'day').getInfo()
switch(chazhi2){
case 4:
var data_collection6 = ee.ImageCollection(data_collection6.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection6);
break;
case 12:
var data_collection6 = data_collection6
var data_collection = Firstdate(data_collection6)
break;
}
break;
case 9:
var data_collection9 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection9 = ee.ImageCollection(data_collection9.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection9);
break;
case 11:
var data_collection11 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection11 = ee.ImageCollection(data_collection11.toList(col.size()).slice(0,-1))
var data_collection = data_collection11.toList(data_collection11.size());
break;
case 2:
var data_collection2 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection2 = ee.ImageCollection(data_collection2.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection2);
break;
}
year = year.add(data_collection)
}
}else if (i%4 === 0){
for(var j=1;j<=12;j++){
switch(j){
case 1:
var data_collection1 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection = data_collection1.toList(data_collection1.size()).slice(0,-1)
break;
case 3:
var data_collection3 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection3 = ee.ImageCollection(data_collection3.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection3);
break;
case 5:
var data_collection5 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection = Firstdate(data_collection5);
break;
case 7:
var data_collection7 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection7 = ee.ImageCollection(data_collection7.toList(col.size()).slice(0,-1))
var imagecon = ee.Date(data_collection7.first().get('system:time_start'))
var chazhi = imagecon.difference(june,'day').getInfo()
switch(chazhi){
case 8:
var data_collectionb = Firstdate(data_collection7);
var data_collection = data_collectionb
break;
case 16:
var data_collectiona = data_collection7.toList(data_collection7.size());
var data_collection = data_collectiona
break;
}
break;
case 8:
case 10:
var data_collection8 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection8 = ee.ImageCollection(data_collection8.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection8);
break;
case 12:
var data_collection12 = dataset.filterDate(i+'-'+j+'-01',i+'-'+j+'-31')
var data_collection = Firstdate(data_collection12);
break;
case 4:
var data_collection4 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection4 = ee.ImageCollection(data_collection4.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection4);
break;
case 6:
var data_collection6 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var junelastimage = data_collection6.toList(data_collection6.size())
var june = ee.Date(ee.Image(junelastimage.get(-1)).get('system:time_start'));
var junelast = ee.Date(i+'-'+'06-30')
var chazhi2 = junelast.difference(june,'day').getInfo()
if(chazhi2 < 8 ){
var data_collection = data_collection6.toList(col.size()).slice(0,-1);
}else {
var data_collection6 = data_collection6
var data_collection = data_collection6.toList(data_collection6.size())
}
break;
case 9:
case 11:
case 2:
var data_collection2 = dataset.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01')
var data_collection2 = ee.ImageCollection(data_collection2.toList(col.size()).slice(0,-1))
var data_collection = Firstdate(data_collection2);
break;
}
year = year.add(data_collection)
}
}
}
var MOD16_ET = ee.ImageCollection(year.flatten()).sort('system:time_start',true).map(copy)
print(MOD16_ET)
这里要注意的点:
1. 划分平年和闰年。在平年,一号有数据的月份为1月、5月、11月;在闰年,一号有数据的月份为1月、6月,所以每个月处理不一样。准确来讲,对于一号有数据的月份,其前一个月最后一景影像不包括下一个月,不需要剔除。
2.对于跨月数据,在定义的函数中不仅分割出了当月1日至当月第一张影像之间的影像,还分割了上一个月至月底的影像,然而原始数据集中的每月最后一张没有被删除,所以在处理中对于当月1日原始数据不存在的情况下,需要删除上个月最后一张影像。
3.在2001年6月,最后一张影像日期是6.18,之后就是7.4,中间少了6.26的影像,如果不加以限制会报错。所以在6、7月分别设置判断规则。在6月,如果最后一张影像日期距离月底(30日)时间小于8,则去掉最后一张影像,如果大于8,表明影像有缺失,保留最后一张影像。在7月,对于第一张影像和上月最后一张影像,如果时间差值为8,表明上个月有26日影像,一切正常处理(使用Firstdate函数),如果差值为16,表明上个月影像缺失,不进行Firstdate函数处理。
总结来讲就是,有1号的数据,就不进行Firstdate函数处理,同时上个月最后一张影像也不需要被剔除,如果没有1号数据,就需要剔除上个月最后一张影像,并且应用Firstdate函数。我这部分代码写的有点乱,在码字的过程中才真正想明白,实际上在Firstdate函数中判断规则如果可以实现的话是不需要每个月单独分析的。这个我后面再考虑写个精简版本。
var copy = function(image){
return image.set('system:id',ee.Date(image.get('system:time_start')).format('yyyy-MM-dd'))
}
因为导出的结果中包含原始影像ID和新设置的ID,有点乱,所以统一将ID设置为年-月-日的格式,方便看。
得到的结果是这样的:
按月合成
var monthly = ee.List([])
for(var i=2001; i<=2022;i++){
var data_collection_month = null;
for(var j=1;j<=12;j++){
switch(j){
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
var starttime = ee.Date.fromYMD(i,j,1)
var endtime = ee.Date.fromYMD(i,j+1,1)
var data_collection_month = MOD16_ET.filterDate(i+'-'+j+'-01',i+'-'+(j+1)+'-01').sum().toInt16().set('system:id',i+'-'+j).set('system:time_start',starttime.millis()).set('system:time_end',endtime.millis())
var monthly = monthly.add(data_collection_month)
break;
case 12:
var starttime = ee.Date.fromYMD(i,j,1)
var endtime = ee.Date.fromYMD(i,j,31)
var data_collection_month = MOD16_ET.filterDate(i+'-'+j+'-01',i+'-'+j+'-31').sum().toInt16().set('system:id',i+'-'+j).set('system:time_start',starttime.millis()).set('system:time_end',endtime.millis())
var monthly = monthly.add(data_collection_month)
break;
}
}
}
var MOD16_ET_monthly = ee.ImageCollection(monthly.flatten())
print(MOD16_ET_monthly)
var Vis = {
min: 0.0,
max: 300.0,
palette: [
'ffffff', 'fcd163', '99b718', '66a000', '3e8601', '207401', '056201',
'004c00', '011301'
],
};
Map.addLayer(MOD16_ET_monthly.filterDate('2002-08-01','2002-08-30').select('ET').mean().clip(table),Vis)
虽然不建议用for循环,但其他的方法我也想不到了。。在这里就是按月做sum合成,然后导出。得到的结果是这样的:
结果的真实性我还没有去验证。希望没有什么错误吧。。
后记
这个代码想了整整一天,让我再次意识到代码基础的薄弱。。感觉这样实在是有点麻烦,蹲一个大佬教教怎么精简。刚才又有了新的想法,后面或许会更一个精简版本。