Initial import
This commit is contained in:
248
tasks/WeatherServer_VectorTileGenerator.md
Normal file
248
tasks/WeatherServer_VectorTileGenerator.md
Normal file
@@ -0,0 +1,248 @@
|
||||
# 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 实现代码
|
||||
|
||||
```python
|
||||
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()
|
||||
Reference in New Issue
Block a user