清晨六点,实验室的咖啡机又一次准时响起。李博士揉了揉酸胀的眼睛,盯着屏幕上刚刚崩溃的遥感数据处理软件——这已经是本周第三次在手动下载和拼接哨兵2号影像时遭遇系统崩溃。这种场景在遥感研究领域屡见不鲜,直到他发现了Google Earth Engine(GEE)这个改变游戏规则的云端平台。
在GEE出现之前,获取可用遥感影像需要经历一个典型的"五步折磨链":
这个过程不仅耗时(通常需要3-5个工作日),还对硬件配置有极高要求。更令人沮丧的是,当研究区域跨越多景影像时,手动处理几乎成为不可能完成的任务。
GEE的颠覆性创新在于:
javascript复制// GEE基础环境设置示例
var roi = ee.Geometry.Point([116.4, 39.9]).buffer(10000); // 研究区域
Map.centerObject(roi, 10); // 地图居中显示
哨兵2号的QA60质量评估波段是去云操作的关键所在。这个16位的波段中,第10和11位分别标记了普通云和卷云的存在:
| 位位置 | 含义 | 掩码值 |
|---|---|---|
| 10 | 云 | 1024 |
| 11 | 卷云 | 2048 |
理解这个编码机制后,我们可以构建精确的云掩膜:
javascript复制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); // 同时进行辐射归一化
}
相比均值合成,中值合成(median composite)能更好抵抗异常值干扰,特别适合处理:
在GEE中实现年度中值合成仅需一行代码:
javascript复制var annualComposite = filteredCollection.median();
注意:中值合成会损失部分时序信息,若需保留物候特征,可考虑分季度合成
一个健壮的GEE脚本应该将关键参数提取为变量,方便不同项目复用:
javascript复制// 用户可修改参数
var startDate = '2020-01-01';
var endDate = '2020-12-31';
var maxCloudCover = 20; // 最大允许云量百分比
var bands = ['B8', 'B4', 'B3']; // 近红外、红、绿波段
var visParams = {min: 0, max: 0.3, bands: bands}; // 可视化参数
// 主处理流程
var collection = ee.ImageCollection('COPERNICUS/S2')
.filterBounds(roi)
.filterDate(startDate, endDate)
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', maxCloudCover))
.map(maskS2clouds)
.select(bands);
导出前建议添加元数据并预览结果:
javascript复制// 添加时间戳作为元数据
var composite = collection.median().set({
'system:time_start': ee.Date(startDate).millis(),
'system:time_end': ee.Date(endDate).millis()
});
// 交互式质量检查
Map.addLayer(composite, visParams, '年度合成结果');
Map.addLayer(roi, {color: 'red'}, '研究区域');
// 导出到Google Drive
Export.image.toDrive({
image: composite.clip(roi),
description: 'Sentinel2_AnnualComposite_2020',
scale: 10,
region: roi,
maxPixels: 1e13,
fileFormat: 'GeoTIFF'
});
当研究区域超过单景哨兵影像范围时,需要特殊处理:
reproject()统一坐标参考系统tileScale参数(2-4之间)javascript复制// 分块导出示例
var grid = roi.boundingBox().coveringGrid(
ee.Projection('EPSG:4326'), 50000); // 5万米网格
grid.toList(grid.size()).evaluate(function(gridList) {
gridList.forEach(function(gridCell){
var cell = ee.Feature(gridCell);
Export.image.toDrive({
image: composite.clip(cell.geometry()),
description: 'Tile_'+ee.Number(gridList.indexOf(gridCell)).format('%02d'),
scale: 10,
region: cell.geometry(),
maxPixels: 1e13
});
});
});
基于年度合成结果,可以进一步开展:
javascript复制// 计算NDVI并导出
var ndvi = composite.normalizedDifference(['B8', 'B4']).rename('NDVI');
var visParams = {min: -1, max: 1, palette: ['red', 'yellow', 'green']};
Map.addLayer(ndvi.clip(roi), visParams, 'NDVI');
在最近的城市扩张研究中,这套工作流将原本需要两周的数据准备时间压缩到20分钟。记得首次成功运行脚本时,我特意保存了那个咖啡杯还没冷却的清晨截图——技术革新带来的效率提升,往往就藏在这些细节里。