GEE实战:CHIRPS降水数据多时间尺度分析与可视化

发布时间:2026/7/7 12:22:31

GEE实战:CHIRPS降水数据多时间尺度分析与可视化 1. 从零开始认识GEE与CHIRPS降水数据如果你对全球气候变化、农业旱情监测或者水文研究感兴趣但又觉得处理海量卫星数据是个技术活那今天这篇文章就是为你准备的。我是老张在遥感数据分析这块摸爬滚打了十来年用过不少平台但要说处理全球长时间序列数据最省心、最高效的Google Earth EngineGEE绝对是首选。它就像一个在线的“地球数据超级计算机”把几十年的卫星影像、气象数据都给你准备好了你只需要写几行简单的JavaScript代码就能调用这些数据进行分析完全不用自己下载动辄几十GB的原始文件这对个人研究者和学生来说简直是福音。今天我们要重点聊的是GEE里一个非常经典且实用的数据集——CHIRPS。它的全称是“Climate Hazards Group InfraRed Precipitation with Station data”翻译过来就是“气候灾害组红外降水融合站点数据”。这个名字听起来有点复杂但其实原理很直观科学家们把卫星观测的红外数据能反映云顶温度间接推断降水和全球地面气象站实测的降水数据结合起来通过算法“融合”成一个更准确、更可靠的全球网格化降水产品。这个数据从1981年一直更新到现在空间分辨率大约是0.05度赤道附近约5.5公里时间上提供了日尺度的数据。这意味着你可以分析地球上几乎任何一个地方从昨天到四十年前的任何一天的降水量。为什么CHIRPS这么受欢迎我自己的体会是它解决了传统降水数据的几个痛点。第一是覆盖面广很多偏远地区、海洋上没有气象站纯站点数据是空白而卫星数据可以全覆盖。第二是一致性高整个时间序列用的是同一套算法和输入数据避免了因为站点搬迁、仪器更换带来的数据不连续问题做长期趋势分析特别靠谱。第三就是获取方便在GEE里你只需要知道它的数据集IDUCSB-CHG/CHIRPS/DAILY就能直接调用省去了找数据源、下载、解压、格式转换等一系列繁琐的预处理步骤。那么谁最适合用这套数据和今天介绍的方法呢如果你是地理、气象、水文、生态、农业等相关专业的学生或研究人员需要分析区域或全球的降水特征或者你是相关领域的工程师或规划人员需要评估旱涝风险、进行水资源管理甚至你只是一个对气候数据可视化感兴趣的爱好者想自己动手制作一张专业的降水分布图接下来的内容都会手把手带你实现。我们不会停留在理论而是直接进入GEE的代码编辑器用实战代码说话从单日数据提取到月、年乃至多年平均的计算与可视化一步步拆解。你会发现原来分析全球降水格局并没有想象中那么难。2. 环境准备与数据初探你的第一个GEE脚本万事开头难但GEE的开头真的不难。首先你需要一个谷歌账号。打开浏览器访问 https://code.earthengine.google.com/用你的谷歌账号登录就进入了GEE的在线代码编辑器。这个界面非常简洁中间是写代码的脚本编辑器Script Editor左边是数据集搜索、脚本管理等功能模块右边是地图显示和结果输出窗口。第一次使用可能需要简单注册一下过程很快。登录成功后我们新建一个脚本。在代码编辑器里我们首先来加载CHIRPS数据并看看它长什么样。这里我给出一个最基础的代码块你可以直接复制粘贴运行// 1. 加载CHIRPS日降水数据集 var chirpsDaily ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY); // 2. 打印数据集的基本信息到控制台 print(数据集基本信息:, chirpsDaily); // 3. 在控制台打印数据集的第一个影像看看数据结构 var firstImage chirpsDaily.first(); print(第一景影像的信息:, firstImage); // 4. 定义一个时间范围并过滤出这个时间段的数据 var startDate 2023-06-01; var endDate 2023-06-10; var filteredCollection chirpsDaily.filterDate(startDate, endDate); // 5. 打印过滤后的影像数量 print(2023年6月1日至10日的影像数量:, filteredCollection.size()); // 6. 计算这10天的累计降水量将每天的影像相加 var totalPrecipitation filteredCollection.sum(); // 7. 定义可视化参数 var precipitationVis { min: 0, // 显示的最小值 max: 100, // 显示的最大值单位毫米 palette: [white, blue, darkblue, green, yellow, red] // 颜色渐变条 }; // 8. 将地图中心定位到中国区域 Map.setCenter(105, 35, 3); // 经度纬度缩放级别 // 9. 将累计降水量图层加载到地图上 Map.addLayer(totalPrecipitation, precipitationVis, 2023-06-01至10日累计降水);运行这段代码点击编辑器上方的Run按钮你会看到右边地图上出现了彩色图案这就是2023年6月前10天全球的累计降水量。颜色越蓝表示降水越多越红表示越少。同时在编辑器下方的Console控制台标签页里会打印出数据集的信息。你会看到这个ImageCollection包含很多属性比如时间范围、波段名称等。其中最关键的一个波段就是precipitation它的单位是毫米mm这就是我们后续所有分析的基础。这里有个小技巧GEE处理数据是“惰性”的。当你执行filterDate或sum()这些操作时它并不是真的在下载和处理所有数据而是生成一个处理任务的描述。只有当你需要可视化Map.addLayer或导出后面会讲时它才会在后台真正开始计算。这种设计使得处理超大规模数据成为可能。你可能注意到全球地图看起来有点“平淡”特别是海洋区域。这是因为CHIRPS主要针对陆地降水海洋上的数据是卫星反演结果精度相对较低。在实际研究中我们通常会用一个感兴趣区域ROI来裁剪数据聚焦在我们关心的地区比如某个省、某个流域。接下来我们就来学习如何定义并应用你的研究区。3. 核心操作一日尺度降水数据的提取与可视化日常研究中我们经常需要分析特定日期或短时间窗口的降水情况比如一次暴雨过程。在GEE中提取单日或数日的CHIRPS数据非常直接。但在此之前我们必须先明确我们的研究区域。定义研究区有几种常见方法最灵活的是手动绘制。在GEE地图窗口的左上角有一排图形工具。找到那个多边形Polygon或矩形Rectangle图标点击它然后在地图上你想研究的地方比如长江流域点几个点围成一个区域双击闭合。这时脚本中会自动生成一个名为geometry的变量。为了代码清晰我们通常把它赋值给我们自己的变量比如叫roiRegion of Interest。// 假设我们已经通过地图工具绘制了一个多边形系统自动生成了geometry变量 var roi geometry; // 将绘制的几何区域赋值给 roi Map.addLayer(roi, {color: FF0000}, 我的研究区域); // 将ROI以红色边框显示在地图上 // 如果不手动绘制也可以直接用坐标定义矩形区域例如四川省的大致范围 // var roi ee.Geometry.Rectangle([97.0, 26.0, 108.0, 34.0]);有了roi我们就可以进行精准的数据提取和分析了。假设我想看2023年7月20日这天全国的降水情况并计算四川省当日的平均降水量代码如下// 1. 定义具体日期 var targetDate 2023-07-20; // 2. 过滤出该日的CHIRPS影像 // 注意filterDate是包含开始时间不包含结束时间所以结束日期要加一天 var dailyImage ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY) .filterDate(targetDate, 2023-07-21) // 过滤出7月20日这一天 .first(); // 获取集合中的第一景也是唯一一景影像 // 3. 将影像裁剪到研究区 var dailyImageClipped dailyImage.clip(roi); // 4. 定义可视化参数调整颜色方案以更好显示单日降水 var dailyVis { min: 0, max: 50, // 单日降水超过50mm就算暴雨了这里设为最大值 palette: [#ffffcc,#a1dab4,#41b6c4,#2c7fb8,#253494] // 从浅黄到深蓝的渐变色 }; // 5. 将裁剪后的日降水图层添加到地图 Map.addLayer(dailyImageClipped, dailyVis, 2023-07-20日降水); // 6. 计算研究区内的平均降水量 var meanStats dailyImageClipped.reduceRegion({ reducer: ee.Reducer.mean(), // 使用均值归约器 geometry: roi, scale: 5500, // 使用CHIRPS数据的大致分辨率0.05度约5500米 bestEffort: true // 如果区域太大或计算复杂尝试最佳估算 }); // 7. 在控制台打印平均降水量结果 print(2023-07-20 研究区平均降水量 (mm):, meanStats.get(precipitation));运行后地图上会高亮显示你定义的roi区域并叠加该区域的日降水彩色图。在控制台你会看到打印出的平均降水量数值。这个reduceRegion函数非常强大它可以将一个区域内所有像素的值进行统计求平均、求和、最大值、最小值等是进行区域统计的关键。但只看一天的数据往往不够我们可能想分析一个连续时段比如一次持续一周的降水过程。这时我们可以计算这个时间段的累计降水量。只需将上面的.first()改为.sum()即可// 计算2023年7月一整月的累计降水量 var start 2023-07-01; var end 2023-08-01; // 注意结束日期不包含所以用8月1号表示7月31日结束 var monthlyAccumulation ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY) .filterDate(start, end) .sum() // 将整个集合的影像相加 .clip(roi); Map.addLayer(monthlyAccumulation, {min: 0, max: 300, palette: [white, blue, darkblue]}, 2023年7月累计降水);通过调整filterDate和选择不同的统计方法sum,mean,max等你可以轻松获取任意时间窗口的降水特征。这是日尺度分析的核心。接下来我们将难度升级看看如何系统性地处理更长时间序列生成月度、年度甚至多年平均的数据。4. 核心操作二自动批量计算月尺度与年尺度数据手动改日期去计算每个月、每一年的数据显然太累了。在GEE里我们可以利用循环和日期列表工具来实现自动化批量处理这是体现GEE强大计算能力的关键。我们先从计算单个月份的平均降水开始。假设我想计算研究区2022年每个月的平均降水量并将结果图片保存到我的Google Drive。思路是先构建一个包含2022年12个月份的列表然后循环遍历这个列表对每个月的数据进行过滤、求平均、裁剪最后导出。代码如下// 定义研究区沿用之前的roi // 定义起始和结束年份 var startYear 2022; var endYear 2022; // 创建一个月份列表 [1,2,3,...,12] var months ee.List.sequence(1, 12); // 使用map函数遍历每个月而不是用for循环。在GEE的服务器端环境中map比for循环更高效。 var monthlyCollection ee.ImageCollection.fromImages( months.map(function(m) { // 为每个月构建起始和结束日期 var startDate ee.Date.fromYMD(startYear, m, 1); // 计算下个月的第一天作为结束日期 var endDate startDate.advance(1, month); // 过滤出该月的所有日数据并计算月平均 var monthlyMean ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY) .filterDate(startDate, endDate) .filterBounds(roi) // 先按空间过滤提高效率 .select(precipitation) // 只选择降水波段 .mean() // 计算月平均 .clip(roi) .set(year, startYear) // 为影像设置属性方便后续识别 .set(month, m) .set(system:time_start, startDate.millis()); // 设置时间属性便于制作时间序列图 return monthlyMean; }) ); // 打印月度集合的信息 print(2022年月平均降水影像集合:, monthlyCollection); // 将第一个月1月的结果可视化到地图上 var januaryImage ee.Image(monthlyCollection.first()); Map.addLayer(januaryImage, {min: 0, max: 5, palette: [white, lightblue, blue]}, 2022年1月平均降水); // 批量导出所有月份的图像到Google Drive months.evaluate(function(monthList) { for (var i 0; i monthList.length; i) { var m monthList[i]; var image ee.Image(monthlyCollection.filter(ee.Filter.eq(month, m)).first()); Export.image.toDrive({ image: image, description: Precip_Monthly_Mean_ startYear _ m, // 导出任务名称 folder: GEE_CHIRPS_Exports, // 在Google Drive中创建的文件夹名 fileNamePrefix: CHIRPS_monthly_ startYear _ m, region: roi, scale: 5500, // 导出分辨率单位米 maxPixels: 1e9 // 允许的最大像素数防止数据过大 }); } });这段代码有几个关键点。首先我们用了ee.List.sequence和.map()函数这是GEE中典型的“服务器端”循环方式比在客户端用JavaScript的for循环更高效。其次我们为每个生成的月平均影像设置了属性set比如年份、月份这样后面如果需要按属性过滤会很方便。最后导出部分用了evaluate来获取客户端列表然后在客户端循环发起多个导出任务。你运行后会在GEE的Tasks标签页看到一堆导出任务点击Run后文件就会陆续存到你的Google Drive指定文件夹里。计算年度数据思路类似但更简单因为不需要内层循环。下面我们计算从1993年到2012年这20年间每一年的年平均降水量// 定义年份范围 var startYear 1993; var endYear 2012; var years ee.List.sequence(startYear, endYear); // 创建年度平均降水影像集合 var annualCollection ee.ImageCollection.fromImages( years.map(function(y) { var startDate ee.Date.fromYMD(y, 1, 1); var endDate startDate.advance(1, year); var annualMean ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY) .filterDate(startDate, endDate) .filterBounds(roi) .select(precipitation) .mean() .clip(roi) .set(year, y) .set(system:time_start, startDate.millis()); return annualMean; }) ); // 将地图中心定位到研究区并添加某一年例如2012年的图层 Map.centerObject(roi, 5); // 以roi为中心缩放级别5 var year2012Image annualCollection.filter(ee.Filter.eq(year, 2012)).first(); Map.addLayer(year2012Image, {min: 1.0, max: 17.0, palette: [001137, 0aab1e, e7eb05, ff4a2d, e90000]}, 2012年平均降水); // 同样可以批量导出这些年度平均图像这里我用了和原始文章示例中一样的调色板palette从深蓝到绿色、黄色再到红色这种配色在显示降水空间分布时非常直观蓝色多雨红色少雨。通过批量生成多年序列你实际上已经构建了一个时间序列数据集可以用来分析降水年际变化趋势。例如你可以计算这20年的平均状态或者找出最旱和最涝的年份。5. 核心操作三多年平均与气候态分析在气候学中我们常使用一个30年的平均值作为“气候态”用来衡量某一年或某个月份相对于长期平均状况的偏差是偏旱还是偏涝。虽然CHIRPS数据从1981年开始但我们可以灵活定义自己的“长期”时段比如计算1993-2012这20年的平均降水作为该时段的“气候态”。计算多年平均非常简单因为我们已经有了每年的平均影像集合annualCollection。只需要对这个集合再求一次平均即可// 计算1993-2012年这20年的平均降水气候态 var longTermMean annualCollection.mean().clip(roi); Map.addLayer(longTermMean, {min: 1.0, max: 17.0, palette: [001137, 0aab1e, e7eb05, ff4a2d, e90000]}, 1993-2012年平均降水气候态); // 计算某一年如2012年相对于这个气候态的距平Anomaly var anomaly2012 year2012Image.subtract(longTermMean); // 2012年值减去气候态 // 定义距平的可视化参数通常用红蓝渐变色红色表示偏多蓝色表示偏少 var anomalyVis { min: -5, // 最小距平值毫米 max: 5, // 最大距平值毫米 palette: [blue, white, red] }; Map.addLayer(anomaly2012, anomalyVis, 2012年降水距平);距平图能非常清晰地告诉我们2012年哪些地方比过去20年平均更湿红色哪些地方更干蓝色。这对于干旱或洪涝监测非常有价值。除了空间分布时间序列分析同样重要。GEE内置了强大的图表功能可以轻松绘制研究区平均降水量的年际变化曲线。我们利用之前已经设置好system:time_start属性的annualCollection// 绘制研究区年平均降水量时间序列图 var chart ui.Chart.image.series({ imageCollection: annualCollection.select(precipitation), // 选择降水波段 region: roi, reducer: ee.Reducer.mean(), // 对区域内像素求平均 scale: 5500, xProperty: system:time_start // 用时间属性作为X轴 }).setOptions({ title: 研究区年平均降水量变化 (1993-2012), hAxis: {title: 年份}, vAxis: {title: 降水量 (mm)}, lineWidth: 2, pointSize: 4 }); // 将图表打印到控制台 print(chart);运行后在控制台会显示一个交互式图表你可以看到一条从1993年到2012年的折线直观反映了研究区年降水量的波动情况。你可以将鼠标悬停在数据点上查看具体数值。这个图表可以直接导出为图片用于报告。更进一步我们还可以计算更长时间尺度的统计量比如每五年的平均降水。这可以帮助我们看清年代际的变化趋势。思路是先将年份分组然后对每组内的年度影像集合求平均// 计算每五年平均降水例如 1993-1997, 1998-2002, ... var fiveYearMeans ee.List.sequence(1993, 2012, 5).map(function(startY){ // 步长为5 var endY ee.Number(startY).add(4); // 五年间隔 var periodCollection annualCollection.filter(ee.Filter.and( ee.Filter.gte(year, startY), ee.Filter.lte(year, endY) )); return periodCollection.mean() .clip(roi) .set(period, startY - endY); }); // 将五年平均结果转换为ImageCollection以便可视化或导出 var fiveYearCollection ee.ImageCollection(fiveYearMeans); print(五年平均降水集合:, fiveYearCollection); // 可视化第一个五年期1993-1997的平均降水 var firstPeriod ee.Image(fiveYearCollection.first()); Map.addLayer(firstPeriod, {min: 1.0, max: 17.0, palette: [001137, 0aab1e, e7eb05, ff4a2d, e90000]}, 1993-1997五年平均);通过这种多时间尺度的分析日、月、年、五年你就能从天气事件、季节内变化、年际波动到年代际趋势等多个维度全面把握研究区域的降水气候特征。这些图表和图像都是强有力的分析工具和成果展示素材。6. 进阶技巧与实战踩坑经验分享掌握了基本操作后我们来聊聊一些能提升效率和结果质量的进阶技巧以及我这些年踩过的一些“坑”。技巧一高效的空间过滤。在之前的代码中我们用了.filterBounds(roi)。这个操作非常必要它能在数据读取阶段就只加载与你的研究区相交的数据极大减少内存占用和计算时间尤其是在处理全球长时间序列数据时。务必养成先空间过滤再进行时间过滤和其他计算的习惯。技巧二分辨率与尺度参数。这是最容易出错的地方之一。当你使用reduceRegion、reduceRegions或者导出图像Export.image.toDrive时必须指定scale参数。这个参数决定了统计或导出时使用的像素大小单位米。对于CHIRPS数据其原生分辨率大约是0.05度在赤道约5500米。如果你设置的scale比这个值小比如30米GEE会进行重采样可能导致数据量暴增和计算错误如果设置得太大则会损失细节。我的经验是对于CHIRPS将scale设置为5500是一个安全且合理的选择。如果不确定可以在数据集的详情页查看其原生的分辨率信息。技巧三导出大区域数据的策略。如果你想导出一个较大区域比如整个中国的高分辨率数据可能会遇到maxPixels限制的错误。GEE默认有像素数量限制以防止过度消耗资源。解决方法是在导出函数中明确设置一个较大的maxPixels值例如1e10100亿像素。同时对于超大区域可以考虑分块导出或降低导出分辨率。技巧四利用clip和updateMask的区别。我们用.clip(roi)将图像裁剪到研究区形状。但有时研究区内的某些像素可能是无效值比如CHIRPS在大型湖泊上的值可能为0或NaN。如果你只想分析有效降水区域可以使用.updateMask()。例如只保留降水量大于0.1mm的像素image.updateMask(image.gt(0.1))。这样后续的统计计算就会自动忽略被掩膜mask掉的像素。踩过的坑时间过滤的边界问题。GEE的filterDate(start, end)是包含start但不包含end的。这意味着filterDate(2023-12-01, 2023-12-02)只会获取12月1日当天的数据。要获取整个12月的数据结束日期应该设为2024-01-01。这个细节在计算月度、年度数据时尤其要注意否则会漏掉最后一天。踩过的坑导出任务的管理。当你批量发起几十个导出任务时它们会排队在Tasks标签页。GEE对同时运行的任务数量有限制。如果任务失败常见原因是超出内存限制或配额不足。对于大量任务建议写一个循环每次只提交几个或者使用Export的crs和crsTransform参数进行更精确的控制。另外导出的文件会保存到你的Google Drive记得定期清理以免占满空间。最后分享一个我常用的调试小技巧多用print()。在写复杂循环或链式调用时在关键步骤打印一下中间变量的类型.type和大小.size()可以快速定位问题所在。例如print(Filtered collection size:, filteredCollection.size());。GEE的调试不像本地编程那样方便良好的打印习惯能节省大量时间。7. 从分析到图表让数据自己说话数据分析的最终目的是为了理解和传达信息。GEE不仅是一个计算平台还内置了强大的可视化图表生成工具能让你的发现一目了然。我们已经看到了如何绘制年降水量时间序列折线图。现在我们来生成更丰富的图表比如月降水量的年内分布柱状图以及空间分布直方图。绘制月降水量 climatology气候月平均柱状图假设我们想看看研究区降水在一年12个月中是如何分布的我们可以计算多年比如1993-2012每个月的平均然后画成柱状图。// 计算1993-2012年期间每个月的多年平均气候月平均 var startYear 1993; var endYear 2012; var years ee.List.sequence(startYear, endYear); var months ee.List.sequence(1, 12); // 这是一个双层循环的逻辑对每一年计算12个月的平均然后再对所有年份的同一个月份求平均。 // 我们可以用更GEE化的方式先创建一个包含所有年月影像的集合再按月份分组求平均。 var allMonthlyImages ee.ImageCollection.fromImages( years.map(function(y) { return months.map(function(m) { var startDate ee.Date.fromYMD(y, m, 1); var endDate startDate.advance(1, month); return ee.ImageCollection(UCSB-CHG/CHIRPS/DAILY) .filterDate(startDate, endDate) .filterBounds(roi) .select(precipitation) .mean() .clip(roi) .set(year, y) .set(month, m) .set(system:time_start, startDate.millis()); }); }).flatten() // 将嵌套的列表展平成一个ImageCollection ); // 现在按月份分组并计算各月的多年平均 var monthlyClimatology ee.ImageCollection.fromImages( months.map(function(m) { // 过滤出所有属于这个月份的影像 var monthCollection allMonthlyImages.filter(ee.Filter.eq(month, m)); // 计算这个月份的多年平均 var meanImage monthCollection.mean(); // 计算研究区在这个月多年平均降水量 var meanValue meanImage.reduceRegion({ reducer: ee.Reducer.mean(), geometry: roi, scale: 5500, bestEffort: true }).get(precipitation); // 返回一个特征Feature包含月份和平均降水量用于制图 return ee.Feature(null, {month: m, precipitation: meanValue}); }) ); // 将特征集合转换为图表可用的格式并绘制柱状图 var chart ui.Chart.feature.byFeature({ features: monthlyClimatology, xProperty: month, yProperties: [precipitation] }).setChartType(ColumnChart) .setOptions({ title: 研究区月平均降水量 (1993-2012 气候态), hAxis: {title: 月份, gridlines: {count: 12}}, vAxis: {title: 降水量 (mm)}, legend: {position: none} }); print(月降水气候态柱状图:, chart);这张图能清晰展示你研究区的雨季和旱季分别在哪些月份。绘制空间分布直方图除了时间变化我们可能还想知道研究区内降水量在不同量级上的像素分布情况。比如在2012年有多少区域的年降水量在500-600mm之间有多少在600-700mm之间这可以用直方图来展示。// 使用2012年的年平均降水影像 var image2012 annualCollection.filter(ee.Filter.eq(year, 2012)).first(); // 生成降水量的直方图 var histogram ui.Chart.image.histogram({ image: image2012, region: roi, scale: 5500, minBucketWidth: 10, // 每个柱子的宽度毫米 maxRaw: 2000, // 忽略超过2000mm的极端值如果有 maxPixels: 1e7 // 用于生成直方图的采样像素数 }).setOptions({ title: 2012年研究区降水量频率分布直方图, hAxis: {title: 降水量 (mm)}, vAxis: {title: 像素数量}, colors: [#1d91c0] }); print(降水量空间分布直方图:, histogram);直方图可以帮助你理解降水数据的统计特征比如分布是否偏态、是否存在多峰等这对于设定干旱/湿润阈值非常有帮助。将这些图表与之前生成的空间分布地图结合起来你就能从空间格局、时间变化和统计分布多个角度对你的研究区降水情况形成一个立体、全面的认识。这些图表都可以通过点击图表右上角的菜单导出为PNG或CSV格式直接插入到你的报告或论文中。

相关新闻