Files
osmWorkflow/.trellis/spec/guides/cross-layer-thinking-guide.md

8.0 KiB
Raw Blame History

跨层思考指南

目的:动手前把数据流走一遍,把"没想到"变成"想过了"。

本项目的层是跨语言、跨进程、跨运行时的,边界比普通应用多得多。


本项目的层与边界

config/areas/*.json          JSON 数据
      ↓ ①
build-area.js                Node宿主机
      ↓ ②  派生配置 JSON
build-osm2streets-qgis.js    Node + osm2streets WASM
      ↓ ③  子进程 + 环境变量
ogr2ogr / ogrinfo / QGIS Python   GDAL/QGIS 运行时
      ↓ ④  GeoJSON / GeoPackage 文件
generate_scene.py            Blender 内嵌 Python
      ↓ ⑤  .blend 文件 + 材质名字符串
export_cesium.py             Blender 内嵌 Python
      ↓ ⑥  GLB + JSON
cesium-preview.js            浏览器
# 边界 常见问题
用户配置 → 归一化 ?? vs ||、相对路径、字段整体替换
两层配置 低层脚本读错配置源
Node → 外部进程 环境变量缺失、退出码与信号、0 字节产物
文件交换 图层集合/顺序漂移、精度丢失
Python → Python 材质名字符串,无校验
Blender → 浏览器 坐标系约定、材质在两种光照下的差异

什么时候该读这篇

  • 改动同时出现在 scripts/blender/
  • 你在改九个 osm2streets 图层中的任何一个
  • 你在改材质名、材质顺序
  • 你在往配置里加字段
  • 你在改任何被 execFileSync / spawnSync 调起的东西
  • 你在改 stage 的 stdout 打印
  • 你要新增一种在 Blender 里生成、要在 Cesium 里看的资产

Step 1画数据流

对每一个箭头问三件事:

  • 格式是什么——JSONGeoJSON FeatureCollectionGeoPackage 图层?字符串键?
  • 可能出什么错——文件不存在0 字节?字段名对不上?
  • 谁负责校验——上游写的时候,还是下游读的时候?

本项目的答案通常是:上游写完就走,下游读的时候校验。因为上游经常是外部工具 ogr2ogr、Blender改不动。

Step 2找出"约定型"边界

最危险的不是有 schema 的边界,是靠约定连接的边界:

边界 靠什么连接 有没有校验
scene-layers.jscatalog.py 图层 id 的集合与顺序 check_layers()warn
generate_scene.pyexport_cesium.py 材质名字符串
GeoJSON 文件名 ↔ 图层 id layerFile()<id>.geojson 部分reimport 会检查 gpkg 图层是否齐全)
stage stdout ↔ parity.js SCENE_DONE / CESIUM_EXPORT_DONE 字面量
GLB 材质索引 ↔ MATERIALS 顺序 隐式的创建顺序 无(靠 parity 事后发现)

没有校验的那几行就是本项目最容易静默出错的地方。

Step 3定契约

对每个边界写清楚:输入格式、输出格式、能出什么错。 本项目已定的契约见产物一致性指南


本项目真实踩过的坑

坑 1同一份事实存了四份

九个图层的顺序曾同时存在于 z_index 表、样式 JSON、QGIS 工程、README。 改一处漏三处,不报错,只是下游场景悄悄错栈。

修法scripts/lib/scene-layers.js 单一事实源 + 四个派生函数。 跨语言那一份(catalog.py)无法消除,改用运行时对账。

图层表

坑 2外部工具失败但留下了文件

ogr2ogr 遇到不存在的图层退出码非零,但已经创建了一个 0 字节文件。 直接覆盖目标目录就会用空文件冲掉好数据,而且看起来像成功。

修法staging 目录 → 全部校验 → 才落盘。

外部工具调用

坑 3为了"输出干净"加了个参数

ogr2ogr 显式设 COORDINATE_PRECISION,触发了 GDAL 的精度裁剪, 7 个箭头多边形丢了 28 个顶点。默认行为本来就能完整往返双精度。

教训:跨边界时,显式设置一个"看起来更安全"的参数,可能触发上游的另一条代码路径

坑 4新资产在 Blender 里对、在 Cesium 里发黑

场景里每一个材质都被手工提亮过(草往亮绿混 72%、建筑自发光 0.18 因为 Cesium 默认光照偏白。新资产没调过,是唯一如实渲染的东西, 放在旁边就显得发黑。

教训同一份数据在两个运行时里的"正确"可能不一样。 第二个运行时如果有一整套补偿,新东西必须也进那套补偿。

资产生成

坑 5两个 Python 脚本靠字符串对接

export_cesium.py 不 import catalog,靠材质名字符串匹配四张覆盖表。 改个材质名Cesium 侧的调色静默失效catalog.CESIUM_EXPORT 想解决这个问题, 但迁移没做完,它现在是死代码。

教训字符串键的跨模块耦合必须配一个对账机制,否则重命名就是定时炸弹。


加东西时的检查清单

加一个图层

  • scene-layers.js:SCENE_LAYERS 末尾追加
  • catalog.py:ROAD_LAYERS 末尾追加,顺序一致
  • 确认 osm2streets 拆分结果里有对应的 splitKey
  • 跑一次构建,确认日志里没有 Layer catalog warning:
  • 跑 parity确认只多了预期的对象

加一个材质

  • catalog.MATERIALS 末尾追加(中间插入会平移 GLB 材质索引)
  • 若在 Cesium 里需要调色,去 export_cesium.py 的四张表加
  • 跑 parity

加一个配置字段

  • normalizeAreaConfig 里用 ?? 不用 ||
  • 需要传给低层脚本?加进 writeDerivedConfig
  • 数值?在消费侧加 Number.isFinite + 范围校验,在任何副作用之前
  • 更新 config/examples/template.json
  • 更新 config spec 的字段表

加一个 stage

  • normalizeAreaConfigstages + resolveStagesaliases
  • 想清楚进不进 all(恢复类/补丁类不进)
  • 与已有 stage 有覆盖关系?加互斥检查
  • 产出新文件?加进 outputs 路径推导
  • 阶段函数开头 ensureFile 校验依赖产物

加一种 OSM 要素

  • 新模块放 osmassets/assemble(...) 签名照抄现有三个
  • 只 import 需要的,纯几何逻辑放 geom.py 并补测试
  • 材质加进 catalog.MATERIALS 末尾
  • generate_scene.py 分发处加一行
  • 需要"随机"外观?用 index 的纯函数或显式 seed不要用 random
  • 跑 parity

通用的四个错误

隐式格式假设

跨边界时假设"上游肯定给的是 X 格式"。本项目的做法是读的时候验 reimport-gpkg.js:175 明确检查 type === "FeatureCollection" && Array.isArray(features)

校验散在各处

同一个约束在三个地方各写一遍,改的时候漏一个。 本项目把参数校验集中在脚本开头(build-osm2streets-qgis.js:41-70 在任何副作用之前一次验完

抽象泄漏

低层脚本如果直接读 config/areas/*.json,两层配置的边界就白设了。 它们只该读派生配置。

每个消费方各自解析同一份数据

看到两处代码用各自的方式从同一份 payload 里挖同一个字段, 就该有一个共享的解析函数了。mergeScene(getCollection) 用回调而不是数组, 就是为了让 build 和 reimport 共用一套合并逻辑。


一条铁律

改任何值之前,先全仓 grep 一遍。

grep -rn "要改的值" scripts blender config

本项目跨两种语言IDE 的"查找引用"帮不上忙。这一个习惯能挡掉大部分 "忘了同步 X" 的 bug。


相关