Initial import of NavSea pbf project
This commit is contained in:
264
tasks/updatedownload.md
Normal file
264
tasks/updatedownload.md
Normal file
@@ -0,0 +1,264 @@
|
||||
# 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()
|
||||
Reference in New Issue
Block a user