GEE13:合成高精度融雪趋势图

该代码在谷歌地球引擎平台上融合了MODISAqua和Terra的雪盖产品,以创建高精度的融雪趋势图。通过对原始MODIS产品进行最大值融合,减少了低估雪盖的情况。脚本还计算了春季融雪和秋季积雪的日期,并进行了简单的回归分析来检测融雪日期是否有随时间变化的趋势,从而分析融雪是提前还是推迟。
摘要由CSDN通过智能技术生成

1. 高精度融雪趋势图合成

该项目中的谷歌地球引擎(GEE)代码结合了MODIS Aqua和Terra的雪盖产品,并得出了融雪——积雪(雪的物候)日期。由美国国家冰雪数据中心(NSIDC)处理的原始MODIS产品(MOD10A1和MYD10A1,分别用于Terra和Aqua平台)有偏低的趋势。
该代码使用最大值方法将这两个数据流融合起来,以考虑到这种偏差。更重要的是,该脚本还生成了一个衍生的雪的物候学产品,我计算了春季融雪(<5%的雪覆盖)和秋季积雪(>5%)的日期。
作为一个例子,该脚本运行一个简单的回归分析,以标记融雪日期的趋势,回答融雪是否随着时间的推移而推迟或提前发生的问题。

//----------------------------------------------------------------------------------
//
// Google Earth Engine code to create a combined MODIS snow product, fusing both
// Terra and Aqua MxD10A1 data streams. Both data streams are prone to 
// underestimate snow cover (due to MODIS properties and sky conditions). A fused
// products aleviates these issues partially by taking the maximum value of the
// fractional snow cover bands of each data stream.
//
// This raw data is stored in the maxCollection imageCollection. This data is used
// calculate the date of first snow melt and the date of snow accumulation. These
// dates are determined registering the DOY where fractional snow cover drops below
// or the day before it last exceeds 5%, only earliest and latest instances are noted. 
// A final product can be derived, namely the maximum snow free period, by taking the
// difference between the snow accumulation and snow melt dates.
// 
// Note that this code is writen for the northern hemisphere, and will not work
// for the southern hemisphere without adjustments (sum days between Jan 1. and
// the northern hemisphere snow melt day and from the accumulation day until 
// Dec. 31th to get to the snow free period, and obviously reverse the captions
// on the dates melt becomes accumulation and vice versa).
//
// Code written by Koen Hufkens, April 2016
// under a GNU AFFERO GENERAL PUBLIC LICENSE - see repository
// 
//----------------------------------------------------------------------------------

// -- settings --

// years to process (from start year t0 to end year t1)
// [2003 is the first year with full coverage]
var t0 = "2003", t1 = "2016";
var t_diff = (t1 - t0) + 1;

// -- functions --

var maxVal = function(feature) {
  
  // load feature and set start time
  // when mapping
  var img = ee.Image(feature);
  var startTime = img.select('NDSI_Snow_Cover').get('system:time_start');
  
  // assign 0 values (which can be confused with NA)
  // a small negative value for both bands
  var b1c = img.expression(
      'b1 == 0 ? -10 : b1', {
        'b1': img.select('NDSI_Snow_Cover')
  });
  
  var b2c = img.expression(
      'b1 == 0 ? -10 : b1', {
        'b1': img.select('NDSI_Snow_Cover_1')
  });
  
  // take the maximum value of the remapped data
  // while unmasking (assigning 0 to NA) the data
  var maxval = b1c.unmask(0).max(b2c.unmask(0));
  
  // update the mask (set all 0 values to NA)
  maxval = maxval.updateMask(maxval.neq(0));
  
  // substitute in the original 0 values, which
  // are currently registered as -10
  maxval = img.expression(
      'b1 == -10 ? 0 : b1', {
        'b1': maxval
  });
  
  // associate the start time with the new image
  // and return the data
  maxval = maxval.set('system:time_start',startTime);
  return maxval;
};

// concat two layers from a collection
// into an image with x bands
// (can be iterated)
var cat = function(a, b) {
  return ee.Image(b).addBands(a);
};

// rename bands for export to ee.Image() format
var rename_bands = function (img) {
  img = ee.Image(img);
  var names = img.bandNames();
  var prefix = ee.String(img.get("system:time_start")).cat("_");
  var new_names = names.map(function(b) {
    return prefix.cat(b); });
  return img.select(names, new_names);
};

// take the difference between snow accumulation
// and snow melt days (doy values)
var difference = function(feature) {
  var img = ee.Image(feature);
  var b1 = img.select('snowmelt');
  var b2 = img.select('snowacc');
  var startTime = b1.get('system:time_start');
  return b2.subtract(b1).set('system:time_start',startTime);
};

// set the snow melt date
var returnDoy = function(feature) {
  var image = ee.Image(feature);
  // Compute time of the image in fractional years relative to the start.
  var doy = ee.Date(image.get('system:time_start')).difference(start, 'day').add(1);
  // replace values smaller than 5% snow cover with
  // their respective DOY
  var replacement = image.expression(
      'SNOW <= 5 ? DOY : 9999', {
        'SNOW': image.select('NDSI_Snow_Cover'),
        'DOY': doy
  }).toInt();
  
  // mask anything above 5% coverage as empty
  var withObs = replacement.neq(9999);
  return replacement.updateMask(withObs);
};

// stick two bands together which are linked with an
// inner join
var concatBands = function(feature) {
  return ee.Image.cat(feature.get('primary'),feature.get('secondary'));
};

// This function adds a time band to the image,
// for subsequent regression analysis
var createTimeBand = function(feature) {
  var image = ee.Image(feature);
  return image.addBands(image.metadata('system:time_start'));
};

