使用 Geemap 从 GEE 下载全球栅格数据
技术教程
GEE
本技术文档提供了使用 geemap Python 库从谷歌地球引擎(GEE)下载大规模栅格数据的完整工作流程,包括分段下载、断点续传和图像镶嵌等关键技术。
1 使用 Geemap 从 GEE 下载全球栅格数据
1.1 📋 概述
本技术文档提供了使用 geemap
Python 库从谷歌地球引擎(GEE)下载大规模(如全球)栅格数据的完整工作流程。它解决了 GEE 直接下载像素数量限制和下载中断等常见挑战,并包括下载后的图像镶嵌以生成完整数据集。
该工作流程适用于大多数 GEE 栅格数据集(如 MODIS、Landsat),可通过调整数据 ID、分辨率和时间范围等参数进行适配。
1.2 🛠️ 前置要求
1.2.1 2.1 软件和库
确保安装以下工具和库:
工具/库 | 用途 | 安装命令 |
---|---|---|
Python | 核心编程环境 | 从 python.org 下载 |
geemap |
GEE 可视化和数据下载 | pip install geemap |
earthengine-api (ee) |
GEE Python API | pip install earthengine-api |
geopandas |
读取矢量网格数据(用于分段) | pip install geopandas |
rasterio |
图像镶嵌和栅格处理 | pip install rasterio |
GDAL | 栅格操作后端(多线程) | 随 rasterio 包含 |
注意: 安装
earthengine-api
后,需要通过earthengine authenticate
进行身份验证。
1.2.2 2.2 数据准备
- GEE 栅格数据集 ID: 在 GEE 中识别目标数据集 ID
- 示例:NPP 数据
MODIS/061/MOD17A3HGF
- 示例:Landsat 8 数据
LANDSAT/LC08/C02/T1_L2
- 示例:NPP 数据
- 全球矢量网格: 准备预分割的矢量网格(GeoPackage
.gpkg
格式)- 将全球研究区域分割为较小的、可下载的块
- 示例:
WorldFishnet.gpkg
(可根据数据分辨率自定义网格大小)
1.3 🚀 步骤 1:初始化环境和预览数据
1.3.1 3.1 加载库
import ee
import geemap
import os
import geopandas as gpd
1.3.2 3.2 身份验证和初始化 GEE
# 身份验证 GEE(运行一次;如果已经身份验证则跳过)
# ee.Authenticate()
# 初始化 GEE API
ee.Initialize()
1.3.3 3.3 加载和预处理 GEE 数据
1.3.3.1 3.3.1 定义核心参数
# 为您的目标数据集自定义这些参数
= "MODIS/061/MOD17A3HGF" # 示例:MODIS NPP 数据集
GEE_DATA_ID = "Npp" # 目标波段名称
BAND_NAME = ["2001-01-01", "2025-01-01"] # 时间范围(开始,结束)
TIME_RANGE = 0.0001 # 缩放因子,将 GEE 数据转换为实际值
SCALING_FACTOR
# 可视化参数
= {
VIS_PARAMS "min": 0,
"max": 3,
"palette": [
"ffffff", "ce7e45", "df923d", "f1b555", "fcd163",
"99b718", "74a901", "66a000", "529400", "3e8601",
"207401", "056201", "004c00", "023b01", "012e01",
"011d01", "011301"
# 根据数据集范围调整
] }
1.3.3.2 3.3.2 加载和预处理数据
# 加载 GEE 图像集合
= ee.ImageCollection(GEE_DATA_ID)\
image_collection 0], TIME_RANGE[1])\
.filterDate(TIME_RANGE[
.select(BAND_NAME)
# 定义应用缩放因子的函数
def apply_scaling(image):
return image.multiply(SCALING_FACTOR)
# 将缩放应用到集合
= image_collection.map(apply_scaling) scaled_collection
1.3.4 3.4 在 Geemap 中预览数据
# 初始化 geemap 界面
= geemap.Map(center=(40, 100), zoom=4) # 以欧亚大陆为中心
Map
# 将集合镶嵌为单个图像进行预览(对时间序列使用中位数/平均值)
= scaled_collection.median()
preview_image
# 将图像添加到地图
f"{GEE_DATA_ID.split('/')[-1]}_Preview")
Map.addLayer(preview_image, VIS_PARAMS,
# 添加颜色条图例
Map.add_colorbar_branca(=VIS_PARAMS["palette"],
colors=VIS_PARAMS["min"],
vmin=VIS_PARAMS["max"],
vmax=BAND_NAME,
label="bottomright"
position
)
# 显示地图(在 Jupyter Notebook/Lab 中运行以获得交互式视图)
Map
1.4 📥 步骤 2:支持断点续传的分段下载
直接下载全球高分辨率数据(如 500 米)会超过 GEE 的像素限制。使用矢量网格将数据分割为可下载的块,并具有跳过现有文件的断点续传功能。
1.4.1 4.1 定义下载参数
# 文件路径(自定义这些)
= "I:/geemap/roi/WorldFishnet.gpkg" # 矢量网格路径
GRID_PATH = "WorldFish30" # GeoPackage 中的图层名称
GRID_LAYER = "I:/geemap/GEE_Global_Data/" # 保存分段文件的目录
OUTPUT_DIR = 500 # 数据集分辨率(米)
RESOLUTION = 5 # 每批处理的网格块数量
BATCH_SIZE = 2001 # 从此年开始恢复(如果下载被中断)
START_YEAR = 0 # 从此网格索引恢复(如果下载被中断)
START_GRID_INDEX = "EPSG:4326" # 目标坐标系(WGS84) CRS
1.4.2 4.2 加载矢量网格和创建输出目录
# 使用 geopandas 加载矢量网格
= gpd.read_file(GRID_PATH, layer=GRID_LAYER)
grid_gdf
# 将 geopandas GeoDataFrame 转换为 GEE FeatureCollection
= geemap.gdf_to_ee(grid_gdf)
grid_ee
# 创建输出目录(如果不存在)
=True) os.makedirs(OUTPUT_DIR, exist_ok
1.4.3 4.3 支持断点续传的批处理下载
# 从时间范围获取年份列表
= range(int(TIME_RANGE[0].split("-")[0]), int(TIME_RANGE[1].split("-")[0]))
years
# 遍历每一年
for year in years:
# 跳过恢复开始年份之前的年份
if year < START_YEAR:
continue
print(f"🔄 正在处理 {year} 年的数据...")
# 提取年度数据
= scaled_collection\
annual_image filter(ee.Filter.calendarRange(year, year, "year"))\
.
.median()
# 获取网格块总数
= grid_ee.size().getInfo()
total_grids
# 计算批次数
= (total_grids - START_GRID_INDEX + BATCH_SIZE - 1) // BATCH_SIZE
num_batches
# 处理每批
for batch in range(num_batches):
= START_GRID_INDEX + batch * BATCH_SIZE
batch_start = min(batch_start + BATCH_SIZE, total_grids)
batch_end = grid_ee.toList(BATCH_SIZE, batch_start)
current_batch
# 处理批次中的每个网格块
for i in range(batch_end - batch_start):
# 获取当前网格要素和几何形状
= ee.Feature(current_batch.get(i))
grid_feature = grid_feature.geometry()
grid_geom
# 生成唯一文件名
= batch_start + i + 1
grid_index = f"{BAND_NAME}_{year}_grid_{grid_index:04d}.tif"
filename = os.path.join(OUTPUT_DIR, filename)
file_path
# 如果文件已存在则跳过(断点续传功能)
if os.path.exists(file_path):
print(f"⏭️ 跳过现有文件: {filename}")
continue
# 下载裁剪后的图像
try:
# 将年度图像裁剪到当前网格
= annual_image.clip(grid_geom)
clipped_image
# 使用 geemap 下载
geemap.download_ee_image(=clipped_image,
image=file_path,
filename=RESOLUTION,
scale=grid_geom,
region=CRS,
crs=1024 # 根据内存调整
max_tile_size
)print(f"✅ 成功下载: {filename}")
except Exception as e:
print(f"❌ 下载 {filename} 失败: {str(e)}")
continue
# 为下一年重置网格索引
= 0
START_GRID_INDEX
print("🎉 所有下载任务完成!")
1.5 🔗 步骤 3:镶嵌分段文件
使用 rasterio
和 GDAL 多线程将分段文件镶嵌为每年的单个完整栅格。
1.5.1 5.1 定义镶嵌参数
# 文件路径(自定义这些)
= OUTPUT_DIR # 与下载输出目录相同
INPUT_DIR = "I:/geemap/GEE_Global_Data/Mosaicked/" # 镶嵌文件的目录
MOSAIC_OUTPUT_DIR = "LZW" # 压缩方法(减少文件大小) COMPRESSION
1.5.2 5.2 启用 GDAL 多线程
# 启用 GDAL 多线程以加速镶嵌
"GDAL_NUM_THREADS"] = "ALL_CPUS" # 使用所有可用 CPU os.environ[
1.5.3 5.3 镶嵌年度文件
import re
from rasterio.merge import merge
from rasterio.enums import Resampling
# 为镶嵌文件创建输出目录
=True)
os.makedirs(MOSAIC_OUTPUT_DIR, exist_ok
# 按年份分组分段文件
= re.compile(rf"{BAND_NAME}_(\d{{4}})_grid_\d{{4}}\.tif$")
file_pattern = {}
yearly_files
for filename in os.listdir(INPUT_DIR):
= file_pattern.match(filename)
match if match:
= match.group(1)
year = os.path.join(INPUT_DIR, filename)
file_path
yearly_files.setdefault(year, []).append(file_path)
# 镶嵌每年的文件
for year, file_list in yearly_files.items():
print(f"🔗 正在镶嵌 {year} 年的 {len(file_list)} 个文件...")
= []
datasets
try:
# 打开所有分段文件
for file_path in file_list:
= rasterio.open(file_path)
src
datasets.append(src)
# 执行镶嵌
= merge(
mosaic_array, out_transform
datasets,=Resampling.nearest # 对离散数据使用最近邻重采样
resampling
)
# 更新镶嵌文件的元数据
= datasets[0].meta.copy()
out_meta
out_meta.update({"driver": "GTiff",
"height": mosaic_array.shape[1],
"width": mosaic_array.shape[2],
"transform": out_transform,
"compress": COMPRESSION,
"bigtiff": "YES" # 对大文件启用(>4GB)
})
# 保存镶嵌文件
= os.path.join(MOSAIC_OUTPUT_DIR, f"{BAND_NAME}_{year}_Global.tif")
output_path with rasterio.open(output_path, "w", **out_meta) as dest:
dest.write(mosaic_array)
print(f"✅ 成功镶嵌: {output_path}")
except Exception as e:
print(f"❌ 镶嵌 {year} 年数据失败: {str(e)}")
continue
finally:
# 关闭所有打开的栅格数据集以释放内存
for ds in datasets:
ds.close()
print("🎉 所有镶嵌任务完成!")
1.6 ⚠️ 注意事项和故障排除
1.6.1 6.1 常见问题
问题 | 解决方案 |
---|---|
GEE 身份验证错误 | 重新运行 ee.Authenticate() 并按照浏览器提示重新进行身份验证 |
下载中断 | 工作流程会跳过现有文件,因此只需重新运行下载脚本即可恢复 |
内存过载 | 减少 BATCH_SIZE (如从 5 到 3)或 max_tile_size (如从 1024 到 512) |
镶嵌失败 | 确保所有分段文件具有相同的 CRS 和分辨率 |
1.6.2 6.2 自定义提示
非年度数据: 对于月度/季节性数据,修改时间过滤器
# 示例:夏季数据 "year")\ ee.Filter.calendarRange(year, year, filter(ee.Filter.calendarRange(6, 8, "month")) .
不同数据集: 更新
GEE_DATA_ID
、BAND_NAME
、SCALING_FACTOR
和VIS_PARAMS
网格调整: 根据数据分辨率使用更粗/更细的网格(如 10°x10° 或 5°x5°)
1.7 📚 参考资料
geemap
文档: geemap.org- GEE Python API 文档: developers.google.com/earth-engine
rasterio
镶嵌指南: rasterio.readthedocs.io- MODIS MOD17A3HGF 数据集: GEE MOD17A3HGF 文档
💡 提示:本工作流程适用于大多数 GEE 栅格数据集,可根据具体需求进行调整和优化。