Files
osmWorkflow/README.md

13 KiB
Raw Blame History

OSM Asset Pipeline

把单个园区/片区 OSM XML 转为可消费的 Blender 场景和 Cesium GLB。osm2streets GeoJSON、GeoPackage、QGIS 工程和预览图都是中间资产,用来提供道路几何、调试标线效果,以及给 Blender/Cesium 生成提供输入。

目标产物

每个区域默认输出到 outputs/<area-id>/

  • <area-id>.blendBlender 场景,包含道路、建筑、水体、植被等
  • <area-id>.pngBlender 预览渲染
  • <area-id>.glbCesium 可加载的 3D 模型
  • <area-id>.jsonCesium 放置元数据和示例代码
  • <area-id>-cesium-preview.htmlCesium 本地预览页
  • <area-id>-compressed-webp768.glb/json/html:显式 compress 阶段生成的可选压缩预览产物
  • osm2streets_web_out/osm2streets GeoJSON 中间层
  • <area-id>.gpkg / <area-id>.qgz / <area-id>-preview.pngQGIS 调试资产

环境

需要:

  • macOS QGIS默认 /Applications/QGIS.app
  • Blender默认 /Applications/Blender.app
  • Node.js / npm

首次使用:

cd /Users/que01/osm2streets-qgis-workflow
npm install

主流程

默认构建南台子湖创新谷样例:

npm run build

指定区域配置:

npm run build:area -- --config config/areas/hanyang-block.json

只跑部分阶段:

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/.pngcesium.blend 导出 .glb/.json,并生成 Cesium 预览 HTML。preview 只在已有 .glb/.json 时补生成 HTML。compress 从已有 .glb/.json/html 生成并列压缩产物,不覆盖默认 GLB。reimport 把手工编辑过的 GeoPackage 回导为 GeoJSON不含在 all 里,详见 QGIS 手工修正工作流

compress 不含在 all 里,也不能从配置文件默认开启。需要重导出 Cesium 后立刻生成压缩产物时,显式跑:

npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages cesium,compress

区域诊断

手工编辑 OSM 或怀疑产物变大时先跑快速诊断。它只读取区域配置、OSM XML 和已有输出 文件,不会启动 QGIS、Blender 或 Cesium 构建:

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 标签:

npm run preflight:area -- --config config/areas/nantaizi-lake-innovation-valley.json

预检有 error 时退出非零且不会更新记录;通过后写 preflight.manifest.json,保留本次 验证的 config / OSM 文件摘要和检查结果。

诊断会输出 OSM bounds、building way / multipolygon relation、显式高度、植被数量、 现有产物状态,以及 GLB 的 size / nodes / meshes / materials / images / extensions。 缺少已期望的基线产物、异常 building relation、GLB 超过保守预算等会进入 Warnings

预检和每个成功的区域构建阶段都会写机器可读的 manifest

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

intermediatesreimport 是同一批 GeoJSON 的两种所有权路径:前者从 OSM 重建, 后者从手工编辑的 GeoPackage 回导。成功运行其中一个会清掉另一个的 manifest避免旧路径 造成假 stale。

manifest 记录阶段输入/输出文件的 bytes、mtime、sha256、耗时和结构摘要前段记录 OSM / GeoJSON feature countsBlender 记录 .blend / renderCesium/压缩记录 GLB digest preview 记录 GLB、metadata、车辆路线和 runtime 文件。diagnose:area 会读取这些 manifest缺失或当前输入/输出 sha/bytes 不一致会在 Stage manifestsWarnings 里标出来。

区域质量门

提交或交付某个区域前,跑只读质量门:

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 和最大贴图。区域可在配置中收紧预算;要放宽 默认值,必须写明原因:

"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不阻断。

区域配置

新区域从模板复制:

cp config/examples/template.json config/areas/my-area.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 覆盖:

{
  "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",
    "compressedGlb": "/absolute/path/to/custom-compressed.glb"
  }
}

预览 Cesium 页面时,需要在输出目录启动 HTTP 服务,避免浏览器拦截本地文件请求:

cd outputs/my-area
python3 -m http.server 8765

然后打开 http://localhost:8765/my-area-cesium-preview.html

实验:车辆巡航

previewcesium 阶段会额外生成 <area-id>-vehicle-route.json<area-id>-vehicle-car.gltf。路线文件从 OSM bounds 内的可行驶 highway way 提取道路中心线,并向右偏移约 1.3 米作为车辆行驶线避免车辆压道路中心线。Cesium 预览页会加载多条道路段并显示多辆实验车辆循环巡航;Vehicle 下拉框决定 Follow 跟随哪一辆车。

这是用于验证高精度巡航可用性的预览层功能,不会改变 Blender/GLB 主资产本身。车辆模型是无 logo 的轻量预览模型,生成在输出目录中。

已沉淀区域

  • config/areas/nantaizi-lake-innovation-valley.json
  • config/areas/hanyang-block.json

低层命令

通常优先使用 npm run build:area。如果只想调试旧 QGIS/osm2streets 阶段,可以直接运行:

npm run build:qgis -- --config config/hanyang-block.json

Blender 低层命令:

/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 可选 naturalproceduralshapesparkshapespark 使用 assets/models/custom/shapespark_plants/ 里的拆分低模植物资产;模型缺失时会回落到 natural

Cesium GLB 低层导出:

/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

压缩 GLB

推荐使用显式 compress 阶段。它不会覆盖默认 <area-id>.glb,只生成并列的压缩 GLB、 metadata 和预览页:

npm run build:area -- \
  --config config/areas/nantaizi-lake-innovation-valley.json \
  --stages compress

底层脚本也可以单独调用:

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 \
  --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

南台子湖创新谷当前可直接使用下面这条命令:

npm run build -- --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 字节文件,直接覆盖会静默损坏数据)
  • intermediatesreimport 互斥,同时指定会直接报错:前者用 OSM 重建 gpkg,正好会抹掉后者要读回的手工修改
  • blender,cesium 阶段读取的是 osm2streets_web_out/*.geojson,不是直接读取 gpkg
  • 如果 Blender 当前环境不稳定,先确认 geojson 已完成回导,再单独排查 Blender 本身
  • 增删图层或调整 z_index 只需改 scripts/lib/scene-layers.js构建、场景合并、场景样式、QGIS 工程会一并同步

文档