// Specify an equals filter for image timestamps.
var filterTimeEq = ee.Filter.equals({
  leftField: 'system:time_start',
  rightField: 'system:time_start'
});

// -- main -- 

// If a longer time series is selected, calculate statistics
// for each year independently. This can probably be done
// with some sort of reduce statement, but the documentation is a bit
// of a mess when it comes to custom functions.
for (var i = parseInt(t0); i <= parseInt(t1); ++i) {
  
  // int to string
  var evalYear = i.toString();
  
  // Load MODIS Fractional Snow Cover product (aqua / terra)
  var terraCollection = ee.ImageCollection('MODIS/006/MOD10A1')
    .select('NDSI_Snow_Cover')
    .filterDate(evalYear.concat("-01-01"),evalYear.concat("-12-31"))
    .sort('system:time_start', true);

  var aquaCollection = ee.ImageCollection('MODIS/006/MYD10A1')
    .select('NDSI_Snow_Cover')
    .filterDate(evalYear.concat("-01-01"),evalYear.concat("-12-31"))
    .sort('system:time_start', true);

  // create a collection of both MOD/MYD data matched
  // joined per band using an inner join
  var innerJoin = ee.Join.inner();
  
  // Apply the join.
  var innerJoinedCollection = innerJoin.apply(terraCollection, aquaCollection, filterTimeEq);
  
  // combine matching images into one image with (2) bands
  var joinedCollection = innerJoinedCollection.map(concatBands);
  
  // calculate the maximum value across the two bands MOD/MYD
  // see maxVal function above
  var yearlyCollection = joinedCollection.map(maxVal);

  if (evalYear == 2003){
    
    // pick a location for visual code validation
    var loc = ee.Geometry.Point([-118.84257812499999, 55.29665181611441]);

    // Register a function to draw a chart when a user clicks on the map.
    var mv = ui.Chart.image.series(yearlyCollection, loc, null, 50)
      .setOptions({title: 'joined MCD10A1'});
  
    var mod = ui.Chart.image.series(terraCollection, loc, null, 50)
      .setOptions({title: 'MOD10A1'});
  
    var myd = ui.Chart.image.series(aquaCollection, loc, null, 50)
      .setOptions({title: 'MYD10A1'});

    print(mv, mod, myd);
  }
  
  // how many bands in the dataset?
  // should be close to 365
  //print(yearlyCollection.size());

  // Get the starting time reference.
  var start = ee.Date(yearlyCollection.first().get('system:time_start'));

  // subsitute all values < 5% with their respective DOY
  var doyCollection = yearlyCollection.map(returnDoy);

  // calculate the snow melt and snow fall (accumulation)
  var snowMeltAcc = ee.ImageCollection(doyCollection)
    .reduce(ee.Reducer.minMax()).unmask(366);
  
  // The latter part is rather ugly, not optimal (but works)
  // if first in loop swap with the original layer
  if (evalYear == t0){

    // snow melt
   var tmpImg = snowMeltAcc
    .select(['constant_min'],['snowmelt'])
    .set('system:time_start',i);
   var snowMelt = ee.ImageCollection(tmpImg);
   
   // snow accumulation
   var tmpImg = snowMeltAcc
    .select(['constant_max'],['snowacc'])
    .set('system:time_start',i);
   var snowAcc = ee.ImageCollection(tmpImg);
   
  }else{
    
    // add snow melt layers
    var tmpImg = snowMeltAcc.select(['constant_min'],['snowmelt'])
      .set('system:time_start',i); 
    var snowMelt = snowMelt.merge(ee.ImageCollection(tmpImg));
    
  // add snow accumulation layers
    var tmpImg = snowMeltAcc.select(['constant_max'],['snowacc'])
      .set('system:time_start',i); 
    var snowAcc = snowAcc.merge(ee.ImageCollection(tmpImg));
  }
}

// rename bands
var snowMeltlist = snowMelt.toList(t_diff);
snowMeltlist = snowMeltlist.map(rename_bands);
var snowMeltImg = snowMeltlist.slice(1).iterate(cat, snowMeltlist.get(0));

var snowAcclist = snowAcc.toList(t_diff);
snowAcclist = snowAcclist.map(rename_bands);
var snowAccImg = snowAcclist.slice(1).iterate(cat, snowAcclist.get(0));

// Apply the join
var innerJoinedCollection = innerJoin.apply(snowMelt, snowAcc, filterTimeEq);

// combine matching images into one image with (2) band
var joinedCollection = innerJoinedCollection.map(concatBands);

// take the difference of the two bands
var snowFree = joinedCollection.map(difference);

// calculate median snow free season length and
// and create a mask from the result
var snowFreeMedian = ee.ImageCollection(snowFree)
  .median();
var snowFreeMask = ee.Image(snowFreeMedian)
  .lt(306);

// -- quick regression analysis -- 

// add a time band
var snowMelt = snowMelt.map(createTimeBand);

// linear regression on the data
var linearFit = ee.ImageCollection(snowMelt).select(['system:time_start', 'snowmelt'])
  .reduce(ee.Reducer.linearFit());

// -- plotting results --

// land-water mask for visualization, removes faulty cover
// values for the oceans and water bodies
var hansenImage = ee.Image('UMD/hansen/global_forest_change_2013');
var data = hansenImage.select('datamask');
var waterMask = data.eq(1);

// final mask, excluding water bodies and
// areas with a too short of a winter season
var mask = waterMask.multiply(snowFreeMask);
var filteredFit = ee.Image(linearFit).multiply(mask);

// Plot the snow free trend
Map.addLayer(linearFit
  .mask(mask),
  {min: 0, max: [-2, 0, 2], bands: ['scale', 'offset', 'scale']}, 'fit');

下面融雪趋势图,早期融雪用红色标记,后期融雪用蓝色标记:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jackson的生态模型

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值