# NavSea Weather Server Task: WeatherServer_GFS_Subset_Downloader Architecture: V11 Codex: codex6 Status: TODO --- # 任务目标 升级现有 gfs_downloader.py 当前问题: 直接下载完整 GFS 文件: gfs.t00z.pgrb2.0p25.f000 文件大小: ≈ 500MB 下载 72 小时预测需要: ≈ 12GB 这是不可接受的。 解决方案: 使用 NOAA 提供的 GRIB Subset API: filter_gfs_0p25.pl 只下载: 1 指定区域 2 指定变量 3 指定高度层 目标: 将单个 GRIB 文件缩小到: 2MB – 10MB --- # 下载区域 NavSea 天气服务器只需要日本附近区域: leftlon = 120 rightlon = 150 toplat = 50 bottomlat = 20 覆盖: 日本海 东海 太平洋日本海域 --- # 下载变量 Weather System V1 需要以下变量: UGRD VGRD APCP PRMSL TMP HTSGW DIRPW PERPW 说明: UGRD VGRD → 风 APCP → 降水 PRMSL → 气压 TMP → 温度 HTSGW → 浪高 DIRPW → 浪方向 PERPW → 浪周期 变量参数来自 NOAA GFS 参数列表。 :contentReference[oaicite:0]{index=0} --- # 下载高度层 需要以下层: 10 m above ground surface mean sea level 参数: lev_10_m_above_ground lev_surface lev_mean_sea_level --- # URL 构造规则 基础地址: https://nomads.ncep.noaa.gov/cgi-bin/filter_gfs_0p25.pl 示例: https://nomads.ncep.noaa.gov/cgi-bin/filter_gfs_0p25.pl? file=gfs.t00z.pgrb2.0p25.f003 &lev_10_m_above_ground=on &lev_surface=on &lev_mean_sea_level=on &var_UGRD=on &var_VGRD=on &var_APCP=on &var_PRMSL=on &var_TMP=on &var_HTSGW=on &var_DIRPW=on &var_PERPW=on &leftlon=120 &rightlon=150 &toplat=50 &bottomlat=20 &dir=%2Fgfs.20260312%2F00%2Fatmos --- # 修改文件 weather_server/downloader/gfs_downloader.py --- # 新代码 ```python import os import requests from datetime import datetime BASE_URL = "https://nomads.ncep.noaa.gov/cgi-bin/filter_gfs_0p25.pl" OUTPUT_DIR = "data/grib" FORECAST_HOURS = [ 0,3,6,9,12,15,18,21,24, 27,30,33,36,39,42,45, 48,51,54,57,60,63,66, 69,72 ] REGION = { "leftlon":120, "rightlon":150, "toplat":50, "bottomlat":20 } VARIABLES = [ "UGRD", "VGRD", "APCP", "PRMSL", "TMP", "HTSGW", "DIRPW", "PERPW" ] LEVELS = [ "lev_10_m_above_ground", "lev_surface", "lev_mean_sea_level" ] os.makedirs(OUTPUT_DIR, exist_ok=True) def get_cycle(): now = datetime.utcnow() hour = (now.hour // 6) * 6 cycle = f"{hour:02d}" date = now.strftime("%Y%m%d") return date, cycle def build_url(date, cycle, fh): filename = f"gfs.t{cycle}z.pgrb2.0p25.f{fh}" params = { "file": filename, "leftlon": REGION["leftlon"], "rightlon": REGION["rightlon"], "toplat": REGION["toplat"], "bottomlat": REGION["bottomlat"], "dir": f"/gfs.{date}/{cycle}/atmos" } for v in VARIABLES: params[f"var_{v}"] = "on" for l in LEVELS: params[l] = "on" return BASE_URL, params def download_file(url, params, path): if os.path.exists(path): print("skip", path) return print("downloading", path) r = requests.get(url, params=params, stream=True) if r.status_code != 200: print("failed", r.status_code) return with open(path, "wb") as f: for chunk in r.iter_content(1024*1024): f.write(chunk) def main(): date, cycle = get_cycle() print("cycle:", date, cycle) for fh in FORECAST_HOURS: fh_str = f"{fh:03d}" url, params = build_url(date, cycle, fh_str) output = os.path.join( OUTPUT_DIR, f"{date}_{cycle}_f{fh_str}.grib2" ) download_file(url, params, output) if __name__ == "__main__": main()