VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > Python基础教程 >
  • 用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")

来源:https://www.cnblogs.com/geoli/p/18708075


相关教程