Files
osmWorkflow/README.md

17 KiB
Raw Permalink Blame History

OSM Asset Pipeline

原始 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>/。其中只有 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.glbbuildings.glbvegetation.glbwater.glb 是可选重叠分层,默认不要与完整场景同时加载

其余内容仍是本项目工作目录:

  • <area-id>.blendBlender 场景,包含道路、建筑、水体、植被等
  • <area-id>.pngBlender 预览渲染
  • <area-id>.glbCesium 可加载的完整 3D 模型(默认压缩交付产物)
  • <area-id>-roads.glb-buildings.glb-vegetation.glb-water.glbCesium 分类检查用的辅助模型
  • <area-id>.jsonCesium 放置元数据和示例代码
  • <area-id>-cesium-preview.htmlCesium 本地预览页
  • native-road/native road compiler 的道路、路口、标线和信号灯运行时产物
  • osm2streets_web_out/:可选的 osm2streets/QGIS 参考中间层
  • <area-id>.gpkg / <area-id>.qgz / <area-id>-preview.pngQGIS 调试资产

环境

默认 native-only 构建需要:

  • Blender
  • Node.js / npm

QGIS/GDAL 仅在显式运行 legacy intermediates/reimport 或 Workbench 参考链路时需要。

macOS 默认使用 QGIS /Applications/QGIS.app 与 Blender /Applications/Blender.app。Linux 配置中,qgisApp 是安装前缀(通常为 /usrblenderApp 是可执行文件路径(通常为 /usr/bin/blender)。

首次使用:

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

主流程

交互式选择区域和构建阶段:

npm run build

使用方向键移动、空格多选阶段、回车开始构建;intermediatesreimport 不能同时选择。

指定区域配置:

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

默认构建依次由 native road compiler、Blender、Cesium、压缩、package 和 preview 组成,不读取 osm2streets_web_out/。native preview 在没有 legacy 路线文件时仍可用,只是不提供可选车辆巡航。intermediates/reimport 仍可显式运行 legacy QGIS 链路,详见 QGIS 手工修正工作流

Cesium 预览默认显示完整场景。点击 Inspect 后会按道路、建筑、绿化与设施、水体加载辅助 GLB该模式用于单独检查生成结果主 GLB 是压缩后的下游交付资产。

compress 包含在完整构建的默认阶段和 all 中。需要对已有标准产物重新压缩时,可以显式跑:

npm run build:area -- --config config/areas/nantaizi-lake-innovation-valley.json --stages 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 文件摘要和检查结果。

Native Road Workbench

road:compile 是独立于 osm2streets 的实验性道路编译器入口。它从 OSM 生成可追溯的 道路模型、基础道路面、诊断与对比摘要,写入 outputs/<area-id>/native-road/,不会覆盖 osm2streets_web_out/ 或影响现有 Blender/Cesium 构建:

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 会额外生成:

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

启动本地浏览器工作台:

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

预检和每个成功的区域构建阶段都会写机器可读的 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 / native-road 或 legacy GeoJSON feature countsBlender 记录 .blend / renderCesium/压缩记录 GLB digest preview 记录 GLB、metadata、provider 对应的道路输入、可选车辆路线和 runtime 文件。legacy 巡航道路区间按 osm2streets internal road 匹配真实 Driving lane 中轴native preview 可省略巡航, 路口 connector 必须通过 intersection surface 越界检查。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": false,
    "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": "",
    "roadProvider": "native"
  },
  "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"
  }
}

预览 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 提供可行驶拓扑与转向语义,实际巡航坐标来自 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 阶段,可以直接运行:

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。它将未压缩 Cesium 导出复制到 _pipeline 下的临时目录, 压缩成功后才替换标准 <area-id>.glb.json 和预览页;最终输出目录不保留并列压缩文件:

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

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

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

文档