1. 项目概述
山体阴影分析是地形研究中的重要环节,尤其在生态评估、太阳能资源规划等领域具有广泛应用价值。传统GIS软件处理大范围山体阴影计算时往往面临效率瓶颈,而Google Earth Engine(GEE)平台凭借其强大的云端计算能力,为全球尺度的地形分析提供了全新解决方案。
本次项目基于GEE平台,开发了一个自动化处理流程,专门用于提取MODIS数据中的山体阴影信息。核心算法通过SRTM数字高程模型计算地形阴影,并结合MODIS地表反射率数据实现阴影区域的精确识别。相比传统单机处理方式,这套方案具有三大优势:1)处理范围可覆盖全球任意区域;2)计算效率提升数十倍;3)支持时间序列分析。
提示:虽然示例代码使用了SRTM 90米分辨率数据,但实际应用中可根据精度需求切换至NASA DEM(30米)或ALOS DEM(12.5米)
2. 核心算法解析
2.1 地形阴影计算原理
山体阴影(Terrain Shadow)本质上是地形遮挡导致的太阳直射光无法到达的区域。其数学表达为:
code复制阴影区域 = 太阳方位角 ∩ 地形遮挡角 > 太阳高度角
在GEE中,我们通过ee.Terrain.hillShadow()方法实现这一计算。该方法需要输入三个关键参数:
- 数字高程模型(DEM)
- 太阳方位角(azimuth)
- 太阳高度角(elevation)
javascript复制var hillShadow = ee.Terrain.hillShadow(
dem, // 数字高程数据
azimuth, // 太阳方位角(度)
elevation, // 太阳高度角(度)
100, // 阴影模糊距离(米)
true // 是否仅计算阴影
);
2.2 MODIS数据融合技术
原始地形阴影需要与地表观测数据结合才能反映真实阴影效果。我们采用MODIS地表反射率产品(MOD09GA)进行融合:
javascript复制var modis = ee.ImageCollection('MODIS/006/MOD09GA')
.filterDate('2020-01-01', '2020-12-31')
.select(['sur_refl_b01','sur_refl_b02','sur_refl_b03']);
var shadowMask = hillShadow.multiply(modis.mean());
融合后的结果既包含地形阴影信息,又包含地表反射特征,可有效区分永久阴影(如北坡)和临时阴影(如云影)。
3. 完整实现流程
3.1 数据准备阶段
-
DEM数据选择:
- SRTM 90m(CGIAR/SRTM90_V4):全球覆盖,适合大范围分析
- NASA DEM 30m(NASA/NASADEM_HGT/001):更高精度
- ALOS DSM 12.5m(JAXA/ALOS/AW3D30/V2_2):最高分辨率
-
MODIS数据筛选:
javascript复制var modisCol = ee.ImageCollection('MODIS/006/MOD09GA')
.filterBounds(geometry) // 研究区域
.filterDate('2020-01-01', '2020-12-31') // 时间范围
.select(['sur_refl_b01','sur_refl_b02','sur_refl_b03']); // 可见光波段
3.2 阴影计算核心代码
javascript复制// 定义研究区域(以落基山脉为例)
var roi = ee.Geometry.Polygon(
[[[-109.0, 37.0],
[-109.0, 39.0],
[-106.0, 39.0],
[-106.0, 37.0]]]);
// 获取DEM数据
var dem = ee.Image('CGIAR/SRTM90_V4').clip(roi);
// 计算夏至日正午阴影(太阳方位角180°,高度角73°)
var summerSolstice = ee.Date('2020-06-21');
var shadow = ee.Terrain.hillShadow(dem, 180, 73, 100, true);
// 计算MODIS平均反射率
var modisMean = modisCol.mean().clip(roi);
// 生成阴影掩膜
var shadowArea = shadow.multiply(modisMean.select('sur_refl_b01'));
3.3 结果可视化
javascript复制// 定义可视化参数
var visParams = {
bands: ['sur_refl_b01','sur_refl_b02','sur_refl_b03'],
min: 0,
max: 3000,
gamma: 1.4
};
// 添加图层到地图
Map.centerObject(roi, 8);
Map.addLayer(modisMean, visParams, 'MODIS Composite');
Map.addLayer(shadow, {palette: ['black']}, 'Shadow Mask');
Map.addLayer(shadowArea, {palette: ['red']}, 'Shadow Area');
4. 关键技术细节
4.1 太阳位置计算优化
精确的阴影计算需要动态太阳位置。推荐使用ee.Algorithms.SunPosition()方法:
javascript复制var date = ee.Date('2020-06-21T12:00:00');
var position = ee.Algorithms.SunPosition(date, roi.centroid());
var azimuth = position.get('azimuth'); // 太阳方位角
var elevation = position.get('elevation'); // 太阳高度角
4.2 核半径(kernelHalfSize)选择
核半径影响阴影边缘的平滑程度:
- 小半径(5-10像素):保留细节,适合陡峭地形
- 大半径(20-30像素):平滑效果,适合平缓地区
javascript复制// 不同核半径效果对比
var shadow_10m = ee.Terrain.hillShadow(dem, 180, 60, 10);
var shadow_30m = ee.Terrain.hillShadow(dem, 180, 60, 30);
4.3 阴影面积统计
计算阴影区域占比的两种方法:
方法一:像素计数法
javascript复制var stats = shadow.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: roi,
scale: 90,
maxPixels: 1e9
});
var shadowRatio = stats.get('hillshadow');
方法二:分类统计法
javascript复制var areaImage = ee.Image.pixelArea().addBands(shadow);
var areaStats = areaImage.reduceRegion({
reducer: ee.Reducer.sum().group({
groupField: 1,
groupName: 'shadow'
}),
geometry: roi,
scale: 90,
maxPixels: 1e9
});
5. 常见问题与解决方案
5.1 数据缺失问题
现象:高纬度地区SRTM数据存在空缺
解决方案:
- 使用NASA DEM替代:
javascript复制var dem = ee.Image('NASA/NASADEM_HGT/001');
- 数据融合:
javascript复制var dem = ee.ImageCollection('JAXA/ALOS/AW3D30/V2_2')
.mosaic()
.select('DSM');
5.2 阴影计算偏差
现象:阴影区域与实地观测不符
排查步骤:
- 检查DEM分辨率是否足够
- 验证太阳位置计算是否正确
- 调整模糊距离参数(建议50-200米)
5.3 处理超时问题
现象:大范围计算时出现超时错误
优化方案:
- 分块处理:
javascript复制var grid = roi.geometry().coveringGrid(roi.projection(), 50000);
var shadows = grid.map(function(feature){
return ee.Terrain.hillShadow(dem.clip(feature), 180, 60, 100);
});
var finalShadow = ee.ImageCollection(shadows).mosaic();
- 降低输出分辨率:
javascript复制var stats = shadow.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: roi,
scale: 500, // 降低分辨率
maxPixels: 1e9
});
6. 进阶应用方向
6.1 时间序列阴影分析
计算全年阴影频率图:
javascript复制var dates = ee.List.sequence(0, 11).map(function(month){
return ee.Date('2020-01-01').advance(month, 'month');
});
var yearlyShadows = ee.ImageCollection.fromImages(
dates.map(function(date){
var position = ee.Algorithms.SunPosition(date, roi.centroid());
return ee.Terrain.hillShadow(
dem,
position.get('azimuth'),
position.get('elevation'),
100
);
})
);
var shadowFrequency = yearlyShadows.sum().divide(yearlyShadows.count());
6.2 生态应用案例
北坡植被分析:
javascript复制// 获取NDVI数据
var ndvi = modisCol.map(function(image){
return image.normalizedDifference(['sur_refl_b02','sur_refl_b01']);
});
// 计算北坡阴影区NDVI
var northShadow = shadow.updateMask(dem.select('aspect').lt(0));
var stats = ndvi.mean().updateMask(northShadow)
.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: roi,
scale: 90
});
注意事项:实际应用中建议使用Landsat或Sentinel-2数据获取更高精度的NDVI
7. 性能优化技巧
- 预处理DEM数据:
javascript复制// 使用地形平滑减少噪点
var smoothedDEM = dem.focal_mean({
radius: 3,
units: 'pixels'
});
- 并行计算策略:
javascript复制// 同时计算多个时间点的阴影
var shadowCollection = ee.ImageCollection(
ee.List.sequence(0, 23).map(function(hour){
var time = ee.Date('2020-06-21').advance(hour, 'hour');
var pos = ee.Algorithms.SunPosition(time, roi.centroid());
return ee.Terrain.hillShadow(
dem,
pos.get('azimuth'),
pos.get('elevation'),
100
).set('time', time);
})
);
- 内存管理技巧:
- 使用
clip()限制处理范围 - 优先使用
reduceRegion()而非sample() - 对大区域使用
Export而非getInfo()
8. 完整代码示例
javascript复制// 山体阴影分析完整流程
function calculateTerrainShadow(roi, date) {
// 1. 准备数据
var dem = ee.Image('CGIAR/SRTM90_V4').clip(roi);
var modis = ee.ImageCollection('MODIS/006/MOD09GA')
.filterDate(date, date.advance(1, 'day'))
.select(['sur_refl_b01','sur_refl_b02','sur_refl_b03']);
// 2. 计算太阳位置
var pos = ee.Algorithms.SunPosition(date, roi.centroid());
var azimuth = pos.get('azimuth');
var elevation = pos.get('elevation');
// 3. 生成阴影
var shadow = ee.Terrain.hillShadow(dem, azimuth, elevation, 100, true);
// 4. 融合MODIS数据
var modisMean = modis.mean().clip(roi);
var shadowArea = shadow.multiply(modisMean.select('sur_refl_b01'));
// 5. 计算阴影面积
var areaStats = ee.Image.pixelArea()
.addBands(shadow)
.reduceRegion({
reducer: ee.Reducer.sum().group({
groupField: 1,
groupName: 'type'
}),
geometry: roi,
scale: 90,
maxPixels: 1e9
});
// 返回结果
return {
shadowMask: shadow,
shadowArea: shadowArea,
stats: areaStats
};
}
// 使用示例
var roi = ee.Geometry.Rectangle([-109, 37, -106, 39]);
var result = calculateTerrainShadow(roi, ee.Date('2020-06-21'));
print(result.stats);
9. 实际应用建议
-
分辨率匹配原则:
- MODIS分辨率:250-500米
- 配套DEM分辨率建议:90米(SRTM)或30米(NASA DEM)
- 更高分辨率DEM不会显著提升效果,反而增加计算负担
-
时间选择技巧:
- 生态研究:选择植被生长期(北半球5-9月)
- 太阳能评估:选择冬至日(12月21日)计算最长阴影
- 冰川研究:选择夏季正午(太阳高度角最大时)
-
验证方法:
- 使用
Map.addLayer()交互式验证 - 选取典型区域导出高分辨率结果
- 与实地照片或高分辨率影像对比
- 使用
10. 扩展思考
-
阴影持续时间分析:
通过计算不同时段的阴影叠加,可以识别永久阴影区(如深谷)和临时阴影区(如东坡上午阴影) -
三维可视化增强:
将结果导出为GeoTIFF后,可在QGIS中使用QGIS2ThreeJS插件生成三维阴影效果图 -
气候变化研究应用:
比较不同年份同期的阴影分布变化,可间接反映冰川退缩或植被覆盖变化
我在实际项目中发现,将阴影分析与地表温度数据结合,能有效识别"冷阴影"区域。例如在干旱区,这些区域往往对应着特殊的微气候环境,可能成为生物多样性热点。一个实用的技巧是:当处理超大区域时,先使用低分辨率DEM进行快速评估,再针对重点区域进行高精度分析,这样能显著提高工作效率。