# 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()