Binchen’s Tech Blog
  • Home
  • Posts
  • About

On this page

  • 1 使用 Geemap 从 GEE 下载全球栅格数据
    • 1.1 📋 概述
    • 1.2 🛠️ 前置要求
      • 1.2.1 2.1 软件和库
      • 1.2.2 2.2 数据准备
    • 1.3 🚀 步骤 1:初始化环境和预览数据
      • 1.3.1 3.1 加载库
      • 1.3.2 3.2 身份验证和初始化 GEE
      • 1.3.3 3.3 加载和预处理 GEE 数据
      • 1.3.4 3.4 在 Geemap 中预览数据
    • 1.4 📥 步骤 2:支持断点续传的分段下载
      • 1.4.1 4.1 定义下载参数
      • 1.4.2 4.2 加载矢量网格和创建输出目录
      • 1.4.3 4.3 支持断点续传的批处理下载
    • 1.5 🔗 步骤 3:镶嵌分段文件
      • 1.5.1 5.1 定义镶嵌参数
      • 1.5.2 5.2 启用 GDAL 多线程
      • 1.5.3 5.3 镶嵌年度文件
    • 1.6 ⚠️ 注意事项和故障排除
      • 1.6.1 6.1 常见问题
      • 1.6.2 6.2 自定义提示
    • 1.7 📚 参考资料

使用 Geemap 从 GEE 下载全球栅格数据

技术教程
GEE
本技术文档提供了使用 geemap Python 库从谷歌地球引擎(GEE)下载大规模栅格数据的完整工作流程,包括分段下载、断点续传和图像镶嵌等关键技术。
Published

