Files
osmWorkflow/README.md

363 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# OSM Asset Pipeline
把单个园区/片区 OSM XML 转为可消费的 Blender 场景和 Cesium GLB。osm2streets GeoJSON、GeoPackage、QGIS 工程和预览图都是中间资产,用来提供道路几何、调试标线效果,以及给 Blender/Cesium 生成提供输入。
## 目标产物
每个区域默认输出到 `outputs/<area-id>/`。其中只有 `package/` 是可复制给下游的发布边界:
- `package/manifest.json`:版本化 `osm-asset-package/v1` manifest声明 WGS84 anchor、ENUX 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>-roads.glb``-buildings.glb``-vegetation.glb``-water.glb`Cesium 分类检查用的辅助模型
- `<area-id>.json`Cesium 放置元数据和示例代码
- `<area-id>-cesium-preview.html`Cesium 本地预览页
- `osm2streets_web_out/`osm2streets GeoJSON 中间层
- `<area-id>.gpkg` / `<area-id>.qgz` / `<area-id>-preview.png`QGIS 调试资产
## 环境
需要:
- QGIS 与 GDAL 工具链
- Blender
- Node.js / npm
macOS 默认使用 QGIS `/Applications/QGIS.app` 与 Blender
`/Applications/Blender.app`。Linux 配置中,`qgisApp` 是安装前缀(通常为
`/usr``blenderApp` 是可执行文件路径(通常为 `/usr/bin/blender`)。
首次使用:
```bash
cd /Users/que01/osm2streets-qgis-workflow
npm install
```
## 主流程
交互式选择区域和构建阶段:
```bash
npm run build
```
使用方向键移动、空格多选阶段、回车开始构建;`intermediates``reimport` 不能同时选择。
指定区域配置:
```bash
npm run build:area -- --config config/areas/hanyang-block.json
```
只跑部分阶段:
```bash
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 preview
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages compress
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` 导出包 staging`compress` 压缩 staging 主 GLB`package` 校验并原子发布 `package/``preview` 生成仅用于本地验证的 HTML 和 `_preview/` 动态描述符。`reimport` 把手工编辑过的 GeoPackage 回导为 GeoJSON不含在 `all` 里,详见 [QGIS 手工修正工作流](#qgis-手工修正工作流)。
Cesium 预览默认显示完整场景。点击 `Inspect` 后会按道路、建筑、绿化与设施、水体加载辅助 GLB该模式用于单独检查生成结果主 GLB 是压缩后的下游交付资产。
`compress` 包含在完整构建的默认阶段和 `all` 中。需要对已有标准产物重新压缩时,可以显式跑:
```bash
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages compress
```
## 区域诊断
手工编辑 OSM 或怀疑产物变大时先跑快速诊断。它只读取区域配置、OSM XML 和已有输出
文件,不会启动 QGIS、Blender 或 Cesium 构建:
```bash
npm run diagnose:area -- --config config/areas/nantaizi-lake-innovation-valley.json
```
每次手工编辑 OSM 后、进入重型构建前,先跑预检。它只读取区域配置和 OSM XML检查
bounds、缺失 node 引用、building way 闭合性、building multipolygon member/ring、以及
显式 `height` / `building:levels` 标签:
```bash
npm run preflight:area -- --config config/areas/nantaizi-lake-innovation-valley.json
```
预检有 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
```
启动本地浏览器工作台:
```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 bounds、building way / multipolygon relation、显式高度、植被数量、
现有产物状态,以及 GLB 的 size / nodes / meshes / materials / images / extensions。
缺少已期望的基线产物、异常 building relation、GLB 超过保守预算等会进入 `Warnings`
预检和每个成功的区域构建阶段都会写机器可读的 manifest
```text
outputs/<area-id>/_pipeline/stages/preflight.manifest.json
outputs/<area-id>/_pipeline/stages/intermediates.manifest.json
outputs/<area-id>/_pipeline/stages/reimport.manifest.json
outputs/<area-id>/_pipeline/stages/blender.manifest.json
outputs/<area-id>/_pipeline/stages/cesium.manifest.json
outputs/<area-id>/_pipeline/stages/preview.manifest.json
outputs/<area-id>/_pipeline/stages/compress.manifest.json
```
`intermediates``reimport` 是同一批 GeoJSON 的两种所有权路径:前者从 OSM 重建,
后者从手工编辑的 GeoPackage 回导。成功运行其中一个会清掉另一个的 manifest避免旧路径
造成假 stale。
manifest 记录阶段输入/输出文件的 bytes、mtime、sha256、耗时和结构摘要前段记录 OSM /
GeoJSON feature countsBlender 记录 `.blend` / renderCesium/压缩记录 GLB digest
preview 记录 GLB、metadata、`lane_polygons.geojson``network.json``intersection_surface.geojson`
车辆路线和 runtime 文件。巡航道路区间按 osm2streets internal road 匹配真实 Driving lane 中轴,
路口 connector 必须通过 intersection surface 越界检查。`diagnose:area` 会读取这些
manifest缺失或当前输入/输出 sha/bytes 不一致会在 `Stage manifests``Warnings`
里标出来。
## 区域质量门
提交或交付某个区域前,跑只读质量门:
```bash
npm run check:area -- --config config/areas/nantaizi-lake-innovation-valley.json
```
`check:area` 复用 `diagnose:area` 的 OSM、产物、metadata、GLB digest 和 stage manifest
检查,但输出更短的 PASS/FAIL 报告。它不会启动 QGIS、Blender、Cesium、压缩或任何重建阶段。
GLB 预算默认阻断 size、nodes、images、实例化后的 render triangles 和嵌入贴图字节。
`diagnose:area` 会显示有效预算、top source 和最大贴图。区域可在配置中收紧预算;要放宽
默认值,必须写明原因:
```json
"budget": {
"nodes": 1400,
"triangles": 350000,
"reason": "Dense campus vegetation approved for this area"
}
```
第一版会在这些条件下退出非零OSM bounds 缺失/无效、building multipolygon relation
异常、建筑 `height` 无法按正数米解析、Cesium GLB / metadata / preview 缺失或类型错误、
metadata JSON 损坏、GLB size / nodes / images / render triangles / embedded image bytes 超过保守预算、期望存在的 stage manifest
缺失/损坏/stale。缺 QGIS preview 目前只作为 warning不阻断。
## 区域配置
新区域从模板复制:
```bash
cp config/examples/template.json config/areas/my-area.json
```
核心配置:
```json
{
"id": "my-area",
"input": "/absolute/path/to/input.osm",
"outputRoot": "/absolute/path/to/outputs",
"qgisApp": "/Applications/QGIS.app",
"blenderApp": "/Applications/Blender.app",
"stages": {
"intermediates": true,
"blender": true,
"cesium": true
},
"qgis": {
"arrowScale": 0.8,
"arrowMergeTriangles": true,
"arrowOutlineSimplifyMeters": 0.05,
"intersectionCornerSourceMaxDimensionMeters": 2.6,
"clipPad": 0.002,
"canvasPad": 0.001,
"previewPad": 0.0007,
"canvasExtent": null,
"previewExtent": null,
"layerPrefix": "osm2streets"
},
"blender": {
"treeStyle": "natural",
"officeOverrides": ""
},
"compress": {
"textureSize": 768,
"quality": 82,
"effort": 80,
"meshopt": false
}
}
```
QGIS road-layer knobs:
- `arrowScale`: scales osm2streets lane-arrow polygons before export.
- `arrowMergeTriangles`: merges each osm2streets lane-arrow triangle mesh into one valid polygon. This preserves the original arrow shape and turn direction while removing renderer gaps along shared triangle edges.
- `arrowOutlineSimplifyMeters`: removes sub-decimeter kinks from the merged arrow exterior. The default `0.05` removes the two malformed tail vertices without changing the arrow head; the remaining tail edge is aligned perpendicular to the shaft.
- `intersectionCornerSourceMaxDimensionMeters`: keeps only small osm2streets `sidewalk corner` polygons. Large intersection-marking polygons are not treated as sidewalk because they can cover the drivable junction.
`scripts/build-area.js` 会按 `id` 自动推导默认输出路径。确实需要定制时,可以增加 `outputs` 覆盖:
```json
{
"outputs": {
"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"
}
}
```
预览 Cesium 页面时,需要在输出目录启动 HTTP 服务,避免浏览器拦截本地文件请求:
```bash
cd outputs/my-area
python3 -m http.server 8765
```
然后打开 `http://localhost:8765/my-area-cesium-preview.html`
## 实验:车辆巡航
`preview``cesium` 阶段会额外生成 `<area-id>-vehicle-route.json``<area-id>-vehicle-car.gltf`。OSM 提供可行驶拓扑与转向语义,实际巡航坐标来自 osm2streets 的 `Driving` lane polygon 中轴;同向多车道按下一次 maneuver 选择兼容车道。路线中的 `laneSegments` 记录每个 polygon fragment 的 OSM way、方向、lane index、width、center offset 和来源。车道数据缺失、歧义、断裂或没有兼容转向车道时,候选路线会被跳过并写入顶层 `diagnostics`不会回退到固定偏移或默认宽度。Cesium 预览页会加载多条道路段并显示多辆实验车辆循环巡航;`Vehicle` 下拉框决定 `Follow` 跟随哪一辆车。
这是用于验证高精度巡航可用性的预览层功能,不会改变 Blender/GLB 主资产本身。车辆模型是无 logo 的轻量预览模型,生成在输出目录中。
## 已沉淀区域
- `config/areas/nantaizi-lake-innovation-valley.json`
- `config/areas/hanyang-block.json`
## 低层命令
通常优先使用 `npm run build:area`。如果只想调试旧 QGIS/osm2streets 阶段,可以直接运行:
```bash
npm run build:qgis -- --config config/hanyang-block.json
```
Blender 低层命令:
```bash
/Applications/Blender.app/Contents/MacOS/Blender \
--background --factory-startup \
--python blender/generate_scene.py -- \
--osm "/path/to/input.osm" \
--geojson "/path/to/osm2streets_web_out" \
--output "/path/to/scene.blend" \
--render "/path/to/preview.png" \
--tree-style natural
```
`--tree-style` 可选 `natural``procedural``shapespark``shapespark` 使用
`assets/models/custom/shapespark_plants/` 里的拆分低模植物资产;模型缺失时会回落到
`natural`
Cesium GLB 低层导出:
```bash
/Applications/Blender.app/Contents/MacOS/Blender \
--background \
--python blender/export_cesium.py -- \
--blend "/path/to/scene.blend" \
--glb "/path/to/scene.glb" \
--metadata "/path/to/scene.json"
```
详见 [blender/README.md](blender/README.md)。
## 压缩 GLB
完整构建默认运行 `compress`。它将未压缩 Cesium 导出复制到 `_pipeline` 下的临时目录,
压缩成功后才替换标准 `<area-id>.glb``.json` 和预览页;最终输出目录不保留并列压缩文件:
```bash
npm run build:area -- \
--config config/areas/nantaizi-lake-innovation-valley.json \
--stages compress
```
底层脚本也可以单独调用:
```bash
npm run compress:glb -- \
--input outputs/nantaizi-lake-innovation-valley/nantaizi-lake-innovation-valley.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
```
该脚本通过 `npx @gltf-transform/cli@4.1.4` 执行 `resize -> webp`。如需进一步压几何,
可追加 `--meshopt`,但会引入 `EXT_meshopt_compression` / `KHR_mesh_quantization`
应先确认目标 Cesium 版本加载正常。
## QGIS 手工修正工作流
如果已经在现成的 `.qgz` 项目里直接编辑了 `gpkg` 图层,不要再重跑 `intermediates`,否则会把手工修改覆盖掉。推荐流程是:
1. 在 QGIS 中打开 `outputs/<area-id>/<area-id>.qgz`
2. 直接编辑项目内关联的 `gpkg` 图层并保存
3.`reimport` 回导 GeoJSON 并重建场景,再接 `blender,cesium`
南台子湖创新谷当前可直接使用下面这条命令:
```bash
npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages reimport,blender,cesium
```
`reimport` 阶段(`scripts/reimport-gpkg.js`)做两件事:
-`<area-id>.gpkg` 里的 9 个图层逐个导出到 `osm2streets_web_out/<layer>.geojson`
-`scripts/lib/scene-layers.js` 的图层表重建 `osm2streets_scene.geojson`(写入 `render_layer` / `z_index`)和 `osm2streets_scene_style.json`
说明:
- 这套流程假设你的手工修改已经保存在 `outputs/<area-id>/<area-id>.gpkg`
- 所有图层先导出到临时目录并校验通过后才写回 `osm2streets_web_out/`;任一图层缺失或导出结果不是合法 FeatureCollection整批都不落盘`ogr2ogr` 遇到不存在的图层会留下 0 字节文件,直接覆盖会静默损坏数据)
- `intermediates``reimport` 互斥,同时指定会直接报错:前者用 OSM 重建 `gpkg`,正好会抹掉后者要读回的手工修改
- `blender,cesium` 阶段读取的是 `osm2streets_web_out/*.geojson`,不是直接读取 `gpkg`
- 如果 Blender 当前环境不稳定,先确认 `geojson` 已完成回导,再单独排查 Blender 本身
- 增删图层或调整 `z_index` 只需改 `scripts/lib/scene-layers.js`构建、场景合并、场景样式、QGIS 工程会一并同步
## 文档
- [docs/input-cases.md](docs/input-cases.md) - 已验证的 OSM 输入案例
- [docs/changelog.md](docs/changelog.md) - 变更记录