Google Earth Engine(GEE)Sentinel2影像特定长时间序列合成,以及导出多幅影像和导出为黑色问题

本篇主要介绍如何加载单幅Sentinel2影像,以及合成长时间序列感兴趣区域的影像集合,自己在写代码的过程中,出现了一些问题,比如导出的影像会以多幅tif影像出现导出的影像在Arcgis中加载全为黑色,也看了一些博主的博客,有的解释很简单,在这我会详细解释一下,让大家少走弯路(被我走完了)。
遇到问题的同学可以直接跳转到第三部分。

目录

    • 1.单幅影像合成
    • 2.特定长时间序列影像合成
    • 3.导出为多幅影像以及导出影像为黑色的问题

1.单幅影像合成

此代码只对感兴趣区域,并辅以时间范围进行影像筛选,使用了经典的Sentinel去云算法,利用QA60波段进行去云操作,并结合了高程数据,将坡度坡向波段加入到影像的波段中。

//导入自己的感兴趣区域
Map.centerObject(roi, 8);  
Map.addLayer(roi, {color: "red"}, "roi");  

//去云函数
function maskS2clouds(image) {
  var qa = image.select('QA60');
  var cloudBitMask = 1 << 10;
  var cirrusBitMask = 1 << 11;

  var mask = qa.bitwiseAnd(cloudBitMask).eq(0)
      .and(qa.bitwiseAnd(cirrusBitMask).eq(0));

  return image.updateMask(mask).divide(10000);
}

var dataset = ee.ImageCollection('COPERNICUS/S2')
                  .filterBounds(roi)
                  .filterDate('2018-04-01', '2018-05-30')
                  // Pre-filter to get less cloudy granules.
                  .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
                  .map(maskS2clouds);
          
//加入高程数据
var srtm = ee.Image("USGS/SRTMGL1_003");  
var dem = ee.Algorithms.Terrain(srtm);  
var elevation = dem.select("elevation");  
var slope = dem.select("slope");  

var s2Image=dataset.median()
                   .addBands(elevation.rename("ELEVATION"))
                   .addBands(slope.rename("SLOPE"))
                   .clip(roi)
                   .select(['B4', 'B3', 'B2']);
var rgbVis = {
  min: 0.0,
  max: 0.3,
  bands: ['B4', 'B3', 'B2'],
};


print(s2Image)
Map.addLayer(s2Image, rgbVis, 'RGB');
//导出影像
Export.image.toDrive({  
  image:s2Image,  
  description:'sentinel2',  
  region: roi,  
  scale:10,  
  crs: "EPSG:4326",  
  maxPixels:1e13  
});    

在GEE中进行影像显示,显示结果符合预期。

Google Earth Engine(GEE)Sentinel2影像特定长时间序列合成,以及导出多幅影像和导出为黑色问题_第1张图片
在这里插入图片描述
在这里其实我并没有导出全部感兴趣区域的影像,而是选择了一个小范围进行导出,可以发现导出的结果可以正常显示。

Google Earth Engine(GEE)Sentinel2影像特定长时间序列合成,以及导出多幅影像和导出为黑色问题_第2张图片在影像导出时还可能会出现一些问题,如果导出的感兴趣区域太大的话,可能会加载很慢,而且会出现导出结果为多幅tif影像,在第三部分会进行解释。
在这里插入图片描述

2.特定长时间序列影像合成

合成长时间序列影像其实就是合成单幅影像向时间范围的推广,可以为进行长时间序列研究提供源数据支持。
以前写过合成landsat影像长时间序列的代码,然后移植到sentinel2影像上时,会发生一些问题,于是进行了一些改进与修改。


var S2SR = ee.ImageCollection('COPERNICUS/S2');

var rawLayer = null;
var bands = ['B4', 'B3', 'B2'];

//融合DEM高程数据
var srtm = ee.Image("USGS/SRTMGL1_003"); 
var dem = ee.Algorithms.Terrain(srtm);  
var elevation = dem.select("elevation");  
var slope = dem.select("slope"); 

//展示影像集合,侧边栏
function addPanel(sCol) {  
  var id_list = sCol.reduceColumns(ee.Reducer.toList(), ['system:index'])  
                    .get('list');  
  id_list.evaluate(function(ids) { 
    // print("id_list ", ids);  
    var total = ids.length;  
    var showTitle = ui.Label("", {fontWeight: 'bold'});  
    var curIndex = 0;  
    var bPlus = ui.Button("+", function() {  
      curIndex += 1;  
      if (curIndex >= total) {  
        curIndex = 0;  
      }  
      showTitle.setValue(ids[curIndex]);  
      showSelectRawImage(sCol, ids[curIndex]);  
    });  
    var bReduce = ui.Button("-", function() {  
      curIndex -= 1;  
      if (curIndex < 0) {  
        curIndex = total - 1;  
      }  
      showTitle.setValue(ids[curIndex]);  
      showSelectRawImage(sCol, ids[curIndex]);  
    });  
    showTitle.setValue(ids[curIndex]);  
    showSelectRawImage(sCol, ids[curIndex]);  
    var main = ui.Panel({  
      widgets: [  
        ui.Label('click "+" or "-" to move time window', {fontWeight: 'bold'}),  
        bPlus, bReduce,  
        ui.Label("select date: ", {fontWeight: 'bold'}),  
        showTitle  
      ],  
      style: {width: '200px', padding: '8px'}  
    });  
    ui.root.insert(0, main);  
  });  
}
 
