GEE筆記:多日期滑動元件

★芒果養樂多★發表於2020-10-15


前言

最近在進行土地覆被變化驗證的相關研究,看到了Cohen等人為LandTrendr開發的 基於Landsat時間序列影像的驗證工具:TimeSync。為了滿足自己的需要(好像TimeSync在GEE上使用還要先將影像下載到本地???),本菜雞參考大神們的程式碼,基於GEE平臺弄了一個顯示Landsat系列可以進行目視解譯驗證的工具(萌新求輕噴)。


一、GEE官方的日期滑動元件程式碼

程式碼如下(原文連結:https://developers.google.com/earth-engine/guides/ui_widgets)

// Use a DateSlider to create annual composites of this collection.
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1');
// Use the start of the collection and now to bound the slider.
var start = ee.Image(collection.first()).date().get('year').format();
var now = Date.now();
var end = ee.Date(now).format();

// Run this function on a change of the dateSlider.
var showMosaic = function(range) {
  var mosaic = ee.Algorithms.Landsat.simpleComposite({
    collection: collection.filterDate(range.start(), range.end())
  });
  // Asynchronously compute the name of the composite.  Display it.
  range.start().get('year').evaluate(function(name) {
    var visParams = {bands: ['B4', 'B3', 'B2'], max: 100};
    var layer = ui.Map.Layer(mosaic, visParams, name + ' composite');
    Map.layers().set(0, layer);
  });
};

// Asynchronously compute the date range and show the slider.
var dateRange = ee.DateRange(start, end).evaluate(function(range) {
  var dateSlider = ui.DateSlider({
    start: range['dates'][0],
    end: range['dates'][1],
    value: null,
    period: 365,
    onChange: showMosaic
  });
  Map.add(dateSlider.setValue(now));
});

二、多日期滑動元件

程式碼如下(示例):

程式碼看起來很長主要分為兩部分:

  1. 對Landsat SR影像的一些預處理(雲掩膜、波段計算等等)
  2. 通過日期滑動元件分別展示研究區的Lansat-5,7,8年度中值合成影像
var roi = ee.FeatureCollection("xxx");

//######################################################################################
//#                                   Preparing                                      #\\
//######################################################################################

//1.-------------------------------------------------------------------
// Define function to get and rename bands of interest from ETM+.
var renameETM =function(img) {
  return img.select(
		['B1', 'B2', 'B3', 'B4', 'B5', 'B7', 'pixel_qa', 'radsat_qa'],
		['B1', 'B2', 'B3', 'B4', 'B5', 'B7', 'pixel_qa', 'radsat_qa']
  );
};

// Define function to get and rename bands of interest from OLI.
var renameOLI = function(img) {
  return img.select(
    ['B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'pixel_qa'],
    ['B1', 'B2', 'B3', 'B4', 'B5', 'B7', 'pixel_qa']
    );
};

//2.-------------------------------------------------------------------.
//cloud mask------------------------------------------------
var cloudMaskL457 = function(image) {
  var qa = image.select('pixel_qa');
  // If the cloud bit (5) is set and the cloud confidence (7) is high
  // or the cloud shadow bit is set (3), then it's a bad pixel.
  var cloud = qa.bitwiseAnd(1 << 5)
          .and(qa.bitwiseAnd(1 << 7))
          .or(qa.bitwiseAnd(1 << 3))
  // Remove edge pixels that don't occur in all bands
  var mask2 = image.mask().reduce(ee.Reducer.min());
  var maskObersaturation = image.select('radsat_qa').eq(0)
  return image.updateMask(cloud.not()).updateMask(mask2.and(maskObersaturation));
};

var maskL8sr = function(image) {
  // Bits 3 and 5 are cloud shadow and cloud, respectively.
  var cloudShadowBitMask = 1 << 3;
  var cloudsBitMask = 1 << 5;

  // Get the pixel QA band.
  var qa = image.select('pixel_qa');

  // Both flags should be set to zero, indicating clear conditions.
  var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
      .and(qa.bitwiseAnd(cloudsBitMask).eq(0));

  // Return the masked image, scaled to reflectance, without the QA bands.
  return image.updateMask(mask);
}

//3.-------------------------------------------------------------------
//Add bands--------------------------------------------------
var addbands = function(img) {
  var ndvi = img.normalizedDifference(["B4", "B3"]).rename(["NDVI"]);
  var img1 = img.addBands(ndvi)
                .set('system:time_start', img.get('system:time_start'));
  return img1;
};

//4.-------------------------------------------------------------------
//divide-----------------------------------------------------
var divide = function(img){
      img = img.select('B1', 'B2', 'B3', 'B4', 'B5','B7')
               .divide(10000)
               .set('system:time_start', img.get('system:time_start'));
  return img;
};

//5.-------------------------------------------------------------------
// Define function to prepare OLI images.
var prepOLI = function(img) {
  var orig = img;
  img = renameOLI(img);
  img = maskL8sr(img);
  img = divide(img);
  img = addbands(img);
  return ee.Image(img.copyProperties(orig, orig.propertyNames()));
}

// Define function to prepare ETM+ images.
var prepETM = function(img) {
  var orig = img;
  img = renameETM(img);
  img = cloudMaskL457(img);
  //img = etm2oli(img);
  img = divide(img);
  img = addbands(img);
  return ee.Image(img.copyProperties(orig, orig.propertyNames()));
}

//6.-------------------------------------------------------------------
var visParams = {
  bands: ['B3', 'B2', 'B1'],
  min: 0,
  max: 0.3,
  gamma: 1.4,
};

Map.centerObject(roi, 9);

var sidePanel = ui.Panel();

//######################################################################################
//#                                    L5 part                                       #\\
//######################################################################################

var L5 = ee.ImageCollection("LANDSAT/LT05/C01/T1_SR");

var start_1 = "1986-1-1";
var end_1 = "2011-12-31";

var showImage_1 = function(range){
  print("---L5", range);
  var colFilter = ee.Filter.and(
    ee.Filter.bounds(roi),
    ee.Filter.date(range.start(), range.end()),
    //ee.Filter.lt('CLOUD_COVER', 50),
    ee.Filter.lt('GEOMETRIC_RMSE_MODEL', 30),
    ee.Filter.or(
      ee.Filter.eq('IMAGE_QUALITY', 9),
      ee.Filter.eq('IMAGE_QUALITY', 7)
      )
      );
      
  // Filter collections and prepare them for merging.
  var col_L5 = L5.filter(colFilter).map(prepETM).map(function(image){return image.clip(roi)});
  var imgL5_med = col_L5.reduce(ee.Reducer.median())
  .select(
    ['B1_median', 'B2_median', 'B3_median', 'B4_median','B5_median','B7_median', 'NDVI_median'],
    ['B1', 'B2', 'B3', 'B4', 'B5','B7', 'NDVI']);
  
  range.start().get('year').evaluate(function(name) {
    var layer_1 = ui.Map.Layer(imgL5_med, visParams, name + ' Landsat 5 composite');
    Map.layers().set(0, layer_1);
  });
};

showImage_1(ee.DateRange(start_1, ee.Date(start_1).advance(1, "year")));

ee.DateRange(start_1, end_1).evaluate(function(range) {
  print("+++L5",range);
  var dateSlider = ui.DateSlider({
    start: range['dates'][0],
    end: range['dates'][1],
    value: start_1,
    period: 365,
    onChange: showImage_1,
    style: {
      width: "500px",
      padding: "2px"
    }
  });
  var main_panel_1 = ui.Panel({
    widgets: [
      ui.Label('Landsat 5 compsites 1986-2011', {fontWeight: 'bold'}),
      dateSlider
      ],
    style: {
      width: "520px",
      padding: "4px"
    }
  });
  sidePanel.add(main_panel_1);
});

//######################################################################################
//#                                    L7 part                                       #\\
//######################################################################################

var L7 = ee.ImageCollection("LANDSAT/LE07/C01/T1_SR");

var start_2 = "1999-1-1";
var end_2 = "2019-12-31";

var showImage_2 = function(range){
  print("---L7", range);
  var colFilter_2 = ee.Filter.and(
    ee.Filter.bounds(roi),
    ee.Filter.date(range.start(), range.end()),
    //ee.Filter.lt('CLOUD_COVER', 50),
    ee.Filter.lt('GEOMETRIC_RMSE_MODEL', 30),
    ee.Filter.or(
      ee.Filter.eq('IMAGE_QUALITY', 9),
      ee.Filter.eq('IMAGE_QUALITY', 7)
      )
      );
      
  // Filter collections and prepare them for merging.
  var col_L7 = L7.filter(colFilter_2).map(prepETM).map(function(image){return image.clip(roi)});
  var imgL7_med = col_L7.reduce(ee.Reducer.median())
  .select(
    ['B1_median', 'B2_median', 'B3_median', 'B4_median','B5_median','B7_median', 'NDVI_median'],
     ['B1', 'B2', 'B3', 'B4', 'B5','B7', 'NDVI']);
  
  range.start().get('year').evaluate(function(name) {
    var layer_2 = ui.Map.Layer(imgL7_med, visParams, name + ' Landsat 7 composite');
    Map.layers().set(1, layer_2);
  });
};

showImage_2(ee.DateRange(start_2, ee.Date(start_2).advance(1, "year")));

ee.DateRange(start_2, end_2).evaluate(function(range) {
  print("+++L7",range);
  var dateSlider = ui.DateSlider({
    start: range['dates'][0],
    end: range['dates'][1],
    value: start_2,
    period: 365,
    onChange: showImage_2,
    style: {
      width: "500px",
      padding: "2px"
    }
  });
  var main_panel_2 = ui.Panel({
    widgets: [
      ui.Label('Landsat 7 compsites 2013-2019', {fontWeight: 'bold'}),
      dateSlider
      ],
    style: {
      width: "520px",
      padding: "4px"
    }
  });
  sidePanel.add(main_panel_2);
});

//######################################################################################
//#                                    L8 part                                       #\\
//######################################################################################

var L8 = ee.ImageCollection("LANDSAT/LC08/C01/T1_SR");

var start_3 = "2013-1-1";
var end_3 = "2019-12-31";

var showImage_3 = function(range){
  print("---L8", range);
  var colFilter = ee.Filter.and(
    ee.Filter.bounds(roi),
    ee.Filter.date(range.start(), range.end()),
    //ee.Filter.lt('CLOUD_COVER', 50),
    ee.Filter.lt('GEOMETRIC_RMSE_MODEL', 30),
    ee.Filter.or(
      ee.Filter.eq('IMAGE_QUALITY', 9),
      ee.Filter.eq('IMAGE_QUALITY_OLI', 9)
      )
      );
      
  // Filter collections and prepare them for merging.
  var col_L8 = L8.filter(colFilter).map(prepOLI).map(function(image){return image.clip(roi)});
  var imgL8_med = col_L8.reduce(ee.Reducer.median())
  .select(
    ['B1_median', 'B2_median', 'B3_median', 'B4_median','B5_median','B7_median', 'NDVI_median', ],
    ['B1', 'B2', 'B3', 'B4', 'B5','B7', 'NDVI']);
  
  range.start().get('year').evaluate(function(name) {
    var layer_3 = ui.Map.Layer(imgL8_med, visParams, name + ' Landsat 8 composite');
    Map.layers().set(2, layer_3);
  });
};

showImage_3(ee.DateRange(start_3, ee.Date(start_3).advance(1, "year")));

ee.DateRange(start_3, end_3).evaluate(function(range) {
  print("+++L8",range);
  var dateSlider = ui.DateSlider({
    start: range['dates'][0],
    end: range['dates'][1],
    value: start_3,
    period: 365,
    onChange: showImage_3,
    style: {
      width: "300px",
      padding: "2px"
    }
  });
  var main_panel_3 = ui.Panel({
    widgets: [
      ui.Label('Landsat 8 compsites 2013-2019', {fontWeight: 'bold'}),
      dateSlider
      ],
    layout: "flow",
    style: {
      width: "520px",
      padding: "4px"
    }
  });
  sidePanel.add(main_panel_3);
});

ui.root.add(sidePanel);

最終的效果圖:
在這裡插入圖片描述


總結

  1. 通過這個元件可以很方便的利用Landsat時間序列影像進行目視解譯驗證,未來希望可以加上驗證點的某引數時間序列折線圖(比如在GEE中匯入一個FeatureCollection, 通過點選某個點就可以顯示時間序列NDVI折線圖)。

  2. 不知道大家除了TimeSync和GE之外還知道其他可以利用時間序列遙感影像驗證的工具嘛?

最後,希望大家能夠多與我交流指出錯誤,我會虛心接受!!!

相關文章