目录
[(一)MODIS NDVI 数据集选择](#(一)MODIS NDVI 数据集选择)
[(一)VCI 的原理与意义](#(一)VCI 的原理与意义)
[(二)VCI 计算的完整流程](#(二)VCI 计算的完整流程)
[1. NDVI 极值计算与缩放](#1. NDVI 极值计算与缩放)
[2. 遍历影像计算单景 VCI](#2. 遍历影像计算单景 VCI)
[(一)2001-2003 年静态干旱分级(短期分析)](#(一)2001-2003 年静态干旱分级(短期分析))
[(二)2001-2024 年动态干旱监测(长期分析)](#(二)2001-2024 年动态干旱监测(长期分析))
[1. 长时间序列 VCI 计算](#1. 长时间序列 VCI 计算)
[2. 逐年干旱等级分类与众数分析](#2. 逐年干旱等级分类与众数分析)
[(一)VCI 直方图绘制](#(一)VCI 直方图绘制)
[六、数据导出:将结果保存到 Google Drive](#六、数据导出:将结果保存到 Google Drive)

若觉得代码对您的研究 / 项目有帮助,欢迎点击打赏支持!需要完整代码的朋友,打赏后可在后台私信(复制文章标题发给我),我会尽快发您完整可运行代码,感谢支持!
本代码基于 Google Earth Engine(GEE)平台,以 MODIS NDVI 数据为核心,构建了 "数据加载 - 指数计算 - 干旱分级 - 动态分析 - 可视化统计 - 数据导出" 的完整干旱监测工作流,覆盖 2001-2003 年静态分析与 2001-2024 年动态监测,以下从技术原理、代码逻辑、参数意义三方面展开深度解析。
一、基础模块:研究区与时间配置
(一)研究区边界定义与可视化
核心作用:确定分析空间范围,是后续数据筛选、影像裁剪、面积统计的基础。
javascript
var roi = table;
Map.addLayer(roi, {color: 'red'}, '研究区');
Map.centerObject(roi, 8);
roi = table:table是 GEE 中预设的矢量数据(如 Shapefile 导入后的资产),包含研究区的边界坐标(经纬度),数据类型为ee.FeatureCollection(要素集合),需确保矢量边界无拓扑错误(如重叠、缝隙),否则会导致后续裁剪或统计结果偏差。Map.addLayer(roi, {color: 'red'}, '研究区'):将研究区矢量边界添加到地图界面,color: 'red'设置边界线颜色为红色,第三个参数为图层名称(用于地图图例区分),若不添加该图层,无法在地图上直观确认研究区位置是否正确。Map.centerObject(roi, 8):自动将地图视角聚焦到研究区中心,第二个参数 "8" 是 GEE 的缩放级别(范围 1-24,1 为全球视角,24 为米级高清视角),缩放级别 8 对应约 10 公里分辨率视角,可完整显示地级市 / 区域级研究区,若缩放级别过高(如 12),可能导致研究区超出地图视野。
(二)时间范围设置
核心作用:划定数据筛选的时间区间,区分 "短期静态分析"(2001-2003 年)与 "长期动态分析"(2001-2024 年)两个研究目标。
javascript
var time_start = '2001';
var time_end = '2003';
var time_start2 = '2001';
var time_end2 = '2024';
- GEE 的
filterDate函数支持多种时间格式(如'2001-01-01'精确到日、'2001'精确到年),此处用 "年" 作为时间单位,会自动筛选该年份 1 月 1 日至 12 月 31 日的所有数据。 - 时间区间为 "左闭右开",即
filterDate('2001','2003')实际筛选 2001 年 1 月 1 日 - 2002 年 12 月 31 日的数据,若需包含 2003 年数据,需将time_end设为'2004',这是 GEE 时间筛选的常见 "坑点",若忽略会导致数据缺失 1 年。
二、数据加载与预处理
(一)MODIS NDVI 数据集选择
javascript
var ndvi = ee.ImageCollection("MODIS/061/MOD13A2")
.select(['NDVI'])
.filterDate(time_start, time_end)
.filterBounds(roi);
- 数据集 ID:
MODIS/061/MOD13A2,其中 "061" 是数据集版本(V6.1),是当前(2024 年)MODIS NDVI 的最新稳定版,相比旧版本(如 V5)优化了大气校正、云去除算法,数据精度更高;"MOD13A2" 表示产品类型:16 天合成的 NDVI 产品,空间分辨率为 500 米(每像元覆盖 500m×500m 区域)。 - 数据波段:
MOD13A2包含 NDVI、EVI、质量控制(QC)等多个波段,此处用.select(['NDVI'])仅保留 NDVI 波段,减少数据量,提升后续计算效率(若保留多余波段,会导致内存占用增加,尤其长期序列分析时易卡顿)。
(二)数据筛选逻辑
- 时间筛选 :
.filterDate(time_start, time_end)根据 1.2 定义的时间范围,从整个 MODIS NDVI 影像集合中筛选出目标时段的影像,例如 2001-2003 年(实际 2001-2002 年)会筛选出(2002-2001+1)×(365/16)≈2×23=46景影像(16 天合成,每年约 23 景)。 - 空间筛选 :
.filterBounds(roi)筛选出 "影像覆盖范围与研究区有交集" 的影像,避免加载无关区域的影像(如研究区为华北某城市,无需加载华南地区的影像),进一步减少数据量。 - 注意事项 :若研究区位于高纬度地区(如北纬 50° 以上),冬季(11-2 月)NDVI 值会接近 0 或为负数(植被枯萎),需确认研究目标是否包含冬季数据,若仅关注生长季(5-10 月),可在
filterDate后添加.filter(ee.Filter.calendarRange(5,10,'month'))筛选月份。
三、核心指数计算:植被状况指数(VCI)
(一)VCI 的原理与意义
VCI(Vegetation Condition Index,植被状况指数)是干旱监测的常用指数,核心逻辑是 "通过当前 NDVI 与历史同期 NDVI 极值的对比,反映植被受干旱影响的程度",公式如下:VCI = ((NDVI_current - NDVI_min) / (NDVI_max - NDVI_min)) × 100
- 取值范围:0-100,数值越低,干旱越严重:
- 0-10:极端干旱(植被严重枯萎,甚至死亡);
- 80-100:无干旱(植被生长旺盛,水分充足)。
- 优势:相比原始 NDVI,VCI 消除了 "不同年份同一季节植被背景差异"(如 2001 年春季植被基数低,2002 年春季基数高),更能客观反映干旱导致的植被胁迫。
(二)VCI 计算的完整流程
javascript
// 步骤1:计算研究时段内NDVI的极值(最小值、最大值)并缩放
var ndvi_min = ndvi.min().multiply(0.0001);
var ndvi_max = ndvi.max().multiply(0.0001);
// 步骤2:遍历每景NDVI影像,计算单景VCI
var vci = ndvi.map(function(img) {
var band = img.multiply(0.0001); // 单景NDVI缩放
var index = band.expression(
'((ndvi - min)/(max - min))*100.0',
{
'ndvi': band,
'min': ndvi_min,
'max': ndvi_max
}
).rename('vci'); // 重命名波段为"vci"
// 保留原始影像的时间属性
return index.copyProperties(img, ['system:time_start', 'system:time_end']);
});
1. NDVI 极值计算与缩放
ndvi.min():对筛选后的 NDVI 影像集合,计算 "每个像元" 在时间序列内的最小值(如 2001-2002 年 46 景影像中,某像元的最低 NDVI 值),结果为单波段影像(ee.Image),数据类型为Int16(整数)。.multiply(0.0001):MODIS NDVI 原始数据采用 "整数存储"(范围 - 32768 至 32767),需乘以缩放系数 0.0001 转换为 "实际 NDVI 值"(范围 - 1 至 1),这是 MODIS 数据的标准预处理步骤,若不缩放,原始值(如 1000)会被误判为 NDVI=1000,导致 VCI 计算结果完全错误。- 为什么用 "极值" 而非 "均值"?NDVI_min 代表研究时段内植被最干旱的状态,NDVI_max 代表最湿润的状态,两者的差值反映了该区域植被对水分条件的最大响应范围,用极值计算的 VCI 能更灵敏地捕捉干旱变化。
2. 遍历影像计算单景 VCI
ndvi.map(function(img) { ... }):map函数是 GEE 中遍历集合的核心函数,此处遍历ndvi影像集合中的每景影像(img为单景影像),对每景影像执行相同的 VCI 计算逻辑,最终返回新的 VCI 影像集合(vci)。band.expression(...):通过表达式计算 VCI,第一个参数是公式字符串(需用单引号包裹,变量用占位符表示),第二个参数是 "占位符 - 实际数据" 的映射表:'ndvi':对应单景影像缩放后的 NDVI 值(band);'min':对应步骤 1 计算的 NDVI 最小值(ndvi_min);'max':对应步骤 1 计算的 NDVI 最大值(ndvi_max)。
copyProperties(img, ['system:time_start', 'system:time_end']):保留原始 NDVI 影像的时间属性(system:time_start为影像起始时间,system:time_end为结束时间),若不保留,后续无法按时间筛选 VCI 影像(如提取 2002 年夏季的 VCI),也无法绘制时间序列图表。
四、干旱分析:静态分级与动态监测
(一)2001-2003 年静态干旱分级(短期分析)
javascript
// 等级标准:1-极端干旱(0-10)| 2-严重干旱(10-20)| ... |7-无干旱3(>80)
var cons = ee.Image.constant(0); // 创建值为0的常量影像
var extreme = cons.where(vci_median.gte(0).and(vci_median.lt(10)), 1);
var severe = extreme.where(vci_median.gte(10).and(vci_median.lt(20)), 2);
// ... 后续等级依次叠加
var no3 = no2.where(vci_median.gte(80), 7);
- 核心逻辑 :基于 VCI 中值(
vci_median,2001-2003 年平均 VCI),通过where函数实现 "阈值分割",将连续的 VCI 值转换为离散的干旱等级(1-7 级)。 - 关键函数解析:
ee.Image.constant(0):创建一幅所有像元值均为 0 的影像(cons),作为等级赋值的初始载体(若不创建初始影像,无法使用where函数叠加等级)。vci_median.gte(0).and(vci_median.lt(10)):构建逻辑条件:"VCI 大于等于 0 且小于 10",gte是 "greater than or equal" 的缩写,and是逻辑 "与",条件成立的像元值为true,否则为false。cons.where(condition, 1):对cons影像中 "条件成立(true)" 的像元赋值为 1(极端干旱),"条件不成立(false)" 的像元保持原值 0,后续等级通过 "叠加where" 实现:severe = extreme.where(...),即在前一个等级影像(extreme)的基础上,对符合 "严重干旱" 条件的像元赋值为 2,未覆盖的像元保持原有等级(如极端干旱的 1、未赋值的 0),依次类推,最终得到完整的 7 级干旱影像。
- 注意事项 :等级赋值必须按 "从低到高" 的顺序(极端干旱→无干旱),若先赋值高等级(如先赋值无干旱 7),后续低等级的
where会覆盖高等级值,导致等级混乱。
(二)2001-2024 年动态干旱监测(长期分析)
1. 长时间序列 VCI 计算
代码 7 部分重复了步骤 3 的 VCI 计算逻辑,但时间范围扩展为 2001-2024 年,得到vci2影像集合(约 24 年 ×23 景 / 年 = 552 景影像),核心差异在于:极值计算范围更大,ndvi_min2和ndvi_max2是 24 年的 NDVI 极值,更能反映研究区长期的植被水分响应范围,计算的 VCI 更具跨年度可比性。
2. 逐年干旱等级分类与众数分析
javascript
// 逐年干旱等级分类
var vci_class = vci2.map(function(img) {
var cons = ee.Image.constant(0);
var extreme = cons.where(img.gte(0).and(img.lt(10)), 1); // 关键修复:用img替代vci_median
// ... 后续等级赋值(同4.1)
return no3.copyProperties(img, img.propertyNames());
});
// 计算24年干旱等级众数
var vci_map = vci_class.mode();
- 核心修复点 :原始代码中可能存在 "用 2001-2003 年 VCI 中值(
vci_median)对 2001-2024 年每景 VCI 影像分类" 的错误,此处修复为 "用单景 VCI 影像(img)自身的数值进行等级分类",确保每景影像的等级是基于自身 VCI 值,而非历史中值,符合动态监测的逻辑。 - 众数分析(
mode) :vci_class.mode()计算 "每个像元" 在 2001-2024 年所有干旱等级中的 "众数"(出现频率最高的等级),例如某像元在 24 年中有 15 年为 "无干旱 2(6 级)",5 年为 "无干旱 1(5 级)",4 年为 "轻度干旱(4 级)",则该像元的众数等级为 6 级,最终生成的vci_map能反映研究区 24 年的 "主导干旱状态",是动态分析的核心结果。
五、可视化与统计:从图表到面积
(一)VCI 直方图绘制
javascript
print(ui.Chart.image.histogram(vci_median, roi, 1000)
.setOptions({
title: '研究区VCI值分布直方图',
hAxis: {title: 'VCI值'},
vAxis: {title: '像元数量'}
})
);
- 核心作用:统计研究区内 VCI 中值的分布特征,通过 "VCI 值区间(横轴)" 与 "对应像元数量(纵轴)" 的柱状图,判断研究区整体干旱程度:
- 若直方图峰值集中在 80-100,说明研究区整体无干旱;
- 若峰值集中在 0-20,说明研究区整体干旱严重。
- 关键参数:
vci_median:待统计的影像(此处为 VCI 中值);roi:统计范围(研究区);1000:统计分辨率(单位:米),即按 1000 米网格统计像元数量,若设为 500(NDVI 原始分辨率),统计精度更高,但数据量更大,图表加载更慢,1000 米是 "精度与效率" 的平衡选择。
(二)干旱面积统计
javascript
// 像元面积单位转换:平方米→平方公里(1平方公里=1e6平方米)
var drought_area = (ee.Image.pixelArea().divide(1e6)).addBands(vci_map);
// 按等级统计面积
print(ui.Chart.image.byClass({
image: drought_area,
classBand: 'constant', // 干旱等级波段(vci_map的默认波段名)
region: roi,
reducer: ee.Reducer.sum(), // 求和计算面积
scale: 1000,
classLabels: ['NaN', '极端干旱', '严重干旱', '中度干旱', '轻度干旱', '无干旱1', '无干旱2', '无干旱3']
}).setOptions({...}));
- 核心原理 :GEE 中每个像元的面积可通过
ee.Image.pixelArea()计算(单位:平方米),结合干旱等级影像,按等级求和得到各等级的总面积。 - 关键步骤解析:
ee.Image.pixelArea():生成一幅 "像元值 = 该像元实际面积(平方米)" 的影像,例如赤道附近 1000 米分辨率的像元面积约为 1 平方公里(1000m×1000m=1e6 平方米),高纬度地区因地球曲率影响,像元面积会略小。.divide(1e6):将面积单位从 "平方米" 转换为 "平方公里"(1 平方公里 = 1000×1000=1e6 平方米),符合面积统计的常用单位。.addBands(vci_map):将 "面积影像" 与 "干旱等级影像(vci_map)" 合并为一幅多波段影像,其中 "面积波段" 用于计算面积,"等级波段(constant)" 用于分组。ee.Reducer.sum():按 "等级波段" 的类别(1-7 级),对 "面积波段" 的像元值求和,得到每个等级的总面积,例如 "极端干旱(1 级)" 的总面积 = 所有等级为 1 的像元面积之和。
- classLabels 说明 :
classLabels的索引需与干旱等级对应,第一个元素'NaN'对应等级 0(未赋值像元,通常为研究区外),第二个元素'极端干旱'对应等级 1,以此类推,若顺序错误,会导致图表标签与实际等级不匹配。
(三)图例面板构建
javascript
var legend = ui.Panel({style: {position: 'bottom-left', padding: '8px 15px'}});
var legendTitle = ui.Label({value: 'VCI 干旱等级图例', style: {...}});
legend.add(legendTitle);
// 干旱等级名称与对应颜色(与干旱众数图调色板一致)
var names = ['极端干旱', '严重干旱', '中度干旱', '轻度干旱', '无干旱1', '无干旱2', '无干旱3'];
var colors = ['black', 'brown', 'red', 'orange', 'yellow', 'lightgreen', 'darkgreen'];
// 循环构建图例行(颜色块+文字)
for (var i = 0; i < names.length; i++) {
var colorBox = ui.Label({style: {backgroundColor: colors[i], padding: '8px', ...}});
var description = ui.Label({value: names[i], style: {...}});
var row = ui.Panel({widgets: [colorBox, description], layout: ui.Panel.Layout.Flow('horizontal')});
legend.add(row);
}
Map.add(legend);
- 核心作用:为地图添加自定义图例,解决 GEE 默认图例无文字说明的问题,让用户能直观对应 "颜色 - 干旱等级"。
- 关键设计:
- 位置与样式:
position: 'bottom-left'将图例放在地图左下角,避免遮挡研究区;padding: '8px 15px'设置内边距,让图例更美观。 - 颜色一致性:
colors数组的颜色与 "干旱众数图(vci_map)" 的调色板完全一致(黑色 = 极端干旱,深绿色 = 无干旱 3),确保图例与地图颜色一一对应,若颜色不一致,会导致解读错误。 - 循环构建:通过
for循环遍历names和colors数组,批量生成 "颜色块(colorBox)+ 文字说明(description)" 的水平行(row),避免重复代码,提升可维护性。
- 位置与样式:
六、数据导出:将结果保存到 Google Drive
javascript
Export.image.toDrive({
image: vci_map.clip(roi), // 导出的影像(裁剪到研究区)
description: 'vci_map', // 导出文件名称
region: roi, // 导出范围(研究区)
maxPixels: 1e13, // 最大像元数限制
crs: 'EPSG:4326', // 坐标系(WGS84)
folder: 'drought', // Drive中存储的文件夹名称
scale: 1000 // 导出分辨率(1000米)
});
- 核心作用:将 GEE 中的干旱众数图导出为本地可使用的影像文件(如 GeoTIFF),用于后续 ArcGIS、QGIS 等软件的进一步分析(如叠加其他数据、制作专题图)。
- 关键参数解析:
image: vci_map.clip(roi):导出的影像为 "裁剪到研究区的干旱众数图",clip(roi)确保导出的影像仅包含研究区范围,无多余背景像元(若不裁剪,会导出整幅 GEE 影像的范围,数据量极大)。maxPixels: 1e13:GEE 默认限制导出影像的最大像元数为 1e8(1 亿),研究区较大时(如省级区域),1e8 像元会不够用,需手动设置更大值(如 1e13,10 万亿),避免导出失败,计算公式:最大像元数 = 研究区面积(平方公里)/(分辨率(公里))^2,例如研究区面积 10 万平方公里,分辨率 1 公里,最大像元数 = 10 万 /(1×1)=1e5,远小于 1e13,足够使用。crs: 'EPSG:4326':指定导出影像的坐标系为 WGS84(经纬度坐标系),是全球通用坐标系,若需投影坐标系(如 UTM),可改为crs: 'EPSG:32649'(UTM 49N,适用于华北地区)。scale: 1000:导出分辨率为 1000 米,与 NDVI 原始分辨率(500 米)相比,降低了分辨率,减少导出文件大小(500 米分辨率的文件大小是 1000 米的 4 倍),若需更高精度,可设为 500,但需注意文件大小(可能达数百 MB)。
- 导出后操作:导出任务完成后,需到 Google Drive 的 "drought" 文件夹中下载 GeoTIFF 文件,下载后需检查文件是否完整(无损坏、边界正确),可在 QGIS 中打开,叠加研究区矢量边界确认。
七、常见问题与解决方案
| 常见问题 | 原因 | 解决方案 |
|---|---|---|
| 导出任务失败,提示 "超出最大像元数" | maxPixels设置过小,研究区像元数超过限制 |
将maxPixels改为 1e13 或更大值 |
| VCI 值出现负数或大于 100 | NDVI 极值计算错误(如未缩放),或公式写错 | 确认ndvi_min、ndvi_max和单景影像均已乘以 0.0001,检查 VCI 公式是否为((ndvi - min)/(max - min))*100 |
| 干旱等级图全为 0 或某一等级 | where函数条件错误(如逻辑运算符用or而非and),或等级赋值顺序错误 |
检查条件是否为gte(x).and(lt(y)),确保等级赋值从低到高(极端干旱→无干旱) |
| 地图上看不到研究区图层 | roi矢量数据错误(如为空集合),或缩放级别过高 |
检查table是否正确导入,用print(roi)确认矢量数据非空,调整Map.centerObject的缩放级别为 6-10 |
八、运行结果
基于植被状况指数(VCI)的干旱监测成果
控制台输出的统计数据相关信息
研究区植被状况指数(VCI)值分布直方图
研究区各干旱等级面积统计柱状图
若觉得代码对您的研究 / 项目有帮助,欢迎点击打赏支持!需要完整代码的朋友,打赏后可在后台私信(复制文章标题发给我),我会尽快发您完整可运行代码,感谢支持!