function showSelectRawImage(sCol, key) {  
  print("show raw image id is: " + key);  
  if (rawLayer !== null) {  
    Map.remove(rawLayer);  
    rawLayer = null;  
  }  
  var visParam = {  
  min: 0.0,
  max: 0.3,
  bands: ['B4', 'B3', 'B2'],
  };  
  var image = ee.Image(sCol.filter(ee.Filter.eq("system:index", key)).first());  
  print(image);
  // Map.addLayer(image, {'bands':["B4","B3","B2"],min:0,max:3000}, key);
  rawLayer = Map.addLayer(image, {'bands':["B4","B3","B2"],min:0,max:0.3}, key);  
} 

//构建哨兵影像集合
var Sentinel2 = {
  //利用QA60波段去云操作
  maskS2clouds: function(image){
    var qa = image.select('QA60');
  var cloudBitMask = 1 << 10;
  var cirrusBitMask = 1 << 11;

  var mask = qa.bitwiseAnd(cloudBitMask).eq(0)
      .and(qa.bitwiseAnd(cirrusBitMask).eq(0));
  return image.updateMask(mask).divide(10000);
  },
  
}

//每年固定日期影像合成
function getYearCol(sDate, eDate) {  
  var yearList = ee.List.sequence(ee.Date(sDate).get("year"), ee.Number(ee.Date(eDate).get("year")).subtract(1));  
  // var monthList = ee.List.sequence(5, 6);  
  var yearImgList = yearList.map(function(year) {  
    year = ee.Number(year);  
    var _sdate = ee.Date.fromYMD(year, 4, 1);  
    var _edate = ee.Date.fromYMD(year, 6, 30);  
    //影像筛选
    var tempCol = S2SR.filterDate(_sdate, _edate)
                      .filterBounds(roi)
                      .filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
                      .map(Sentinel2.maskS2clouds)
                  // .select(bands);  
    //影像中值合成
    var img = tempCol.median()
                    .clip(roi)
                    .addBands(elevation.rename("ELEVATION"))
                    .addBands(slope.rename("SLOPE"));
                    
    
    img = img.set("year", year);  
    img = img.set("system:index", ee.String(year.toInt()));  
    return img;  
  });  
  var yearImgCol = ee.ImageCollection.fromImages(yearImgList);  
  return yearImgCol;  
} 


//导出所有影像
function exportImageCollection(imgCol) {
  var indexList = imgCol.reduceColumns(ee.Reducer.toList(), ["system:index"])
                        .get("list");
  indexList.evaluate(function(indexs) {
    for (var i=0; i<indexs.length; i++) {
      var image = imgCol.filter(ee.Filter.eq("system:index", indexs[i])).first();
      image = image.toInt16();
 
      Export.image.toDrive({
        image: image.clip(roi),
        description: "s2"+indexs[i],
        fileNamePrefix: indexs[i],
        region: roi,
        scale: 10,
        crs: "EPSG:4326",
        maxPixels: 1e13
      });
    }
  });
}


function main(){
  Map.centerObject(roi, 8);  
  var sDate = "2017-1-1";  
  var eDate = "2022-1-1";
  var imgCol = getYearCol(sDate, eDate); 
  print("imgCol", imgCol); 
  exportImageCollection(imgCol)
  addPanel(imgCol);
  
}
main();

运行结果图如下所示,本代码生成了2017-2018年4,5,6三个月的中值合成影像,在侧边栏加入了用户交互界面,并可将结果按年份导出。

3.导出为多幅影像以及导出影像为黑色的问题

1.有的同学会发现导出到Drive中的影像会出现多个tif影像文件,并且加载到arcgis中时影像全为黑色,其实打开属性表,会发现波段的值全是未知,所以该影像根本没有波段值,怎么拉伸都不会出现影像。可能是因为导出的影像波段数量过多,导致影像内存过于庞大,GEE会自动将其分为多个文件进行存储,如果网不好,还可能会有波段信息丢失的情况,所以在进行影像导出的时候,先进行波段选择,会大概率避免这种情况。
Google Earth Engine(GEE)Sentinel2影像特定长时间序列合成,以及导出多幅影像和导出为黑色问题_第3张图片

Google Earth Engine(GEE)Sentinel2影像特定长时间序列合成,以及导出多幅影像和导出为黑色问题_第4张图片
Google Earth Engine(GEE)Sentinel2影像特定长时间序列合成,以及导出多幅影像和导出为黑色问题_第5张图片
2. 影像全为黑色还有另一种情况,再进行landsat影像合成的时候,一般都会把landsat影像的波段值乘上0.0001,但是sentinel数据不需要进行波段拉伸,不然就会造成波段值过小,会自动忽略不计,导致波段值全为空,影像自然为黑色。
以下代码为罪魁祸首,在合成哨兵数据时千万不要带着他

scaleImage: function(image) {  
    var time_start = image.get("system:time_start");   
    image = image.multiply(0.0001);  
    image = image.set("system:time_start", time_start);  
    return image; 
  },

Google Earth Engine(GEE)Sentinel2影像特定长时间序列合成,以及导出多幅影像和导出为黑色问题_第6张图片
如果还有同学有问题,可以私信我,有些共同的错误可以及时改正。

你可能感兴趣的:(GEE,javascript,算法,大数据)