GEE东拼西凑之MOD16A2_ET 月合成

想使用现有的蒸散发产品,发现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合成,然后导出。得到的结果是这样的:

结果的真实性我还没有去验证。希望没有什么错误吧。。 

后记

这个代码想了整整一天,让我再次意识到代码基础的薄弱。。感觉这样实在是有点麻烦,蹲一个大佬教教怎么精简。刚才又有了新的想法,后面或许会更一个精简版本。

  • 6
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值