feat: 初始化老年群体高温预警项目基础工程

搭建完整的项目目录结构,配置项目依赖与元信息,添加数据下载、预处理、模型训练、可视化相关的核心业务代码,补充项目设计文档与.gitignore配置,导入初始外部参考数据文件。
This commit is contained in:
2026-05-26 20:05:10 +08:00
commit a0478b0b11
20 changed files with 3300 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
"""从 Copernicus CDS 下载 ERA5-Land 再分析数据"""
import logging
import time
from pathlib import Path
import cdsapi
from src.utils.config import (
CITIES,
DATA_RAW,
ERA5_END_YEAR,
ERA5_START_YEAR,
ERA5_VARIABLES,
)
logger = logging.getLogger(__name__)
def build_request(city: str, year: int, month: int) -> dict:
"""构建 CDS API 请求参数,提取城市周围 0.5 度区域
Args:
city: 城市键名("jiaozuo""zhengzhou"
year: 年份
month: 月份(1-12),0 表示全年所有月份
Returns:
CDS API 请求参数字典
"""
lat = CITIES[city]["lat"]
lon = CITIES[city]["lon"]
return {
"product_type": ["reanalysis"],
"format": "netcdf",
"variable": ERA5_VARIABLES,
"year": [str(year)],
"month": [f"{m:02d}" for m in (range(1, 13) if month == 0 else [month])],
"day": [f"{d:02d}" for d in range(1, 32)],
"time": [f"{h:02d}:00" for h in range(24)],
"area": [lat + 0.5, lon - 0.5, lat - 0.5, lon + 0.5], # [N, W, S, E]
}
def download_era5_city(
city: str,
start_year: int = ERA5_START_YEAR,
end_year: int = ERA5_END_YEAR,
max_retries: int = 3,
retry_delay: int = 30,
) -> None:
"""逐月下载指定城市的 ERA5-Land 数据,避免单次请求过大超时
Args:
city: 城市键名
start_year: 起始年份
end_year: 结束年份
max_retries: 失败重试次数
retry_delay: 重试等待秒数
"""
client = cdsapi.Client()
out_dir = Path(DATA_RAW) / "era5" / city
out_dir.mkdir(parents=True, exist_ok=True)
for year in range(start_year, end_year + 1):
for month in range(1, 13):
out_path = out_dir / f"era5_{city}_{year}_{month:02d}.nc"
if out_path.exists():
logger.info("跳过已存在: %s", out_path)
continue
request = build_request(city, year, month)
for attempt in range(1, max_retries + 1):
try:
logger.info(
"正在下载 %s %d-%02d (第 %d/%d 次尝试)...",
city, year, month, attempt, max_retries,
)
client.retrieve(
"reanalysis-era5-land",
request,
str(out_path),
)
logger.info("下载完成: %s", out_path)
break
except Exception:
logger.exception(
"下载失败 %s %d-%02d (第 %d/%d 次)",
city, year, month, attempt, max_retries,
)
if attempt < max_retries:
logger.info("等待 %d 秒后重试...", retry_delay)
time.sleep(retry_delay)
else:
logger.error(
"下载彻底失败 %s %d-%02d,已达最大重试次数",
city, year, month,
)
if __name__ == "__main__":
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
)
for city_name in CITIES:
download_era5_city(city_name)