September 1, 2025

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
  • 全球矢量网格: 准备预分割的矢量网格(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 定义核心参数

# 为您的目标数据集自定义这些参数
GEE_DATA_ID = "MODIS/061/MOD17A3HGF"  # 示例:MODIS NPP 数据集
BAND_NAME = "Npp"                       # 目标波段名称
TIME_RANGE = ["2001-01-01", "2025-01-01"]  # 时间范围(开始,结束)
SCALING_FACTOR = 0.0001                 # 缩放因子,将 GEE 数据转换为实际值

# 可视化参数
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 图像集合
image_collection = ee.ImageCollection(GEE_DATA_ID)\
    .filterDate(TIME_RANGE[0], TIME_RANGE[1])\
    .select(BAND_NAME)

# 定义应用缩放因子的函数
def apply_scaling(image):
    return image.multiply(SCALING_FACTOR)

# 将缩放应用到集合
scaled_collection = image_collection.map(apply_scaling)

1.3.4 3.4 在 Geemap 中预览数据

# 初始化 geemap 界面
Map = geemap.Map(center=(40, 100), zoom=4)  # 以欧亚大陆为中心

# 将集合镶嵌为单个图像进行预览(对时间序列使用中位数/平均值)
preview_image = scaled_collection.median()

# 将图像添加到地图
Map.addLayer(preview_image, VIS_PARAMS, f"{GEE_DATA_ID.split('/')[-1]}_Preview")

# 添加颜色条图例
Map.add_colorbar_branca(
    colors=VIS_PARAMS["palette"],
    vmin=VIS_PARAMS["min"],
    vmax=VIS_PARAMS["max"],
    label=BAND_NAME,
    position="bottomright"
)

# 显示地图(在 Jupyter Notebook/Lab 中运行以获得交互式视图)
Map

1.4 📥 步骤 2:支持断点续传的分段下载

直接下载全球高分辨率数据(如 500 米)会超过 GEE 的像素限制。使用矢量网格将数据分割为可下载的块,并具有跳过现有文件的断点续传功能。

1.4.1 4.1 定义下载参数

# 文件路径(自定义这些)
GRID_PATH = "I:/geemap/roi/WorldFishnet.gpkg"  # 矢量网格路径
GRID_LAYER = "WorldFish30"                       # GeoPackage 中的图层名称
OUTPUT_DIR = "I:/geemap/GEE_Global_Data/"        # 保存分段文件的目录
RESOLUTION = 500                                  # 数据集分辨率(米)
BATCH_SIZE = 5                                    # 每批处理的网格块数量
START_YEAR = 2001                                 # 从此年开始恢复(如果下载被中断)
START_GRID_INDEX = 0                             # 从此网格索引恢复(如果下载被中断)
CRS = "EPSG:4326"                                # 目标坐标系(WGS84)

1.4.2 4.2 加载矢量网格和创建输出目录

# 使用 geopandas 加载矢量网格
grid_gdf = gpd.read_file(GRID_PATH, layer=GRID_LAYER)

# 将 geopandas GeoDataFrame 转换为 GEE FeatureCollection
grid_ee = geemap.gdf_to_ee(grid_gdf)

# 创建输出目录(如果不存在)
os.makedirs(OUTPUT_DIR, exist_ok=True)

1.4.3 4.3 支持断点续传的批处理下载

# 从时间范围获取年份列表
years = range(int(TIME_RANGE[0].split("-")[0]), int(TIME_RANGE[1].split("-")[0]))

# 遍历每一年
for year in years:
    # 跳过恢复开始年份之前的年份
    if year < START_YEAR:
        continue
    print(f"🔄 正在处理 {year} 年的数据...")

    # 提取年度数据
    annual_image = scaled_collection\
        .filter(ee.Filter.calendarRange(year, year, "year"))\
        .median()

    # 获取网格块总数
    total_grids = grid_ee.size().getInfo()

    # 计算批次数
    num_batches = (total_grids - START_GRID_INDEX + BATCH_SIZE - 1) // BATCH_SIZE

    # 处理每批
    for batch in range(num_batches):
        batch_start = START_GRID_INDEX + batch * BATCH_SIZE
        batch_end = min(batch_start + BATCH_SIZE, total_grids)
        current_batch = grid_ee.toList(BATCH_SIZE, batch_start)

        # 处理批次中的每个网格块
        for i in range(batch_end - batch_start):
            # 获取当前网格要素和几何形状
            grid_feature = ee.Feature(current_batch.get(i))
            grid_geom = grid_feature.geometry()

            # 生成唯一文件名
            grid_index = batch_start + i + 1
            filename = f"{BAND_NAME}_{year}_grid_{grid_index:04d}.tif"
            file_path = os.path.join(OUTPUT_DIR, filename)

            # 如果文件已存在则跳过(断点续传功能)
            if os.path.exists(file_path):
                print(f"⏭️  跳过现有文件: {filename}")
                continue

            # 下载裁剪后的图像
            try:
                # 将年度图像裁剪到当前网格
                clipped_image = annual_image.clip(grid_geom)

                # 使用 geemap 下载
                geemap.download_ee_image(
                    image=clipped_image,
                    filename=file_path,
                    scale=RESOLUTION,
                    region=grid_geom,
                    crs=CRS,
                    max_tile_size=1024  # 根据内存调整
                )
                print(f"✅ 成功下载: {filename}")

            except Exception as e:
                print(f"❌ 下载 {filename} 失败: {str(e)}")
                continue

    # 为下一年重置网格索引
    START_GRID_INDEX = 0

print("🎉 所有下载任务完成!")

1.5 🔗 步骤 3:镶嵌分段文件

使用 rasterio 和 GDAL 多线程将分段文件镶嵌为每年的单个完整栅格。

1.5.1 5.1 定义镶嵌参数

# 文件路径(自定义这些)
INPUT_DIR = OUTPUT_DIR                    # 与下载输出目录相同
MOSAIC_OUTPUT_DIR = "I:/geemap/GEE_Global_Data/Mosaicked/"  # 镶嵌文件的目录
COMPRESSION = "LZW"                       # 压缩方法(减少文件大小)

1.5.2 5.2 启用 GDAL 多线程

# 启用 GDAL 多线程以加速镶嵌
os.environ["GDAL_NUM_THREADS"] = "ALL_CPUS"  # 使用所有可用 CPU

1.5.3 5.3 镶嵌年度文件

import re
from rasterio.merge import merge
from rasterio.enums import Resampling

# 为镶嵌文件创建输出目录
os.makedirs(MOSAIC_OUTPUT_DIR, exist_ok=True)

# 按年份分组分段文件
file_pattern = re.compile(rf"{BAND_NAME}_(\d{{4}})_grid_\d{{4}}\.tif$")
yearly_files = {}

for filename in os.listdir(INPUT_DIR):
    match = file_pattern.match(filename)
    if match:
        year = match.group(1)
        file_path = os.path.join(INPUT_DIR, filename)
        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:
            src = rasterio.open(file_path)
            datasets.append(src)

        # 执行镶嵌
        mosaic_array, out_transform = merge(
            datasets,
            resampling=Resampling.nearest  # 对离散数据使用最近邻重采样
        )

        # 更新镶嵌文件的元数据
        out_meta = datasets[0].meta.copy()
        out_meta.update({
            "driver": "GTiff",
            "height": mosaic_array.shape[1],
            "width": mosaic_array.shape[2],
            "transform": out_transform,
            "compress": COMPRESSION,
            "bigtiff": "YES"  # 对大文件启用(>4GB)
        })

        # 保存镶嵌文件
        output_path = os.path.join(MOSAIC_OUTPUT_DIR, f"{BAND_NAME}_{year}_Global.tif")
        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 自定义提示

  • 非年度数据: 对于月度/季节性数据,修改时间过滤器

    # 示例:夏季数据
    ee.Filter.calendarRange(year, 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 栅格数据集,可根据具体需求进行调整和优化。

© 2025 Binchen Chen

 

Built with Quarto