Compare commits
67 Commits
main
...
fengshu-er
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a8dbc1a74 | |||
| 5403936ae4 | |||
| 0bc949bc24 | |||
| 6a0437ff83 | |||
| 3cad384b25 | |||
| 9e0e25aadf | |||
| e9ab1f03eb | |||
| 1154de3ba1 | |||
| c057258085 | |||
| 41d32457f2 | |||
| 5accc0a4b1 | |||
| 7204c28161 | |||
| a63a3ae0d8 | |||
| f7e71cfe9c | |||
| e41bfd13ec | |||
| 46542c5f4e | |||
| 1896884900 | |||
| 9bb97d4507 | |||
| bd38aa55d3 | |||
| b318f7b641 | |||
| d065dcdaf3 | |||
| 472a83c670 | |||
| bc045177a5 | |||
| 8409d2c6c3 | |||
| 5f59ad0ef0 | |||
| 47efb78f1e | |||
| 8bfc02e66e | |||
| 9d42181246 | |||
| fb863dafb2 | |||
| 7dc5c947a3 | |||
| 866b59acf8 | |||
| 4c4f4534c0 | |||
| ea8a3622b9 | |||
| 03a8d6159b | |||
| 1b9829d9ed | |||
| 822e6ef936 | |||
| 3eea12c6ea | |||
| 3d57655497 | |||
| 65cf8b96d9 | |||
| e1f3fc10ca | |||
| f43a122ff7 | |||
| 3e22a1872e | |||
| 1da985c932 | |||
| 817e361477 | |||
| 25287777f6 | |||
| a556c0fc97 | |||
| 1876472bf8 | |||
| 707e7f82f9 | |||
| 850e9eb344 | |||
| df81a22ce8 | |||
| b5fa4482f0 | |||
| ddd15f68b3 | |||
| aa5b1f85c8 | |||
| 761a52646c | |||
| f13d890ffb | |||
| 41a0e81a6f | |||
| eff7a01b2b | |||
| 0102ffbb3c | |||
| f2b8d79f5d | |||
| db0fba5bb5 | |||
| c925890848 | |||
| f385009043 | |||
| b4a81331e8 | |||
| e0cdf9e00b | |||
| 0790cbd0a9 | |||
| b68be063ad | |||
| d26921c6d1 |
@@ -8,14 +8,14 @@
|
||||
|
||||
## 两层配置
|
||||
|
||||
用户只写第一层,第二层是机器生成的中间产物:
|
||||
用户只写第一层;legacy QGIS 链路需要时才生成第二层中间产物:
|
||||
|
||||
```
|
||||
config/areas/<id>.json ← 你写的
|
||||
│ scripts/lib/area-config.js: normalizeAreaConfig()
|
||||
│ 补默认值 + 推导输出路径
|
||||
▼
|
||||
<areaDir>/_pipeline/osm2streets-qgis.config.json ← 生成的,不要手改
|
||||
<areaDir>/_pipeline/osm2streets-qgis.config.json ← legacy 生成的,不要手改
|
||||
│
|
||||
▼ build-osm2streets-qgis.js / reimport-gpkg.js
|
||||
```
|
||||
@@ -54,7 +54,7 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
| `turnLaneArrows` | | 见下 | 从 OSM `turn:lanes:*` 生成自定义车道箭头的发布开关 |
|
||||
| `osm2streets` | | 见下 | 透传给 osm2streets 的选项 |
|
||||
| `blender` | | 见下 | Blender 侧选项 |
|
||||
| `compress` | | 见下 | 显式 `compress` 阶段的 GLB 压缩选项 |
|
||||
| `compress` | | 见下 | 默认交付压缩阶段的 GLB 压缩选项 |
|
||||
| `budget` | | 见下 | 区域 GLB 性能与体量预算 |
|
||||
| `outputs` | | 从 `id` 推导 | 输出路径覆盖,逃生舱 |
|
||||
|
||||
@@ -65,15 +65,17 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
|
||||
| 字段 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
| `intermediates` | `true` | 旧名 `qgis` 仍被接受 |
|
||||
| `intermediates` | `false` | native-only 默认;旧名 `qgis` 仍被接受,显式开启才运行 legacy |
|
||||
| `blender` | `true` | |
|
||||
| `cesium` | `true` | |
|
||||
| `compress` | `true` | 压缩 staged GLB,供随后发布使用 |
|
||||
| `package` | `true` | 将通过校验的静态模型原子发布到 `package/` |
|
||||
| `preview` | `true` | 基于已发布 package 写验证预览及 `_preview/` 动态资源 |
|
||||
|
||||
`reimport`、`preview` 和 `compress` **在这里配也没用**——`normalizeAreaConfig` 把它们
|
||||
硬编码为 `false`,只能靠 `--stages` 显式请求。
|
||||
`reimport` **在这里配也没用**——`normalizeAreaConfig` 把它硬编码为 `false`,只能靠
|
||||
`--stages` 显式请求。`compress`、`package` 和 `preview` 始终默认开启。
|
||||
|
||||
> 恢复动作(reimport)、补丁动作(preview)和替代产物动作(compress)不该被一份
|
||||
> 配置文件变成默认行为。
|
||||
> 恢复动作(reimport)不该被一份配置文件变成默认行为;压缩、发布和验证预览是标准交付链的一部分。
|
||||
|
||||
`--stages` 会整体覆盖这里的默认值。
|
||||
|
||||
@@ -130,11 +132,13 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
|---|---|---|
|
||||
| `treeStyle` | `"natural"` | 合法值见 `generate_scene.py` 的 `TREE_STYLES`(`natural`、`procedural`、`shapespark`) |
|
||||
| `officeOverrides` | `""` | 旧名 `office_overrides` 仍被接受 |
|
||||
| `roadProvider` | `"native"` | Blender 道路来源。`"native"` 时仅使用 `native-road/` 的道路、路口和人行道面;`"osm2streets"` 仅用于显式 legacy/debug 构建。 |
|
||||
|
||||
### `compress`
|
||||
|
||||
只影响显式 `--stages compress`。默认压缩链是 texture resize + WebP transcode,
|
||||
不覆盖默认 `<area-id>.glb`。
|
||||
完整构建和显式 `--stages compress` 都使用此配置。默认压缩链是 texture resize + WebP
|
||||
transcode,成功后替换**package staging** 中的主 GLB 与 manifest;未压缩源只保留在构建临时目录。
|
||||
只有随后的 `package` 阶段才会原子发布到 `package/`。
|
||||
|
||||
| 字段 | 默认 | 说明 |
|
||||
|---|---|---|
|
||||
@@ -162,14 +166,16 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
|
||||
### `outputs`(逃生舱)
|
||||
|
||||
默认全部从 `id` 推导为 `<outputRoot>/<id>/<fileStem>.<ext>`。需要定制时逐项覆盖:
|
||||
默认全部从 `id` 推导。发布给下游的静态资产固定在
|
||||
`<outputRoot>/<id>/package/`(含 `models/` 与可控信号灯 `runtime/`),临时静态资产在
|
||||
`_pipeline/package-staging/`,预览专用车辆/路线资源在 `<outputRoot>/<id>/_preview/`。需要定制时逐项覆盖:
|
||||
|
||||
```json
|
||||
{
|
||||
"outputs": {
|
||||
"areaDir": "/absolute/path/to/custom-area",
|
||||
"blend": "/absolute/path/to/custom.blend",
|
||||
"glb": "/absolute/path/to/custom.glb",
|
||||
"packageDir": "/absolute/path/to/custom-package",
|
||||
"cesiumPreview": "/absolute/path/to/custom-preview.html"
|
||||
}
|
||||
}
|
||||
@@ -177,9 +183,14 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
|
||||
可覆盖的键(`scripts/lib/area-config.js`):`areaDir`、`fileStem`、`geojsonDir`、`gpkg`、
|
||||
`qgisProject`、`qgisPreview`、`blend`、`render`、`glb`、`metadata`、`cesiumPreview`、
|
||||
`compressedFileStem`、`compressedGlb`、`compressedMetadata`、`compressedCesiumPreview`、
|
||||
`vehicleRoute`、`vehicleModel`、`pipelineDir`、`stageManifestDir`。
|
||||
|
||||
静态发布路径另有 `packageDir`、`packageStagingDir`、`packageManifest`、
|
||||
`packageStagingManifest`、`packageModelDir`、`packageStagingModelDir` 与
|
||||
`packagePrimaryGlb`;预览路径另有 `previewDir`、`previewDescriptor` 与
|
||||
`trafficSimulation`(native preview 的可迁移仿真描述符)。除非在迁移旧调用,
|
||||
不要覆盖 `glb` / `metadata`:它们是 staging 内部路径,不是下游资产入口。
|
||||
|
||||
**优先改 `fileStem` 或 `areaDir`**——它们能一次性影响全部派生路径。逐个覆盖容易漏。
|
||||
|
||||
---
|
||||
@@ -221,7 +232,8 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
| 布尔字段用 `\|\|` 兜底 | `false` 被翻转 |
|
||||
| 给新字段造顶层平铺别名 | 扩大历史包袱 |
|
||||
| 逐个覆盖 `outputs` 而不用 `fileStem` | 漏掉某个产物路径 |
|
||||
| 在 `stages` 里配 `reimport` / `preview` / `compress` | 无效,被硬编码为 false |
|
||||
| 将 `glb` / `metadata` 当作下游入口 | 它们位于 staging;应只读取 `package/manifest.json` |
|
||||
| 在 `stages` 里配 `reimport` | 无效,被硬编码为 false |
|
||||
| 加数值字段不做范围校验 | 错配置在中途才崩,输出已被破坏 |
|
||||
|
||||
---
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
全部是 CommonJS(`package.json` 的 `"type": "commonjs"`),无构建步骤、无 TypeScript、
|
||||
零运行时依赖(唯一依赖 `osm2streets-js-node` 只被 `build-osm2streets-qgis.js` 用)。
|
||||
|
||||
## 可选 GLB 压缩阶段
|
||||
## 默认 GLB 压缩阶段
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
`compress` 是显式请求的替代产物阶段,不属于 `all`,也不覆盖默认 `<area-id>.glb`。
|
||||
它用于在已有 Cesium GLB 上生成并列压缩 GLB、metadata 和预览页。当前主路径是
|
||||
texture resize + WebP transcode。
|
||||
`compress` 是完整构建的默认阶段,位于 `cesium` 与 `package` 之间。它在
|
||||
`_pipeline/package-staging/` 的主 GLB 上执行 texture resize + WebP transcode,并替换 staged
|
||||
主 GLB 与 staged manifest;不触碰已发布的 `package/`,也不重写 preview HTML。未压缩版本只在
|
||||
`<areaDir>/_pipeline/compress-*` 临时目录中存在,成功或失败后都会清理。
|
||||
|
||||
`scripts/compress-glb.js` 是该阶段调用的低层脚本,也可单独运行做实验。
|
||||
|
||||
@@ -62,20 +63,17 @@ npm run compress:glb -- --input in.glb --output out.glb [options]
|
||||
### 3. Contracts
|
||||
|
||||
- 输入必须是现有 `.glb` 文件;`--output` 必须不同于 `--input`。
|
||||
- 输出是并列压缩 GLB;默认构建产物不被替换。
|
||||
- `compress` 阶段依赖默认 `glb`、`metadata` 和 `cesiumPreview` 已存在。
|
||||
- 低层脚本仍输出到与输入不同的路径;`build-area.js` 使用临时输入/输出路径,避免原地压缩。
|
||||
- `compress` 阶段依赖 staged `glb` 与 `metadata` 已存在,并在全部临时交付文件生成和校验后
|
||||
以 `renameSync` 替换它们。
|
||||
- 默认链固定为 `gltf-transform resize -> gltf-transform webp`。
|
||||
- `compress` 阶段默认 `textureSize=768`;低层脚本单独运行时默认 `--texture-size 1024`。
|
||||
- `build-area.js` 默认写:
|
||||
- `<fileStem>-compressed-webp768.glb`
|
||||
- `<fileStem>-compressed-webp768.json`
|
||||
- `<fileStem>-compressed-webp768-cesium-preview.html`
|
||||
- 伴生 metadata 的 `asset` 与 `id="main"` 资产 URL 改为压缩 GLB 文件名;其余资产(包括
|
||||
`category="semantic"` 的 Cesium 分类检查 GLB)必须原样保留。
|
||||
- preview HTML 只替换 `window.OSM_ASSET_PREVIEW_CONFIG` 的 `glbName` /
|
||||
`metadataName` 和 loading 文案,不改 preview runtime。
|
||||
- `build-area.js` 不会写并列 `-compressed-*` 交付物;`package` 是唯一能发布静态文件到
|
||||
`package/` 的阶段。
|
||||
- staged manifest 的 `assets[*].uri` 必须保持 package-relative,如 `models/<area>.glb`;分类
|
||||
layer GLB 必须原样保留。
|
||||
- 成功时 stdout 打印 `GLB_COMPRESS_DONE <json>`,包含压缩前后大小、image /
|
||||
non-image bytes、结构计数、扩展、metadata / preview 输出路径。
|
||||
non-image bytes、结构计数、扩展与 metadata 输出路径。
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
@@ -86,27 +84,24 @@ npm run compress:glb -- --input in.glb --output out.glb [options]
|
||||
| `--output` 等于 `--input` | 抛错,避免覆盖源 GLB |
|
||||
| 数值参数超范围 | 抛错并指出合法范围 |
|
||||
| `gltf-transform` 退出非零 | 抛错并带上 status / signal |
|
||||
| `--preview` 没有 `--metadata` | 抛错,因为 preview 必须指向存在的 metadata |
|
||||
| preview HTML 找不到配置块 | 抛错,不做猜测替换 |
|
||||
| `--stages compress` 但默认 preview 不存在 | `Cesium preview not found: <path>` |
|
||||
| `--stages compress` 但 staged manifest 不存在 | `Cesium metadata not found: <path>` |
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: `--stages cesium,compress` 先重导默认 GLB,再生成并列压缩产物。
|
||||
- Good: `--texture-size 768 --metadata --preview` 生成压缩 GLB、metadata、HTML,
|
||||
源 GLB 保持不变。
|
||||
- Good: 默认构建先导出 Cesium,再压缩 staged 资产,最后由 `package` 发布。
|
||||
- Good: `--stages compress` 对已有 staged 交付物重新压缩,临时源 GLB 不会留在输出目录。
|
||||
- Base: 只传 `--input --output` 生成压缩 GLB,不生成伴生文件。
|
||||
- Bad: 使用 `--meshopt` 后没有做 Cesium 兼容性验证就当默认产物发布。
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- `node --check scripts/compress-glb.js`
|
||||
- `npm run test:compress-glb`:断言压缩 metadata 只替换主资产,不丢失语义资产。
|
||||
- `npm run test:compress-glb`:断言压缩 manifest 只替换主资产,不丢失语义 layer。
|
||||
- `node --check scripts/build-area.js`
|
||||
- 对目标区域跑一次 `npm run compress:glb -- ... --metadata --preview`
|
||||
- 对目标区域跑一次 `npm run build:area -- --stages compress`
|
||||
- `node scripts/glb-digest.js <compressed.glb>` 确认可解析结构和扩展
|
||||
- 浏览器/Cesium 预览压缩 HTML,确认 `EXT_texture_webp` 在目标环境可加载
|
||||
- 对目标区域跑一次 `npm run compress:glb -- ... --metadata`
|
||||
- 对目标区域跑一次默认 `npm run build:area`
|
||||
- `node scripts/glb-digest.js <area>.glb` 确认可解析结构和扩展
|
||||
- 浏览器/Cesium 预览标准 HTML,确认 `EXT_texture_webp` 在目标环境可加载
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
@@ -116,10 +111,110 @@ Wrong:
|
||||
npm run compress:glb -- --input outputs/a/a.glb --output outputs/a/a.glb
|
||||
```
|
||||
|
||||
Correct:
|
||||
Correct for the low-level script:
|
||||
|
||||
```bash
|
||||
npm run compress:glb -- --input outputs/a/a.glb --output outputs/a/a-compressed-webp768.glb
|
||||
npm run compress:glb -- --input outputs/a/a.glb --output /tmp/a-compressed.glb
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 可复用资产包发布
|
||||
|
||||
### 1. Scope / Trigger
|
||||
|
||||
`package` 将已验证的 static staging 提升为下游可消费的唯一交付边界:
|
||||
`<areaDir>/package/`。它不复制大文件到第二个发布目录;成功时将整个 staging 目录原子 rename
|
||||
为 package。车辆、路线和动态信号属于预览能力,必须留在 `<areaDir>/_preview/`,不能进入 package。
|
||||
|
||||
### 2. Signatures
|
||||
|
||||
```bash
|
||||
npm run build:area -- --config config/areas/<area>.json --stages cesium,compress,package
|
||||
npm run build:area -- --config config/areas/<area>.json --stages preview
|
||||
node scripts/test-package-contract.js
|
||||
node scripts/test-package-examples.js
|
||||
```
|
||||
|
||||
下游入口永远是:
|
||||
|
||||
```text
|
||||
outputs/<area>/package/manifest.json
|
||||
```
|
||||
|
||||
### 3. Contracts
|
||||
|
||||
发布 manifest 使用 `schema: "osm-asset-package/v1"`,至少包含:
|
||||
|
||||
```json
|
||||
{
|
||||
"schema": "osm-asset-package/v1",
|
||||
"packageVersion": "1.0.0",
|
||||
"areaId": "example",
|
||||
"coordinateSystem": { "axes": "ENU", "units": "meters", "x": "east", "y": "north", "z": "up" },
|
||||
"placement": { "longitude": 114.3, "latitude": 30.5, "height": 0, "headingCorrectionDegrees": -90 },
|
||||
"bounds": { "minLon": 114.2, "minLat": 30.4, "maxLon": 114.4, "maxLat": 30.6 },
|
||||
"assets": [{
|
||||
"id": "main",
|
||||
"role": "scene",
|
||||
"category": "scene",
|
||||
"uri": "models/example.glb",
|
||||
"defaultLoad": true,
|
||||
"integrity": { "bytes": 123, "sha256": "..." }
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
- `coordinateSystem` 固定为 ENU meters,`placement` 是 WGS84 锚点与 heading correction。
|
||||
- 必须恰有一个 `role="scene"` / `category="scene"` / `defaultLoad=true` 主资产;roads、buildings、
|
||||
vegetation、water 是 `role="layer"` 且 `defaultLoad=false` 的可选语义层。
|
||||
- `assets[*].uri` 与 `runtime[*].uri` 只能是包内正向相对路径,禁止绝对路径、反斜杠和 `..`。
|
||||
- `runtime` 声明可编程运行时资产;当前至少包含 `traffic-signals` 锚点 JSON、
|
||||
`traffic-signals-dynamic` 灯珠 GLB,以及两个 `traffic-signals-countdown-*` 倒计时 GLB。
|
||||
它们是资产包的一部分,不属于 `_preview/`。
|
||||
- `package` 在晋升前调用 `validateManifest()`;再为每个模型写 bytes 与 SHA-256,然后复验。
|
||||
- `package` 是唯一拥有最终 package file records 的 stage。`cesium` / `compress` 只能拥有 staging
|
||||
或计算摘要,不能留下指向最终 package 文件的 outputs record。
|
||||
- 下游代码和 preview 都从 manifest URL 解析 `assets[*].uri`。参考
|
||||
`examples/cesium-asset-package.js` 与 `examples/three-asset-package.js`。
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
| 条件 | 结果 |
|
||||
|---|---|
|
||||
| staging manifest 缺失或无效 JSON | `package` 失败,不替换上次成功 package |
|
||||
| URI 是绝对路径、含 `..` 或 `\\` | `validateManifest()` 失败 |
|
||||
| ENU / WGS84 字段缺失或越界 | `validateManifest()` 失败 |
|
||||
| 没有或多于一个 scene asset | `validateManifest()` 失败 |
|
||||
| manifest 声明的模型文件缺失 | `validateManifest()` 失败 |
|
||||
| rename 发布异常 | 尝试恢复 `<package>.previous`,保留原 package |
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
- Good: 下游只带走 `package/`,按 manifest 的 WGS84 placement 放置主模型,按需加载 layer。
|
||||
- Base: 只有 main scene 的区域仍是合法 package;没有几何的语义类别不写空 GLB。
|
||||
- Bad: 将 `_preview/` 的车辆模型或路线加进 package;它们是本项目的验证运行时,信号灯 runtime 则必须发布。
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
- `npm run test:package-contract`:schema、URI、placement、唯一 scene 和模型存在性。
|
||||
- `npm run test:package-examples`:Cesium / Three.js resolver 都相对 manifest 解析 URI。
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages cesium,compress,package,preview`
|
||||
- `npm run check:area -- --config config/areas/nantaizi-lake-innovation-valley.json`
|
||||
- 将 `package/` 复制到其他目录后,重新运行 manifest validator;所有 `assets[*].uri` 必须仍能解析。
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
Wrong:
|
||||
|
||||
```js
|
||||
const modelUrl = asset.uri; // Relative to preview HTML by accident.
|
||||
```
|
||||
|
||||
Correct:
|
||||
|
||||
```js
|
||||
const modelUrl = new URL(asset.uri, new URL(manifestUrl, window.location.href)).href;
|
||||
```
|
||||
|
||||
---
|
||||
@@ -272,6 +367,341 @@ if (!networkSaysIntersection && (roadCounts.get(endpoint.id) || 0) < 3) return n
|
||||
fs.writeFileSync(diagnosticsPath, `${JSON.stringify(diagnostics, null, 2)}\n`);
|
||||
```
|
||||
|
||||
## Native 道路标线
|
||||
|
||||
### 1. 范围与触发条件
|
||||
|
||||
`node scripts/compile-native-roads.js --config <area>` 将原生道路标线写入
|
||||
`outputs/<area>/native-road/layers/`。它不读取 osm2streets 渲染几何;只复用
|
||||
`turn-lane-arrows.js` 中已测试的箭头模板。工作台 `GET /api/state` 原样服务这些
|
||||
GeoJSON,native Blender 构建通过 `catalog.NATIVE_ROAD_LAYERS` 消费它们。
|
||||
|
||||
### 2. 图层契约
|
||||
|
||||
| 文件 | 语义 | 必需 provenance | Blender material layer |
|
||||
|---|---|---|---|
|
||||
| `lane_separators.geojson` | 同向相邻车道的分隔线 | `native-road-lane-separator/v1` | `lane_separators` |
|
||||
| `direction_arrows.geojson` | 沿定向车道重复的直行方向箭头 | `native-road-direction-arrow/v1` | `lane_arrows_webscale` |
|
||||
| `turn_arrows.geojson` | 明确 `turn:lanes` 的路口动作箭头 | `native-road-turn-arrow/v1` | `lane_arrows_webscale` |
|
||||
|
||||
方向箭头必须带 `road_id`、`lane_id`、`osm_way_ids`、`direction`、`lane_index`、
|
||||
`sequence`、`distance_along_lane_meters` 和 `placement_interval_meters`。转向箭头
|
||||
必须带 `maneuver` 与 `placement_distance_meters`。两者不能共用 provenance 或假装为
|
||||
彼此:前者表达沿路行驶方向,后者表达路口处允许动作。
|
||||
|
||||
### 3. 放置与错误矩阵
|
||||
|
||||
| 条件 | 结果 |
|
||||
|---|---|
|
||||
| 车道长度不足以容纳两端 14m 缓冲 | 不生成道路方向箭头 |
|
||||
| 可用车道长度 | 从 14m 起按 32m 间距生成 `through` 箭头 |
|
||||
| OSM 未提供 `turn:lanes` | 不生成路口转向箭头 |
|
||||
| `turn:lanes` 存在但动作不受已测试模板支持 | 记录诊断,不猜测动作 |
|
||||
| native Blender 构建缺任一图层文件 | 在 `ensureNativeRoadLayers()` 失败,不能静默漏画 |
|
||||
|
||||
### 4. 必需测试
|
||||
|
||||
- `npm run test:native-road`:方向箭头的 Polygon、provenance、间距,以及无标签道路
|
||||
不生成路口转向箭头。
|
||||
- `npm run test:road-workbench`:方向箭头开关、选择溯源和概览标签。
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,preview --road-provider native`:日志必须列出
|
||||
`direction_arrows` 与 `turn_arrows`,且不运行 `package`。
|
||||
|
||||
### 5. 错误与正确写法
|
||||
|
||||
错误:把默认 `through` 当作路口 `turn:lanes` 动作,统一写入 `turn_arrows.geojson`。
|
||||
|
||||
正确:道路方向箭头进入 `direction_arrows.geojson`;只有 OSM 明确标注的动作进入
|
||||
`turn_arrows.geojson`。工作台用两个开关呈现,Blender 复用同一现有箭头材质。
|
||||
|
||||
## Native 普通路口圆角
|
||||
|
||||
### 1. 范围与触发条件
|
||||
|
||||
`compile-native-roads.js` 为普通 T / 十字路口生成 `intersection_surface.geojson`
|
||||
和 `sidewalk_surface.geojson` 的路口边界。路口道路面必须在同一 cutback 处结束,不能
|
||||
用未裁剪的道路矩形覆盖圆角边界。
|
||||
|
||||
### 2. 几何契约
|
||||
|
||||
- 相邻道路边缘使用两条支持切线的交点作为二次曲线控制点;采样段数由
|
||||
`JUNCTION_CURVE_SEGMENTS` 统一控制。
|
||||
- 机动车路口边界、人行道内侧路缘和人行道外侧边界都必须使用同一切线圆角规则;外侧
|
||||
不能只对内侧采样点做线性偏移,避免内外曲率不一致。
|
||||
- `boundary_mode` 使用 `rounded-approach-envelope`,无法安全构造的角保持确定性直线
|
||||
回退,并写入 `junction-rounded-corner-fallback` warning。
|
||||
- 已发布的 connector 必须包含在最终边界内,边界退化或 connector 越界时才允许使用
|
||||
`connector-convex-fallback`。
|
||||
|
||||
### 3. 校验与错误矩阵
|
||||
|
||||
| 条件 | 结果 |
|
||||
|---|---|
|
||||
| 支持切线交点有限且曲线不过远 | 生成采样圆角 |
|
||||
| 切线近似平行或交点退化 | 保留该角直线并记录 `junction-rounded-corner-fallback` |
|
||||
| 边界自相交或 connector 越界 | 使用 connector 凸包兜底;仍自相交则不发布路口面 |
|
||||
|
||||
### 4. 必需测试
|
||||
|
||||
- `npm run test:native-road`:普通 T / 十字路口的圆角顶点数、内收方向、内外人行道
|
||||
曲线和 continuation 语义。
|
||||
- `npm run test:road-workbench`:工作台仍能加载 native 路口及人行道图层。
|
||||
- `npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json`。
|
||||
- `npm run road:check -- --config config/areas/nantaizi-lake-innovation-valley.json`。
|
||||
|
||||
### 5. 错误与正确写法
|
||||
|
||||
错误:先对路缘生成圆角,再把外侧边界按每个采样点线性平移;这会导致内外曲率不同,
|
||||
在人行道角落留下不一致的折面。
|
||||
|
||||
正确:对内侧和外侧分别用相同的道路边缘支持切线规则生成曲线,仅在外侧切线退化时
|
||||
使用确定性的偏移回退。
|
||||
|
||||
## Native 道路中心虚线
|
||||
|
||||
### 1. 范围与触发条件
|
||||
|
||||
`node scripts/compile-native-roads.js --config <area>` 为可确认的双向非 service
|
||||
道路段写入 `native-road/layers/center_lines.geojson`。这是原生几何:只能依据
|
||||
canonical OSM 中心线、native 双方向道路模型和 native 路口 cutback 生成,osm2streets
|
||||
的 `center_lines.geojson` 只可作为视觉基准,绝不能作为输入。
|
||||
|
||||
### 2. 调用形式
|
||||
|
||||
```bash
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:workbench -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,preview --road-provider native
|
||||
```
|
||||
|
||||
工作台 `GET /api/state` 通过 `layers.centerLines` 返回该 FeatureCollection;native
|
||||
Blender adapter 必须将 source `center_lines` 映射到既有 `center_lines` material layer。
|
||||
|
||||
### 3. 契约
|
||||
|
||||
- 每一 dash 均为 Polygon,长 `2m`、宽 `0.25m`、确定性间隔 `2m`,使用
|
||||
`native-road-center-line/v1` provenance。
|
||||
- 要素必须含有 `native_id`、`segment_id`、`road_id`、`directional_road_ids`、
|
||||
`osm_way_ids`、`dash_index`、`dash_length_m`、`dash_gap_m` 与 `placement_rule`,以便
|
||||
Workbench 用中文显示“道路中心虚线”并可回溯来源。
|
||||
- 只有同一 `segment_id` 恰有一条 forward 和一条 backward native road 时才生成;单向、
|
||||
`highway=service` 或退化中心线都不能伪造中心线。退化中心线须写
|
||||
`invalid-center-line` diagnostic。
|
||||
- 线段必须先经过 native junction cutback;再与 crosswalk、vehicle stop line 的控制面
|
||||
求冲突,冲突 dash 直接略去。控制标线优先于中心虚线。
|
||||
- `compiled.json.layers.centerLines`、`comparison.json.nativeCenterLineFeatures`、
|
||||
`build-area.js` 的 native input records 与 required-layer 验证必须全部使用
|
||||
`layers/center_lines.geojson`;缺失时在 Blender 启动前失败,不得静默漏画。
|
||||
|
||||
### 4. 校验与错误矩阵
|
||||
|
||||
| 条件 | 结果 |
|
||||
|---|---|
|
||||
| 可确认的双向普通道路段 | 生成 2m / 0.25m 黄虚线,固定 2m gap |
|
||||
| 单向或 `highway=service` 道路 | 不生成中心虚线 |
|
||||
| 中心线不足两点、长度不可用 | `invalid-center-line` diagnostic,不写畸形 Polygon |
|
||||
| dash 进入 junction cutback | trim 后不生成该范围 dash |
|
||||
| dash 与斑马线或停止线相交 | 不生成冲突 dash |
|
||||
| native Blender 输入缺 `center_lines.geojson` | `ensureNativeRoadLayers()` 抛错 |
|
||||
|
||||
### 5. 正常、基础与错误示例
|
||||
|
||||
- 正常:一条有 forward/backward carriageway 的 residential 段在两个方向道路之间生成黄虚线。
|
||||
- 基础:没有双向证据的道路仍可有车道分隔线,但不产生道路中心虚线。
|
||||
- 错误:从 osm2streets 图层复制或裁剪中心线;这会将渲染器缺陷重新变成 native 数据依赖。
|
||||
|
||||
### 6. 必需测试
|
||||
|
||||
- `npm run test:native-road`:断言双向生成、provenance、2m 尺寸/间隔、单向和 service 跳过。
|
||||
- `npm run test:road-workbench`:断言 `centerLines` API、中文开关、选择溯源和概览计数。
|
||||
- `npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json`:输出合法图层,检查中心线与控制标线不重叠。
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,preview --road-provider native`:日志列出 `center_lines`,且不运行 `package`。
|
||||
|
||||
### 7. 错误与正确写法
|
||||
|
||||
错误:只按一条 directional road 生成中心线,或忽略控制标线。
|
||||
|
||||
```js
|
||||
const line = road.centerline;
|
||||
features.push(makeDash(line));
|
||||
```
|
||||
|
||||
正确:先确认成对的双方向道路、做路口裁剪,再排除控制标线冲突。
|
||||
|
||||
```js
|
||||
if (roads.length !== 2 || !forward || !backward || forward.highway === "service") continue;
|
||||
const line = trimLineAtJunctions(forward.centerline, forward.sourceNodeIds, junctionPlans);
|
||||
if (!ringsOverlapControl([ring], [...controls.crosswalks, ...controls.stopLines])) features.push(dash);
|
||||
```
|
||||
|
||||
## Native 道路中心线样式覆写
|
||||
|
||||
### 1. 范围与触发条件
|
||||
|
||||
Road Workbench 选中 `native-road-center-line/v1` 要素后,可为其 `segment_id`
|
||||
保存样式覆写。覆写属于 `native-road-overrides.json`,不是对
|
||||
`center_lines.geojson` 的手工编辑;重新编译必须从覆写重建图层。
|
||||
|
||||
### 2. 调用形式
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "道路中心线:segment:way/123/1",
|
||||
"kind": "center-line-style",
|
||||
"segmentId": "segment:way/123/1",
|
||||
"color": "white",
|
||||
"pattern": "solid"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 契约
|
||||
|
||||
- `color` 只能是 `yellow` 或 `white`;`pattern` 只能是 `dashed` 或 `solid`。
|
||||
目标 `segmentId` 必须属于当前 native road model。
|
||||
- `double: true` 只表示双黄实线,因此只能与 `color: "yellow"`、
|
||||
`pattern: "solid"` 组合。编译器为每段的每个有效实线 mark 写两条相距 0.32m 的
|
||||
平行 Polygon,并记录 `double: true`、`effective_style:
|
||||
"double-yellow-solid"`;不得把它实现为可任意组合的双线开关。
|
||||
- 未覆写段保持黄色虚线(2m dash、2m gap);`solid` 为 0 gap,但相邻的 2m
|
||||
几何块须重叠 `0.04m`,避免投影精度造成可见裂缝。
|
||||
- 每个生成面记录 `color`、`pattern` 和 `effective_style`。Workbench 以这些属性
|
||||
着色;native Blender 将 white centre lines 分派至 `Native Center Line White`,
|
||||
yellow 则继续复用 `Center Line`。
|
||||
- 下拉框变化即暂存覆写,顶部“保存并重新生成”是唯一写盘/重编译动作;不要求用户
|
||||
再点击一个容易遗漏的暂存按钮。
|
||||
|
||||
### 4. 校验与错误矩阵
|
||||
|
||||
| 条件 | 结果 |
|
||||
|---|---|
|
||||
| 合法颜色、图案和当前 segment | 保存后重新生成有效样式 |
|
||||
| 非法颜色/图案或不存在 segment | `validateOverrides()` 拒绝整个请求 |
|
||||
| 实线块触及控制标线 | 该块略去,不以连续性为由穿过控制标线 |
|
||||
| 白色中心线进入 native Blender | 使用白线材质,不改变 legacy 图层材质 |
|
||||
|
||||
### 5. 正常、基础与错误示例
|
||||
|
||||
- 正常:点选任意 dash,选择“白色实线”,保存重编译后整段显示连续白线。
|
||||
- 基础:选择“黄色虚线(默认)”仍是显式覆写,但几何与默认规则一致。
|
||||
- 错误:只在 Workbench 改填充色;Blender/Cesium 会继续显示旧黄色。
|
||||
|
||||
### 6. 必需测试
|
||||
|
||||
- `npm run test:native-road`:合法/非法样式覆写、solid 属性、控制标线避让。
|
||||
- `npm run test:road-workbench`:中文样式面板、下拉框自动暂存和 API payload。
|
||||
- `python3 -m unittest discover blender/tests`:catalog 仍是可导入的纯 Python。
|
||||
- Nantaizi native `blender,cesium,preview` 构建:既有 yellow centre lines 不回归。
|
||||
|
||||
### 7. 错误与正确写法
|
||||
|
||||
错误:实线块仅以零间隔精确相接,且每块使用高对比 outline。
|
||||
|
||||
正确:小幅重叠相邻块,并让 Workbench 实线 stroke 与 fill 同色。
|
||||
|
||||
## Native 道路外缘线样式覆写
|
||||
|
||||
`edge_lines.geojson` 是车行道最外侧边界标线,使用 `native-road-edge-line/v1`
|
||||
provenance。单向道路生成左右两条外缘线;双向道路每个方向只生成远离道路中心的外缘
|
||||
线,双向道路中间分界由 `center_lines.geojson` 负责。默认是白色实线,必须带
|
||||
`road_id`、`side`(`left` / `right`)、`osm_way_ids`、`color`、`pattern` 与
|
||||
`effective_style`。
|
||||
|
||||
区域配置中的 `nativeRoad.edgeLines` 默认是 `false`;因此 native Blender/Cesium 默认不
|
||||
生成或显示该层。只有明确设置为 `true` 才会发布 `edge_lines.geojson` 并交给下游消费。
|
||||
|
||||
Road Workbench 点选外缘线后以中文显示其方向侧边,可暂存以下 area-local 覆写:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "道路外缘线:road:way/123:forward:left",
|
||||
"kind": "edge-line-style",
|
||||
"roadId": "road:way/123:forward",
|
||||
"side": "left",
|
||||
"color": "white",
|
||||
"pattern": "solid"
|
||||
}
|
||||
```
|
||||
|
||||
`roadId` 必须是当前 directional native road 的精确 ID,`side` 必须为 `left` 或 `right`。
|
||||
实线使用连续 offset line;虚线使用确定性
|
||||
2m mark / 2m gap。工作台的暂存与“保存并重新生成”是唯一的写盘路径,不能只改浏览器填充色;
|
||||
native Blender 将该层映射到既有 `lane_separators` material layer。
|
||||
|
||||
## Native 控制标线
|
||||
|
||||
### 1. 范围与触发条件
|
||||
|
||||
`node scripts/compile-native-roads.js --config <area>` 为 native road provider
|
||||
生成 `crosswalks.geojson` 和 `vehicle_stop_lines.geojson`。这是原生道路的独立
|
||||
产物,禁止读取 osm2streets 的渲染图层作为几何输入。
|
||||
|
||||
### 2. 调用形式
|
||||
|
||||
```bash
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:workbench -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
```
|
||||
|
||||
工作台 `GET /api/state` 通过 `layers.crosswalks` 和
|
||||
`layers.vehicleStopLines` 返回两个 FeatureCollection。
|
||||
|
||||
### 3. 契约
|
||||
|
||||
- 只有 `highway=crossing` 且 `crossing:markings` 不是 `no`、`none` 或
|
||||
`unmarked` 的 OSM 节点可以生成斑马线。每个安全匹配点生成六条 stripe,带
|
||||
`crossing_node_id`、`road_id`、`lane_id`、`osm_way_ids`、`direction`、
|
||||
`placement_method`、`junction_inset_m` 和 `native-road-crosswalk/v1` provenance。
|
||||
- 停止线还必须对应一个启用的 native arrival endpoint,且过街节点位于该进口到
|
||||
路口的安全距离内;其 provenance 是 `native-road-stop-line/v1`。不能确认进口
|
||||
时保留斑马线并写 `crossing-no-safe-stop-line` diagnostic,不得猜测一条线。停止线
|
||||
必须复用斑马线的 `junction_inset_m`,保持与斑马线的上游间距。
|
||||
- 有安全进口且存在普通路口 plan 时,斑马线中心推进到 cutback 内约 1.5 米;单次
|
||||
最大推进 4 米。`junction_inset_m` 记录实际推进量,避免远离路口的 crossing 被过度
|
||||
移动。
|
||||
- `catalog.NATIVE_ROAD_LAYERS` 将两个源层映射到现有的 `crosswalks` 和
|
||||
`vehicle_stop_lines` 材质层。不得把它们加入 legacy `SCENE_LAYERS`。
|
||||
- 控制标线优先于箭头:默认直行箭头与其相交时跳过;路口转向箭头依次尝试在距路口
|
||||
6、10、14、18、22 米处放置,均冲突时记录 `turn-arrow-control-conflict`。
|
||||
|
||||
### 4. 校验与错误矩阵
|
||||
|
||||
| 条件 | 结果 |
|
||||
|---|---|
|
||||
| 标记过街没有可匹配 native lane | `crossing-no-native-lane`,不生成任何控制标线 |
|
||||
| 有横道但没有安全进口方向 | 生成斑马线,记录 `crossing-no-safe-stop-line`,不生成停止线 |
|
||||
| 箭头与任一控制标线相交 | 直行箭头跳过;转向箭头后移或记录冲突 diagnostic |
|
||||
| native Blender 输入缺任一控制图层 | `ensureNativeRoadLayers()` 在启动 Blender 前失败 |
|
||||
|
||||
### 5. 正常、基础与错误示例
|
||||
|
||||
- 正常:一个靠近路口的 marked crossing 生成 6 条斑马线和 1 条进口停止线。
|
||||
- 基础:一条孤立的 marked crossing 可以生成斑马线,但不能凭邻近道路方向臆造停止线。
|
||||
- 错误:先生成箭头再叠加停止线,导致两者重叠;控制标线是道路控制语义,必须优先。
|
||||
|
||||
### 6. 必需测试
|
||||
|
||||
- `npm run test:native-road`:断言 marked / unmarked / 无 native lane 的输出,停止线的
|
||||
provenance、斑马线与停止线共享 `junction_inset_m`,以及箭头遇控制标线时后移。
|
||||
- `npm run test:road-workbench`:断言 controls 开关、两条 API layer 和中文选中溯源。
|
||||
- `npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json`:核查两层
|
||||
feature count 及每条停止线都是 native arrival direction。
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,preview --road-provider native`:不运行 `package`。
|
||||
|
||||
### 7. 错误与正确写法
|
||||
|
||||
错误:以最近任意方向车道和固定正向偏移生成停止线。
|
||||
|
||||
```js
|
||||
const stopCenter = offsetByMeters(nearestLane.point, nearestLane.axis, 2.7);
|
||||
```
|
||||
|
||||
正确:先确认该方向的终点是一个已启用的路口 arrival,再在人行横道的上游生成停止线。
|
||||
|
||||
```js
|
||||
const approach = candidates.find((item) => arrivalEndpointIds.has(`endpoint:${item.road.id}:end`));
|
||||
const stopCenter = offsetByMeters(laneCenterAtCrossing, approach.placement.axis, -2.7);
|
||||
```
|
||||
|
||||
## 斑马线与停止线来源
|
||||
|
||||
### 1. 范围与触发条件
|
||||
@@ -607,7 +1037,7 @@ const gate = classifyAreaQuality(result);
|
||||
### 1. Scope / Trigger
|
||||
|
||||
Stage manifest 是区域构建阶段或独立验证通过后的机器可读产物契约。它覆盖预检记录与完整区域链:
|
||||
`preflight`、`intermediates`、`reimport`、`blender`、`cesium`、`preview` 和 `compress`。
|
||||
`preflight`、`intermediates`、`reimport`、`blender`、`cesium`、`compress`、`package` 和 `preview`。
|
||||
它用于诊断产物是否存在、是否 stale、体量是否超预算,以及后续 `check:area` /
|
||||
增量构建判断。
|
||||
|
||||
@@ -629,6 +1059,7 @@ Manifest 路径固定:
|
||||
<areaDir>/_pipeline/stages/cesium.manifest.json
|
||||
<areaDir>/_pipeline/stages/preview.manifest.json
|
||||
<areaDir>/_pipeline/stages/compress.manifest.json
|
||||
<areaDir>/_pipeline/stages/package.manifest.json
|
||||
```
|
||||
|
||||
代码入口:
|
||||
@@ -721,9 +1152,18 @@ File records use this shape:
|
||||
- `summary.glb.extensionsUsed`
|
||||
- `summary.budget`:effective limits、usage 和 violations;warnings 来自同一个预算评估
|
||||
|
||||
`cesium` 会调用 preview 生成函数,但 preview HTML / route / vehicle model 的 freshness
|
||||
所有权属于独立 `preview` manifest。否则单跑 `--stages preview` 会把 Cesium manifest
|
||||
错误判 stale。
|
||||
`cesium` 只拥有 staging 内的静态模型与 manifest;完成后必须依次由 `compress`、`package`
|
||||
交付。preview HTML / route / vehicle model 的 freshness 所有权属于独立 `preview` manifest。
|
||||
|
||||
`compress` manifest 不记录最终文件 records:它们位于可被 `package` rename 的 staging 目录。
|
||||
`package` manifest 是唯一记录 `package/manifest.json`、主 GLB 与 package 目录的 stage manifest,
|
||||
避免发布后 compress manifest 因路径迁移而立刻 stale。
|
||||
|
||||
`package` manifest:
|
||||
|
||||
- `inputs.stagingManifest`:发布后的同一 manifest 文件记录
|
||||
- `outputs.packageDir`、`outputs.manifest`、`outputs.primaryGlb`
|
||||
- `summary.assets` 与 `summary.packageDir`
|
||||
|
||||
### Preview Assembly Boundary
|
||||
|
||||
@@ -758,14 +1198,11 @@ runtime 与 HTML,并写 preview manifest。预览内容实现不得回流到
|
||||
|
||||
`compress` manifest:
|
||||
|
||||
- `inputs.glb`
|
||||
- `inputs.metadata`
|
||||
- `inputs.cesiumPreview`
|
||||
- `outputs.compressedGlb`
|
||||
- `outputs.compressedMetadata`
|
||||
- `outputs.compressedCesiumPreview`
|
||||
- `outputs.glb`
|
||||
- `outputs.metadata`
|
||||
- `outputs.cesiumPreview`
|
||||
- `summary.sourceGlb`
|
||||
- `summary.compressedGlb`
|
||||
- `summary.glb`
|
||||
- `summary.options`
|
||||
- `summary.compressionRatio`
|
||||
- `summary.savedBytes`
|
||||
@@ -791,8 +1228,8 @@ Manifest files are written atomically via `*.tmp` then `renameSync`.
|
||||
- Good: `--stages intermediates` 成功后写 `intermediates.manifest.json`,诊断显示
|
||||
`ok intermediates`。
|
||||
- Good: `--stages blender` 成功后写 `blender.manifest.json`,诊断显示 `ok blender`。
|
||||
- Good: `--stages cesium` 成功后写 `cesium.manifest.json` 和 `preview.manifest.json`。
|
||||
- Good: `--stages preview` 只更新 preview manifest,不让 cesium manifest stale。
|
||||
- Good: `--stages cesium,compress,package,preview` 成功后依序写四份各自拥有的 manifest。
|
||||
- Good: `--stages preview` 只更新 preview manifest,不让 package manifest stale。
|
||||
- Good: `--stages compress` 成功后写 `compress.manifest.json`,summary 记录压缩比和节省字节。
|
||||
- Base: 旧产物没有当前 ownership 路径的 manifest,诊断显示 expected manifest missing,
|
||||
提示重跑对应阶段。
|
||||
@@ -811,6 +1248,7 @@ Manifest files are written atomically via `*.tmp` then `renameSync`.
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages intermediates`
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender`
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages cesium`
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages compress,package,preview`
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages preview`
|
||||
- `npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages compress`
|
||||
- `npm run diagnose:area -- --config config/areas/nantaizi-lake-innovation-valley.json`
|
||||
@@ -926,18 +1364,19 @@ gpkg: path.resolve(outputOverrides.gpkg || path.join(areaDir, `${fileStem}.gpkg`
|
||||
|
||||
| 阶段 | 做什么 | 读 | 写 |
|
||||
|---|---|---|---|
|
||||
| `intermediates` | OSM → osm2streets GeoJSON → GeoPackage → QGIS 工程 + 预览图 | `.osm` | `osm2streets_web_out/`、`.gpkg`、`.qgz`、`-preview.png` |
|
||||
| `intermediates` |(显式 legacy)OSM → osm2streets GeoJSON → GeoPackage → QGIS 工程 + 预览图 | `.osm` | `osm2streets_web_out/`、`.gpkg`、`.qgz`、`-preview.png` |
|
||||
| `reimport` | GeoPackage → GeoJSON(**反向**) | `.gpkg` | `osm2streets_web_out/` |
|
||||
| `blender` | OSM + GeoJSON → 场景 | `.osm`、`osm2streets_web_out/` | `.blend`、`.png` |
|
||||
| `cesium` | 场景 → GLB + 元数据 + 预览页 | `.blend` | `.glb`、`.json`、预览 HTML 及其静态资源 |
|
||||
| `preview` | 只补生成预览页 | `.glb`、`.json` | 预览 HTML 及其静态资源 |
|
||||
| `compress` | 生成并列压缩 Cesium 产物 | `.glb`、`.json`、默认预览 HTML | `-compressed-webp*.glb/json/html` |
|
||||
| `blender` | 默认 OSM → native road compiler → Blender 场景;legacy provider 时读取 GeoJSON | `.osm`、`native-road/`(legacy 为 `osm2streets_web_out/`) | `.blend`、`.png` |
|
||||
| `cesium` | 场景 → static staging GLB + manifest | `.blend` | `_pipeline/package-staging/models/*.glb`、staged manifest;动态 GLB 到 `_preview/` |
|
||||
| `compress` | 压缩 Cesium staging 主模型 | staged `.glb`、manifest | 压缩 staged `.glb`、manifest |
|
||||
| `package` | 校验并原子发布静态资产包 | staging manifest 与 models | `package/manifest.json`、`package/models/*.glb` |
|
||||
| `preview` | 用已发布 manifest 生成验证预览 | `package/manifest.json`、动态输入 | 预览 HTML、静态 runtime、`_preview/` 动态资源 |
|
||||
|
||||
调度是顶层的阶段 `if`(`build-area.js` 开头),顺序固定,**阶段之间不传内存状态,
|
||||
只通过磁盘产物耦合**。这就是单跑某个阶段能work 的原因。
|
||||
|
||||
`cesium` 阶段结束时会直接调 `writeCesiumPreview(area)`(`build-area.js:285`),所以
|
||||
`preview` 只在"已有 GLB、只想重生成 HTML"时才需要单独跑。
|
||||
完整构建中 `preview` 在 `package` 后运行。单跑 `preview` 用于已发布 package 但想重生成
|
||||
验证 UI 或 `_preview/` 动态资源的情况。
|
||||
|
||||
### 别名
|
||||
|
||||
@@ -947,16 +1386,15 @@ gpkg: path.resolve(outputOverrides.gpkg || path.join(areaDir, `${fileStem}.gpkg`
|
||||
|
||||
未知阶段名**抛错并列出合法值**(`:181`),不静默忽略。
|
||||
|
||||
### `all` 不含 `reimport`
|
||||
### `all` 是 native-only 完整构建
|
||||
|
||||
```js
|
||||
// 'reimport' is deliberately absent from 'all': it is a recovery step for
|
||||
// hand-edited GeoPackages, never part of a full build. build-area.js:159-160
|
||||
all: ["intermediates", "blender", "cesium"],
|
||||
// Legacy intermediates/reimport are deliberately absent from 'all'.
|
||||
all: ["blender", "cesium", "compress", "package", "preview"],
|
||||
```
|
||||
|
||||
`preview` 同样不在 `all` 里——`cesium` 已经包含它。`compress` 也不在 `all` 里——
|
||||
它生成的是替代压缩产物,不是 baseline GLB。
|
||||
`compress`、`package`、`preview` 均在 `all` 中,确保完整构建以压缩后的可复用静态 package
|
||||
以及可运行的验证预览交付。
|
||||
|
||||
### `intermediates` 与 `reimport` 互斥
|
||||
|
||||
@@ -966,9 +1404,8 @@ all: ["intermediates", "blender", "cesium"],
|
||||
|
||||
这是**显式拒绝而不是警告**——两者同时开,无论谁先跑,另一个的工作都白做。
|
||||
|
||||
`normalizeAreaConfig` 里 `stages.reimport`、`stages.preview` 和 `stages.compress` 硬编码为 `false`,
|
||||
**不能从配置文件打开**,只能靠 `--stages` 显式请求。
|
||||
恢复动作、补丁动作和替代产物动作都不该被一份配置文件变成默认行为。
|
||||
`normalizeAreaConfig` 里只有 `stages.reimport` 硬编码为 `false`,只能靠 `--stages` 显式请求;
|
||||
`stages.compress`、`stages.package` 和 `stages.preview` 为默认 `true`。
|
||||
|
||||
---
|
||||
|
||||
@@ -1005,7 +1442,7 @@ parity 校验依赖 stage 的 stdout 标记来判断阶段是否跑到(如 `SC
|
||||
| 在阶段函数里现拼输出路径 | 路径规则出现第二份定义 |
|
||||
| 低层脚本直接读 `config/areas/*.json` | 打破两层配置边界 |
|
||||
| 布尔配置用 `\|\|` 兜底 | `false` 被翻转成默认值 |
|
||||
| 让 `reimport` / `preview` 能从配置文件默认开启 | 恢复动作变成常规行为 |
|
||||
| 让 `reimport` 能从配置文件默认开启 | 恢复动作变成常规行为 |
|
||||
| 新阶段忘了 `ensureFile` 前置校验 | 单跑时报底层堆栈而非人话 |
|
||||
| 改 stage 的 stdout 标记 | 静默破坏 parity 契约 |
|
||||
| 顺手把多份 `parseArgs` 合并 | 扩大 diff,且独立入口的独立性是刻意的 |
|
||||
|
||||
@@ -120,6 +120,56 @@ SCENE_LAYERS.forEach((layer, index) => {
|
||||
|
||||
## Blender 调用
|
||||
|
||||
### macOS Blender 4.5 的 Metal 启动兼容
|
||||
|
||||
#### 1. Scope / Trigger
|
||||
|
||||
`export_cesium.py` 在 macOS 的 Blender 4.5.12 后台启动时,可能在 Python 脚本加载前的 Metal 扩展探测中崩溃;这不是场景或道路数据错误。
|
||||
|
||||
#### 2. Signatures
|
||||
|
||||
Cesium 阶段的调用参数必须包含:
|
||||
|
||||
```text
|
||||
--background --factory-startup --debug-gpu-force-workarounds --python blender/export_cesium.py -- ...
|
||||
```
|
||||
|
||||
#### 3. Contracts
|
||||
|
||||
`--debug-gpu-force-workarounds` 是 Blender 的官方 CLI 参数。它只约束导出进程的 GPU 扩展探测,不改变 `.blend`、GeoJSON 或导出脚本的输入输出契约。
|
||||
|
||||
#### 4. Validation & Error Matrix
|
||||
|
||||
| 情况 | 结果 |
|
||||
|---|---|
|
||||
| 缺少该参数且启动时崩在 Metal 初始化 | 不应归因于道路数据;补齐参数后重跑 Cesium 阶段 |
|
||||
| 参数存在且 `CESIUM_EXPORT_DONE` / stage manifest 写出 | 继续 GLB digest 与预览验证 |
|
||||
|
||||
#### 5. Good / Base / Bad Cases
|
||||
|
||||
- Good: 保留 `--factory-startup`,并在 Cesium 导出加入 workaround。
|
||||
- Base: Blender 场景阶段未受影响时,不额外改变其启动参数。
|
||||
- Bad: 为绕过启动崩溃删除 `--factory-startup`,这会重新引入本机偏好和 addon 的不确定性。
|
||||
|
||||
#### 6. Tests Required
|
||||
|
||||
- `npm run test:build-stages` 断言导出参数仍包含 workaround。
|
||||
- 对目标区域运行 `--stages blender,cesium,preview`,并用 `glb-digest.js` 解析输出。
|
||||
|
||||
#### 7. Wrong vs Correct
|
||||
|
||||
Wrong:
|
||||
|
||||
```text
|
||||
--background --python blender/export_cesium.py
|
||||
```
|
||||
|
||||
Correct:
|
||||
|
||||
```text
|
||||
--background --factory-startup --debug-gpu-force-workarounds --python blender/export_cesium.py
|
||||
```
|
||||
|
||||
### 两种调用姿势
|
||||
|
||||
| 阶段 | 参数 | 出处 |
|
||||
|
||||
@@ -30,7 +30,7 @@ config/areas/<id>.json
|
||||
▼ build-area.js — 阶段调度
|
||||
_pipeline/osm2streets-qgis.config.json (派生配置)
|
||||
│
|
||||
├─[intermediates]─▶ build-osm2streets-qgis.js
|
||||
├─[intermediates]─▶ build-osm2streets-qgis.js(显式 legacy/reference)
|
||||
│ osm2streets-js-node 解析 .osm
|
||||
│ → splitLayers() 拆成九个图层
|
||||
│ → normalize-lane-arrows.py(QGIS Python)
|
||||
@@ -45,23 +45,27 @@ config/areas/<id>.json
|
||||
│ → 重建 scene.geojson + scene_style.json
|
||||
│ → _pipeline/stages/reimport.manifest.json
|
||||
│
|
||||
├─[blender]───────▶ Blender + blender/generate_scene.py
|
||||
│ 读 .osm + osm2streets_web_out/
|
||||
├─[blender]───────▶ compile-native-roads.js + Blender + blender/generate_scene.py
|
||||
│ 默认读 .osm + native-road/
|
||||
│ → <id>.blend + <id>.png
|
||||
│ → _pipeline/stages/blender.manifest.json
|
||||
│
|
||||
├─[cesium]────────▶ Blender + blender/export_cesium.py
|
||||
│ 读 .blend → <id>.glb + <id>.json
|
||||
│ → 并自动执行 preview
|
||||
│ 读 .blend → _pipeline/package-staging/models/<id>.glb
|
||||
│ + staged manifest,动态预览 GLB 写入 _preview/
|
||||
│ → _pipeline/stages/cesium.manifest.json
|
||||
│
|
||||
├─[preview]───────▶ 生成 <id>-cesium-preview.html
|
||||
│ + 拷贝 lib/cesium-preview.{js,css}
|
||||
│ + 车辆巡航路线与模型
|
||||
│ → _pipeline/stages/preview.manifest.json
|
||||
├─[compress]──────▶ 压缩 staging 内主 GLB 并更新 staged manifest
|
||||
│ → _pipeline/stages/compress.manifest.json
|
||||
│
|
||||
└─[compress]──────▶ 生成并列压缩 GLB / metadata / preview
|
||||
→ _pipeline/stages/compress.manifest.json
|
||||
├─[package]───────▶ 校验 manifest 与全部静态模型,原子发布 package/
|
||||
│ → package/manifest.json + package/models/*.glb
|
||||
│ → _pipeline/stages/package.manifest.json
|
||||
│
|
||||
└─[preview]───────▶ 生成 <id>-cesium-preview.html(native 可无车辆路线)
|
||||
│ + 拷贝 lib/cesium-preview.{js,css}
|
||||
+ _preview/ 车辆巡航路线、模型、动态信号
|
||||
│ → _pipeline/stages/preview.manifest.json
|
||||
```
|
||||
|
||||
**阶段之间只通过磁盘产物耦合**,不传内存状态。这是单跑任意阶段能work 的前提。
|
||||
@@ -87,7 +91,7 @@ config/areas/<id>.json
|
||||
|
||||
| 文件 | 行数 | 职责 |
|
||||
|---|---|---|
|
||||
| `build-area.js` | 约 530 | 主入口:区域配置读取、阶段调度、preview 文件写入和 stage manifest ownership |
|
||||
| `build-area.js` | 主入口:区域配置读取、阶段调度、package 发布、preview 文件写入和 stage manifest ownership |
|
||||
| `diagnose-area.js` | 36 | 快速诊断入口:调用共享 area diagnostics 并打印完整报告 |
|
||||
| `check-area.js` | 74 | 区域质量门入口:调用共享 area diagnostics,输出 PASS/FAIL 并设置退出码 |
|
||||
| `lib/area-diagnostics.js` | 776 | 共享区域诊断事实源:OSM、产物、metadata、stage manifest、GLB digest 和质量门分类 |
|
||||
@@ -99,6 +103,7 @@ config/areas/<id>.json
|
||||
| `lib/vehicle-route.js` | 约 180 | 从 OSM 提取确定性预览巡航路线 |
|
||||
| `lib/vehicle-model.js` | 约 150 | 生成内嵌 buffer 的预览车辆 glTF |
|
||||
| `lib/area-preview.js` | 约 110 | 复制 preview runtime、生成 HTML 与转义配置注入 |
|
||||
| `lib/package-contract.js` | package manifest 校验、相对 URI 与 SHA-256 完整性记录 |
|
||||
| `lib/cesium-preview.js` / `.css` | 672 / 230 | 预览页运行时,见 [../preview/](../preview/index.md) |
|
||||
| `normalize-lane-arrows.py` | 182 | 合并 osm2streets 的三角网箭头(跑在 QGIS Python 里) |
|
||||
| `parity.js` | 270 | 产物一致性校验驱动 |
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
## 定位
|
||||
|
||||
预览层是**验证性的,不是产物本身**。它加载 `cesium` 阶段导出的 `.glb` + `.json`,
|
||||
用来确认资产在真实 Cesium 里的样子。改这一层**不会**改变 Blender/GLB 主资产。
|
||||
预览层是**验证性的,不是产物本身**。它加载 `package/manifest.json`,用来确认已发布资产
|
||||
在真实 Cesium 里的样子。改这一层**不会**改变 package 内的 Blender/GLB 静态资产。
|
||||
|
||||
车辆巡航同理——README 里写明它是"用于验证高精度巡航可用性的预览层功能"。
|
||||
|
||||
@@ -53,9 +53,14 @@ const config = window.OSM_ASSET_PREVIEW_CONFIG || {}; // :4
|
||||
**加一个新的可配置项**:`cesiumPreviewHtml()` 里加进注入的 JSON,JS 侧从 `config` 读,
|
||||
两边都要动。
|
||||
|
||||
`build-area.js` 只保留 GLB / metadata 依赖检查、写入顺序和 preview manifest ownership;
|
||||
`glbName` 与 `metadataName` 都是 `package/manifest.json`。浏览器读取 package manifest 后,
|
||||
每个 `assets[*].uri` 必须相对**manifest 文件**解析,绝不能相对 preview HTML 解析;否则将
|
||||
错误请求 `outputs/<area>/models/...` 而不是 `outputs/<area>/package/models/...`。
|
||||
|
||||
`build-area.js` 只保留已发布 package / 动态输入的依赖检查、写入顺序和 preview manifest ownership;
|
||||
不要把 HTML 模板、runtime copy 或转义实现移回阶段调度器。路线 JSON 与车辆 glTF 分别由
|
||||
`vehicle-route.js` 和 `vehicle-model.js` 生成,二者都是不启动外部工具的 Node 模块。
|
||||
交通信号灯 runtime 则从 package manifest 的 `runtime` 读取;预览只负责驱动其状态,不拥有这些文件。
|
||||
|
||||
---
|
||||
|
||||
@@ -185,6 +190,21 @@ let baseStatus = "";
|
||||
这会让用户误判材质和模型质量。若确实需要性能模式,应做成显式开关,而不是默认牺牲
|
||||
预览清晰度。
|
||||
|
||||
### 车辆事件仅属于预览会话
|
||||
|
||||
点击车辆的信息卡可在 `normal`、`breakdown`、`accident` 三态间切换。这是验证交互,
|
||||
不得写入 `package/`、路线 JSON 或 OSM。状态必须附着在 `addCruiseVehicle()` 返回的记录上;
|
||||
`createTrafficAwarePositions()` 只在状态为 `normal` 时推进已有的 route distance,恢复正常从
|
||||
当前停点继续。
|
||||
|
||||
- `breakdown`:黄色扳手 label,路线保留原色。
|
||||
- `accident`:红色警示 label,路线设为红色。
|
||||
- `normal`:隐藏 label,恢复原路线颜色。
|
||||
|
||||
车辆实体以 `properties.vehicleId` 标识;点击拾取必须只处理此属性,不能把静态模型、路线或
|
||||
信号灯当作车辆。Cesium InfoBox 在本预览中关闭,信息卡必须使用 HTML/CSS,并把新增 DOM 句柄
|
||||
集中在 `cesium-preview.js` 顶部。
|
||||
|
||||
### 单资产 vs 多资产的开关
|
||||
|
||||
```js
|
||||
|
||||
@@ -31,7 +31,7 @@ classifyConnection(incomingEdge, outgoingEdge) =>
|
||||
`maneuvers`、`edgeIds`、`laneSegments` 与 `connectors`。道路区间来自匹配的 Driving lane polygon 中轴。
|
||||
- 路线拓扑以 `network.json` 的 internal road 和 intersection 为准;禁止把整个 OSM way 直接当作一条不可分割 edge。
|
||||
- connector 必须绑定同一个 internal intersection,并位于对应 `intersection_surface.geojson` 内或允许的边界容差内;越界时拒绝候选路线。
|
||||
- preview 必须将 `lane_polygons.geojson`、`network.json` 和 `intersection_surface.geojson` 作为强制输入;缺失或无效时在写产物前失败。
|
||||
- legacy osm2streets preview 必须将 `lane_polygons.geojson`、`network.json` 和 `intersection_surface.geojson` 作为强制输入;native preview 不读取这些文件,路线缺失时保留可用预览并省略车辆巡航。
|
||||
- route 经纬度由 Cesium 按 WGS84 直接放置;最终道路 GLB 必须由 WGS84 ECEF→ENU
|
||||
`Projector` 生成。禁止以固定米/度近似投影道路,否则即使 route 与 lane polygon
|
||||
完全一致,最终画面仍会随离锚点距离产生横向偏移。
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,70 @@
|
||||
# Design
|
||||
|
||||
## Authority And Boundaries
|
||||
|
||||
The native compiler remains the only road authority. The existing traffic-signal runtime remains the
|
||||
only signal authority. The new feature adds a route/simulation layer beside those artifacts; it does
|
||||
not alter signal geometry, phase grouping, or Blender static assets.
|
||||
|
||||
```text
|
||||
native-road/compiled.json + native-road/layers/*
|
||||
-> native route compiler
|
||||
-> _preview/<area>-traffic-simulation.json
|
||||
-> Cesium preview runtime
|
||||
|
||||
package/runtime/traffic-signals.json
|
||||
-> signal phase clock + stop events
|
||||
```
|
||||
|
||||
The descriptor is intentionally package-adjacent, not embedded in the GLB. A consumer can copy the
|
||||
package and descriptor to another platform and reproduce the simulation without running Node.
|
||||
|
||||
## Descriptor Contract
|
||||
|
||||
Schema: `native-preview-traffic-simulation/v1`.
|
||||
|
||||
Required top-level fields:
|
||||
|
||||
- `areaId`, `coordinateSystem` (`WGS84` route coordinates, `ENU` model placement), `generatedAt`
|
||||
- `source` with relative artifact references and SHA-256 records
|
||||
- `settings` with speed, acceleration, deceleration, reaction time, vehicle length, and minimum gap
|
||||
- `routes[]` with ordered WGS84 coordinates, cumulative distances, connector/maneuver IDs, and
|
||||
`stops[]`
|
||||
- `signals[]` copied by stable `signal_uid` from the runtime contract, including phase group and
|
||||
stop-line association; no regenerated pose fields
|
||||
- `migration` describing schema version, coordinate conversion, update-loop expectations, and
|
||||
compatibility notes
|
||||
|
||||
The descriptor is deterministic for the same OSM, overrides, native compiler version, and settings.
|
||||
|
||||
## Route Construction
|
||||
|
||||
Use native directional roads and published connector geometry. Build a small number of deterministic
|
||||
closed demonstration routes that cross real connectors and retain lane/road IDs. Route generation
|
||||
must reject routes with missing geometry or disabled connectors and write structured diagnostics.
|
||||
Native stop lines are matched by `road_id`/`lane_id` where available, with a bounded geometric
|
||||
fallback recorded in the stop record. No fixed-width or legacy polygon fallback is allowed.
|
||||
|
||||
## Browser Simulation
|
||||
|
||||
Keep the existing signal phase functions and `signalData` payload. Replace the current per-vehicle
|
||||
independent distance advance with a shared simulation state:
|
||||
|
||||
- each vehicle has route distance, speed, desired speed, status, and active stop reason;
|
||||
- signal stop constraints are evaluated before the native stop line for red/yellow phases;
|
||||
- leader constraints are evaluated on the same route and wrap around the loop;
|
||||
- acceleration/deceleration clamps advance toward the minimum of desired speed, signal limit, and
|
||||
leader-safe speed;
|
||||
- vehicle position and orientation remain Cesium callback properties, with no per-frame allocation
|
||||
of heavyweight Cesium objects;
|
||||
- incident-card behavior remains local to each vehicle and must not mutate the descriptor or package.
|
||||
|
||||
Diagnostics expose route count, signal count, stopped vehicles, and queue length in the existing
|
||||
preview diagnostics panel.
|
||||
|
||||
## Migration And Rollback
|
||||
|
||||
Document the descriptor, runtime signal schema, coordinate conversion, and a reference update loop in
|
||||
`docs/native-preview-traffic-simulation.md`. Include a JSON example and a consumer checklist. Legacy
|
||||
preview route files remain readable only through the existing legacy provider path; native generation
|
||||
must not depend on them. Removing the new descriptor restores the existing no-cruise native preview.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,37 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Add a native route compiler module that consumes `native-road/compiled.json` and native layers,
|
||||
emits `native-preview-traffic-simulation/v1`, and records source hashes and migration metadata.
|
||||
2. Wire native preview generation to write the descriptor and keep legacy osm2streets route
|
||||
generation unchanged.
|
||||
3. Extend the browser preview with shared deterministic vehicle state, signal stop constraints,
|
||||
leader following, queue diagnostics, and graceful empty-route behavior.
|
||||
4. Preserve the existing signal phase, `signal_uid`, pose, dynamic lens, countdown, disabled-signal,
|
||||
and incident-card contracts; add focused regression tests for each boundary.
|
||||
5. Add `docs/native-preview-traffic-simulation.md` with schema, example, coordinate rules, update
|
||||
loop, migration checklist, and rollback notes.
|
||||
6. Run focused Node tests, native compile/check, a native build with no legacy directory, and browser
|
||||
preview validation in the available Blender/Cesium environment.
|
||||
|
||||
## Validation Commands
|
||||
|
||||
```bash
|
||||
node --check scripts/lib/native-preview-traffic-simulation.js
|
||||
node --check scripts/build-area.js
|
||||
npm run test:native-road
|
||||
npm run test:traffic-signals
|
||||
npm run test:preview-assets
|
||||
npm run test:native-preview-traffic
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:check -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,compress,package,preview
|
||||
```
|
||||
|
||||
## Risk Controls
|
||||
|
||||
- Do not change `scripts/lib/traffic-signals.js` geometry or phase semantics unless a test proves a
|
||||
migration bug; prefer adapter code.
|
||||
- Do not add route data to the static package manifest; it belongs beside `_preview/` and is optional.
|
||||
- If native route generation produces no valid route, emit a valid descriptor with diagnostics and
|
||||
keep preview usable.
|
||||
- Keep all route and simulation math unit-testable without Cesium.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Native preview traffic simulation and migration contract
|
||||
|
||||
## Goal
|
||||
|
||||
Extend the native road compiler preview with a deterministic, portable vehicle traffic demonstration that consumes native road topology and the already-validated traffic-signal runtime. The result must be useful as a visual validation tool and as a migration reference for Cesium, Blender, or another platform consuming the generated area package.
|
||||
|
||||
## Confirmed Baseline
|
||||
|
||||
- Native road compilation is the production authority; QGIS/osm2streets is not a runtime input.
|
||||
- The previous traffic-signal work is the source of truth and must remain compatible: deterministic `signal_uid`, `phase_group`, `mast_heading_deg`, `face_heading_deg`, stop-line anchors, shared `pose.*`, dynamic lens/countdown assets, and disabled-signal filtering.
|
||||
- Existing browser preview already has signal phase timing, signal visualization, route stop matching, vehicle incident cards, and a missing-route fallback. Native preview currently omits the route.
|
||||
- Native road output contains compiled roads/connectors and `vehicle_stop_lines.geojson`; the native package contains `runtime/traffic-signals.json`.
|
||||
- The feature is preview-level deterministic behavior, not a legal navigation or microscopic traffic simulator.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: Generate a native route artifact from `native-road/compiled.json` and native layer geometry; it must not read `osm2streets_web_out`, QGIS files, or legacy route files.
|
||||
- R2: Preserve and consume the existing traffic-signal contract without deriving a second signal layout in the browser.
|
||||
- R3: Vehicles must stop before a red/yellow signal at the native stop line, resume on green, and expose the active stop reason in preview diagnostics.
|
||||
- R4: Multiple vehicles on the same route must maintain a configurable minimum gap; a stopped front vehicle must cause following vehicles to decelerate and queue rather than overlap it.
|
||||
- R5: Route, signal, vehicle, stop-event, and following-distance data must be written as versioned, package-adjacent JSON with stable ENU/WGS84 and migration metadata. Another platform must be able to consume the artifact without executing the Node compiler.
|
||||
- R6: The browser preview must degrade gracefully when there are no valid routes or signals; the primary scene remains usable and diagnostics explain the missing optional capability.
|
||||
- R7: Documentation must describe the data flow, schemas, coordinate conventions, signal identity, stop-line association, update loop, and migration/rollback guidance.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Native build emits a versioned route/traffic-simulation descriptor under the area output and preview loads it without any legacy directory present.
|
||||
- [ ] At least one route crosses a native connector and contains a native stop-line association.
|
||||
- [ ] A red/yellow phase visibly stops a vehicle before the stop line; green releases it.
|
||||
- [ ] Two or more vehicles never overlap and maintain the configured minimum gap while cruising and while queued at a signal.
|
||||
- [ ] Disabled native signals are absent from runtime control and do not create vehicle stops.
|
||||
- [ ] Preview diagnostics report route count, signal count, stopped vehicles, queue length, and the active simulation descriptor version.
|
||||
- [ ] Migration documentation and focused tests allow another consumer to reproduce route, signal, stopping, and gap behavior from generated assets alone.
|
||||
- [ ] Existing traffic-signal tests and native road tests remain green; legacy provider behavior is unchanged.
|
||||
|
||||
## Decided Scope
|
||||
|
||||
- Simulation fidelity: MVP is a deterministic preview simulation with configurable speed, acceleration/deceleration, reaction time, and minimum gap. A full lane-changing, collision, priority, and multi-intersection traffic engine is deferred.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-preview-traffic-simulation",
|
||||
"name": "native-preview-traffic-simulation",
|
||||
"title": "Native preview traffic simulation and migration contract",
|
||||
"description": "",
|
||||
"status": "in_progress",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-18",
|
||||
"completedAt": null,
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Verify package stage ordering, manifest freshness, and external-tool boundaries."}
|
||||
{"file":".trellis/spec/config/index.md","reason":"Verify all new output paths remain centralized in area-config."}
|
||||
{"file":".trellis/spec/preview/index.md","reason":"Verify preview remains a consumer of package assets, not part of the published package."}
|
||||
{"file":".trellis/spec/guides/artifact-parity-guide.md","reason":"Assess intentional output-contract changes and asset verification scope."}
|
||||
@@ -0,0 +1,135 @@
|
||||
# Asset Package Contract Design
|
||||
|
||||
## Product Boundary
|
||||
|
||||
`outputs/<area-id>/` remains the build workspace. Its `package/` child is the only
|
||||
publishable subtree and may be copied unchanged to another project. The package has
|
||||
no dependency on the parent directory, repository checkout, absolute local paths,
|
||||
QGIS, Blender, or the Cesium preview.
|
||||
|
||||
```
|
||||
outputs/<area-id>/
|
||||
package/ # publishable boundary
|
||||
manifest.json # asset-package/v1
|
||||
models/
|
||||
<area-id>.glb # compressed complete static scene
|
||||
roads.glb
|
||||
buildings.glb
|
||||
vegetation.glb
|
||||
water.glb
|
||||
<area-id>-cesium-preview.html # local verification only
|
||||
_preview/ # vehicles, routes, dynamic signal runtime
|
||||
_pipeline/ # staging, manifests and build diagnostics
|
||||
osm2streets_web_out/, *.gpkg, *.qgz, *.blend, *.png
|
||||
```
|
||||
|
||||
The package does not duplicate the GLBs: Cesium export and compression target the
|
||||
package model paths. Build staging lives under `_pipeline/` and is removed before a
|
||||
successful package is published.
|
||||
|
||||
## Manifest v1
|
||||
|
||||
The JSON root uses `schema: "osm-asset-package/v1"`. Required top-level fields are:
|
||||
|
||||
| Field | Contract |
|
||||
|---|---|
|
||||
| `schema`, `packageVersion`, `areaId` | Stable identity and schema evolution point |
|
||||
| `coordinateSystem` | Literal local ENU contract: X east, Y north, Z up, meters |
|
||||
| `placement` | WGS84 anchor longitude/latitude/height plus heading correction degrees |
|
||||
| `bounds` | WGS84 `minLon`, `minLat`, `maxLon`, `maxLat` |
|
||||
| `assets` | Deterministic list of declared publishable assets |
|
||||
| `sceneStats` | Optional descriptive OSM-derived counts, not loading-critical |
|
||||
|
||||
Each asset has `id`, `role`, `category`, `uri`, and `defaultLoad`. `uri` is a
|
||||
forward-slash relative path inside `package/`; it must not be absolute, start with
|
||||
`/`, contain `..`, or resolve outside the package. Roles are `scene` for the complete
|
||||
static scene and `layer` for separately loadable semantic subsets. Categories are
|
||||
fixed in v1: `scene`, `roads`, `buildings`, `vegetation`, `water`.
|
||||
|
||||
`models/<area-id>.glb` is the only `scene` entry and defaults to load. Layer GLBs are
|
||||
optional alternatives for selective loading and default off; consumers must not load
|
||||
both the complete scene and overlapping layers unless intentionally composing them.
|
||||
|
||||
The manifest deliberately excludes `source_osm`, `source_geojson`, arbitrary Cesium
|
||||
JavaScript, vehicle routes, vehicle models, dynamic signal states, countdown models,
|
||||
preview runtime paths, build timestamps and desktop paths. File digests and sizes may
|
||||
be added under an optional `integrity` object only after they are computed from final
|
||||
compressed files.
|
||||
|
||||
## Pipeline Design
|
||||
|
||||
### 1. Static export seed
|
||||
|
||||
`blender/export_cesium.py` continues to own static GLB generation, WGS84 bounds,
|
||||
anchor and semantic collection selection. It must emit data suitable for a manifest
|
||||
seed, not a preview metadata document. Static traffic-signal geometry already in the
|
||||
main scene remains part of the static scene; animated traffic signal and countdown
|
||||
collections are not package assets.
|
||||
|
||||
### 2. Package staging and compression
|
||||
|
||||
The build orchestrator owns output paths through `normalizeAreaConfig()`. It adds
|
||||
explicit package paths and exports static GLBs to a package staging directory beneath
|
||||
`_pipeline/`. Compression works only on staged package files and rewrites the staged
|
||||
manifest's main GLB URI after successful compression. It never needs to copy or alter
|
||||
preview HTML.
|
||||
|
||||
After path validation, file existence checks, and manifest validation, the package
|
||||
stage promotes the whole staging directory to `outputs/<area-id>/package/`. Promotion
|
||||
is directory-level and replaces a previous package only after the new one is valid.
|
||||
On failure, the previous published package remains usable and staging is retained or
|
||||
reported for diagnosis according to existing pipeline failure conventions.
|
||||
|
||||
### 3. Preview adapter
|
||||
|
||||
The existing Cesium preview remains a verification tool outside the package. It reads
|
||||
the package manifest via its relative path, then loads preview-only route, vehicle and
|
||||
dynamic-signal descriptors from `_preview/`. Dynamic traffic assets are declared in a
|
||||
preview-specific descriptor, never appended to package `assets`. This preserves
|
||||
existing high-precision cruise validation without making it a downstream requirement.
|
||||
|
||||
### 4. Stage and compatibility behavior
|
||||
|
||||
Canonical order becomes `intermediates/reimport -> blender -> cesium -> compress ->
|
||||
package -> preview`. `package` is included in the full default build and is callable
|
||||
explicitly to validate/publish existing staged static exports. `preview` stays an
|
||||
optional/verification stage, not a package dependency.
|
||||
|
||||
Existing `npm run build:area -- --config ... --stages ...` remains supported. Root
|
||||
level legacy GLB/JSON/HTML files are not deleted by the migration; they are not read
|
||||
as a fallback by the new package contract. A clean full build produces the package as
|
||||
the canonical downstream asset source.
|
||||
|
||||
## Consumer Examples
|
||||
|
||||
Examples live in repository source, outside individual packages:
|
||||
|
||||
- Cesium: fetch `manifest.json`, derive the ENU frame from `placement`, apply heading
|
||||
correction, then load `assets` by `uri`.
|
||||
- Three.js: load the selected model URL relative to the manifest URL; expose the same
|
||||
ENU placement object to the host application's georeferencing adapter. It must not
|
||||
imply that Three.js alone converts ENU to WGS84.
|
||||
|
||||
Both examples load the complete scene and demonstrate selecting a single `layer`.
|
||||
They validate that URLs are resolved relative to the manifest rather than the current
|
||||
page or repository root.
|
||||
|
||||
## Validation and Rollback
|
||||
|
||||
Unit tests validate schema fields, category/role legality, package-relative URI rules,
|
||||
and rejection of omitted/extra files. An integration fixture constructs a minimal
|
||||
package and verifies that Cesium and Three.js example resolvers choose identical
|
||||
relative URLs and placement values. A target-area package test checks final compressed
|
||||
GLB parsing and manifest references.
|
||||
|
||||
The package stage writes a stage manifest containing only final published records and
|
||||
their integrity. Diagnostic checks distinguish missing package, invalid package, and
|
||||
stale package. Rollback is to the prior `package/` directory; legacy preview outputs
|
||||
remain untouched throughout rollout.
|
||||
|
||||
## Deferred Decisions
|
||||
|
||||
- Rich per-feature semantics, routing graph delivery and simulation inputs are not in
|
||||
v1; they should be introduced through a later schema version only when a downstream
|
||||
consumer requires them.
|
||||
- Asset coverage improvements are downstream of this contract work.
|
||||
@@ -0,0 +1,4 @@
|
||||
{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Stage ordering, manifests, external-command boundary, and compression contracts."}
|
||||
{"file":".trellis/spec/config/index.md","reason":"Area output-path normalization and configuration contract."}
|
||||
{"file":".trellis/spec/preview/index.md","reason":"Preview config injection and browser runtime compatibility."}
|
||||
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"Blender-to-Node-to-browser coordinate and artifact contract risks."}
|
||||
@@ -0,0 +1,108 @@
|
||||
# Asset Package Contract Implementation Plan
|
||||
|
||||
## Phase 0: Contract Lock-In
|
||||
|
||||
- [ ] Add a package-contract module that owns v1 schema constants, allowed roles and
|
||||
categories, URI containment validation, manifest construction and validation.
|
||||
- [ ] Add focused unit fixtures for valid manifests and each invalid condition:
|
||||
missing schema/placement/bounds, malformed coordinates, invalid role/category,
|
||||
absolute or traversing URI, duplicate asset ID, missing primary scene, and referenced
|
||||
file outside the package.
|
||||
- [ ] Document `package/` as the sole downstream boundary in README and pipeline/config
|
||||
specs; retain a clear distinction between release assets and preview artifacts.
|
||||
|
||||
Gate: manifest v1 can be created and structurally validated without invoking Blender,
|
||||
Cesium or QGIS.
|
||||
|
||||
## Phase 1: Package Paths and Static Export
|
||||
|
||||
- [ ] Extend `scripts/lib/area-config.js` with paths for `packageDir`, package manifest,
|
||||
package model directory, primary GLB and semantic layer GLBs; keep all path derivation
|
||||
in this module.
|
||||
- [ ] Change `blender/export_cesium.py` metadata output to a static manifest seed:
|
||||
bounds, ENU placement and static scene/layer declarations only. Remove local source
|
||||
paths and inline Cesium source code from the published data.
|
||||
- [ ] Direct static GLB output to package staging paths and retain dynamic signal/countdown
|
||||
output as preview-only paths outside package staging.
|
||||
- [ ] Update `build-area.js`, semantic asset checks, stage manifests and diagnostics to
|
||||
consume the new path contract rather than constructing paths locally.
|
||||
|
||||
Gate: a Cesium export produces a valid static manifest seed and every declared static
|
||||
asset exists under package staging; no preview-only asset appears in it.
|
||||
|
||||
## Phase 2: Compression and Atomic Publication
|
||||
|
||||
- [ ] Refactor the default compression stage to operate on staged package files,
|
||||
preserving the final manifest shape while replacing only the primary GLB bytes and
|
||||
integrity data.
|
||||
- [ ] Add a `package` stage after `compress`; validate every asset and atomically promote
|
||||
staging to `outputs/<area-id>/package/`.
|
||||
- [ ] Update canonical stage ordering, `all`, interactive CLI labels and defaults.
|
||||
- [ ] Ensure a failed compression/package validation never replaces an existing published
|
||||
package; record diagnostics sufficient to identify the failed source/staging path.
|
||||
|
||||
Gate: a full build creates one publishable package with compressed primary GLB and no
|
||||
duplicate static GLBs at the area root.
|
||||
|
||||
## Phase 3: Preview Adapter and Compatibility
|
||||
|
||||
- [ ] Change preview HTML generation to reference `package/manifest.json` relative to
|
||||
the area output root.
|
||||
- [ ] Move route/vehicle/dynamic-signal discovery into a preview-only descriptor beneath
|
||||
`_preview/`, and make `cesium-preview.js` merge it after loading the package manifest.
|
||||
- [ ] Preserve existing preview controls and graceful behavior when preview-only data is
|
||||
absent; preview must still render the package static scene.
|
||||
- [ ] Update `diagnose:area`, `check:area` and stage manifest freshness to distinguish
|
||||
package artifacts from preview-only files.
|
||||
|
||||
Gate: the local Cesium preview loads the published package and continues to show
|
||||
optional cruise/dynamic preview content without adding either to package manifest.
|
||||
|
||||
## Phase 4: Downstream Consumption Proof
|
||||
|
||||
- [ ] Add Cesium example code that resolves the manifest URL, validates placement and
|
||||
loads the full scene or selected layers.
|
||||
- [ ] Add Three.js example code with a manifest-relative resolver and an explicit ENU
|
||||
placement handoff to the host's georeferencing integration.
|
||||
- [ ] Add automated resolver tests so neither example can regress to page-relative,
|
||||
desktop-absolute or repository-relative URLs.
|
||||
- [ ] Publish consumer documentation including overlap rules for complete scene vs layers.
|
||||
|
||||
Gate: examples run from a copied `package/` directory and require no files outside it.
|
||||
|
||||
## Phase 5: Full Verification and Migration
|
||||
|
||||
- [ ] Run focused Node tests for package schema, stage resolution, compression metadata,
|
||||
preview behavior, diagnostics and asset budgets.
|
||||
- [ ] Run the full default build for the primary area, then `check:area` and package
|
||||
integrity validation.
|
||||
- [ ] Inspect the packaged GLB structure and validate the Cesium example in browser;
|
||||
verify Three.js resolver output with the same manifest.
|
||||
- [ ] Capture expected intentional output-contract changes in docs/changelog and update
|
||||
relevant Trellis pipeline/config/preview specs.
|
||||
- [ ] Preserve legacy root-level artifacts during rollout; document that downstream
|
||||
consumers must migrate to `package/manifest.json` rather than treating legacy files
|
||||
as fallback.
|
||||
|
||||
## High-Risk Boundaries
|
||||
|
||||
- `blender/export_cesium.py` to Node: static metadata fields and semantic asset file
|
||||
names are a cross-runtime contract.
|
||||
- Node packaging to preview runtime: preview-only dynamic data must never silently enter
|
||||
the package manifest.
|
||||
- Compression to publication: paths must remain package-relative and a partial write must
|
||||
not replace the last valid package.
|
||||
- Consumers: ENU axes and heading correction are load-bearing; examples must not create
|
||||
a second, incompatible georeferencing convention.
|
||||
|
||||
## Validation Commands
|
||||
|
||||
```bash
|
||||
npm run test:build-stages
|
||||
npm run test:compress-glb
|
||||
npm run test:preview-assets
|
||||
npm run test:budgets
|
||||
npm run test:preflight
|
||||
npm run check:area -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
node scripts/glb-digest.js outputs/nantaizi-lake-innovation-valley/package/models/nantaizi-lake-innovation-valley.glb
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
# Reusable asset package contract
|
||||
|
||||
## Goal
|
||||
|
||||
将当前面向 Cesium 预览的区域输出升级为可由多个下游项目稳定消费的静态资产包。资产包
|
||||
必须有版本化 manifest、仅使用包内相对引用,并明确发布资产与本项目调试/预览产物的边界。
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- `blender/export_cesium.py` 已生成 `<area>.json`,包含主 GLB、roads/buildings/
|
||||
vegetation/water 分层 GLB、WGS84 anchor、ENU 坐标约定、heading correction 和 OSM bounds。
|
||||
- 该 JSON 同时包含桌面绝对 `source_osm` / `source_geojson` 路径、Cesium 代码片段,以及
|
||||
动态信号灯和倒计时预览资产;因此不是可发布的下游契约。
|
||||
- `outputs/<area>/` 还包含 QGIS、GeoJSON、Blender、Cesium HTML/runtime、车辆巡航和自检文件;
|
||||
它是构建工作目录,不是干净的发布目录。
|
||||
- 当前默认 Cesium GLB 已在构建末端压缩,且 metadata 中的资产 URL 使用同目录相对文件名。
|
||||
- 项目目标是生成可复用资产;交通仿真、跟车和信号调度属于下游运行时能力,不属于资产包首版。
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1:定义一个版本化 `manifest.json` v1,描述区域 ID、WGS84 anchor、ENU 轴向与 heading
|
||||
correction、WGS84 bounds、发布资产、语义类别和包内相对路径。
|
||||
- R2:资产包只含可复用的静态交付资产;主场景和道路、建筑、植被、水体等分层模型必须有
|
||||
明确的角色、加载语义和默认行为。
|
||||
- R3:manifest 和包内文件不得包含桌面绝对路径、构建临时目录、Cesium HTML/runtime、
|
||||
QGIS、GeoJSON、`.blend`、车辆巡航或动态交通信号调度依赖。
|
||||
- R4:在不破坏现有区域预览、诊断和构建中间产物的前提下,增加明确的资产包发布阶段。
|
||||
- R5:提供 Cesium 与 Three.js 的最小加载示例,均从 manifest 读取包内相对 URL,并按相同的
|
||||
WGS84/ENU 契约放置主场景或分层资产。
|
||||
- R6:定义发布资产、可选静态资产和本项目仅检查产物的分类规则,并由测试验证。
|
||||
- R7:完整构建后的资产包应可独立复制到其他项目,而无需本仓库的 `outputs` 目录结构或本地路径。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] 目标区域生成一个可独立分发的资产包目录,其中仅有 manifest 和 manifest 引用的发布资产。
|
||||
- [ ] manifest 的 schema version、坐标契约、bounds、所有资产类别和 URL 可由程序校验。
|
||||
- [ ] 所有 manifest URL 都是安全的包内相对路径;不得含绝对路径、`..` 或未声明文件。
|
||||
- [ ] 主 GLB 与每个发布分层资产都能在 Cesium 和 Three.js 示例中按 manifest 正确放置与加载。
|
||||
- [ ] 车辆、巡航路线、Cesium preview HTML/runtime、QGIS/GeoJSON/`.blend` 和动态信号调度资产
|
||||
不会进入发布包。
|
||||
- [ ] 既有 `outputs/<area>/` 预览工作流继续可用,现有非交互 build 命令保持兼容。
|
||||
- [ ] 发布包缺文件、manifest 路径越界、坐标字段无效或资产类别不合法时,构建/校验非零退出。
|
||||
|
||||
## Proposed Delivery Phases
|
||||
|
||||
1. 契约与目录边界:冻结 manifest v1 schema、发布目录结构、资产角色和坐标定义。
|
||||
2. 资产包发布阶段:从现有 Cesium 导出与压缩结果收集、校验并写入独立包目录。
|
||||
3. 下游消费证明:Cesium / Three.js 示例仅依赖 manifest 与包内容,并覆盖主场景和按类别加载。
|
||||
4. 质量门与迁移:增加结构、路径隔离、坐标和加载验证;保留旧预览输出并记录迁移规则。
|
||||
|
||||
## Key Decision
|
||||
|
||||
- 发布包根目录固定为 `outputs/<area-id>/package/`。该目录是可整体复制给下游项目的唯一
|
||||
发布边界;GLB 和 manifest 直接生成或移动到这里,避免与工作目录再保留一套大模型副本。
|
||||
本项目的预览页可通过包内相对路径读取发布资产,但 HTML/runtime 本身不属于发布包。
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- 交通流、车辆行为、信号相位控制、路口调度和其他运行时仿真。
|
||||
- 在本任务中扩展 OSM 到建筑、路灯、标志、植被等资产覆盖率或生成质量。
|
||||
- 删除既有 `outputs/<area>/` 中由用户保留的历史调试产物。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "asset-package-contract",
|
||||
"name": "asset-package-contract",
|
||||
"title": "Reusable asset package contract",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-11",
|
||||
"completedAt": "2026-08-12",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,54 @@
|
||||
# Interactive Area Build CLI Design
|
||||
|
||||
## Scope
|
||||
|
||||
Add a zero-dependency terminal interface for choosing an area configuration and one or more existing
|
||||
pipeline stages. It delegates execution to the existing `build-area.js` entry point and does not change
|
||||
the pipeline's stage implementations or generated artifacts.
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
npm run build
|
||||
-> scripts/interactive-build.js
|
||||
-> terminal menus (TTY only)
|
||||
-> node scripts/build-area.js --config <area> --stages <canonical-list>
|
||||
-> existing build pipeline
|
||||
```
|
||||
|
||||
Extract the stage metadata, aliases, canonical execution order, and mutual-exclusion validation from
|
||||
`build-area.js` into a CommonJS module under `scripts/lib/`. Both the interactive script and
|
||||
`build-area.js` consume this module, making the menu's options the same source of truth as execution.
|
||||
|
||||
## Interaction
|
||||
|
||||
1. Discover and sort `config/areas/*.json`; present a single-select area menu.
|
||||
2. Present a multi-select stage menu with descriptions and selection markers.
|
||||
3. Arrow keys move focus, space toggles, enter confirms, and Ctrl-C/Escape cancels.
|
||||
4. The menu prevents or reports the `intermediates`/`reimport` conflict before spawning a build.
|
||||
5. The selected stages are normalized to canonical pipeline order and passed to `build-area.js`.
|
||||
|
||||
The interactive entry requires both stdin and stdout to be TTYs. Otherwise it exits non-zero with an
|
||||
instruction to use `npm run build:area -- --config ... --stages ...`; it never falls back to defaults.
|
||||
|
||||
## Compatibility
|
||||
|
||||
- `build-area.js` remains the execution owner and keeps its CLI flags and defaults.
|
||||
- `npm run build:area` continues to invoke it directly.
|
||||
- `npm run build` changes only from the former default build alias to the interactive wrapper.
|
||||
- A successful `cesium` selection continues to generate preview output through the existing behavior;
|
||||
selecting `preview` explicitly remains valid for a standalone preview refresh.
|
||||
|
||||
## Validation
|
||||
|
||||
- Unit-test stage normalization, alias expansion, ordering, and mutual exclusion.
|
||||
- Unit-test config discovery and non-TTY refusal without launching a build.
|
||||
- Syntax-check both CLI entry scripts.
|
||||
- Manually exercise the menu in a TTY: select an area, select unordered stages, confirm the canonical
|
||||
command preview, and cancel before execution.
|
||||
|
||||
## Risks And Rollback
|
||||
|
||||
Raw terminal input must always restore raw mode and cursor state on confirmation, cancellation, and
|
||||
errors. The wrapper only spawns the unchanged existing build script, so rollback is deleting the new
|
||||
interactive entry and restoring the `build` package script.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,13 @@
|
||||
# Interactive Area Build CLI Implementation Plan
|
||||
|
||||
1. Extract stage definitions, aliases, canonical ordering, and conflict validation from
|
||||
`scripts/build-area.js` into a shared CommonJS module.
|
||||
2. Update `build-area.js` to consume the shared resolver without changing non-interactive semantics.
|
||||
3. Add `scripts/interactive-build.js` with TTY detection, area discovery, raw-mode selection menus,
|
||||
cancellation handling, and child-process delegation to `build-area.js`.
|
||||
4. Change `package.json` so `npm run build` calls the new interactive script while `build:area` remains
|
||||
unchanged.
|
||||
5. Add focused Node tests for stage planning and non-TTY behavior; do not invoke Blender, QGIS, or a
|
||||
real area build in tests.
|
||||
6. Run syntax checks, the focused tests, existing preflight/preview tests, and manually exercise the
|
||||
interactive flow in a TTY.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Interactive area build CLI
|
||||
|
||||
## Goal
|
||||
|
||||
提供一个交互式区域构建入口,减少手写 `npm run build:area -- --config ... --stages ...`
|
||||
的频率;用户可选择区域和一个或多个构建阶段,CLI 按既有阶段约束执行。
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- 当前 `scripts/build-area.js` 支持 `intermediates`、`reimport`、`blender`、`cesium`、
|
||||
`preview` 与 `compress` 六个阶段,并接受逗号分隔的 `--stages`。
|
||||
- `intermediates` 与 `reimport` 互斥:前者会从 OSM 重建 GeoPackage,后者回导 QGIS 编辑。
|
||||
- 执行顺序固定为 `intermediates/reimport -> blender -> cesium -> preview -> compress`;
|
||||
`cesium` 当前会自动写入 preview 产物。
|
||||
- 项目没有终端交互依赖;现有命令行调用必须继续可用于脚本和自动化。
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1:提供交互式入口,列出可用区域配置并允许用户选择目标区域。
|
||||
- R2:交互式入口允许一次选择多个阶段,并清晰展示阶段名称与用途。
|
||||
- R3:选择互斥阶段时必须阻止执行并解释原因;不得静默选择其中之一。
|
||||
- R4:执行多个阶段时必须使用既有依赖顺序,而非用户勾选顺序。
|
||||
- R5:保留 `npm run build:area -- --config ... --stages ...` 的现有非交互行为与语义。
|
||||
- R6:无 TTY、取消输入或无效输入时必须安全退出,不启动任何构建。
|
||||
- R7:TTY 交互使用方向键移动、空格多选、回车确认;菜单应显示当前选择状态。
|
||||
- R8:`npm run build` 启动交互式入口;`npm run build:area` 继续保留为非交互入口。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] 用户可从终端选择区域和多项阶段,无需手写配置路径或逗号分隔阶段名。
|
||||
- [ ] 选择 `intermediates` 与 `reimport` 时,CLI 明确报互斥错误且不运行构建。
|
||||
- [ ] 选择乱序的多个阶段时,实际执行顺序仍遵循既有管线顺序。
|
||||
- [ ] 既有非交互 `build:area` 调用保持兼容。
|
||||
- [ ] 取消或在非交互环境调用不会触发构建。
|
||||
- [ ] 用户可用方向键、空格和回车完成区域及阶段选择。
|
||||
- [ ] `npm run build` 启动交互菜单,`npm run build:area` 的现有调用保持不变。
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- 采用终端原生多选菜单,而非输入逗号分隔编号;避免引入运行时依赖。
|
||||
- `npm run build` 作为日常交互入口;`npm run build:area` 继续服务自动化和精确调用。
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- 修改各 stage 的构建业务逻辑、依赖关系或产物格式。
|
||||
- 将巡航路线、QGIS 图层编辑或区域配置编辑纳入本次交互流程。
|
||||
- GUI 或浏览器界面的构建控制台。
|
||||
|
||||
## Notes
|
||||
|
||||
- Keep `prd.md` focused on requirements, constraints, and acceptance criteria.
|
||||
- Lightweight tasks can remain PRD-only.
|
||||
- For complex tasks, add `design.md` for technical design and `implement.md` for execution planning before `task.py start`.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "interactive-area-cli",
|
||||
"name": "interactive-area-cli",
|
||||
"title": "Interactive area build CLI",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-11",
|
||||
"completedAt": "2026-08-11",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,48 @@
|
||||
# Design: Vehicle Incident And Information Preview
|
||||
|
||||
## Boundary
|
||||
|
||||
This is browser-only Cesium preview state. It changes neither generated OSM data nor `package/` assets.
|
||||
Vehicle routes and models remain `_preview/` resources; refresh resets all vehicle event state.
|
||||
|
||||
## Data Model
|
||||
|
||||
Each object returned by `addCruiseVehicle()` gains:
|
||||
|
||||
```js
|
||||
{
|
||||
id: "vehicle-1",
|
||||
modelName: "car_a01_002",
|
||||
status: "normal" | "breakdown" | "accident",
|
||||
incidentNote: "",
|
||||
motion: { state },
|
||||
markerEntity
|
||||
}
|
||||
```
|
||||
|
||||
The current route distance remains the single motion source. The clock tick leaves it unchanged while
|
||||
status is not `normal`; returning to normal resumes from that position.
|
||||
|
||||
## Interaction Flow
|
||||
|
||||
1. Vehicle Cesium Entities carry a `vehicleId` property.
|
||||
2. A `ScreenSpaceEventHandler` picks a clicked entity, resolves its vehicle, synchronizes the existing
|
||||
vehicle selector, and opens a positioned information card.
|
||||
3. The card displays vehicle number, model family, route label/length, configured speed, state, and note.
|
||||
4. Card controls set normal/breakdown/accident and persist only in the in-memory vehicle object.
|
||||
5. State changes update marker visibility, the route material, card content, and Cesium render request.
|
||||
|
||||
## Visual Contract
|
||||
|
||||
- Normal: no marker; original route color; motion active.
|
||||
- Breakdown: yellow wrench label above the vehicle; route retains its original color; motion frozen.
|
||||
- Accident: red warning-triangle label above the vehicle; route material becomes red; motion frozen.
|
||||
- Marker positions use the same vehicle `CallbackProperty`, so they move with the vehicle before an event
|
||||
and remain at the incident location while stopped.
|
||||
|
||||
## Compatibility And Risks
|
||||
|
||||
- Picking must ignore routes, static models, and signal entities.
|
||||
- No route must retain the existing disabled cruise controls and never show a card.
|
||||
- The card must be HTML/CSS UI, not Cesium InfoBox, because the viewer deliberately disables InfoBox.
|
||||
- The feature must not alter signal runtime behavior or vehicle asset URLs.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,17 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Add an initially hidden vehicle information card to `area-preview.js` and responsive card styles to
|
||||
`cesium-preview.css`.
|
||||
2. Extend the browser runtime vehicle records with stable identity, parsed model metadata, incident status,
|
||||
note, and a marker entity.
|
||||
3. Gate route-distance advancement on normal state; add status transition helper for marker, route material,
|
||||
and card refresh.
|
||||
4. Add Cesium click picking and card controls. Keep the existing selector synchronized for follow/route use.
|
||||
5. Add focused assertions to `test-preview-assets.js` for card wiring, status behavior, and marker/route
|
||||
contracts.
|
||||
6. Run syntax checks, preview test, stage test, target-area preview regeneration, and `check:area`.
|
||||
|
||||
## Rollback
|
||||
|
||||
Revert only the preview HTML/CSS/runtime/test files. No package, Blender, route, or OSM product files should
|
||||
need rollback.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Vehicle incident and information preview
|
||||
|
||||
## Goal
|
||||
|
||||
在 Cesium 验证预览中,让使用者能够查看每辆巡航车辆的基础信息,并人为标记车辆事故/故障状态,直观看到该状态对车辆和路线展示的影响。
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- 车辆是 `scripts/lib/cesium-preview.js` 创建的 Cesium Entity;当前已有车型、路线、巡航速度、
|
||||
跟随、显隐与路线选择。
|
||||
- 车辆模型和路线都属于 `_preview/` 验证资源,不是 area package 的下游资产契约。
|
||||
- 当前不存在车辆业务资料、事故状态、碰撞检测或自动事件推演。
|
||||
- 项目定位是资产生成与验证,不能将交通流/事故仿真作为本任务的范围。
|
||||
|
||||
## Requirements
|
||||
|
||||
- 每辆预览车辆必须有稳定的显示编号、车型、当前路线、设定速度和行驶状态。
|
||||
- 点击场景中的车辆后,必须弹出该车辆的信息卡,展示基础信息与当前事件状态;信息卡内完成状态与说明编辑。
|
||||
- 使用者必须能将选中车辆在正常、故障和事故状态之间切换,并能提供简短事件说明。
|
||||
- 故障和事故是不同状态:二者均停止巡航;故障使用黄色标记,事故使用红色标记并提示路线中断。
|
||||
- 故障使用车辆顶部黄色扳手标记;事故使用红色警示三角标记,并将该车辆当前路线改为红色。
|
||||
- 非正常车辆必须有可见的场景标记;恢复正常后继续按既有路线巡航。
|
||||
- 事故/故障仅在当前浏览器预览会话中存在;刷新或重建预览后不保留。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
- 自动碰撞检测、车辆间物理碰撞、自动事故生成。
|
||||
- 信号配时、交通流、调度策略或持久化事件记录。
|
||||
- 向 `package/` 加入车辆、路线或事故数据。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] 用户可点击任一可见车辆,弹出其基础信息、状态和事件说明;现有车辆选择控件仍可用于路线和跟随。
|
||||
- [ ] 用户可标记故障或事故,并看到车辆停止、状态文本和明显的场景标记。
|
||||
- [ ] 故障显示黄色扳手;事故显示红色警示三角且当前路线变红。
|
||||
- [ ] 用户可恢复正常,车辆继续其现有路线;其他车辆不受影响。
|
||||
- [ ] 无可用路线时,车辆信息和事故控件按既有禁用语义处理。
|
||||
- [ ] 预览资产、路线与现有信号灯 runtime 继续可加载;不新增下游 package 内容。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "vehicle-incident-info",
|
||||
"name": "vehicle-incident-info",
|
||||
"title": "Vehicle incident and information preview",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-12",
|
||||
"completedAt": "2026-08-12",
|
||||
"branch": null,
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/pipeline/index.md","reason":"Check command ownership, disk artifact boundaries, and legacy pipeline compatibility."}
|
||||
{"file":".trellis/spec/preview/index.md","reason":"Check browser workbench state, error handling, and no-build browser constraints."}
|
||||
{"file":".trellis/spec/config/index.md","reason":"Check new area output/config normalization and compatibility."}
|
||||
@@ -0,0 +1,110 @@
|
||||
# Native Road Compiler Workbench Design
|
||||
|
||||
## Architecture
|
||||
|
||||
The native compiler is an additive pipeline path. It owns a separate output
|
||||
directory under an area and never writes into `osm2streets_web_out/`.
|
||||
|
||||
```text
|
||||
OSM XML + native-road-overrides.json
|
||||
|
|
||||
v
|
||||
Canonical Road Model
|
||||
roads / endpoints / junction candidates / provenance
|
||||
|
|
||||
v
|
||||
Native Geometry Compiler
|
||||
road surfaces / initial junction surfaces / diagnostics
|
||||
|
|
||||
+--> native-road/compiled.json
|
||||
+--> native-road/layers/*.geojson
|
||||
+--> native-road/diagnostics.json
|
||||
+--> native-road/comparison.json
|
||||
|
|
||||
v
|
||||
Road Workbench HTTP service
|
||||
browser map + inspect/edit/save API
|
||||
```
|
||||
|
||||
The canonical model is the authority. Render layers, browser display data, and
|
||||
future Blender compatibility adapters are derived from it.
|
||||
|
||||
## Commands And Ownership
|
||||
|
||||
- `npm run road:compile -- --config <area-config>` performs no browser work.
|
||||
It reads OSM plus the persisted override file, writes a staged native-road
|
||||
result, validates it, and atomically promotes the result directory.
|
||||
- `npm run road:workbench -- --config <area-config>` compiles first unless
|
||||
`--no-compile` is supplied, then starts a local HTTP server scoped to that
|
||||
one area.
|
||||
- The server exposes read-only compiler artifacts and one explicit save API
|
||||
for validated overrides. It does not expose arbitrary filesystem paths.
|
||||
- Existing `build:area`, `intermediates`, QGIS, Blender, Cesium, and package
|
||||
paths remain unchanged in the first iteration.
|
||||
|
||||
## Data Contracts
|
||||
|
||||
### Canonical road model
|
||||
|
||||
Each road direction carries a stable ID derived from OSM identifiers, source
|
||||
way IDs, endpoint node IDs, centerline, explicit/inferred attributes, applied
|
||||
override IDs, and diagnostics. Junction candidates likewise use their OSM node
|
||||
ID when available. Values include provenance such as `tag:lanes:forward`,
|
||||
`inferred:highway-default`, or `override:<id>`.
|
||||
|
||||
Each `Movement` is a stable semantic record joining a connection, source and
|
||||
target road/lane, turn class, provenance, and an optional connector geometry.
|
||||
`geometryStatus="connector"` publishes a connector curve;
|
||||
`geometryStatus="continuous"` means the lane centerlines meet at the node and
|
||||
does not invent a zero-length curve; `deferred-too-long` retains the movement
|
||||
while withholding unsafe geometry.
|
||||
|
||||
### Override file
|
||||
|
||||
`<area>/native-road-overrides.json` is versioned and human-reviewable. It
|
||||
contains an array of uniquely identified changes whose targets are stable road
|
||||
or endpoint IDs. Supported v1 records are `road` parameter overrides and
|
||||
`junction-connection` decisions. The save endpoint validates schema, target
|
||||
existence, finite values, and duplicate/conflicting edits before atomic write.
|
||||
|
||||
### Compiler artifacts
|
||||
|
||||
`<area>/native-road/compiled.json` is the workbench's single read model and
|
||||
contains the canonical `movements` list.
|
||||
`layers/` contains generated GeoJSON with source/provenance properties.
|
||||
`diagnostics.json` contains severity, stable subject ID, source IDs, rule,
|
||||
message, and optional geometry. `comparison.json` reports counts and coverage
|
||||
against available osm2streets layers; it does not claim quality solely from
|
||||
visual differences.
|
||||
|
||||
## Browser Workbench
|
||||
|
||||
The browser uses OpenLayers as its sole GIS runtime, served directly from the
|
||||
local allowlisted `node_modules` packages with a browser import map. The map
|
||||
renders fit-to-data OSM centerlines, native surfaces, optional osm2streets
|
||||
reference layers, diagnostics, selected-object provenance, movements, and
|
||||
overrides. This provides mature map selection and hit detection without a
|
||||
framework or bundler.
|
||||
|
||||
The user can select a road or endpoint, edit only v1 fields, inspect the
|
||||
resulting override record, explicitly save it, and recompile/reload. Saved
|
||||
state is visibly differentiated from unsaved state. The workbench must not
|
||||
offer freehand final-polygon editing, since that would break reproducibility.
|
||||
|
||||
## Geometry And Validation
|
||||
|
||||
V1 produces road segments from projected centerline offsets and terminal
|
||||
cross-sections. It only generates a junction surface when endpoints satisfy
|
||||
the supported ordinary T/cross shape and geometry checks; otherwise it emits a
|
||||
diagnostic rather than inventing an invalid polygon. Validation detects
|
||||
dangling endpoints, unclosed/self-intersecting rings, non-finite coordinates,
|
||||
unsupported multi-level intersections, and source/topology ambiguity. Small
|
||||
numerical cleanup may be explicit and recorded; semantic failures are never
|
||||
silently repaired.
|
||||
|
||||
## Compatibility And Rollout
|
||||
|
||||
The first compiler's layers use existing render-layer names where meaningful,
|
||||
but are stored separately. A later, explicitly enabled Blender provider option
|
||||
may consume native layers after comparison gates pass. Delete/replace behavior
|
||||
is out of scope; rollback is selecting the existing osm2streets pipeline.
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/pipeline/index.md","reason":"Native compiler commands, artifacts, and area config extend the Node pipeline while preserving legacy stages."}
|
||||
{"file":".trellis/spec/preview/index.md","reason":"The browser workbench is a new DOM runtime and must follow local preview loading and state conventions where applicable."}
|
||||
{"file":".trellis/spec/config/index.md","reason":"New native-road output paths and config behavior extend the normalized area contract."}
|
||||
@@ -0,0 +1,32 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Add area output/config normalization and command entrypoints for the native
|
||||
compiler, preserving existing stage behavior and paths.
|
||||
2. Implement a shared OSM road parser and canonical road/endpoints model with
|
||||
source provenance, explicit versus inferred properties, and stable IDs.
|
||||
3. Implement v1 override schema, validation, load/apply behavior, atomic save,
|
||||
and focused unit tests.
|
||||
4. Implement projected road segment geometry, supported T/cross junction
|
||||
detection, native GeoJSON artifact emission, diagnostics, and comparison
|
||||
summary.
|
||||
5. Implement a dependency-free local workbench server and browser UI with
|
||||
selection, provenance display, v1 parameter/topology editing, explicit save,
|
||||
compile/reload, and error states.
|
||||
6. Add native compiler tests using focused fixtures plus nantaizi analysis;
|
||||
run existing relevant Node tests to confirm legacy behavior remains intact.
|
||||
7. Compare nantaizi and at least one supplied problematic OSM sample. Record
|
||||
metrics, unsupported cases, and follow-up work in task research.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:workbench -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run test:build-stages
|
||||
npm run test:preflight
|
||||
npm run test:preview-assets
|
||||
```
|
||||
|
||||
Browser validation includes loading the workbench, editing a road parameter,
|
||||
saving, verifying the override file, recompiling, reloading, and confirming
|
||||
provenance identifies the saved override.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Native road compiler workbench
|
||||
|
||||
## Goal
|
||||
|
||||
Build an incremental native road compiler for Chinese urban and campus OSM
|
||||
data that can progressively exceed osm2streets in geometry quality,
|
||||
explainability, and repeatable correction. The existing osm2streets pipeline
|
||||
must remain usable while the native compiler is developed and compared.
|
||||
|
||||
The first deliverable is a browser-based Road Workbench. It must expose the
|
||||
native compiler's source data, generated geometry, and diagnostics, allow
|
||||
users to make small semantic/topology corrections, persist those corrections
|
||||
as versionable overrides, and reload them automatically in later runs.
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- Nantaizi currently works relatively well because its OSM data received
|
||||
deliberate supplemental tagging; it still has missing boundaries and
|
||||
polygons that cannot be closed.
|
||||
- Other tested OSM inputs expose osm2streets sensitivity to input structure
|
||||
and leave too much opaque, final-polygon repair work in QGIS.
|
||||
- Existing Blender consumes the nine GeoJSON render layers from
|
||||
`osm2streets_web_out/`; QGIS GeoPackage edits can currently be reimported
|
||||
only as a whole batch.
|
||||
- The repository has no existing interactive browser editing service. Existing
|
||||
Cesium preview is a static, generated verification page.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: Add a native-road-compiler path without replacing or regressing the
|
||||
existing osm2streets path.
|
||||
- R2: Parse OSM into a canonical, source-traceable road model with stable
|
||||
references to OSM ways and nodes, explicit values versus inferred values,
|
||||
and diagnostics.
|
||||
- R3: Compile at least ordinary road segments and the initial supported
|
||||
junction subset into the existing render-layer contract, allowing existing
|
||||
Blender/Cesium consumers to be reused.
|
||||
- R4: Provide a browser Road Workbench that overlays raw OSM topology,
|
||||
generated geometry, osm2streets comparison geometry when available, and
|
||||
compiler diagnostics.
|
||||
- R5: The workbench must permit scoped user adjustments and save them to an
|
||||
area-local, human-reviewable override file. Future compile and workbench
|
||||
runs must load that file automatically.
|
||||
- R6: Each generated object and diagnostic must be traceable to OSM source
|
||||
IDs, compiler rule/inference evidence, and relevant override IDs.
|
||||
- R7: Validate topology and geometry before publishing generated layers;
|
||||
report unresolved semantic errors instead of silently disguising them as
|
||||
geometric repair.
|
||||
- R8: Develop against nantaizi plus problem inputs and report native versus
|
||||
osm2streets comparison metrics.
|
||||
|
||||
## Scope Boundaries
|
||||
|
||||
- First implementation targets Chinese urban/campus roads, ordinary road
|
||||
segments, T/cross junctions, directed/multi-lane roads, and data already
|
||||
tagged in nantaizi where possible.
|
||||
- Existing Blender, Cesium export, package format, building, vegetation, and
|
||||
water generators are out of scope unless a compatibility adapter requires a
|
||||
narrowly scoped change.
|
||||
- Directly editing final render polygons is not the intended correction model;
|
||||
generated layers remain derived output.
|
||||
- Complex interchanges, arbitrary multilayer junctions, and full worldwide OSM
|
||||
coverage are deferred until driven by concrete samples.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] A native compile command produces a canonical road model, generated
|
||||
layers, diagnostics, and comparison artifacts for a configured area without
|
||||
changing the osm2streets output path.
|
||||
- [ ] A browser command serves a Road Workbench for an area and clearly shows
|
||||
source topology, generated output, diagnostics, provenance, and saved
|
||||
overrides.
|
||||
- [ ] A user can make the agreed first-scope override edits in the browser,
|
||||
save them explicitly, and receive a durable area-local override artifact.
|
||||
- [ ] Re-running compile or reopening the workbench applies saved overrides
|
||||
automatically and exposes their provenance.
|
||||
- [ ] The compiler reports invalid/unclosed geometry, dangling road ends,
|
||||
and unresolved junction/lane ambiguity with source IDs.
|
||||
- [ ] Nantaizi and at least one known problematic area can run through the
|
||||
native analysis/preview path, with comparison metrics captured rather than
|
||||
a claim based only on visual inspection.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- The first browser editing surface supports road parameters (width, directed
|
||||
lane counts, left/right sidewalk state) plus junction endpoint
|
||||
connect/disconnect decisions.
|
||||
- Turn restrictions, stop lines, and crosswalk placement are deferred until
|
||||
the compiler has a validated road/junction editing loop.
|
||||
- Overrides are a versioned, human-reviewable JSON artifact owned by the area,
|
||||
not edits to generated polygon layers.
|
||||
- Native output and osm2streets output remain parallel during development;
|
||||
neither silently overwrites the other.
|
||||
@@ -0,0 +1,50 @@
|
||||
# Two-Area Native Road Comparison
|
||||
|
||||
## Runs
|
||||
|
||||
2026-08-14:
|
||||
|
||||
```bash
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run build:area -- --config config/areas/hanyang-block.json --stages intermediates
|
||||
npm run road:compile -- --config config/areas/hanyang-block.json
|
||||
```
|
||||
|
||||
`comparison.json` is a coverage and diagnostic record. Feature counts are not
|
||||
a geometry-quality score: osm2streets and the native compiler segment roads at
|
||||
different levels.
|
||||
|
||||
| Area | Directional roads | Native surfaces | Native junctions | Movements | Internal ends | Manual candidates | osm2streets road surfaces |
|
||||
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
|
||||
| nantaizi-lake-innovation-valley | 34 | 19 | 6 | 46 | 4 | 2 | 50 |
|
||||
| hanyang-block | 475 | 352 | 20 | 369 | 161 | 83 | 1826 |
|
||||
|
||||
## Observed Failure Modes
|
||||
|
||||
The hanyang osm2streets/QGIS run completed, but its log reported repeated:
|
||||
|
||||
- roads trimmed into oblivion;
|
||||
- degenerate intersections that could not be collapsed because layers, names,
|
||||
highway types, or lane specifications differ;
|
||||
- intersection polygon requests with no roads.
|
||||
|
||||
The native compiler did not hide the related uncertainty. It reported 161
|
||||
internal road ends. Eighty-three have one or more nearby, direction-compatible
|
||||
candidate departures within 35 metres; these remain suggestions for explicit
|
||||
review rather than automatic topology edits. The initial five one-way junctions
|
||||
that lacked connector curves now publish road-surface envelopes based on their
|
||||
semantic movements. Across hanyang, 369 movements are identified: 243 require
|
||||
a connector curve, while 126 are continuous at their OSM node and intentionally
|
||||
have no separate geometry.
|
||||
|
||||
## Resulting Priorities
|
||||
|
||||
1. Keep manual candidate suggestions and semantic overrides as the correction
|
||||
path for near-miss topology. Do not bulk-connect candidates.
|
||||
2. Expand ordinary junction support from 3/4 physical approaches only after
|
||||
identifying a repeated unsupported topology; one-way movements that are
|
||||
continuous at a node are already supported without fake connector geometry.
|
||||
3. Add an inspectable movement artifact so turn geometry is not the only
|
||||
representation of a road-to-road movement.
|
||||
4. Use a visual review of a few explicit hanyang diagnostics before changing
|
||||
connection-distance or road-class rules.
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"id": "native-road-compiler",
|
||||
"name": "native-road-compiler",
|
||||
"title": "Native road compiler workbench",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-13",
|
||||
"completedAt": "2026-08-18",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [
|
||||
"08-14-native-road-lane-markings",
|
||||
"08-17-native-road-control-markings",
|
||||
"08-17-native-road-center-lines",
|
||||
"08-17-native-rounded-junctions",
|
||||
"08-18-native-traffic-signal-parity"
|
||||
],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,77 @@
|
||||
# Native Lane Markings Design
|
||||
|
||||
## Architecture
|
||||
|
||||
The native compiler remains the source of truth. It derives two new polygon
|
||||
layers alongside its existing surface, sidewalk, lane-centerline, and
|
||||
connector outputs:
|
||||
|
||||
```text
|
||||
canonical directed roads + lane centerlines + junction cutbacks
|
||||
|
|
||||
+-- lane separators: paint polygons between adjacent same-direction lanes
|
||||
|
|
||||
+-- direction arrows: repeated through-arrow template on directed lanes,
|
||||
| outside the reserved junction marking zone
|
||||
|
|
||||
+-- turn arrows: tested existing template, anchored to an incoming lane
|
||||
only when that lane has an explicit supported turn:lanes value
|
||||
```
|
||||
|
||||
No OSM2streets rendered geometry is consumed by this path. The existing arrow
|
||||
template library is reused only as a geometry/style asset, so the native lane
|
||||
ID and OSM tags remain the evidence for placement.
|
||||
|
||||
## Contracts
|
||||
|
||||
- `layers/lane_separators.geojson`: polygon FeatureCollection. Each feature
|
||||
records `native_id`, directed `road_id`, adjacent lane indices, source OSM
|
||||
ways, and `native-road-lane-separator/v1` provenance.
|
||||
- `layers/turn_arrows.geojson`: polygon FeatureCollection. Each feature records
|
||||
its `native_id`, `road_id`, `lane_id`, OSM way IDs, direction, lane index,
|
||||
maneuver, template asset, placement distance, and placement provenance.
|
||||
- Unsupported turn values, a missing usable incoming-lane segment, or an
|
||||
insufficient pre-junction placement distance create a diagnostic and no
|
||||
arrow geometry.
|
||||
- `layers/direction_arrows.geojson`: polygon FeatureCollection. Each feature
|
||||
records the native lane and directed road, OSM way IDs, a stable sequence
|
||||
index, its distance along the lane, and `native-road-direction-arrow/v1`
|
||||
provenance. It uses the tested `through` template but is not a turn claim.
|
||||
- The workbench serves both layers, draws them separately from its current
|
||||
centerline/connector debug layer, and selects them by `native_id`.
|
||||
- `catalog.NATIVE_ROAD_LAYERS` maps the two native sources to the existing
|
||||
`lane_separators` and `lane_arrows_webscale` Blender materials. The native
|
||||
adapter does not extend the osm2streets scene-layer registry.
|
||||
|
||||
## Placement
|
||||
|
||||
An incoming lane is oriented in driving direction. A turn arrow is sampled
|
||||
from that lane's endpoint backwards by the configured safe distance, staying
|
||||
outside the junction cutback. Its template basis uses the sampled lane tangent;
|
||||
therefore it is on and aligned with the lane rather than the OSM centerline or
|
||||
a screen-space direction. Multiple template rings remain separate polygons.
|
||||
|
||||
Lane separators are narrow polygons centered between adjacent lane centerlines
|
||||
on a single directional carriageway. They stop at the same junction cutbacks as
|
||||
the lane centerlines. A one-lane direction produces none.
|
||||
|
||||
Direction arrows are sampled at a fixed road-scale interval along the same
|
||||
directed lane centerline. Their candidates exclude both endpoint buffers and
|
||||
the turn-arrow reserve at the incoming end. This preserves a readable repeated
|
||||
direction cue without overlapping a turn instruction at a junction.
|
||||
|
||||
## Compatibility And Rollback
|
||||
|
||||
All new files are additive under `native-road/layers/`. The existing
|
||||
osm2streets/QGIS layer contract and `package/` are unchanged. Selecting
|
||||
`--road-provider osm2streets` remains rollback. Missing native marking files
|
||||
are a native Blender build error rather than a silent omission.
|
||||
|
||||
## Risks
|
||||
|
||||
- OSM turn tags can be incomplete or incompatible with the inferred lane
|
||||
count. These are diagnostics, not guessed arrows.
|
||||
- Very short approaches can have no safe position before the cutback. They are
|
||||
skipped with a source-traceable diagnostic.
|
||||
- Blender and Cesium need a real native build to verify the mesh/material
|
||||
contract, not only GeoJSON unit tests.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Add reusable template-placement helper to the existing turn-arrow module,
|
||||
preserving its supported-asset gate and output ring shape.
|
||||
2. Extend the native compiler with lane-separator, repeated road-direction
|
||||
arrow, and explicit-turn-arrow polygon generation plus source diagnostics
|
||||
and layer persistence.
|
||||
3. Extend native output records/counts and Blender adapter mappings. Reuse
|
||||
existing `lane_separators` and `lane_arrows_webscale` materials only.
|
||||
4. Add Workbench layers, toggles, selection/provenance inspector entries, and
|
||||
preserve the existing Workbench-only direction triangle behavior.
|
||||
5. Add focused fixtures for supported turn placement, unsupported maneuver,
|
||||
short approach skip, and separator geometry; run native/workbench/build
|
||||
stage tests.
|
||||
6. Build Nantaizi with `blender,cesium,preview --road-provider native`, inspect
|
||||
the final preview, and confirm no `package/` publication occurred.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
npm run test:native-road
|
||||
npm run test:road-workbench
|
||||
npm run test:turn-lane-arrows
|
||||
npm run test:build-stages
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json \
|
||||
--stages blender,cesium,preview --road-provider native
|
||||
```
|
||||
|
||||
Rollback is selecting `--road-provider osm2streets`; no existing output path
|
||||
is replaced.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Native road lane markings and turn arrows
|
||||
|
||||
## Goal
|
||||
|
||||
Complete the native-road visual language for Nantaizi before any cross-area
|
||||
migration: lane separators, travel-direction markers, and OSM-backed turn
|
||||
arrows must be inspectable in the Road Workbench and visible in Blender and
|
||||
Cesium output.
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- Native output currently contains directed lane centerlines and connector
|
||||
curves, but it does not emit paintable lane-separator or turn-arrow polygons.
|
||||
- The existing osm2streets path has tested arrow templates in
|
||||
`scripts/lib/turn-lane-arrows.js`, the `lane_arrows_webscale` material layer,
|
||||
and a matching Blender material. Reuse these instead of introducing a second
|
||||
arrow style.
|
||||
- Earlier reviews established that direction markers must sit on the OSM / lane
|
||||
centerline, use a clearly directional sharp triangle, and never be treated as
|
||||
a road-surface decoration that drifts sideways.
|
||||
- Turn arrows must follow the actual incoming lane and be placed before its
|
||||
junction, with OSM source way, direction, lane index, and maneuver retained
|
||||
as provenance.
|
||||
- Scope remains Nantaizi only. Existing osm2streets output remains untouched.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: Native compilation emits polygonal lane-separator markings derived from
|
||||
its own directed lane geometry.
|
||||
- R2: Native compilation emits turn-arrow polygons for supported OSM
|
||||
`turn:lanes` maneuvers, using the existing tested arrow templates and the
|
||||
exact native incoming-lane centerline for placement.
|
||||
- R2a: Native compilation emits repeated straight-ahead direction-arrow
|
||||
polygons along directed lanes, matching the visual role of osm2streets'
|
||||
ordinary road arrows. These are a separate layer from turn arrows, retain
|
||||
their own provenance, and leave a clear buffer around junction turn arrows.
|
||||
- R3: Native output preserves provenance for every marking: native road/lane
|
||||
ID, OSM way IDs, direction, lane number, maneuver, and placement method.
|
||||
- R4: The Workbench renders markings in a separately controllable layer and
|
||||
exposes those provenance fields on selection.
|
||||
- R5: The native Blender adapter consumes native marking layers through the
|
||||
existing lane-separator and lane-arrow material layers; Cesium must receive
|
||||
the same geometry through the exported GLB.
|
||||
- R6: Unsupported, unplaceable, or ambiguous arrow inputs become diagnostics;
|
||||
the compiler must not invent a maneuver.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Nantaizi native output contains valid polygon GeoJSON for generated lane
|
||||
separators, repeated road direction arrows, and every supported, explicitly
|
||||
tagged turn arrow.
|
||||
- [ ] A Workbench user can toggle, select, and inspect a generated marker and
|
||||
see its lane, OSM, maneuver, and placement provenance.
|
||||
- [ ] A selected direction marker is geometrically aligned to its directed lane
|
||||
centerline; a selected turn arrow is on its incoming lane before the junction.
|
||||
- [ ] Blender scene output and Cesium GLB contain native lane markings and
|
||||
arrows with the existing visual material language.
|
||||
- [ ] Unit tests cover a normal supported arrow, an unsupported maneuver, and
|
||||
an unsafe/too-short placement; native compile and final Nantaizi visual build
|
||||
pass without publishing `package/`.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Inventing turn arrows for untagged lanes, traffic-control semantics, changing
|
||||
QGIS/osm2streets layers, or processing another area.
|
||||
|
||||
## Key Decision
|
||||
|
||||
- Sharp travel-direction triangles remain a Workbench-only inspection aid.
|
||||
They explain raw OSM node order after a road is selected.
|
||||
- Repeated `through` direction arrows are final road markings, distinct from
|
||||
both those debug triangles and OSM-backed junction turn arrows. They are
|
||||
placed on native lane centerlines at a fixed interval and enter the same
|
||||
Blender/Cesium material layer as turn arrows.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-road-lane-markings",
|
||||
"name": "native-road-lane-markings",
|
||||
"title": "Native road lane markings and turn arrows",
|
||||
"description": "Complete Nantaizi native lane separators, travel direction and turn-arrow geometry through Road Workbench, Blender and Cesium before cross-area migration.",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P1",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-14",
|
||||
"completedAt": "2026-08-14",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-13-native-road-compiler",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,35 @@
|
||||
# Native Lane Separator Style Overrides Design
|
||||
|
||||
## Contract
|
||||
|
||||
The override is identified by the stable `roadId` plus the adjacent lane pair:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "车道分隔线:road:way/123:forward:1-2",
|
||||
"kind": "lane-separator-style",
|
||||
"roadId": "road:way/123:forward",
|
||||
"leftLaneIndex": 1,
|
||||
"rightLaneIndex": 2,
|
||||
"color": "white",
|
||||
"pattern": "dashed"
|
||||
}
|
||||
```
|
||||
|
||||
The same `yellow|white` and `dashed|solid` enums are reused. A default is
|
||||
white dashed. The generated Polygon stores `color`, `pattern` and
|
||||
`effective_style`; source IDs remain unchanged.
|
||||
|
||||
## Rendering
|
||||
|
||||
The existing lane separator polygon represents the whole lane-pair path.
|
||||
`solid` retains it as one continuous feature. `dashed` samples regular dash
|
||||
polygons along the shared centreline with deterministic spacing and preserves
|
||||
junction cutback. White uses the existing `lane_separators` material; yellow
|
||||
uses a native-only yellow material route. Workbench separates lane separators
|
||||
from turn arrows so their styles remain selectable and visible.
|
||||
|
||||
## Compatibility
|
||||
|
||||
No override means current visual default. Road edges, curbs and centre lines
|
||||
are untouched. Existing `native-road-overrides/v1` files remain valid.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Validate and resolve per-lane-pair style overrides.
|
||||
2. Generate default/dashed/solid style geometry and effective properties.
|
||||
3. Add the native Blender yellow lane-separator material route.
|
||||
4. Add Workbench selection, Chinese editor and automatic staging.
|
||||
5. Test compiler, Workbench, Blender catalog and Nantaizi native build.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Native lane separator style overrides
|
||||
|
||||
## Goal
|
||||
|
||||
Make native same-direction lane separators selectable and persistently editable
|
||||
in Road Workbench, using the style override pattern proven for centre lines.
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- `compileLaneMarkings()` writes one continuous `0.12m` Polygon separator
|
||||
between each adjacent lane pair, identified by `road_id`, `left_lane_index`
|
||||
and `right_lane_index`.
|
||||
- Lane separators currently have fixed light marking material and are displayed
|
||||
together with turn arrows in Workbench.
|
||||
- `native-road-overrides.json` already supports validated, persistent centre
|
||||
line styles by logical native segment. The new type must not alter road-edge,
|
||||
curb or sidewalk geometry.
|
||||
|
||||
## Initial Requirements
|
||||
|
||||
- Selecting a lane separator must show its lane-pair source and a Chinese
|
||||
style editor.
|
||||
- The first style catalog should mirror centre lines: white/yellow and
|
||||
dashed/solid.
|
||||
- The effective style must be generated into GeoJSON, visible in Workbench,
|
||||
and consumed by native Blender/Cesium.
|
||||
- Default output must remain the current white dashed-style separator.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Road-edge lines, curbs, sidewalks, double lines, partial ranges, lane-specific
|
||||
legal restrictions and osm2streets layers.
|
||||
|
||||
## Key Decision
|
||||
|
||||
Each override applies only to the selected adjacent lane pair. This preserves
|
||||
separate marking semantics on roads with three or more lanes.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Selecting a separator exposes a Chinese style editor for its adjacent
|
||||
lane pair; choosing a style stages it automatically.
|
||||
- [ ] Save and regeneration preserve the per-pair style across reloads while
|
||||
unedited separators retain the default.
|
||||
- [ ] GeoJSON, Workbench, Blender and Cesium show the same effective style.
|
||||
- [ ] Tests cover validation, defaults, per-pair isolation, automatic staging
|
||||
and native scene consumption.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-lane-separator-styles",
|
||||
"name": "native-lane-separator-styles",
|
||||
"title": "Native lane separator style overrides",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-17",
|
||||
"completedAt": "2026-08-17",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,64 @@
|
||||
# Native Road Centre-Line Style Overrides Design
|
||||
|
||||
## Architecture
|
||||
|
||||
```text
|
||||
Workbench selects a generated centre-line dash
|
||||
-> segment_id identifies the logical road segment
|
||||
-> staged center-line-style override
|
||||
-> native-road-overrides.json
|
||||
-> compileCenterLines resolves default or effective style
|
||||
-> center_lines.geojson polygons with style fields
|
||||
-> Workbench / Blender / Cesium
|
||||
```
|
||||
|
||||
The authoritative edit is a new override kind, not a mutation of
|
||||
`center_lines.geojson`. It has a deterministic ID based on `segmentId`, so a
|
||||
later recompilation replaces the segment's style rather than accumulating
|
||||
records.
|
||||
|
||||
## Override Contract
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "道路中心线:segment:way/123/1",
|
||||
"kind": "center-line-style",
|
||||
"segmentId": "segment:way/123/1",
|
||||
"color": "yellow",
|
||||
"pattern": "dashed"
|
||||
}
|
||||
```
|
||||
|
||||
`validateOverrides()` accepts only known model segment IDs and the finite
|
||||
string enums `yellow|white` and `dashed|solid`. A style override applies once
|
||||
to the paired forward/backward native roads for that segment. The existing
|
||||
schema version remains `native-road-overrides/v1` because this is an additive
|
||||
kind and old files remain valid.
|
||||
|
||||
## Geometry and Material
|
||||
|
||||
- Default remains yellow dashed: 2m dash, 2m gap, 0.25m width.
|
||||
- `solid` generates deterministic adjacent 2m pieces with no gap. Pieces still
|
||||
undergo the same junction cutback and control-marking exclusion as dashed
|
||||
lines; this avoids creating a solid polygon across an excluded crossing.
|
||||
- Style fields `color`, `pattern`, `dash_length_m`, `dash_gap_m`, and
|
||||
`effective_style` are stored on every generated polygon.
|
||||
- Blender currently maps all `center_lines` to one yellow material, so the
|
||||
native adapter must support a white centre-line material route without
|
||||
changing legacy osm2streets layers. The route must preserve the existing
|
||||
yellow material for default and yellow overrides.
|
||||
|
||||
## Workbench UX
|
||||
|
||||
Selecting a centre-line dash shows a compact Chinese style panel in the
|
||||
existing form area. A select control presents the four named choices. Choosing
|
||||
one stages an override; the existing 保存 / 保存并重新生成 actions remain the
|
||||
only persistence and generation actions. The panel also shows whether the
|
||||
style is default or overridden and identifies the native road segment.
|
||||
|
||||
## Compatibility and Rollback
|
||||
|
||||
Unedited segments generate byte-compatible geometry style defaults apart from
|
||||
the added style properties. Oneway/service filtering and control priority are
|
||||
unchanged. Selecting `--road-provider osm2streets` remains a full rollback.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Add validated `center-line-style` overrides and a helper which resolves the
|
||||
effective default/override style by segment ID.
|
||||
2. Generate yellow/white and dashed/solid centre-line geometry while retaining
|
||||
cutback and control-marking exclusion behaviour; publish effective style
|
||||
properties.
|
||||
3. Extend the native Blender layer adapter/material handling to distinguish
|
||||
white from yellow centre-line features without affecting legacy layers.
|
||||
4. Add Workbench style controls, staging, Chinese selection evidence, and
|
||||
immediate regenerated-layer display.
|
||||
5. Add focused compiler, override, Workbench, Blender catalog, and build-stage
|
||||
tests; compile/check Nantaizi and build Blender/Cesium/preview without the
|
||||
package stage.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
npm run test:native-road
|
||||
npm run test:road-workbench
|
||||
python3 -m unittest discover blender/tests
|
||||
npm run test:build-stages
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:check -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages blender,cesium,preview --road-provider native
|
||||
```
|
||||
|
||||
## Rollback
|
||||
|
||||
Remove the override records or choose `--road-provider osm2streets`; no legacy
|
||||
output is modified.
|
||||
@@ -0,0 +1,62 @@
|
||||
# Native road center line style overrides
|
||||
|
||||
## Goal
|
||||
|
||||
Allow a Road Workbench user to select a generated native road centre line and
|
||||
persistently override its marking style, without turning the generated GeoJSON
|
||||
into the source of truth.
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- Native `center_lines.geojson` currently contains automatic yellow dashed
|
||||
polygons (`2m` dash, `2m` gap, `0.25m` width), each tied to a `segment_id`.
|
||||
- A displayed dash can already be selected and exposes its segment provenance,
|
||||
but has no editing controls.
|
||||
- `native-road-overrides.json` is the persistent authority for existing road
|
||||
and connection edits. The Workbench already stages, validates, saves, then
|
||||
recompiles those overrides.
|
||||
- The generated layer is consumed by both Workbench and native Blender/Cesium;
|
||||
styling must therefore be compiled geometry and use the existing
|
||||
`center_lines` material path, not a Workbench-only display tint.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: Selecting a centre-line dash must expose a Chinese style editor for its
|
||||
logical target and show the current effective style.
|
||||
- R2: Supported styles must include at least solid/dashed and white/yellow
|
||||
marking colours.
|
||||
- R3: Style choices must be stored in `native-road-overrides.json`, validated,
|
||||
reapplied during compilation, and survive a future Workbench launch.
|
||||
- R4: The regenerated GeoJSON must carry source traceability and effective
|
||||
style fields so Workbench, Blender and Cesium show the same marking.
|
||||
- R5: Default automatic centre lines remain unchanged for segments without an
|
||||
override; one-way/service exclusions and control-marking avoidance remain
|
||||
authoritative.
|
||||
|
||||
## Initial Scope Boundary
|
||||
|
||||
- Editing individual dash polygons is out of scope: they are derived pieces,
|
||||
not user-owned objects.
|
||||
- Per-segment control is recommended for the first version because current
|
||||
native segments already stop at junctions and have stable IDs.
|
||||
- Double-line semantics, legal `overtaking` inference, hand-drawn partial
|
||||
ranges, and changes to osm2streets output are out of scope unless explicitly
|
||||
accepted during planning.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] A user can select a centre line, choose a supported style in Chinese,
|
||||
save it, regenerate, and see the result immediately in the Workbench.
|
||||
- [ ] After reload and recompilation, the selected segment retains its style
|
||||
while unedited segments retain the automatic yellow dashed default.
|
||||
- [ ] Native Blender/Cesium uses the same effective style and does not require
|
||||
osm2streets geometry.
|
||||
- [ ] Tests cover schema validation, style geometry, default fallback,
|
||||
persistence/API wiring, and Workbench selection/edit controls.
|
||||
|
||||
## Key Decisions
|
||||
|
||||
- The first version applies one override to the complete native segment between
|
||||
junctions. Individual dash and partial-range editing are deferred.
|
||||
- The initial style catalog is four explicit choices: yellow dashed, white
|
||||
dashed, yellow solid, and white solid. Double-line semantics are deferred.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-road-center-line-styles",
|
||||
"name": "native-road-center-line-styles",
|
||||
"title": "Native road center line style overrides",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-17",
|
||||
"completedAt": "2026-08-17",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,50 @@
|
||||
# Native Road Centre Lines Design
|
||||
|
||||
## Architecture
|
||||
|
||||
The native compiler adds one independent polygon layer:
|
||||
|
||||
```text
|
||||
canonical bidirectional segment + trimmed OSM centreline
|
||||
-> repeated 2m x 0.25m dash polygons
|
||||
-> native-road/layers/center_lines.geojson
|
||||
-> Workbench / native Blender adapter / Cesium
|
||||
```
|
||||
|
||||
It does not read osm2streets `center_lines.geojson`. The legacy layer is a
|
||||
visual baseline only.
|
||||
|
||||
## Placement
|
||||
|
||||
- A candidate must have exactly forward and backward native roads for the same
|
||||
`segmentId`, neither be `highway=service`, and have a valid trimmed OSM
|
||||
centreline.
|
||||
- Dashes use the legacy observed dimensions: 2m length, 0.25m width, with a
|
||||
deterministic 2m gap. The first dash starts at a fixed segment-local offset
|
||||
so rebuilds do not drift.
|
||||
- The line is trimmed with the same `junctionPlans` cutback used by native lane
|
||||
centreline generation. Any dash intersecting a crosswalk or stop line is
|
||||
omitted, preserving the control-marking priority already established.
|
||||
- One-way and service segments have no generated feature. A degenerate source
|
||||
line produces a diagnostic rather than malformed geometry.
|
||||
|
||||
## Contracts
|
||||
|
||||
`layers/center_lines.geojson` is a Polygon FeatureCollection. Every feature
|
||||
has `native_id`, `segment_id`, `directional_road_ids`, `osm_way_ids`,
|
||||
`dash_index`, `dash_length_m`, `dash_gap_m`, `placement_rule`, and
|
||||
`provenance="native-road-center-line/v1"`.
|
||||
|
||||
`compiled.json.layers.centerLines`, comparison count
|
||||
`nativeCenterLineFeatures`, and `build-area.js` required native records use the
|
||||
same filename. `catalog.NATIVE_ROAD_LAYERS` maps source `center_lines` to the
|
||||
existing `center_lines` material layer.
|
||||
|
||||
The Workbench loads this source into the marking overlay, exposes a Chinese
|
||||
toggle, and identifies it as `道路中心虚线` on selection.
|
||||
|
||||
## Compatibility
|
||||
|
||||
The change is additive within `native-road/`; `--road-provider osm2streets`
|
||||
remains unaffected and is rollback. A native Blender build treats a missing
|
||||
layer as an input error rather than silently omitting it.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Add centre-dash constants and a pure native compiler routine based on the
|
||||
shared bidirectional segment model and junction-trimmed centreline.
|
||||
2. Filter invalid, one-way, service, and control-marking-conflicting dashes;
|
||||
record actionable diagnostics for invalid geometry only.
|
||||
3. Write `center_lines.geojson`, compilation/comparison counts, native build
|
||||
records, required-layer checks, and Blender material adapter mapping.
|
||||
4. Add Workbench API state, Chinese toggle, summary count, rendering, and
|
||||
source evidence for a selected centre dash.
|
||||
5. Add focused native and Workbench tests for generation, skips, control
|
||||
avoidance, provenance, and file/API contracts.
|
||||
6. Run native/unit/workbench/build-stage tests, compile/check Nantaizi, then
|
||||
validate `blender,cesium,preview --road-provider native` without `package`.
|
||||
|
||||
## Rollback
|
||||
|
||||
Choose `--road-provider osm2streets`; no legacy layer or published package is
|
||||
modified.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Native road center lines
|
||||
|
||||
## Goal
|
||||
|
||||
Restore the existing osm2streets-style road-centre dashed markings in the
|
||||
native-road provider before treating native output as ready for broader quality
|
||||
gates or new-area validation.
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- Nantaizi's existing `osm2streets_web_out/center_lines.geojson` has 885
|
||||
`type="center line"` polygons. Its sampled dash geometry is approximately
|
||||
2.00m long by 0.25m wide.
|
||||
- The legacy extraction excludes centre lines that overlap crosswalk zones and
|
||||
that fall on service driving polygons (`build-osm2streets-qgis.js:474-480`).
|
||||
- Native output currently has only same-direction `lane_separators`; it has no
|
||||
`center_lines.geojson`, so a bidirectional road's directional carriageways
|
||||
lack their visual and semantic divider.
|
||||
- Existing Blender already owns a `center_lines` / `Center Line` material
|
||||
layer. Native must adapt to it rather than creating a second scene registry.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: For supported non-service, bidirectional native road segments, emit a
|
||||
`center_lines.geojson` dashed centre divider derived from the canonical OSM
|
||||
centreline and the segment's two directional carriageways.
|
||||
- R2: Match the established visual baseline: yellow 0.25m-wide, 2m-long
|
||||
dashes, clipped away from ordinary junction cutbacks and control markings.
|
||||
- R3: Preserve source traceability: each dash records its native segment,
|
||||
source OSM way, involved directional roads, placement interval/rule, and a
|
||||
dedicated provenance value.
|
||||
- R4: Add the layer to compilation records, Road Workbench display/selection
|
||||
in Chinese, and the existing Blender/Cesium `center_lines` material path.
|
||||
- R5: Do not render a centre divider on one-way or `highway=service` segments;
|
||||
report invalid geometry instead of fabricating a divider.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Native Nantaizi output contains a valid `layers/center_lines.geojson`
|
||||
with source-traceable 2m x 0.25m dashed polygons.
|
||||
- [ ] No native centre dash intersects a generated crosswalk or vehicle stop
|
||||
line, and no dash reaches into the supported junction surface cutback.
|
||||
- [ ] The Workbench can toggle and select centre lines, presenting their
|
||||
source and rule in Chinese rather than calling them lane separators.
|
||||
- [ ] Native `blender,cesium,preview` consumes the layer using the existing
|
||||
`center_lines` material and does not run `package`.
|
||||
- [ ] Tests cover two-way generation, one-way/service skips, control-marking
|
||||
avoidance, provenance, output contract, and Workbench wiring.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Centre-line editing overrides, solid/double-centre-line semantics, arbitrary
|
||||
OSM `overtaking` interpretation, lane colouring changes, and other areas.
|
||||
|
||||
## Key Decision
|
||||
|
||||
This task deliberately matches the existing stable visual layer first. It does
|
||||
not claim to infer a country-wide legal marking taxonomy from sparse OSM tags.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-road-center-lines",
|
||||
"name": "native-road-center-lines",
|
||||
"title": "Native road center lines",
|
||||
"description": "Align native-road output with the existing center_lines layer before broader quality work.",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-17",
|
||||
"completedAt": "2026-08-17",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-13-native-road-compiler",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,66 @@
|
||||
# Native Road Control Markings Design
|
||||
|
||||
## Architecture
|
||||
|
||||
The native compiler remains the source of truth. It extends its canonical OSM
|
||||
parse with marked crossing nodes, then derives two additive polygon layers from
|
||||
the crossing evidence, native directed lanes, and native junction plans:
|
||||
|
||||
```text
|
||||
OSM crossing node + native directed lane centerlines + junction plans
|
||||
|
|
||||
+-- crosswalks.geojson: six zebra stripe polygons per safe crossing
|
||||
|
|
||||
+-- vehicle_stop_lines.geojson: one safe approach stop-line polygon
|
||||
```
|
||||
|
||||
No osm2streets GeoJSON is read. Existing crossing geometry helpers may be
|
||||
extracted or adapted only when they operate on native lane data and retain
|
||||
native provenance.
|
||||
|
||||
## Source And Placement
|
||||
|
||||
- A source node is eligible only when `highway=crossing` and
|
||||
`crossing:markings` is not `no`, `none`, or `unmarked`.
|
||||
- The compiler finds native roads containing the crossing's OSM node and uses
|
||||
the nearest compatible directed lane centerline to obtain the road tangent.
|
||||
- Crosswalk stripes are perpendicular to that tangent and constrained to the
|
||||
native road width. Existing fixed zebra dimensions are retained initially:
|
||||
six 0.45m stripes with 0.45m gaps, 0.45m stripe width, and a highway-based
|
||||
stripe length.
|
||||
- A stop line is generated only when the crossing can be associated with a
|
||||
supported junction approach and a safe outside-of-junction side. Otherwise
|
||||
the crosswalk may remain valid but the missing stop line is diagnostic.
|
||||
- Duplicate nearby crossing nodes use a stable cluster representative so one
|
||||
physical crosswalk does not produce duplicate stripes.
|
||||
|
||||
## Contracts
|
||||
|
||||
`layers/crosswalks.geojson` and `layers/vehicle_stop_lines.geojson` are Polygon
|
||||
FeatureCollections. Each feature records its crossing OSM node, source OSM way,
|
||||
native directed road/lane when available, direction, placement method, and
|
||||
`native-road-crosswalk/v1` or `native-road-stop-line/v1` provenance.
|
||||
|
||||
The Workbench API returns both layers as `state.layers.crosswalks` and
|
||||
`state.layers.vehicleStopLines`. Its browser map uses separate toggleable
|
||||
layers and selection evidence; the scene-preview toggle leaves real control
|
||||
markings visible.
|
||||
|
||||
`catalog.NATIVE_ROAD_LAYERS` maps the sources to existing `crosswalks` and
|
||||
`vehicle_stop_lines` material layers. This native adapter must not add them to
|
||||
the osm2streets `ROAD_LAYERS` / `SCENE_LAYERS` registry.
|
||||
|
||||
## Compatibility And Rollback
|
||||
|
||||
The new files are additive under `native-road/layers/`. Existing osm2streets
|
||||
output and QGIS input are unchanged. Selecting `--road-provider osm2streets`
|
||||
remains rollback. A native Blender build treats a missing new layer as an error
|
||||
rather than silently omitting a visible marking.
|
||||
|
||||
## Risks
|
||||
|
||||
- Crossing nodes can be detached from a routable road or lie on an ambiguous
|
||||
multi-road segment. These become diagnostics rather than guessed geometry.
|
||||
- A physical crossing split into several OSM nodes must deduplicate stably.
|
||||
- A crosswalk near a non-supported junction may get stripes but no valid stop
|
||||
line; this difference must be exposed in workbench provenance.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Extend the native OSM parse/model with marked crossing-node evidence while
|
||||
preserving existing road IDs and parser behavior.
|
||||
2. Add native crossing clustering, tangent resolution from directed lane
|
||||
centerlines, stripe geometry, safe stop-line placement, and source
|
||||
diagnostics.
|
||||
3. Persist the two new layers in `compile-native-roads.js`, comparison counts,
|
||||
native build records, required native layer checks, and existing Blender
|
||||
material mappings.
|
||||
4. Add Workbench API/state fields, Chinese layer toggles, selection evidence,
|
||||
and summary counts while retaining scene-preview behavior.
|
||||
5. Add focused fixtures for marked and unmarked crossings, duplicate cluster
|
||||
handling, a missing native-lane diagnostic, and output layer contracts.
|
||||
6. Run native/unit/workbench/build-stage tests, compile/check Nantaizi, then
|
||||
build `blender,cesium,preview --road-provider native` without `package/`.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
npm run test:native-road
|
||||
npm run test:road-workbench
|
||||
npm run test:build-stages
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:check -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json \
|
||||
--stages blender,cesium,preview --road-provider native
|
||||
```
|
||||
|
||||
Rollback is selecting `--road-provider osm2streets`; no legacy output path is
|
||||
changed.
|
||||
@@ -0,0 +1,61 @@
|
||||
# Native road control markings
|
||||
|
||||
## Goal
|
||||
|
||||
Give Nantaizi's native-road provider inspectable, source-traceable crosswalk
|
||||
and vehicle stop-line geometry, so intersection control markings do not depend
|
||||
on the osm2streets render output.
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- Native road output already owns road surfaces, sidewalks, lane separators,
|
||||
repeated direction arrows, and explicit junction-turn arrows.
|
||||
- Nantaizi's OSM input contains explicit marked crossings, including zebra and
|
||||
traffic-signal crossings. The existing osm2streets output currently has 48
|
||||
crosswalk-stripe polygons and 8 stop-line polygons.
|
||||
- `build-osm2streets-qgis.js:1067` derives these markings from crossing nodes
|
||||
plus osm2streets driving lanes. Native must not read that rendered geometry;
|
||||
it can reuse only the tested geometry rules after adapting them to native
|
||||
directed lanes and junction plans.
|
||||
- Existing Blender materials already provide `crosswalks` and
|
||||
`vehicle_stop_lines`; native may map into them without changing the legacy
|
||||
osm2streets layer registry.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: Native compilation uses explicit marked OSM crossing nodes only, and
|
||||
emits crosswalk-stripe polygons plus approach stop lines only where safe
|
||||
native directed-road placement exists.
|
||||
- R2: Each generated feature preserves crossing node, OSM road, native road or
|
||||
lane, direction, placement method, and relevant junction provenance.
|
||||
- R3: The Road Workbench independently toggles, selects, and describes native
|
||||
crosswalks and stop lines in Chinese.
|
||||
- R4: Native Blender and Cesium builds consume both layers with the existing
|
||||
control-marking materials; no `package/` publication is part of validation.
|
||||
- R5: Missing compatible road context, ambiguous geometry, and unsupported
|
||||
crossing inputs are diagnostics; the compiler must not invent a crossing.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Nantaizi native output contains valid `crosswalks.geojson` and
|
||||
`vehicle_stop_lines.geojson` features with source-traceable properties,
|
||||
without reading osm2streets rendered layers.
|
||||
- [ ] A Workbench user can toggle and select either marking type and see the
|
||||
crossing node, associated road/direction, and placement evidence.
|
||||
- [ ] Native Blender/Cesium output contains both marking types using existing
|
||||
materials, after `blender,cesium,preview --road-provider native` and without
|
||||
publishing `package/`.
|
||||
- [ ] Tests cover a marked crossing, an unmarked crossing skip, a missing or
|
||||
ambiguous native-road placement skip, and output-layer contract checks.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Hand-placed control-marking overrides, traffic-signal state-machine changes,
|
||||
freehand polygon editing, processing another region, and importing
|
||||
osm2streets-rendered crosswalk geometry.
|
||||
|
||||
## Key Decision
|
||||
|
||||
The first version is explicit-OSM-only. This is feasible for Nantaizi and
|
||||
keeps control markings evidence-backed; missing data remains a diagnostic for
|
||||
OSM improvement rather than a silent geometric guess.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-road-control-markings",
|
||||
"name": "native-road-control-markings",
|
||||
"title": "Native road control markings",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-17",
|
||||
"completedAt": "2026-08-17",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-13-native-road-compiler",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Native road edge markings
|
||||
|
||||
## Goal
|
||||
|
||||
TBD.
|
||||
|
||||
## Requirements
|
||||
|
||||
- TBD
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] TBD
|
||||
|
||||
## Notes
|
||||
|
||||
- Keep `prd.md` focused on requirements, constraints, and acceptance criteria.
|
||||
- Lightweight tasks can remain PRD-only.
|
||||
- For complex tasks, add `design.md` for technical design and `implement.md` for execution planning before `task.py start`.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-road-edge-markings",
|
||||
"name": "native-road-edge-markings",
|
||||
"title": "Native road edge markings",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-17",
|
||||
"completedAt": "2026-08-17",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Check compiler and workbench marking contracts."}
|
||||
{"file":".trellis/spec/blender/testing.md","reason":"Run required Blender test suite."}
|
||||
{"file":".trellis/spec/guides/cross-layer-thinking-guide.md","reason":"Verify browser, compiler, and Blender consume the same output."}
|
||||
@@ -0,0 +1,22 @@
|
||||
# Design
|
||||
|
||||
The compiler owns marking semantics. `center-line-style` remains segment
|
||||
scoped because a bidirectional road segment shares one centre-line decision.
|
||||
`edge-line-style` is scoped to `{ roadId, side }`, since a directional
|
||||
carriageway has independently editable left and right outside edges.
|
||||
|
||||
`double: true` is valid only with yellow solid centre lines. Generation emits
|
||||
two 0.32m-separated parallel polygons for each normal solid mark, preserving
|
||||
junction cutback and crosswalk/stop-line clearance. This keeps the existing
|
||||
solid line implementation and avoids a separate geometry pipeline.
|
||||
|
||||
Road edge lines use the existing road-edge offset geometry. A solid style is
|
||||
one continuous buffered offset line; a dashed style uses deterministic 2m
|
||||
marks at 4m spacing. Both carry `effective_style`, source IDs, side, and
|
||||
native provenance.
|
||||
|
||||
The Workbench uses one marking form with an explicit selected target type.
|
||||
Only a centre-line target exposes the double-yellow option; lane separators
|
||||
and edge lines retain the ordinary colour/pattern choices. Staged changes pass
|
||||
through the existing save and regenerate flow, so browser, compiler and
|
||||
Blender read the same persisted override.
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Native-road output, override, and stage contracts."}
|
||||
{"file":".trellis/spec/blender/asset-generation.md","reason":"Native provider adapter and Blender output requirements."}
|
||||
{"file":".trellis/spec/guides/artifact-parity-guide.md","reason":"Intentional render-output change validation."}
|
||||
@@ -0,0 +1,10 @@
|
||||
# Implementation
|
||||
|
||||
1. Extend override validation and native marking generation for explicit
|
||||
double-yellow centre lines and per-side edge-line styles.
|
||||
2. Extend the Chinese Workbench selection/form flow without adding another
|
||||
editor or output format.
|
||||
3. Add compiler and Workbench regression coverage for valid and invalid
|
||||
payloads, generated geometry, and layer adapter completeness.
|
||||
4. Compile/check Nantaizi, run native/Workbench/build-stage/Blender tests,
|
||||
then run the native Blender/Cesium/preview chain.
|
||||
@@ -0,0 +1,36 @@
|
||||
# Native road marking semantics
|
||||
|
||||
## Goal
|
||||
|
||||
Finish the native road marking model so the Nantaizi Road Workbench can
|
||||
persist meaningful centre-line and road-edge-line styles, regenerate native
|
||||
geometry deterministically, and deliver the same result to Blender/Cesium.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Add an explicit double-yellow-solid centre-line semantic. It must not be a
|
||||
generic double-line switch that permits invalid colour/pattern combinations.
|
||||
- Keep all marking edits area-local in `native-road-overrides.json`; generated
|
||||
GeoJSON remains derived output.
|
||||
- Make road edge lines selectable and style-editable in the Chinese Road
|
||||
Workbench, alongside existing centre lines and lane separators.
|
||||
- Preserve the default Nantaizi output unless a user supplies an override.
|
||||
- Native geometry must remain independent of osm2streets render geometry.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] A `center-line-style` override with `double: true`, `yellow`, and
|
||||
`solid` emits two offset centre-line polygons with an explicit effective
|
||||
style, while invalid double combinations are rejected.
|
||||
- [ ] An `edge-line-style` override targets one directional road side and
|
||||
supports white/yellow plus solid/dashed styles.
|
||||
- [ ] The Workbench exposes Chinese selection feedback, stages the correct
|
||||
override payload, and only shows the double-yellow choice for centre lines.
|
||||
- [ ] Nantaizi native compile/check, Workbench/native tests, Blender pure
|
||||
tests, and native Blender/Cesium/preview build pass.
|
||||
|
||||
## Notes
|
||||
|
||||
- Keep `prd.md` focused on requirements, constraints, and acceptance criteria.
|
||||
- Lightweight tasks can remain PRD-only.
|
||||
- For complex tasks, add `design.md` for technical design and `implement.md` for execution planning before `task.py start`.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-road-marking-semantics",
|
||||
"name": "native-road-marking-semantics",
|
||||
"title": "Native road marking semantics",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-17",
|
||||
"completedAt": "2026-08-17",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Native geometry correctness and output contracts."}
|
||||
{"file":".trellis/spec/blender/testing.md","reason":"Blender validation requirements."}
|
||||
@@ -0,0 +1,18 @@
|
||||
# Design
|
||||
|
||||
Each approach contributes its two carriageway-edge points at the common
|
||||
cutback distance. Points are ordered around the junction node. For each pair
|
||||
from adjacent approaches, the compiler samples a deterministic quadratic
|
||||
Bezier whose control point follows the pedestrian-side curb arc toward the
|
||||
junction. Rounded plans use a larger cutback than the legacy straight envelope
|
||||
so this visible curb shape still contains all turning connectors. Approach road
|
||||
surfaces terminate at the same cutback, so they cannot cover the junction
|
||||
outline in 3D output.
|
||||
|
||||
The curve is accepted only when the support intersection is finite, the pair
|
||||
belongs to different approaches, and the resulting ring remains valid and
|
||||
contains all published connector coordinates. Otherwise the original straight
|
||||
chord remains for that corner and the plan reports a mixed/fallback boundary.
|
||||
|
||||
Lane connectors remain a separate vehicle-path layer. This task changes only
|
||||
the road/intersection outline and sidewalk-corner shape.
|
||||
@@ -0,0 +1,2 @@
|
||||
{"file":".trellis/spec/pipeline/cli-and-stages.md","reason":"Native geometry and build-stage contracts."}
|
||||
{"file":".trellis/spec/guides/artifact-parity-guide.md","reason":"Intentional geometry output change validation."}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Implementation
|
||||
|
||||
1. Build a rounded junction boundary from ordered approach-edge records with
|
||||
tangent support-line intersections and deterministic curve samples.
|
||||
2. Expose boundary mode/provenance and preserve containment fallback.
|
||||
3. Extend focused native-road tests for curved ordinary intersections.
|
||||
4. Validate Nantaizi compile/check, workbench tests, and native 3D build.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Rounded native road junctions
|
||||
|
||||
## Goal
|
||||
|
||||
Replace the octagonal native junction outline with smooth, tangentially joined
|
||||
road-edge corners for ordinary Nantaizi T and cross junctions.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Junction surface boundaries must connect adjacent approach carriageway edges
|
||||
with a smooth outward curve rather than a straight octagonal chord.
|
||||
- Sidewalk corner surfaces must use the same rounded boundary concept so road
|
||||
and pedestrian geometry do not disagree visually.
|
||||
- Preserve a deterministic straight-edge fallback and an explicit diagnostic
|
||||
when a corner cannot be safely constructed.
|
||||
- Do not change lane connector semantics or derive geometry from osm2streets.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] Ordinary cross/T fixtures generate rounded junction polygons with more
|
||||
than the prior eight straight boundary vertices and `boundary_mode` records
|
||||
the chosen style.
|
||||
- [x] Connector containment remains valid and degenerate geometry falls back
|
||||
without publishing self-intersecting polygons.
|
||||
- [x] Nantaizi compile/check and native Blender/Cesium/preview succeed.
|
||||
|
||||
## Verification Record
|
||||
|
||||
- Native geometry and Workbench tests passed.
|
||||
- Nantaizi native compile/check passed with `ok=true`.
|
||||
- Native Blender/Cesium/preview verification was completed manually in a
|
||||
working Blender environment.
|
||||
|
||||
## Notes
|
||||
|
||||
- Keep `prd.md` focused on requirements, constraints, and acceptance criteria.
|
||||
- Lightweight tasks can remain PRD-only.
|
||||
- For complex tasks, add `design.md` for technical design and `implement.md` for execution planning before `task.py start`.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-rounded-junctions",
|
||||
"name": "native-rounded-junctions",
|
||||
"title": "Rounded native road junctions",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-17",
|
||||
"completedAt": "2026-08-18",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-13-native-road-compiler",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,51 @@
|
||||
# Design
|
||||
|
||||
## Provider Boundary
|
||||
|
||||
`build-area.js` will resolve a native provider by default. Native stages consume only OSM, native
|
||||
road overrides, and `native-road/` outputs. Legacy `intermediates`, `reimport`, and osm2streets
|
||||
Blender input remain behind explicit legacy selection and keep their existing output contract.
|
||||
|
||||
## Native Data Flow
|
||||
|
||||
```text
|
||||
OSM + native-road-overrides.json
|
||||
-> compile-native-roads.js
|
||||
-> native-road/layers/*.geojson + compiled.json + traffic-signals.json
|
||||
-> Blender generate_scene.py --native-road --traffic-signals
|
||||
-> .blend
|
||||
-> Cesium/package GLB + manifest + runtime/traffic-signals.json
|
||||
-> preview HTML + optional native vehicle route
|
||||
```
|
||||
|
||||
The native compiler is the sole authority for road geometry, lane semantics, intersection surfaces,
|
||||
stop lines, and signal runtime. No native stage reads `geojsonDir` or legacy assembly files.
|
||||
|
||||
## Preview Route
|
||||
|
||||
Add a native route adapter that derives route segments from `compiled.json` model roads/endpoints and
|
||||
native lane/connector geometry. If the native route cannot be built for a valid area, preview remains
|
||||
usable without vehicles and records a warning; missing legacy osm2streets lane polygons is never a
|
||||
hard failure on the native path.
|
||||
|
||||
## Package Runtime
|
||||
|
||||
The package stage will copy the native-road signal runtime into its staging runtime directory and
|
||||
declare it in the package manifest. Preview receives the package-relative runtime URI. Legacy signal
|
||||
runtime publication remains conditional on the legacy provider.
|
||||
|
||||
## Configuration And Compatibility
|
||||
|
||||
`roadProvider` defaults to `native`; native stages are the default stage set. QGIS config fields and
|
||||
legacy stage aliases remain accepted for explicit migration/debug commands, but native manifests and
|
||||
docs do not claim them as inputs.
|
||||
|
||||
The Workbench may continue to read an existing `geojsonDir` only for an explicitly labeled reference
|
||||
layer and comparison counters. Those reads are optional, isolated from native compile state, and must
|
||||
never become required inputs for `/api/state`, native editing, package publication, or preview.
|
||||
|
||||
## Rollback
|
||||
|
||||
The original QGIS/osm2streets branch baseline is tagged
|
||||
`qgis-original-baseline-20260818` on `main`. Reverting the native-only work can therefore use the tag
|
||||
or switch to `main`; no destructive cleanup of legacy scripts is required.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,27 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Add native provider/default-stage contracts and explicit legacy provider routing.
|
||||
2. Make package publication and stage manifests consume native runtime artifacts.
|
||||
3. Replace preview's hard dependency on osm2streets lane/network/intersection files with a native
|
||||
route adapter and optional-route behavior.
|
||||
4. Audit Blender, Cesium, preview, diagnostics, and area config for native-path legacy reads; retain
|
||||
only the Workbench's optional, clearly labeled osm2streets reference layer.
|
||||
5. Update README/config/spec documentation and add dependency-boundary regression tests.
|
||||
6. Run native-only tests with legacy directories absent, then run focused legacy compatibility tests.
|
||||
7. Run area compile/check plus available Blender/Cesium/preview validation; record any environment-only
|
||||
limitation without weakening native contracts.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
npm run test:native-road
|
||||
npm run test:traffic-signals
|
||||
npm run test:road-workbench
|
||||
npm run test:preview-assets
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:check -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
node --check scripts/build-area.js
|
||||
```
|
||||
|
||||
The final gate must also exercise a native-only config/output directory without QGIS/osm2streets
|
||||
intermediates and assert package/runtime/preview outputs.
|
||||
@@ -0,0 +1,63 @@
|
||||
# Complete native-only pipeline replacement
|
||||
|
||||
## Goal
|
||||
|
||||
Make the native road compiler the primary and complete production path. A normal build of an
|
||||
area must generate Blender, Cesium/package, and preview outputs directly from OSM plus native
|
||||
artifacts without installing or running QGIS, GDAL, or osm2streets.
|
||||
|
||||
The existing QGIS/osm2streets workflow remains available only as an explicit legacy/debug path and
|
||||
an optional Workbench reference layer. It is protected by the `qgis-original-baseline-20260818` tag
|
||||
on `main`.
|
||||
|
||||
## Confirmed Current Gaps
|
||||
|
||||
- `normalizeAreaConfig()` defaults `stages.intermediates` to true and `blender.roadProvider` to
|
||||
`osm2streets`.
|
||||
- `build-area.js` sends `intermediates` to `build-osm2streets-qgis.js` and `reimport` to GDAL/QGIS.
|
||||
- Native Blender input exists, but `preview` still requires `osm2streets_web_out/lane_polygons.geojson`,
|
||||
`network.json`, and `intersection_surface.geojson` for vehicle-route generation.
|
||||
- Package/runtime publication still copies `outputs.<area>.trafficSignals` from the legacy GeoJSON
|
||||
directory instead of the native-road runtime artifact.
|
||||
- README, config templates, stage names, and manifest descriptions still present QGIS as the normal
|
||||
workflow.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: Native is the default road provider and default build stages do not invoke QGIS, GDAL, or
|
||||
osm2streets.
|
||||
- R2: Native build generates all required road, lane, marking, junction, stop-line, and traffic-signal
|
||||
inputs consumed by Blender and preview; no native stage reads `osm2streets_web_out`.
|
||||
- R3: Native package publication copies runtime traffic signals and any other runtime descriptors
|
||||
from native-road outputs, with stable package-relative URIs.
|
||||
- R4: Native preview route generation uses native lane/road topology and native intersection geometry,
|
||||
or explicitly omits the optional vehicle route when native route data is unavailable; it must not
|
||||
fail because legacy osm2streets files are absent.
|
||||
- R5: QGIS/osm2streets stages remain callable only through an explicit legacy provider/stage selection
|
||||
and are not part of native defaults or native manifests. Workbench may display their existing
|
||||
GeoJSON as a clearly labeled reference/comparison layer, but native editing, compile, package, and
|
||||
preview results must not depend on it.
|
||||
- R6: Documentation and config templates describe native-only as the primary workflow and clearly
|
||||
mark the legacy path as transitional/debug-only.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] A clean native-only area build succeeds with QGIS absent and no `osm2streets-js-node` runtime
|
||||
call, producing `.blend`, package manifest/GLB, runtime traffic signals, and preview HTML.
|
||||
- [ ] Native preview opens when `osm2streets_web_out/` is absent; optional vehicle cruise either uses
|
||||
native route data or is omitted with a visible non-fatal diagnostic.
|
||||
- [ ] Native package manifest contains runtime traffic-signal anchors sourced from
|
||||
`native-road/traffic-signals.json`; disabled signals remain absent from runtime.
|
||||
- [ ] Workbench edits survive save, native recompile, package publication, and preview reload without
|
||||
QGIS or osm2streets files.
|
||||
- [ ] Explicit legacy QGIS/osm2streets commands still pass their existing focused tests, but no longer
|
||||
run when using native defaults.
|
||||
- [ ] Workbench remains usable when the optional osm2streets reference directory is absent; only the
|
||||
reference layer/comparison counters degrade to unavailable.
|
||||
- [ ] Tests assert the native-only dependency boundary and the QGIS baseline tag is documented.
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- Reimplementing QGIS editing features in the native compiler beyond existing Workbench controls.
|
||||
- Deleting legacy scripts or historical output files in this task; they remain rollback/debug tooling.
|
||||
- Changing Blender/Cesium visual semantics unrelated to removing the input dependency.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-only-pipeline-replacement",
|
||||
"name": "native-only-pipeline-replacement",
|
||||
"title": "Complete native-only pipeline replacement",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-18",
|
||||
"completedAt": "2026-08-18",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": null,
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,55 @@
|
||||
# Design
|
||||
|
||||
## Source Of Truth And Migration
|
||||
|
||||
The native compiler owns a versioned area-local signal model and override
|
||||
artifact. It is generated from OSM controls and native geometry inputs, then
|
||||
edited by the Workbench without QGIS. The existing
|
||||
`traffic_signal_assemblies.geojson` is a migration adapter: it can be imported
|
||||
into the native model and exported for legacy QGIS/reimport workflows, but a
|
||||
native compile never requires it to exist.
|
||||
|
||||
The existing `buildTrafficSignalFeatures()` and validation functions remain the
|
||||
compatibility implementation for initial generation and import/export. Imported
|
||||
features retain their legacy `signal_uid` where valid; newly generated native
|
||||
features use the same deterministic identity rule so downstream runtime IDs do
|
||||
not fork.
|
||||
|
||||
## Workbench API And Editing
|
||||
|
||||
`GET /api/state` adds the normalized native signal model, source control
|
||||
metadata, migration provenance, and derived runtime signal records. A signal
|
||||
edit is represented as an atomic replacement of the validated native signal
|
||||
override artifact through a dedicated signal save endpoint. A separate import
|
||||
or export action handles the legacy QGIS collection; road overrides remain in
|
||||
their existing file and schema.
|
||||
|
||||
The browser uses stable `signal_uid` values. It supports:
|
||||
|
||||
- generate: choose an OSM traffic-signal control and arm, then create the
|
||||
deterministic assembly using the existing generator contract;
|
||||
- move: update the Point coordinates while retaining stop-line/source fields;
|
||||
- rotate: update `heading_deg` with normalized degrees;
|
||||
- delete: remove the assembly from the editable collection;
|
||||
- edit enabled state, display ID, mast reach, z offset, and phase group.
|
||||
|
||||
Every save validates uniqueness, identity, finite geometry, source references,
|
||||
and field ranges before an atomic write. Deleted features are absent from the
|
||||
runtime output; disabled features remain in the editable/QGIS layer but are
|
||||
omitted by `buildTrafficSignalsFromFeatures()`.
|
||||
|
||||
## Delivery Flow
|
||||
|
||||
The native road compile result includes signal assemblies and derived runtime
|
||||
metadata without adding them to road geometry layers. Native Blender/Cesium
|
||||
stages consume `traffic_signals.json` and dynamic GLB inputs generated directly
|
||||
from the native model. The legacy QGIS adapter may materialize the old GeoJSON,
|
||||
but it is not in the native build's critical path.
|
||||
|
||||
## Compatibility And Rollback
|
||||
|
||||
QGIS reimport continues to read/export the compatibility GeoJSON while the
|
||||
legacy path remains unchanged. If native signal editing fails validation, the
|
||||
previous atomic native override remains in place and the user receives a
|
||||
field-level error. Rollback is selecting the legacy provider or exporting the
|
||||
last native state to the QGIS adapter.
|
||||
@@ -0,0 +1 @@
|
||||
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
|
||||
@@ -0,0 +1,31 @@
|
||||
# Implementation Plan
|
||||
|
||||
1. Define the native signal model/override artifact and migration adapter;
|
||||
reuse existing generation, validation, and deterministic signal UID rules.
|
||||
2. Add QGIS import/export commands that translate
|
||||
`traffic_signal_assemblies.geojson` to/from the native artifact without
|
||||
making native compile depend on QGIS.
|
||||
3. Extend native compile/workbench state to expose signal assemblies, OSM
|
||||
controls/arms, and derived runtime provenance.
|
||||
4. Add validated atomic signal save operations for generate, move, rotate,
|
||||
delete, and field edits; preserve legacy ID compatibility.
|
||||
5. Add Workbench map styling, selection, editing controls, dirty state, save,
|
||||
compile/reload, and clear error handling for signal assemblies.
|
||||
6. Ensure native Blender/Cesium/preview stages consume runtime signal data
|
||||
generated from the native model, while legacy stages remain compatible.
|
||||
7. Add focused traffic-signal, Workbench, migration, and cross-layer round-trip tests;
|
||||
run the existing legacy traffic-signal and preview suites.
|
||||
|
||||
## Validation
|
||||
|
||||
```bash
|
||||
npm run test:traffic-signals
|
||||
npm run test:preview-assets
|
||||
npm run test:road-workbench
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:check -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
```
|
||||
|
||||
Manual acceptance must cover native-only generate/move/rotate/delete -> save ->
|
||||
recompile -> runtime JSON and preview, plus QGIS import/export compatibility;
|
||||
disabled signals must be omitted from runtime output.
|
||||
@@ -0,0 +1,55 @@
|
||||
# Native traffic signal parity with QGIS
|
||||
|
||||
## Goal
|
||||
|
||||
Move traffic-signal ownership from the QGIS editing chain into the native road
|
||||
compiler and Workbench. Existing QGIS signal assemblies remain import/export
|
||||
compatibility data during migration, but native overrides become the long-term
|
||||
source for generation, editing, and Blender/Cesium/preview delivery.
|
||||
|
||||
## Confirmed Facts
|
||||
|
||||
- QGIS currently edits `traffic_signal_assemblies.geojson`; this is the
|
||||
migration input/output contract, not the desired long-term authority.
|
||||
- `scripts/reimport-gpkg.js` validates and reimports that legacy layer.
|
||||
- `scripts/build-area.js` currently derives runtime `traffic_signals.json` from
|
||||
the edited assemblies; the native path must replace this dependency.
|
||||
- Existing native road layers do not read traffic-signal artifacts. Historical
|
||||
native Cesium work intentionally omitted signal runtime assets.
|
||||
- Stable signal identity and editable fields already include `signal_uid`,
|
||||
enabled state, source/control/approach IDs, arm direction, pose, mast reach,
|
||||
and phase-group/runtime data.
|
||||
|
||||
## Requirements
|
||||
|
||||
- R1: Native compile/workbench must own a versioned area-local signal model and
|
||||
override artifact, with an explicit one-time/import compatibility path from
|
||||
existing QGIS assemblies.
|
||||
- R2: Workbench state must expose signal provenance and the existing editable
|
||||
signal fields using stable IDs, and support generating, moving, rotating,
|
||||
and deleting signal assemblies.
|
||||
- R3: Native compile and preview delivery must generate runtime signal data
|
||||
directly from the native model, preserving enabled/disabled state, arm
|
||||
direction/pose, and phase-group data without requiring QGIS.
|
||||
- R4: A compatibility adapter must import/export the existing QGIS assembly
|
||||
format during migration and preserve legacy signal IDs where possible.
|
||||
|
||||
## Scope Boundary
|
||||
|
||||
- Native signal model and overrides are the long-term source of truth.
|
||||
- QGIS GeoJSON/GeoPackage support is transitional compatibility only; do not
|
||||
make native compile depend on QGIS or regenerate native edits from QGIS.
|
||||
- Do not redesign signal geometry, timing logic, or vehicle behavior in this
|
||||
task.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] A native compile/reopen round trip preserves edited signal assemblies,
|
||||
stable IDs, enabled state, and provenance without QGIS running.
|
||||
- [ ] The Road Workbench can generate, inspect, move, rotate, and delete signal
|
||||
assemblies, then save durable edits without breaking QGIS reimport.
|
||||
- [ ] Native Blender/Cesium/preview consume runtime signal data generated from
|
||||
the native model, including disabled signals being omitted from runtime.
|
||||
- [ ] QGIS import/export compatibility and legacy build stages continue to
|
||||
pass while the migration adapter exists.
|
||||
- [ ] Nantaizi has documented native-only and QGIS-imported round trips.
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "native-traffic-signal-parity",
|
||||
"name": "native-traffic-signal-parity",
|
||||
"title": "Align native traffic signals with QGIS",
|
||||
"description": "",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P2",
|
||||
"creator": "dingkang",
|
||||
"assignee": "dingkang",
|
||||
"createdAt": "2026-08-18",
|
||||
"completedAt": "2026-08-18",
|
||||
"branch": null,
|
||||
"base_branch": "feature/native-road-compiler",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "08-13-native-road-compiler",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
<!-- @@@auto:current-status -->
|
||||
- **Active File**: `journal-1.md`
|
||||
- **Total Sessions**: 27
|
||||
- **Last Active**: 2026-08-07
|
||||
- **Total Sessions**: 39
|
||||
- **Last Active**: 2026-08-18
|
||||
<!-- @@@/auto:current-status -->
|
||||
|
||||
---
|
||||
@@ -19,7 +19,7 @@
|
||||
<!-- @@@auto:active-documents -->
|
||||
| File | Lines | Status |
|
||||
|------|-------|--------|
|
||||
| `journal-1.md` | ~575 | Active |
|
||||
| `journal-1.md` | ~845 | Active |
|
||||
<!-- @@@/auto:active-documents -->
|
||||
|
||||
---
|
||||
@@ -29,6 +29,18 @@
|
||||
<!-- @@@auto:session-history -->
|
||||
| # | Date | Title | Commits | Branch |
|
||||
|---|------|-------|---------|--------|
|
||||
| 39 | 2026-08-18 | Complete native-only pipeline replacement | `9e0e25a` | `feature/native-road-compiler` |
|
||||
| 38 | 2026-08-18 | Complete native traffic signal workflow | `5accc0a` | `feature/native-road-compiler` |
|
||||
| 37 | 2026-08-18 | Complete rounded native junction verification | `e41bfd1`, `f7e71cf` | `feature/native-road-compiler` |
|
||||
| 36 | 2026-08-17 | Complete native road marking semantics | `9bb97d4` | `feature/native-road-compiler` |
|
||||
| 35 | 2026-08-17 | Complete native lane separator styles | `bc04517` | `feature/native-road-compiler` |
|
||||
| 34 | 2026-08-17 | Add center line style overrides | `47efb78` | `feature/native-road-compiler` |
|
||||
| 33 | 2026-08-17 | Add native road center lines | `fb863da` | `feature/native-road-compiler` |
|
||||
| 32 | 2026-08-17 | Native road control markings | `4c4f453` | `feature/native-road-compiler` |
|
||||
| 31 | 2026-08-14 | Native road lane markings | `1b9829d` | `feature/native-road-compiler` |
|
||||
| 30 | 2026-08-12 | Add vehicle incident preview cards | `761a526` | `main` |
|
||||
| 29 | 2026-08-12 | Publish reusable area asset packages | `f385009`, `c925890`, `db0fba5`, `f2b8d79`, `0102ffb` | `main` |
|
||||
| 28 | 2026-08-11 | 默认压缩交付 | `0790cbd` | `main` |
|
||||
| 27 | 2026-08-07 | QGIS traffic signal editing and countdown stability | `e153a1c` | `main` |
|
||||
| 26 | 2026-08-07 | 交通信号拓扑与部分构建同步修复 | `1c077a3` | `main` |
|
||||
| 25 | 2026-08-06 | Cesium traffic signal countdowns | `0e1574f` | `main` |
|
||||
|
||||
@@ -573,3 +573,273 @@ Implemented editable traffic signal assemblies in QGIS with stable IDs, position
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 28: 默认压缩交付
|
||||
|
||||
**Date**: 2026-08-11
|
||||
**Task**: 默认压缩交付
|
||||
**Branch**: `main`
|
||||
|
||||
### Summary
|
||||
|
||||
将 GLB 压缩纳入默认构建并使用标准产物路径交付,移除并列压缩输出契约;同步诊断、测试与文档。
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `0790cbd` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 29: Publish reusable area asset packages
|
||||
|
||||
**Date**: 2026-08-12
|
||||
**Task**: Publish reusable area asset packages
|
||||
**Branch**: `main`
|
||||
|
||||
### Summary
|
||||
|
||||
Published the reusable osm-asset-package/v1 contract, moved static models under package/, kept dynamic cruise and signal assets in _preview/, fixed preview manifest-relative loading, and verified the target area quality gate.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `f385009` | (see git log) |
|
||||
| `c925890` | (see git log) |
|
||||
| `db0fba5` | (see git log) |
|
||||
| `f2b8d79` | (see git log) |
|
||||
| `0102ffb` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 30: Add vehicle incident preview cards
|
||||
|
||||
**Date**: 2026-08-12
|
||||
**Task**: Add vehicle incident preview cards
|
||||
**Branch**: `main`
|
||||
|
||||
### Summary
|
||||
|
||||
Added click-to-open vehicle information cards with normal, breakdown, and accident preview-only states, floating vehicle-anchored cards, route and motion changes, plus bundled repair and warning icons.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `761a526` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 31: Native road lane markings
|
||||
|
||||
**Date**: 2026-08-14
|
||||
**Task**: Native road lane markings
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Implemented native lane separators, repeated road direction arrows, explicit junction turn arrows, Workbench inspection/toggles, and Blender/Cesium adapter support for Nantaizi; validated native compile, checks, tests, and non-package visual build.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `1b9829d` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 32: Native road control markings
|
||||
|
||||
**Date**: 2026-08-17
|
||||
**Task**: Native road control markings
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Added native-source crosswalk and stop-line layers with workbench inspection, Blender/Cesium delivery, and control-marking arrow avoidance.
|
||||
|
||||
### Main Changes
|
||||
|
||||
- Compiled explicit marked OSM crossings into source-traceable native GeoJSON.
|
||||
- Added Chinese workbench control-layer toggle, selection evidence, and summary counts.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `4c4f453` | (see git log) |
|
||||
|
||||
### Testing
|
||||
|
||||
- [OK] Passed native-road, road-workbench, turn-lane-arrows, build-stages, native compile/check, and Blender/Cesium/preview build.
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
### Next Steps
|
||||
|
||||
- Continue native road compiler workbench with the next user-approved priority.
|
||||
|
||||
|
||||
## Session 33: Add native road center lines
|
||||
|
||||
**Date**: 2026-08-17
|
||||
**Task**: Add native road center lines
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Implemented Nantaizi native-road center_lines output, workbench selection/toggle, Blender adapter, control-marking avoidance, and cross-layer verification.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `fb863da` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 34: Add center line style overrides
|
||||
|
||||
**Date**: 2026-08-17
|
||||
**Task**: Add center line style overrides
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Added persistent native road centre-line colour and pattern overrides, automatic Workbench staging, continuous solid-line rendering, and Blender white material support.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `47efb78` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 35: Complete native lane separator styles
|
||||
|
||||
**Date**: 2026-08-17
|
||||
**Task**: Complete native lane separator styles
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Completed per lane-pair separator colour/pattern overrides, Workbench staging, Blender yellow material routing, and centre-line control clearance.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `bc04517` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 36: Complete native road marking semantics
|
||||
|
||||
**Date**: 2026-08-17
|
||||
**Task**: Complete native road marking semantics
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Added explicit double-yellow centre lines and per-side edge-line overrides; made all three marking classes selectable in the Chinese Road Workbench; synchronized Blender adapter coverage and verified Nantaizi native compile, quality gate, Blender/Cesium/preview manifests, and automated tests.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `9bb97d4` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 37: Complete rounded native junction verification
|
||||
|
||||
**Date**: 2026-08-18
|
||||
**Task**: Complete rounded native junction verification
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Recorded user-confirmed native Blender/Cesium/preview verification for rounded native junctions, checked off all acceptance criteria, and archived the completed 08-17-native-rounded-junctions task.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `e41bfd1` | (see git log) |
|
||||
| `f7e71cf` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 38: Complete native traffic signal workflow
|
||||
|
||||
**Date**: 2026-08-18
|
||||
**Task**: Complete native traffic signal workflow
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Completed native traffic-signal ownership and Workbench editing. Added independent pole position, mast heading/reach, and face heading controls; removed native QGIS dependency; verified runtime preview, browser rendering, compile, tests, and quality gates. Archived native traffic signal and road compiler tasks.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `5accc0a` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
|
||||
## Session 39: Complete native-only pipeline replacement
|
||||
|
||||
**Date**: 2026-08-18
|
||||
**Task**: Complete native-only pipeline replacement
|
||||
**Branch**: `feature/native-road-compiler`
|
||||
|
||||
### Summary
|
||||
|
||||
Made native road compilation the default provider and full-build stage set; decoupled package traffic signals and preview from QGIS/osm2streets, made diagnostics provider-aware, updated docs/specs/tests, and preserved explicit legacy reference/debug paths. Native road compile/check and all available Node tests passed. QGIS baseline remains tagged qgis-original-baseline-20260818.
|
||||
|
||||
### Git Commits
|
||||
|
||||
| Hash | Message |
|
||||
|------|---------|
|
||||
| `9e0e25a` | (see git log) |
|
||||
|
||||
### Status
|
||||
|
||||
[OK] **Completed**
|
||||
|
||||
106
README.md
106
README.md
@@ -1,29 +1,37 @@
|
||||
# OSM Asset Pipeline
|
||||
|
||||
把单个园区/片区 OSM XML 转为可消费的 Blender 场景和 Cesium GLB。osm2streets GeoJSON、GeoPackage、QGIS 工程和预览图都是中间资产,用来提供道路几何、调试标线效果,以及给 Blender/Cesium 生成提供输入。
|
||||
原始 QGIS/osm2streets 基线保留在 Git tag `qgis-original-baseline-20260818`,用于回撤和对照;当前分支的默认生产链路是 native-only。
|
||||
|
||||
把单个园区/片区 OSM XML 转为可消费的 Blender 场景和 Cesium GLB。Native road compiler 是默认生产道路来源;osm2streets GeoJSON、GeoPackage、QGIS 工程和预览图仅作为显式 legacy/debug 资产或 Workbench 参考层。
|
||||
|
||||
## 目标产物
|
||||
|
||||
每个区域默认输出到 `outputs/<area-id>/`:
|
||||
每个区域默认输出到 `outputs/<area-id>/`。其中只有 `package/` 是可复制给下游的发布边界:
|
||||
|
||||
- `package/manifest.json`:版本化 `osm-asset-package/v1` manifest,声明 WGS84 anchor、ENU(X east/Y north/Z up,米)、heading correction 和包内模型 URL
|
||||
- `package/models/<area-id>.glb`:完整静态场景;`roads.glb`、`buildings.glb`、`vegetation.glb`、`water.glb` 是可选重叠分层,默认不要与完整场景同时加载
|
||||
|
||||
其余内容仍是本项目工作目录:
|
||||
|
||||
- `<area-id>.blend`:Blender 场景,包含道路、建筑、水体、植被等
|
||||
- `<area-id>.png`:Blender 预览渲染
|
||||
- `<area-id>.glb`:Cesium 可加载的完整 3D 模型(基线产物)
|
||||
- `<area-id>.glb`:Cesium 可加载的完整 3D 模型(默认压缩交付产物)
|
||||
- `<area-id>-roads.glb`、`-buildings.glb`、`-vegetation.glb`、`-water.glb`:Cesium 分类检查用的辅助模型
|
||||
- `<area-id>.json`:Cesium 放置元数据和示例代码
|
||||
- `<area-id>-cesium-preview.html`:Cesium 本地预览页
|
||||
- `<area-id>-compressed-webp768.glb/json/html`:显式 `compress` 阶段生成的可选压缩预览产物
|
||||
- `osm2streets_web_out/`:osm2streets GeoJSON 中间层
|
||||
- `native-road/`:native road compiler 的道路、路口、标线和信号灯运行时产物
|
||||
- `osm2streets_web_out/`:可选的 osm2streets/QGIS 参考中间层
|
||||
- `<area-id>.gpkg` / `<area-id>.qgz` / `<area-id>-preview.png`:QGIS 调试资产
|
||||
|
||||
## 环境
|
||||
|
||||
需要:
|
||||
默认 native-only 构建需要:
|
||||
|
||||
- QGIS 与 GDAL 工具链
|
||||
- Blender
|
||||
- Node.js / npm
|
||||
|
||||
QGIS/GDAL 仅在显式运行 legacy `intermediates`/`reimport` 或 Workbench 参考链路时需要。
|
||||
|
||||
macOS 默认使用 QGIS `/Applications/QGIS.app` 与 Blender
|
||||
`/Applications/Blender.app`。Linux 配置中,`qgisApp` 是安装前缀(通常为
|
||||
`/usr`),`blenderApp` 是可执行文件路径(通常为 `/usr/bin/blender`)。
|
||||
@@ -37,12 +45,14 @@ npm install
|
||||
|
||||
## 主流程
|
||||
|
||||
默认构建南台子湖创新谷样例:
|
||||
交互式选择区域和构建阶段:
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
使用方向键移动、空格多选阶段、回车开始构建;`intermediates` 与 `reimport` 不能同时选择。
|
||||
|
||||
指定区域配置:
|
||||
|
||||
```bash
|
||||
@@ -60,15 +70,14 @@ npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages reimport
|
||||
```
|
||||
|
||||
`intermediates` 会生成 osm2streets GeoJSON、GeoPackage、QGIS 工程和 QGIS 预览图。`blender` 使用 OSM 和 osm2streets GeoJSON 生成 `.blend`/`.png`。`cesium` 从 `.blend` 导出 `.glb`/`.json`,并生成 Cesium 预览 HTML。`preview` 只在已有 `.glb/.json` 时补生成 HTML。`compress` 从已有 `.glb/.json/html` 生成并列压缩产物,不覆盖默认 GLB。`reimport` 把手工编辑过的 GeoPackage 回导为 GeoJSON,不含在 `all` 里,详见 [QGIS 手工修正工作流](#qgis-手工修正工作流)。
|
||||
默认构建依次由 native road compiler、Blender、Cesium、压缩、package 和 preview 组成,不读取 `osm2streets_web_out/`。native preview 在没有 legacy 路线文件时仍可用,只是不提供可选车辆巡航。`intermediates`/`reimport` 仍可显式运行 legacy QGIS 链路,详见 [QGIS 手工修正工作流](#qgis-手工修正工作流)。
|
||||
|
||||
Cesium 预览默认显示完整场景。点击 `Inspect` 后会按道路、建筑、绿化与设施、水体加载辅助 GLB;该模式用于单独检查生成结果,主 GLB 仍是完整场景和下游使用的基线。
|
||||
压缩预览同样保留这些分类检查资产;重跑 `compress` 后应打开新生成的压缩预览 HTML。
|
||||
Cesium 预览默认显示完整场景。点击 `Inspect` 后会按道路、建筑、绿化与设施、水体加载辅助 GLB;该模式用于单独检查生成结果,主 GLB 是压缩后的下游交付资产。
|
||||
|
||||
`compress` 不含在 `all` 里,也不能从配置文件默认开启。需要重导出 Cesium 后立刻生成压缩产物时,显式跑:
|
||||
`compress` 包含在完整构建的默认阶段和 `all` 中。需要对已有标准产物重新压缩时,可以显式跑:
|
||||
|
||||
```bash
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages cesium,compress
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages compress
|
||||
```
|
||||
|
||||
## 区域诊断
|
||||
@@ -91,6 +100,53 @@ npm run preflight:area -- --config config/areas/nantaizi-lake-innovation-valley.
|
||||
预检有 error 时退出非零且不会更新记录;通过后写 `preflight.manifest.json`,保留本次
|
||||
验证的 config / OSM 文件摘要和检查结果。
|
||||
|
||||
## Native Road Workbench
|
||||
|
||||
`road:compile` 是独立于 osm2streets 的实验性道路编译器入口。它从 OSM 生成可追溯的
|
||||
道路模型、基础道路面、诊断与对比摘要,写入 `outputs/<area-id>/native-road/`,不会覆盖
|
||||
`osm2streets_web_out/` 或影响现有 Blender/Cesium 构建:
|
||||
|
||||
```bash
|
||||
npm run road:compile -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
npm run road:check -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
```
|
||||
|
||||
`road:check` 验证已发布 connector 与语义行驶动作的一致性,并在诊断包含 error、图层缺失或
|
||||
动作/几何对应关系不一致时以非零退出。warning 保留给工作台审查,不会阻止产物打开。
|
||||
|
||||
### Native Preview Traffic Simulation
|
||||
|
||||
native preview 会额外生成:
|
||||
|
||||
```text
|
||||
outputs/<area-id>/_preview/<area-id>-traffic-simulation.json
|
||||
```
|
||||
|
||||
该文件使用 `native-preview-traffic-simulation/v1`,记录 native 路线、connector、停止点、
|
||||
信号 `signal_uid`、源文件 SHA-256、坐标系和仿真参数。它与 `package/manifest.json` 一起可
|
||||
迁移到其他平台;消费方不需要运行 Node 或 QGIS。预览中的车辆会根据现有信号相位在停止线
|
||||
前停车,并按最小车距跟车排队。该功能是确定性的验证预览,不是法规级导航或完整交通仿真。
|
||||
|
||||
迁移契约、JSON 示例、更新循环和回撤方式见
|
||||
[native preview traffic simulation](docs/native-preview-traffic-simulation.md)。
|
||||
|
||||
启动本地浏览器工作台:
|
||||
|
||||
```bash
|
||||
npm run road:workbench -- --config config/areas/nantaizi-lake-innovation-valley.json
|
||||
```
|
||||
|
||||
工作台默认先编译,并在 `http://127.0.0.1:8787/` 启动基于 OpenLayers 的 GIS 工作台,展示
|
||||
OSM 道路、原生结果、车道、行驶动作、诊断和每个值的来源。可编辑道路宽度、车道数、两侧
|
||||
人行道,以及候选路口端点连接。保存写入
|
||||
`outputs/<area-id>/native-road-overrides.json`;此文件是版本化的可审查输入,下一次编译和
|
||||
启动工作台时会自动加载。编辑不会直接修改最终 polygon。
|
||||
|
||||
道路按行驶方向显示:点击道路面、中心线、车道或转向路径后,蓝色三角形标记当前方向在 OSM
|
||||
原始中心线上的方向;右侧“路口连接”只列出该方向到达终点路口后可驶入的目标道路,并标记为
|
||||
左转、直行、右转或掉头。车道按当前方向从左向右编号。内部断头诊断中的近邻候选不会自动连接,
|
||||
必须由用户显式暂存、保存并重新编译。
|
||||
|
||||
诊断会输出 OSM bounds、building way / multipolygon relation、显式高度、植被数量、
|
||||
现有产物状态,以及 GLB 的 size / nodes / meshes / materials / images / extensions。
|
||||
缺少已期望的基线产物、异常 building relation、GLB 超过保守预算等会进入 `Warnings`。
|
||||
@@ -112,9 +168,9 @@ outputs/<area-id>/_pipeline/stages/compress.manifest.json
|
||||
造成假 stale。
|
||||
|
||||
manifest 记录阶段输入/输出文件的 bytes、mtime、sha256、耗时和结构摘要:前段记录 OSM /
|
||||
GeoJSON feature counts,Blender 记录 `.blend` / render,Cesium/压缩记录 GLB digest,
|
||||
preview 记录 GLB、metadata、`lane_polygons.geojson`、`network.json`、`intersection_surface.geojson`、
|
||||
车辆路线和 runtime 文件。巡航道路区间按 osm2streets internal road 匹配真实 Driving lane 中轴,
|
||||
native-road 或 legacy GeoJSON feature counts,Blender 记录 `.blend` / render,Cesium/压缩记录 GLB digest,
|
||||
preview 记录 GLB、metadata、provider 对应的道路输入、可选车辆路线和 runtime 文件。legacy
|
||||
巡航道路区间按 osm2streets internal road 匹配真实 Driving lane 中轴,native preview 可省略巡航,
|
||||
路口 connector 必须通过 intersection surface 越界检查。`diagnose:area` 会读取这些
|
||||
manifest;缺失或当前输入/输出 sha/bytes 不一致会在 `Stage manifests` 和 `Warnings`
|
||||
里标出来。
|
||||
@@ -165,7 +221,7 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
"qgisApp": "/Applications/QGIS.app",
|
||||
"blenderApp": "/Applications/Blender.app",
|
||||
"stages": {
|
||||
"intermediates": true,
|
||||
"intermediates": false,
|
||||
"blender": true,
|
||||
"cesium": true
|
||||
},
|
||||
@@ -183,7 +239,8 @@ cp config/examples/template.json config/areas/my-area.json
|
||||
},
|
||||
"blender": {
|
||||
"treeStyle": "natural",
|
||||
"officeOverrides": ""
|
||||
"officeOverrides": "",
|
||||
"roadProvider": "native"
|
||||
},
|
||||
"compress": {
|
||||
"textureSize": 768,
|
||||
@@ -209,8 +266,7 @@ QGIS road-layer knobs:
|
||||
"areaDir": "/absolute/path/to/custom-area",
|
||||
"blend": "/absolute/path/to/custom.blend",
|
||||
"glb": "/absolute/path/to/custom.glb",
|
||||
"cesiumPreview": "/absolute/path/to/custom-preview.html",
|
||||
"compressedGlb": "/absolute/path/to/custom-compressed.glb"
|
||||
"cesiumPreview": "/absolute/path/to/custom-preview.html"
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -275,8 +331,8 @@ Cesium GLB 低层导出:
|
||||
|
||||
## 压缩 GLB
|
||||
|
||||
推荐使用显式 `compress` 阶段。它不会覆盖默认 `<area-id>.glb`,只生成并列的压缩 GLB、
|
||||
metadata 和预览页:
|
||||
完整构建默认运行 `compress`。它将未压缩 Cesium 导出复制到 `_pipeline` 下的临时目录,
|
||||
压缩成功后才替换标准 `<area-id>.glb`、`.json` 和预览页;最终输出目录不保留并列压缩文件:
|
||||
|
||||
```bash
|
||||
npm run build:area -- \
|
||||
@@ -289,7 +345,7 @@ npm run build:area -- \
|
||||
```bash
|
||||
npm run compress:glb -- \
|
||||
--input outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.glb \
|
||||
--output outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley-compressed-webp768.glb \
|
||||
--output /tmp/nantaizi-lake-innovation-valley-compressed.glb \
|
||||
--texture-size 768 \
|
||||
--metadata outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.json \
|
||||
--preview outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley-cesium-preview.html
|
||||
@@ -310,7 +366,7 @@ npm run compress:glb -- \
|
||||
南台子湖创新谷当前可直接使用下面这条命令:
|
||||
|
||||
```bash
|
||||
npm run build -- --config config/areas/nantaizi-lake-innovation-valley.json --stages reimport,blender,cesium
|
||||
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages reimport,blender,cesium
|
||||
```
|
||||
|
||||
`reimport` 阶段(`scripts/reimport-gpkg.js`)做两件事:
|
||||
@@ -323,7 +379,7 @@ npm run build -- --config config/areas/nantaizi-lake-innovation-valley.json --st
|
||||
- 这套流程假设你的手工修改已经保存在 `outputs/<area-id>/<area-id>.gpkg` 中
|
||||
- 所有图层先导出到临时目录并校验通过后才写回 `osm2streets_web_out/`;任一图层缺失或导出结果不是合法 FeatureCollection,整批都不落盘(`ogr2ogr` 遇到不存在的图层会留下 0 字节文件,直接覆盖会静默损坏数据)
|
||||
- `intermediates` 与 `reimport` 互斥,同时指定会直接报错:前者用 OSM 重建 `gpkg`,正好会抹掉后者要读回的手工修改
|
||||
- `blender,cesium` 阶段读取的是 `osm2streets_web_out/*.geojson`,不是直接读取 `gpkg`
|
||||
- 在显式 legacy provider 下,`blender,cesium` 阶段读取的是 `osm2streets_web_out/*.geojson`,不是直接读取 `gpkg`;native provider 不读取这些文件
|
||||
- 如果 Blender 当前环境不稳定,先确认 `geojson` 已完成回导,再单独排查 Blender 本身
|
||||
- 增删图层或调整 `z_index` 只需改 `scripts/lib/scene-layers.js`,构建、场景合并、场景样式、QGIS 工程会一并同步
|
||||
|
||||
|
||||
BIN
assets/preview/vehicle-accident.png
Normal file
BIN
assets/preview/vehicle-accident.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
assets/preview/vehicle-breakdown.png
Normal file
BIN
assets/preview/vehicle-breakdown.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user