Files
pbf/tasks/WeatherServer_VectorTileGenerator.md
2026-03-17 19:48:15 +08:00

3.7 KiB
Raw Permalink Blame History

NavSea Weather Server

Task: WeatherServer_VectorTileGenerator Architecture: NavSea V11 Codex: codex6 Status: TODO


1 任务目标

实现 Vector Tile Generator。

功能:

将 Weather Grid JSON 转换为 MapLibre Vector Tile (PBF)。

输入:

data/grid/*.json

输出:

output/weather/{time}/{z}/{x}/{y}.pbf

用于 NavSea 客户端加载天气图层。


2 使用库

需要安装:

pip install mercantile pip install mapbox-vector-tile


3 输入数据

GridBuilder v2 生成:

data/grid/

grid_20260312_00_f000.json grid_20260312_00_f003.json

结构:

{ "time": "...", "grid": { "lat": [...], "lon": [...], "wind_speed": ..., "wind_dir": ..., "rain": ..., "temp": ..., "pressure": ... } }

grid 尺寸:

121 × 121


4 Tile Zoom 设计

Weather tile 只需要 4 个 zoom

2 4 6 8


5 Tile 输出结构

output/weather/

time/ z/ x/ y.pbf

示例:

output/weather/20260312_00_f000/4/10/7.pbf


6 Feature 结构

每个 grid 点 → 一个 feature

geometry

POINT(lon lat)

properties

{ "ws": wind_speed "wd": wind_dir "r": rain "t": temp "p": pressure }

字段缩写减少 tile 大小。


7 创建文件

weather_server/tiles/vector_tile_generator.py


8 实现代码

import os
import json
import mercantile
import mapbox_vector_tile

GRID_DIR = "data/grid"
OUTPUT_DIR = "output/weather"

ZOOMS = [2,4,6,8]

def load_grid(path):

    with open(path) as f:
        data = json.load(f)

    return data["time"], data["grid"]


def grid_to_features(grid):

    lat = grid["lat"]
    lon = grid["lon"]

    ws = grid["wind_speed"]
    wd = grid["wind_dir"]
    rain = grid["rain"]
    temp = grid["temp"]
    pres = grid["pressure"]

    features = []

    for i in range(len(lat)):
        for j in range(len(lon)):

            feature = {
                "geometry":{
                    "type":"Point",
                    "coordinates":[lon[j], lat[i]]
                },
                "properties":{
                    "ws":ws[i][j],
                    "wd":wd[i][j],
                    "r":rain[i][j],
                    "t":temp[i][j],
                    "p":pres[i][j]
                }
            }

            features.append(feature)

    return features


def generate_tiles(time, features):

    for z in ZOOMS:

        tiles = mercantile.tiles(120,20,150,50,z)

        for tile in tiles:

            bounds = mercantile.bounds(tile)

            tile_features = []

            for f in features:

                lon, lat = f["geometry"]["coordinates"]

                if (
                    bounds.west <= lon <= bounds.east
                    and bounds.south <= lat <= bounds.north
                ):
                    tile_features.append(f)

            if not tile_features:
                continue

            layer = {
                "weather": tile_features
            }

            tile_data = mapbox_vector_tile.encode(layer)

            path = os.path.join(
                OUTPUT_DIR,
                time,
                str(z),
                str(tile.x)
            )

            os.makedirs(path, exist_ok=True)

            filename = os.path.join(
                path,
                f"{tile.y}.pbf"
            )

            with open(filename,"wb") as f:
                f.write(tile_data)

            print("tile", time, z, tile.x, tile.y)


def main():

    for file in os.listdir(GRID_DIR):

        if not file.endswith(".json"):
            continue

        path = os.path.join(GRID_DIR, file)

        print("processing", file)

        time, grid = load_grid(path)

        features = grid_to_features(grid)

        generate_tiles(time, features)


if __name__ == "__main__":
    main()