215 lines
3.2 KiB
Markdown
215 lines
3.2 KiB
Markdown
# NavSea Weather Server
|
||
Task: WeatherServer_GridBuilder_v2
|
||
Architecture: NavSea V11
|
||
Codex: codex6
|
||
Status: TODO
|
||
|
||
---
|
||
|
||
# 1 任务目标
|
||
|
||
升级 Grid Builder:
|
||
|
||
grid_builder.py → GridBuilder v2
|
||
|
||
目标:
|
||
|
||
1 裁剪日本区域 grid
|
||
2 使用数组结构保存 grid
|
||
3 大幅减少 JSON 文件大小
|
||
|
||
---
|
||
|
||
# 2 当前问题
|
||
|
||
旧版 GridBuilder:
|
||
|
||
生成全球 grid:
|
||
|
||
1440 × 721
|
||
≈ 1,038,240 points
|
||
|
||
JSON 文件:
|
||
|
||
≈ 215MB
|
||
|
||
这是不可接受的。
|
||
|
||
Downloader 已经只下载:
|
||
|
||
120E – 150E
|
||
20N – 50N
|
||
|
||
GridBuilder 必须只输出这个区域。
|
||
|
||
---
|
||
|
||
# 3 区域范围
|
||
|
||
REGION:
|
||
|
||
lon_min = 120
|
||
lon_max = 150
|
||
|
||
lat_min = 20
|
||
lat_max = 50
|
||
|
||
理论 grid:
|
||
|
||
(150-120)/0.25 = 120
|
||
(50-20)/0.25 = 120
|
||
|
||
≈ 14400 grid points
|
||
|
||
---
|
||
|
||
# 4 新 Grid 数据结构
|
||
|
||
旧结构:
|
||
|
||
points list
|
||
|
||
{
|
||
"points":[
|
||
{lat,lon,...}
|
||
]
|
||
}
|
||
|
||
新结构:
|
||
|
||
grid arrays
|
||
|
||
{
|
||
"time": "...",
|
||
"lat": [...],
|
||
"lon": [...],
|
||
"wind_speed": [...],
|
||
"wind_dir": [...],
|
||
"rain": [...],
|
||
"temp": [...],
|
||
"pressure": [...]
|
||
}
|
||
|
||
优点:
|
||
|
||
1 文件更小
|
||
2 读取更快
|
||
3 tile generator 更容易
|
||
|
||
---
|
||
|
||
# 5 创建文件
|
||
|
||
weather_server/grid/grid_builder_v2.py
|
||
|
||
---
|
||
|
||
# 6 实现代码
|
||
|
||
```python
|
||
import os
|
||
import json
|
||
import numpy as np
|
||
import xarray as xr
|
||
|
||
INPUT_DIR = "data/grib"
|
||
OUTPUT_DIR = "data/grid"
|
||
|
||
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
||
|
||
REGION = {
|
||
"lon_min":120,
|
||
"lon_max":150,
|
||
"lat_min":20,
|
||
"lat_max":50
|
||
}
|
||
|
||
|
||
def compute_wind(u, v):
|
||
|
||
speed = np.sqrt(u**2 + v**2)
|
||
|
||
direction = (np.degrees(np.arctan2(u, v)) + 360) % 360
|
||
|
||
return speed, direction
|
||
|
||
|
||
def process_file(path):
|
||
|
||
ds = xr.open_dataset(path, engine="cfgrib")
|
||
|
||
u = ds["u10"].values
|
||
v = ds["v10"].values
|
||
rain = ds["tp"].values
|
||
pressure = ds["msl"].values
|
||
temp = ds["t2m"].values
|
||
|
||
lat = ds.latitude.values
|
||
lon = ds.longitude.values
|
||
|
||
wind_speed, wind_dir = compute_wind(u, v)
|
||
|
||
lat_idx = np.where(
|
||
(lat >= REGION["lat_min"]) &
|
||
(lat <= REGION["lat_max"])
|
||
)[0]
|
||
|
||
lon_idx = np.where(
|
||
(lon >= REGION["lon_min"]) &
|
||
(lon <= REGION["lon_max"])
|
||
)[0]
|
||
|
||
lat_region = lat[lat_idx]
|
||
lon_region = lon[lon_idx]
|
||
|
||
wind_speed = wind_speed[np.ix_(lat_idx, lon_idx)]
|
||
wind_dir = wind_dir[np.ix_(lat_idx, lon_idx)]
|
||
|
||
rain = rain[np.ix_(lat_idx, lon_idx)]
|
||
temp = temp[np.ix_(lat_idx, lon_idx)]
|
||
pressure = pressure[np.ix_(lat_idx, lon_idx)]
|
||
|
||
return {
|
||
"lat": lat_region.tolist(),
|
||
"lon": lon_region.tolist(),
|
||
"wind_speed": wind_speed.tolist(),
|
||
"wind_dir": wind_dir.tolist(),
|
||
"rain": rain.tolist(),
|
||
"temp": temp.tolist(),
|
||
"pressure": pressure.tolist()
|
||
}
|
||
|
||
|
||
def main():
|
||
|
||
for file in os.listdir(INPUT_DIR):
|
||
|
||
if not file.endswith(".grib2"):
|
||
continue
|
||
|
||
path = os.path.join(INPUT_DIR, file)
|
||
|
||
print("processing", file)
|
||
|
||
grid = process_file(path)
|
||
|
||
output = os.path.join(
|
||
OUTPUT_DIR,
|
||
f"grid_{file.replace('.grib2','')}.json"
|
||
)
|
||
|
||
data = {
|
||
"time": file.replace(".grib2",""),
|
||
"grid": grid
|
||
}
|
||
|
||
with open(output, "w") as f:
|
||
|
||
json.dump(data, f)
|
||
|
||
print("saved", output)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
|
||
main() |