用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")
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)