229 lines
3.0 KiB
Plaintext
229 lines
3.0 KiB
Plaintext
# NavSea Weather Server
|
||
Task: WeatherServer_GridBuilder
|
||
Architecture: NavSea V11
|
||
Codex: codex6
|
||
Status: TODO
|
||
|
||
---
|
||
|
||
# 1 任务目标
|
||
|
||
实现 Weather Grid Builder 模块。
|
||
|
||
功能:
|
||
|
||
将 GRIB 数据解析为统一的 Weather Grid 数据结构。
|
||
|
||
输入:
|
||
|
||
data/grib/*.grib2
|
||
|
||
输出:
|
||
|
||
data/grid/*.json
|
||
|
||
Weather Grid 将作为:
|
||
|
||
Vector Tile Generator
|
||
Routing Engine
|
||
Weather Analysis
|
||
|
||
的基础数据源。
|
||
|
||
---
|
||
|
||
# 2 输入数据
|
||
|
||
GRIB 文件来自 Downloader。
|
||
|
||
示例:
|
||
|
||
data/grib/
|
||
|
||
20260312_00_f000.grib2
|
||
20260312_00_f003.grib2
|
||
20260312_00_f006.grib2
|
||
|
||
变量:
|
||
|
||
UGRD
|
||
VGRD
|
||
APCP
|
||
PRMSL
|
||
TMP
|
||
|
||
---
|
||
|
||
# 3 Weather Grid 数据结构
|
||
|
||
每个 grid 点结构:
|
||
|
||
{
|
||
"lat": float,
|
||
"lon": float,
|
||
"wind_speed": float,
|
||
"wind_dir": float,
|
||
"rain": float,
|
||
"temp": float,
|
||
"pressure": float
|
||
}
|
||
|
||
说明:
|
||
|
||
lat 纬度
|
||
lon 经度
|
||
|
||
wind_speed m/s
|
||
wind_dir 度
|
||
|
||
rain mm
|
||
|
||
temp 摄氏度
|
||
|
||
pressure hPa
|
||
|
||
---
|
||
|
||
# 4 风速计算
|
||
|
||
使用 U/V 分量计算:
|
||
|
||
wind_speed = sqrt(u² + v²)
|
||
|
||
---
|
||
|
||
# 5 风向计算
|
||
|
||
公式:
|
||
|
||
wind_dir = (atan2(u, v) * 180 / π + 360) % 360
|
||
|
||
结果:
|
||
|
||
0–360°
|
||
|
||
---
|
||
|
||
# 6 Grid Builder 输出
|
||
|
||
文件:
|
||
|
||
data/grid/
|
||
|
||
示例:
|
||
|
||
grid_20260312_00_f000.json
|
||
|
||
结构:
|
||
|
||
{
|
||
"time": "20260312_00_f000",
|
||
"points": [...]
|
||
}
|
||
|
||
---
|
||
|
||
# 7 创建文件
|
||
|
||
weather_server/grid/grid_builder.py
|
||
|
||
---
|
||
|
||
# 8 实现代码
|
||
|
||
```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)
|
||
|
||
|
||
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)
|
||
|
||
points = []
|
||
|
||
for i in range(len(lat)):
|
||
|
||
for j in range(len(lon)):
|
||
|
||
point = {
|
||
|
||
"lat": float(lat[i]),
|
||
"lon": float(lon[j]),
|
||
|
||
"wind_speed": float(wind_speed[i][j]),
|
||
"wind_dir": float(wind_dir[i][j]),
|
||
|
||
"rain": float(rain[i][j]),
|
||
|
||
"temp": float(temp[i][j]),
|
||
|
||
"pressure": float(pressure[i][j])
|
||
}
|
||
|
||
points.append(point)
|
||
|
||
return points
|
||
|
||
|
||
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)
|
||
|
||
points = process_file(path)
|
||
|
||
output = os.path.join(
|
||
OUTPUT_DIR,
|
||
f"grid_{file.replace('.grib2','')}.json"
|
||
)
|
||
|
||
data = {
|
||
|
||
"time": file.replace(".grib2",""),
|
||
"points": points
|
||
}
|
||
|
||
with open(output, "w") as f:
|
||
|
||
json.dump(data, f)
|
||
|
||
print("saved", output)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
|
||
main() |