用python直接提取gee数据的value(无需下载)

用python直接提取gee数据的value(无需下载),适合小体量的数据,免去了下载、提取的繁琐步骤
用@dataclass定义配置,后面可以继续沿用

from typing import List, Dict, Tuple, Callable
import pandas as pd
import logging
from __future__ import annotations
import numpy as np
from dataclasses import dataclass
import math
for handler in logging.root.handlers[:]:
logging.root.removeHandler(handler)
logging.basicConfig(
level=logging.INFO, # 设置日志级别为 DEBUG,记录所有级别的日志
format='%(asctime)s %(levelname)s %(message)s', # 设置日志格式
datefmt='%Y-%m-%d %H:%M:%S', # 设置时间格式
handlers=[
logging.StreamHandler() # 输出到终端(控制台)
])
def get_ee_points_list(longitude, latitude, **kwargs) -> list:
"""
创建一个由 Earth Engine Feature 对象组成的列表。
参数:
longitude (list): 经度列表。
latitude (list): 纬度列表。
**kwargs: 其他属性的列表,键为属性名,值为对应属性值的列表(长度应与 longitude 和 latitude 一致)。
返回:
list: 包含 ee.Feature 对象的列表,每个对象表示一个点及其动态属性。
"""
# 提取其他属性的键和值
other_properties = {key: kwargs[key] for key in kwargs}
# 校验所有属性长度是否一致
list_lengths = [len(longitude), len(latitude)] + [len(v) for v in other_properties.values()]
if len(set(list_lengths)) > 1:
raise ValueError("所有输入列表的长度必须一致")
points = [
ee.Feature(
ee.Geometry.Point(lon, lat),
{
**{'longitude': lon, 'latitude': lat},
**{k: v[idx] for k, v in other_properties.items()}
}
)
for idx, (lon, lat) in enumerate(zip(longitude, latitude))
]
return points
@dataclass
class GeeConfig:
"""
配置类,用于存储与 Google Earth Engine 数据集相关的设置和参数。
属性:
DATASET_NAME (str): 数据集名称,例如 "NASA/FLDAS/NOAH01/C/GL/M/V001"。
VAR_LIST (List[str]): 要提取的变量名称列表。
VAR_NAME_MAP (Dict[str, str]): 变量名称映射,用于重命名下载后的变量。
METHOD (str): 统计方法,如 "mean", "median", "max", "min"。
SAVE_FOLDER (str): 数据保存的文件夹路径。
DATASET_PROCESS (function): 数据集处理方法,输入一个函数,接收 GeeConfig 和 int 输入,返回 ee.Image。
"""
DATASET_NAME: str
DATASET_PROCESS: Callable[[GeeConfig, int], ee.Image] # 注意:使用字符串 "GeeConfig" 避免循环引用
VAR_LIST: List[str]
VAR_NAME_MAP: Dict[str, str] = None
METHOD: str = None
SAVE_FOLDER: str = "./"
def get_raw_image(gcfg: GeeConfig, year: int) -> ee.Image:
"""
根据给定的年份和配置对象,整合多个变量的图像数据。
参数:
gcfg (GeeConfig): 配置对象,包含数据集名称、变量列表、变量名称映射等信息。
year (int): 需要获取数据的年份。
返回:
ee.Image: 整合后的变量图像数据。
"""
# 加载数据集并筛选时间范围
# africa_bbox = ee.Geometry.Rectangle([-17.5, -34.5, 51.5, 37.5])
collection = ee.ImageCollection(gcfg.DATASET_NAME).filterDate(f"{year-1}-01-01", f"{year}-12-31")
# 校验变量是否存在于数据集中
band_names = collection.first().bandNames().getInfo()
logging.debug("Band names: ",band_names)
for var in gcfg.VAR_LIST:
if var not in band_names:
raise ValueError(f"变量 {var} 不在数据集 {gcfg.DATASET_NAME} 中,支持的变量有: {band_names}")
# 根据 method 选择对应的计算方法
if gcfg.METHOD == "mean":
vars_image = collection.select(gcfg.VAR_LIST).mean()
elif gcfg.METHOD == "median":
vars_image = collection.select(gcfg.VAR_LIST).median()
elif gcfg.METHOD == "max":
vars_image = collection.select(gcfg.VAR_LIST).max()
elif gcfg.METHOD == "min":
vars_image = collection.select(gcfg.VAR_LIST).min()
else:
raise ValueError("method 参数不支持,请选择 mean, median, max, min")
return vars_image
def download_vars(year: int, table: pd.DataFrame, gcfg: GeeConfig) -> pd.DataFrame:
"""
从 Google Earth Engine 下载指定年份的多个变量数据,并对其进行处理和重命名。
参数:
year (int): 需要获取数据的年份。
table (pd.DataFrame): 包含经纬度和其他属性的数据框。
gcfg (GeeConfig): 配置对象,包含数据集名称、变量列表、变量名称映射等信息。
返回:
pd.DataFrame: 包含下载并重命名后的变量数据的 DataFrame。
"""
# 检查 DataFrame 是否包含 'longitude' 和 'latitude' 列
if 'longitude' not in table.columns or 'latitude' not in table.columns:
raise ValueError("DataFrame 必须包含 'longitude' 和 'latitude' 列")
# 提取经纬度和其他属性
lons = table.longitude.values
lats = table.latitude.values
other_property = table.drop(columns=['longitude', 'latitude']).to_dict(orient="list")
# 获取经纬度点列表 获取配置中的数据集、变量和时间范围
points = get_ee_points_list(lons, lats, **other_property)
vars_image = gcfg.DATASET_PROCESS(gcfg, year)
# 创建点集合
points_fc = ee.FeatureCollection(points)
# 从图像集合中提取数据到点
points_with_vars = vars_image.reduceRegions(
collection=points_fc,
reducer=ee.Reducer.mean(),
scale=2000
)
# 获取结果并转换为 DataFrame
results = points_with_vars.getInfo()['features']
data = []
for feature in results:
properties = feature['properties']
longitude, latitude = feature['geometry']['coordinates']
row = {
"longitude": longitude,
"latitude": latitude,
**properties,
}
if len(gcfg.VAR_LIST) == 1:
row[gcfg.VAR_LIST[0]] = properties.get("mean", None)
del row['mean']
else:
for var in gcfg.VAR_LIST:
row[var] = properties.get(var, None)
data.append(row)
# 使用 pandas 保存为 DataFrame 并重命名列
df = pd.DataFrame(data)
if gcfg.VAR_NAME_MAP is not None:
df = df.rename(columns=gcfg.VAR_NAME_MAP)
return df
def main_yearly(
gcfg: GeeConfig,
pop_file_path: str,
year_str:str="year"):
"""
按年份处理和下载数据集变量,并将其保存为CSV文件。
如果特定年份的数据包含超过5000行,那么它将被分割成更小的块。
结果将保存到指定的保存文件夹中,并在此过程中生成日志。
Args:
gcfg (GeeConfig): Configuration object containing settings like the save folder path and dataset name.
pop_file_path (str): Path to the CSV file containing household data with [year_str] columns.
year_key(str): download year
Returns:
None: Saves results to CSV files in the specified folder.
"""
# Load the household data and fill missing values
pop_file = pd.read_csv(pop_file_path).fillna(0)
# Iterate over each unique country within the current year
for year in pop_file[year_str].unique():
pop_df = pop_file[pop_file[year_str] == year]
# If there are more than 5000 rows, split the data into smaller chunks
if len(pop_df) > 3500:
hh_df_list = np.array_split(pop_df, (len(pop_df) // 3000) + 1)
else:
hh_df_list = [pop_df]
for index, split_df in enumerate(hh_df_list):
# Construct file path with a chunk index
filepath = Path(gcfg.SAVE_FOLDER) / f"{gcfg.DATASET_NAME.replace('/', '_')}_{year}_{index}.csv"
if filepath.exists():
logging.info(f"{filepath} already exists, skipping")
continue
# Log the data download progress
logging.debug(f"Downloading variables for {year}, chunk: {index} ({split_df.shape})")
# Download the variables and save the result
res = download_vars(year, split_df, gcfg)
Path(gcfg.SAVE_FOLDER).mkdir(parents=True, exist_ok=True)
res.to_csv(filepath, index=False, encoding="utf-8")
logging.info(f"Saved data for {gcfg.DATASET_NAME} to {filepath}")
gcfg = GeeConfig(
DATASET_NAME='MODIS/061/MOD11A1',
DATASET_PROCESS=get_raw_image,
VAR_LIST=["LST_Day_1km","LST_Night_1km"],
VAR_NAME_MAP={
"LST_Day_1km": "MdsDayTmp",
"LST_Night_1km": "MdsNgtTmp"},
METHOD="mean",
SAVE_FOLDER=Path(GEE_SAVE_DIR)/"POP"/"MOD11A1")
main_yearly(gcfg, POP_LIST, "year")
posted @   GeoRepublic  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示