1. 为什么要在GEE里折腾逐日温度数据做气候分析、农业估产、城市热岛研究或者疾病传播建模的人几乎都绕不开一个基础需求拿到某块区域从2000年至今、每一天的最高温和最低温。听起来简单但真动手就知道坑不少。本地气象站数据稀疏、空间插值误差大、跨年份数据格式不统一一套流程跑下来数据还没开始分析人已经先累趴了。GEEGoogle Earth Engine在这件事上的价值就很直接了它把多源遥感产品统一挂在云端你写几行代码就能按时间、按区域批量拉取不用自己下载几十GB的原始影像再慢慢裁剪。这篇内容就是围绕“下载2000年到2023年逐日最高温、最低温”这个具体任务把产品选型、时间范围处理、区域裁剪、批量导出、常见报错这一整条链路拆开讲清楚。适合谁来参考如果你已经能打开GEE代码编辑器、跑过最简单的ImageCollection筛选那这篇基本可以照着抄如果你是完全的新手也没关系我会把每个参数为什么这么设、不这么设会出什么问题都讲明白。核心关键词就三个逐日温度、最高温最低温、GEE批量导出后面所有内容都围绕它们展开。先说结论性的选型逐日最高温和最低温业界最常用的产品是Daymet V4北美区域1km逐日和ERA5-Land Daily Aggregated全球约9km逐日聚合。前者精度高但只覆盖北美后者全球覆盖、时间从1950年至今是绝大多数全球尺度研究的默认选择。标题里没限定区域所以我按全球通用的ERA5-Land Daily来主讲同时在选型章节把Daymet作为对照讲清楚方便你按自己的研究区切换。2. 数据产品选型与核心思路拆解2.1 逐日温度产品的三个候选与取舍逻辑在GEE里搜“daily temperature”能跳出来一堆产品但真正适合“2000到2023年逐日最高最低温”这个需求的主要就三个方向。我把它们的核心参数摆在一起对比你一眼就能看出差别。产品空间分辨率时间覆盖最高/最低温字段覆盖范围适用场景ERA5-Land Daily Aggregated约9km0.1°1950至今temperature_2m_max / temperature_2m_min全球全球尺度、长时序、气候趋势Daymet V41km1980至今tmax / tmin北美北美区域、高精度、生态农业MODIS MOD11A11km2000至今LST_Day_1km / LST_Night_1km全球地表温度非气温城市热岛这里有个特别容易踩的坑MODIS的LST是地表温度Land Surface Temperature不是气象意义上的气温2米高度空气温度。很多新手看它分辨率高、时间从2000年开始就直接拿来当“最高温最低温”用结果做出来的结果和气象站对不上被审稿人一顿质疑。地表温度和气温在裸土、城市下垫面差异能到十几度这个必须分清。所以选型逻辑很清晰要全球、要长时序、要气象口径的气温选ERA5-Land Daily研究区在北美、追求1km精细度选Daymet只有明确要做地表温度分析才碰MODIS LST。标题没限定区域我主讲ERA5-Land Daily这是最稳妥的通用解。2.2 为什么是“Daily Aggregated”而不是原始ERA5-LandERA5-Land本身是逐小时的GEE里对应的是ECMWF/ERA5_LAND/HOURLY。你完全可以自己从逐小时数据里算每日最大值和最小值但没必要。官方已经提供了聚合好的日产品ECMWF/ERA5_LAND/DAILY_AGGR它内部已经帮你做了“当日24小时取最大/最小”的运算字段直接就是temperature_2m_max和temperature_2m_min。自己聚合的问题在于逐小时数据量是逐日的24倍导出时请求的像素和时间片数量暴涨很容易触发GEE的“计算超时”或“用户内存超限”。而且自己写reducer聚合还得处理时区、缺测值这些细节纯属给自己找麻烦。用官方聚合产品既省算力又少出错这是我在多个项目里验证过的选择。注意ERA5-Land Daily的时间标签是UTC日如果你研究的是本地时区的“日最高温”严格来说存在几个小时的偏移。对大多数气候趋势分析影响可忽略但做极端天气事件逐日对齐时要留意这个细节。2.3 整体流程的骨架设计把任务拆开其实是四步定义研究区 → 筛选时间与产品 → 映射单位与裁剪 → 批量导出。每一步都有它的设计考量。定义研究区用ee.Geometry或者导入的矢量边界这一步决定了后面导出的空间范围范围越大导出越慢、文件越大。筛选时间用filterDate(2000-01-01, 2024-01-01)注意结束日期是开区间写2024-01-01才能把2023-12-31包含进来这是很多人少下一年数据的元凶。映射单位是因为ERA5-Land的温度单位是开尔文K要减273.15转摄氏度。批量导出则是把每年甚至每天拆成一个导出任务避免单个任务过大。这个骨架的好处是模块化换区域只改第一步换产品只改第二步换单位只改映射函数互不干扰。下面几章就按这个骨架逐层展开。3. 核心细节解析与实操要点3.1 研究区定义的三种写法与适用场景研究区定义看着简单但写法不同后面导出的效率和结果差别很大。我常用三种写法。第一种是矩形范围用ee.Geometry.Rectangle([xmin, ymin, xmax, ymax])坐标顺序是经度、纬度、经度、纬度。这种写法最快适合大区域气候分析比如整个华北平原或者某个流域的粗略范围。缺点是边界是矩形会包含研究区外的像素后续统计时得再裁一次。第二种是导入矢量边界在GEE的Assets里上传Shapefile或GeoJSON然后ee.FeatureCollection(users/xxx/boundary).geometry()。这种最精确适合行政区、流域这种不规则边界。缺点是矢量复杂时比如几万个顶点的省界clip操作会变慢导出时可能超时。第三种是用GEE自带的公共矢量比如FAO/GAUL/2015/level1这种国界数据集按名称筛选。适合快速试验不用自己准备数据。实操心得如果研究区是复杂矢量我一般先用geometry().bounds()取外接矩形去下载下载完在本地用矢量再裁一次。这样GEE端的计算量小很多导出成功率高本地裁剪也就几秒钟的事。这个“先粗后精”的策略帮我省过无数次超时重试。3.2 时间筛选的边界陷阱与年份循环filterDate的结束日期是不包含的这个规则必须刻在脑子里。想拿2000-01-01到2023-12-31的全部数据正确写法是var tempCol ee.ImageCollection(ECMWF/ERA5_LAND/DAILY_AGGR) .filterDate(2000-01-01, 2024-01-01) .select([temperature_2m_max, temperature_2m_min]);如果你写成2023-12-31那2023年最后一天的数据就丢了而且这种错误特别隐蔽不逐日核对根本发现不了。我见过有人的论文数据少了一整年就是因为这个。另一个细节是一次性导出23年逐日数据任务量是8000多张影像。虽然GEE能处理但单个导出任务扛不住这么大的量。我的做法是按年循环每年生成一个导出任务23个任务排队跑。这样单个任务失败只影响一年重跑成本低。循环的写法var startYear 2000; var endYear 2023; for (var y startYear; y endYear; y) { var start ee.Date.fromYMD(y, 1, 1); var end ee.Date.fromYMD(y 1, 1, 1); var yearly tempCol.filterDate(start, end); // 后续导出逻辑 }用ee.Date.fromYMD而不是字符串拼接能避免月份补零、闰年这些手工处理的麻烦代码也更干净。3.3 单位换算与缺测值处理ERA5-Land的温度字段单位是开尔文。转摄氏度就是减273.15用subtract(273.15)即可。但这里有个容易忽略的点缺测值。ERA5-Land在水体、冰川边缘可能有无效像素转完摄氏度后这些位置会变成异常大的负数比如-273左右。如果不处理统计最小值时会被这些假值污染。稳妥的做法是在映射时加一个有效性掩膜或者导出后在本地用合理范围比如-60到60摄氏度过滤。GEE端可以这样写var toCelsius function(image) { var tmax image.select(temperature_2m_max).subtract(273.15); var tmin image.select(temperature_2m_min).subtract(273.15); return tmax.addBands(tmin) .rename([tmax_c, tmin_c]) .copyProperties(image, [system:time_start]); };copyProperties这步很关键它把时间戳带过去否则导出后的影像丢失时间信息后续按日分析就抓瞎了。这是我在早期项目里踩过的坑导出的tif全是无名氏根本对不上日期。3.4 分辨率与导出的参数权衡ERA5-Land原生分辨率是0.1度约9km。导出时scale参数设多少设成原生10000米左右最合理强行重采样到1km只会让文件变大、精度并不会真的提升因为源数据就那个精度。我一般设scale: 10000既尊重原始分辨率又控制文件体积。crs参数默认是EPSG:4326WGS84经纬度做全球或大区域分析够用。如果你的下游分析要用投影坐标系比如UTM可以在导出时指定但要注意跨多个UTM带时会有变形全球数据还是老老实实用4326。maxPixels是个高频报错点。默认值约1e8大区域导出经常不够报“Image too large”。设成1e13基本能覆盖大多数区域但别无脑设太大太大反而可能因为请求超限被拒。我的经验值是区域小于一个省用默认大于一个省设1e11到1e13。4. 完整实操流程与批量导出实现4.1 从零到导出任务的完整代码把前面几章的碎片拼起来给一份可以直接跑的完整脚本。这段代码按年循环每年导出一个多波段tmax、tmin的GeoTIFF到云端硬盘。// 1. 定义研究区这里用矩形示例替换成你的边界 var roi ee.Geometry.Rectangle([100, 20, 120, 40]); // 2. 加载产品并筛选时间、波段 var tempCol ee.ImageCollection(ECMWF/ERA5_LAND/DAILY_AGGR) .filterDate(2000-01-01, 2024-01-01) .select([temperature_2m_max, temperature_2m_min]); // 3. 单位换算函数 var toCelsius function(image) { var tmax image.select(temperature_2m_max).subtract(273.15); var tmin image.select(temperature_2m_min).subtract(273.15); return tmax.addBands(tmin) .rename([tmax_c, tmin_c]) .copyProperties(image, [system:time_start]); }; // 4. 按年循环导出 for (var y 2000; y 2023; y) { var start ee.Date.fromYMD(y, 1, 1); var end ee.Date.fromYMD(y 1, 1, 1); var yearly tempCol.filterDate(start, end).map(toCelsius); // 转成多波段单影像每个波段对应一天 var stacked yearly.toBands(); Export.image.toDrive({ image: stacked.clip(roi), description: temp_daily_ y, folder: GEE_Temp, fileNamePrefix: temp_daily_ y, region: roi, scale: 10000, crs: EPSG:4326, maxPixels: 1e13 }); }跑完之后在GEE的Tasks面板里会看到23个待运行任务逐个点Run或者用批量运行的方式排队。每个任务导出的是一张多波段影像波段名形如tmax_c_0、tmin_c_0数字对应集合里的序号需要结合时间戳还原日期。4.2 多波段堆叠的日期还原技巧toBands()把影像集合压成一张多波段影像但波段名里的序号和日期的对应关系导出后容易乱。我的做法是额外导出一个CSV记录每个序号对应的日期或者更直接——按天单独导出文件名里带日期。按天导出的代码量稍大但日期信息最可靠var dailyList yearly.toList(yearly.size()); var n yearly.size().getInfo(); for (var i 0; i n; i) { var img ee.Image(dailyList.get(i)); var date img.date().format(YYYYMMdd).getInfo(); Export.image.toDrive({ image: img.clip(roi), description: temp_ date, folder: GEE_Temp_Daily, fileNamePrefix: temp_ date, region: roi, scale: 10000, maxPixels: 1e13 }); }注意getInfo()是同步调用循环8000多次会很慢甚至卡死浏览器。所以按天导出适合小区域、短时段长时序还是按年堆叠 单独导日期表更实际。这个取舍要根据你的数据量来定没有一刀切的方案。4.3 导出后的本地校验数据下下来别急着分析先做三件事校验。第一用QGIS或Python打开一张看数值范围是否在合理区间比如-50到50摄氏度如果出现-273这种值说明缺测没处理干净。第二抽查几个像素和公开气象站数据对比偏差在2到3度以内算正常再分析数据本身有偏差。第三检查时间序列完整性2000到2023年应该有8766天含闰年少几天要查是不是filterDate边界写错了。我用Python做批量校验时习惯用rasterio读一张算统计再用xarray把整年拼成时间序列看有没有断档。这一步花十分钟能避免后面分析全盘重来。5. 常见问题与排查技巧实录5.1 导出报错速查表实际操作里报错五花八门我把高频的整理成表方便你对号入座。报错信息可能原因解决办法Image too largemaxPixels不够或区域太大提高maxPixels或分块导出Computation timed out单任务影像太多按年/按季度拆分任务User memory limit exceeded集合太大或toBands波段过多减少单次处理量分年循环No bands foundselect字段名拼错核对产品文档的波段名Export failed: region too large矢量边界太复杂用bounds()外接矩形先导“Computation timed out”是我遇到最多的。一开始我图省事想一次性导出23年结果任务跑了半小时直接超时。后来改成按年每个任务几分钟就完成成功率接近100%。这个教训就是GEE的算力是共享的别指望一个任务干完所有活拆小是王道。5.2 数值异常与时间错位的排查有一类问题不报错但结果不对更隐蔽。比如导出的tmax比tmin还小这通常是波段顺序搞反了addBands的顺序和rename的顺序没对齐。再比如某一年数据全是同一个值可能是filterDate的年份写错筛到了空集合toBands出来就是空或常量。排查思路是先看集合大小yearly.size().getInfo()正常一年应该有365或366。如果是0时间筛选有问题如果远大于366说明产品本身有重复影像某些产品一天多景需要去重。去重用yearly.distinct(system:time_start)或者按日期取first。时间错位还有一种情况导出的文件名日期和影像内容对不上。这多半是copyProperties没做或者toBands后波段序号和日期映射错了。我的经验是只要涉及批量导出就一定单独维护一份“序号-日期”对照表别指望从波段名反推。5.3 几个能省大量时间的实操心得第一个心得先用小区域、短时段跑通全流程再放大。我习惯先用一个月的矩形小区域测试确认单位、波段、日期都对再改成23年全区域。这样调试成本极低不会因为一个参数错等半小时才发现。第二个心得导出任务用文件夹分类。GEE的Drive导出默认堆在根目录23个文件混在一起很难找。folder参数设成按项目命名fileNamePrefix带年份下载后一目了然。第三个心得保留中间脚本版本。GEE的脚本是云端保存的改错了不好回退。我习惯每个关键版本在本地存一份或者用注释标记“v1按年导出”“v2按天导出”出问题能快速对比。第四个心得注意配额。GEE的Drive导出有每日配额限制23个大文件一天可能导不完分批跑别熬夜等。配额用完了第二天再跑任务不会丢。6. 换区域与换产品的适配思路前面全按ERA5-Land全球产品讲但你的研究区要是在北美Daymet V4的1km精度明显更香。切换的逻辑其实很简单换产品ID、换波段名、换单位。Daymet的温度单位直接是摄氏度不用减273.15字段是tmax和tmin产品ID是NASA/ORNL/DAYMET_V4。其余的时间筛选、按年循环、导出参数完全复用。var daymet ee.ImageCollection(NASA/ORNL/DAYMET_V4) .filterDate(2000-01-01, 2024-01-01) .select([tmax, tmin]);唯一要注意的是Daymet只覆盖北美研究区超出范围会得到空集合导出前先用size()确认一下。另外Daymet的空间分辨率1km导出时scale设1000文件会比ERA5-Land大不少按年导出时单年文件可能上百MB磁盘空间要留够。这个“换产品只换三处”的思路是我做多源数据对比时的标准操作。把产品相关的参数抽成变量放在脚本开头切换时只改那几行主体逻辑不动既不容易出错也方便做A/B对比。7. 数据拿到之后能做什么逐日最高最低温拿到手能延伸的分析方向很多。最直接的是算月均、年均、生长季均温做长期趋势分析再进一步算日较差tmax减tmin研究昼夜温差变化还可以基于阈值做极端高温日数统计、无霜期计算、积温累积。这些分析都建立在“逐日数据完整、单位正确、日期对齐”这三个前提上所以前面那些校验步骤一个都不能省。我自己在做农业相关分析时最常用的是积温GDD计算公式是max((tmaxtmin)/2 - 基准温度, 0)逐日累加。这个计算对逐日数据的完整性极其敏感少几天数据积温就偏低所以每次拿到数据第一件事就是查天数对不对。这个习惯帮我避免过好几次因为数据缺失导致的结论偏差。如果后续要做空间可视化把导出的GeoTIFF拖进QGIS用时间管理器做逐日动画或者用Python的xarraymatplotlib画时间序列热力图都很直观。数据格式统一了下游怎么玩都顺手。
阅读完成 · 觉得有帮助?