feat: add reimport stage for hand-edited GeoPackages
QGIS 手工修正后的回导流程此前是 README 里的三段 shell:一个硬编码
area-id 和 ogr2ogr 绝对路径的 for 循环、一段内联 node heredoc、再加一次
npm run build。改为一个 reimport 阶段:
npm run build -- --config config/areas/<area-id>.json \
--stages reimport,blender,cesium
scripts/reimport-gpkg.js 先把全部图层导出到临时目录并逐个校验,全部通过
才写回 osm2streets_web_out/。ogr2ogr 对不存在的图层退出码非 0 但仍会留下
0 字节文件,原先逐图层 mv 会静默用空图层覆盖好数据。
intermediates 与 reimport 同时指定直接报错——前者用 OSM 重建 GeoPackage,
正好抹掉后者要读回的手工修改。reimport 不含在 all 中。
同时新增 scripts/lib/scene-layers.js 作为 9 个渲染图层的唯一定义源。此前
该表在合并场景、场景样式 JSON、生成的 QGIS 工程、README 手工流程中各有
一份副本,改一处漏其余会导致图层叠放顺序出错并流入 Blender/Cesium。
验证:同一 OSM 输入下,改动前后 intermediates 产物逐字节一致
(scene_style.json、.qgz 符号定义、9 个图层几何与属性);reimport 对
未修改图层无损回导。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,16 @@ const requestedStages = args.stages
|
||||
: null;
|
||||
const stages = resolveStages(area.stages, requestedStages);
|
||||
|
||||
// intermediates deletes and rebuilds the GeoPackage from OSM, which is exactly
|
||||
// the manual work reimport exists to recover. Refuse the combination instead of
|
||||
// silently letting one undo the other.
|
||||
if (stages.intermediates && stages.reimport) {
|
||||
throw new Error(
|
||||
"Stages 'intermediates' and 'reimport' are mutually exclusive: " +
|
||||
"intermediates rebuilds the GeoPackage from OSM and would discard the QGIS edits reimport reads back.",
|
||||
);
|
||||
}
|
||||
|
||||
console.log(`Area: ${area.id}`);
|
||||
console.log(`Config: ${configPath}`);
|
||||
console.log(`Output: ${area.outputs.areaDir}`);
|
||||
@@ -22,6 +32,9 @@ console.log(`Output: ${area.outputs.areaDir}`);
|
||||
if (stages.intermediates) {
|
||||
buildIntermediates(area);
|
||||
}
|
||||
if (stages.reimport) {
|
||||
reimportGpkg(area);
|
||||
}
|
||||
if (stages.blender) {
|
||||
buildBlenderScene(area);
|
||||
}
|
||||
@@ -97,6 +110,7 @@ function normalizeAreaConfig(raw) {
|
||||
intermediates: raw.stages?.intermediates ?? raw.stages?.qgis ?? true,
|
||||
blender: raw.stages?.blender ?? true,
|
||||
cesium: raw.stages?.cesium ?? true,
|
||||
reimport: false,
|
||||
preview: false,
|
||||
},
|
||||
qgis: {
|
||||
@@ -142,6 +156,8 @@ function splitList(value) {
|
||||
|
||||
function resolveStages(defaults, requested) {
|
||||
if (!requested) return defaults;
|
||||
// 'reimport' is deliberately absent from 'all': it is a recovery step for
|
||||
// hand-edited GeoPackages, never part of a full build.
|
||||
const aliases = {
|
||||
all: ["intermediates", "blender", "cesium"],
|
||||
qgis: ["intermediates"],
|
||||
@@ -149,6 +165,8 @@ function resolveStages(defaults, requested) {
|
||||
geojson: ["intermediates"],
|
||||
intermediate: ["intermediates"],
|
||||
intermediates: ["intermediates"],
|
||||
reimport: ["reimport"],
|
||||
gpkg: ["reimport"],
|
||||
blender: ["blender"],
|
||||
scene: ["blender"],
|
||||
cesium: ["cesium"],
|
||||
@@ -157,18 +175,18 @@ function resolveStages(defaults, requested) {
|
||||
html: ["preview"],
|
||||
cesiumPreview: ["preview"],
|
||||
};
|
||||
const out = { intermediates: false, blender: false, cesium: false, preview: false };
|
||||
const out = { intermediates: false, reimport: false, blender: false, cesium: false, preview: false };
|
||||
for (const stage of requested) {
|
||||
const mapped = aliases[stage];
|
||||
if (!mapped) {
|
||||
throw new Error(`Unknown stage '${stage}'. Use intermediates, blender, cesium, preview, or all.`);
|
||||
throw new Error(`Unknown stage '${stage}'. Use intermediates, reimport, blender, cesium, preview, or all.`);
|
||||
}
|
||||
for (const key of mapped) out[key] = true;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function buildIntermediates(area) {
|
||||
function writeDerivedConfig(area) {
|
||||
fs.mkdirSync(area.outputs.pipelineDir, { recursive: true });
|
||||
const derivedConfig = {
|
||||
qgisApp: area.qgisApp,
|
||||
@@ -191,6 +209,11 @@ function buildIntermediates(area) {
|
||||
};
|
||||
const derivedConfigPath = path.join(area.outputs.pipelineDir, "osm2streets-qgis.config.json");
|
||||
fs.writeFileSync(derivedConfigPath, `${JSON.stringify(derivedConfig, null, 2)}\n`);
|
||||
return derivedConfigPath;
|
||||
}
|
||||
|
||||
function buildIntermediates(area) {
|
||||
const derivedConfigPath = writeDerivedConfig(area);
|
||||
|
||||
console.log("Stage: intermediates (osm2streets GeoJSON + QGIS)");
|
||||
runCommand(process.execPath, [
|
||||
@@ -200,6 +223,17 @@ function buildIntermediates(area) {
|
||||
], "intermediates");
|
||||
}
|
||||
|
||||
function reimportGpkg(area) {
|
||||
const derivedConfigPath = writeDerivedConfig(area);
|
||||
|
||||
console.log("Stage: reimport (GeoPackage -> GeoJSON)");
|
||||
runCommand(process.execPath, [
|
||||
path.join(repoRoot, "scripts", "reimport-gpkg.js"),
|
||||
"--config",
|
||||
derivedConfigPath,
|
||||
], "reimport");
|
||||
}
|
||||
|
||||
function buildBlenderScene(area) {
|
||||
ensureFile(blenderExecutable(area), "Blender executable");
|
||||
ensureFile(path.join(repoRoot, "blender", "generate_scene.py"), "Blender scene generator");
|
||||
|
||||
Reference in New Issue
Block a user