refactor: move road compiler core into package
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const { readAreaConfig } = require("./lib/area-config");
|
||||
const { compileRoadModel, compileGeometry, loadOverrides, validateOverrides, writeJsonAtomic } = require("./lib/native-road");
|
||||
const { loadOrGenerate, runtime } = require("./lib/native-traffic-signals");
|
||||
const { readAreaConfig, toRoadCompilerInput } = require("./lib/area-config");
|
||||
const { compileInput } = require("../packages/road-compiler/src/compile/compiler");
|
||||
|
||||
const repoRoot = path.resolve(__dirname, "..");
|
||||
|
||||
@@ -21,112 +19,8 @@ function parseArgs(argv) {
|
||||
|
||||
function compileArea(configPath) {
|
||||
const area = readAreaConfig(configPath, { repoRoot });
|
||||
const overrides = loadOverrides(area.outputs.nativeRoadOverrides);
|
||||
const model = compileRoadModel(fs.readFileSync(area.input, "utf8"), overrides);
|
||||
// Editing the source OSM retires the ids some overrides point at. Those
|
||||
// entries can no longer match anything, so drop them with a diagnostic rather
|
||||
// than aborting the whole compile — otherwise every OSM edit blocks the
|
||||
// pipeline until the file is hand-pruned, one error message at a time.
|
||||
const validated = validateOverrides(overrides, model, { skipStaleTargets: true });
|
||||
for (const item of validated.stale) console.warn(`[warning] 忽略失效的 override(目标已不存在):${item.id} -> ${item.target}`);
|
||||
fs.mkdirSync(area.outputs.pipelineDir, { recursive: true });
|
||||
const compiled = compileGeometry(model, overrides, { edgeLines: area.nativeRoad.edgeLines, junctionTemplates: area.nativeRoad.junctionTemplates });
|
||||
compiled.diagnostics.push(...validated.stale.map((item) => ({
|
||||
id: `diagnostic:stale-override:${item.id}`,
|
||||
severity: "warning",
|
||||
subjectId: item.id,
|
||||
sourceIds: [],
|
||||
rule: "stale-override-target",
|
||||
message: `该设置指向的 ${item.kind} 目标 ${item.target} 已不存在(OSM 改动后 id 失效),本次编译已忽略。可在工作台重新设置,或从 native-road-overrides.json 中删除。`,
|
||||
geometry: null,
|
||||
})));
|
||||
const signalDocument = loadOrGenerate(area.outputs.nativeTrafficSignals, fs.readFileSync(area.input, "utf8"), compiled.vehicleStopLines, compiled.intersectionSurface);
|
||||
const signalRuntime = runtime(signalDocument);
|
||||
// Persist validation normalization, including one-time legacy heading migration.
|
||||
writeJsonAtomic(area.outputs.nativeTrafficSignals, signalDocument);
|
||||
const staging = fs.mkdtempSync(path.join(area.outputs.pipelineDir, "native-road-"));
|
||||
try {
|
||||
const result = {
|
||||
schema: "native-road-compiled/v1",
|
||||
areaId: area.id,
|
||||
source: { osm: area.input, overrides: area.outputs.nativeRoadOverrides, trafficSignals: area.outputs.nativeTrafficSignals },
|
||||
model: { roads: model.roads, endpoints: model.endpoints, connections: model.connections },
|
||||
movements: compiled.movements,
|
||||
trafficSignals: { assemblies: "traffic-signal-assemblies.json", runtime: "traffic-signals.json", count: signalRuntime.signals.length },
|
||||
diagnostics: compiled.diagnostics,
|
||||
layers: { roadSurface: "layers/road_surface.geojson", edgeLines: "layers/edge_lines.geojson", sidewalkSurface: "layers/sidewalk_surface.geojson", intersectionSurface: "layers/intersection_surface.geojson", laneCenterlines: "layers/lane_centerlines.geojson", laneSeparators: "layers/lane_separators.geojson", centerLines: "layers/center_lines.geojson", directionArrows: "layers/direction_arrows.geojson", turnArrows: "layers/turn_arrows.geojson", crosswalks: "layers/crosswalks.geojson", vehicleStopLines: "layers/vehicle_stop_lines.geojson", connectors: "layers/connectors.geojson" },
|
||||
};
|
||||
const comparison = compareOsm2Streets(area, result.model, compiled);
|
||||
writeJsonAtomic(path.join(staging, "compiled.json"), result);
|
||||
writeJsonAtomic(path.join(staging, "diagnostics.json"), { schema: "native-road-diagnostics/v1", diagnostics: compiled.diagnostics });
|
||||
writeJsonAtomic(path.join(staging, "comparison.json"), comparison);
|
||||
writeJsonAtomic(path.join(staging, "traffic-signal-assemblies.json"), signalDocument.assemblies);
|
||||
writeJsonAtomic(path.join(staging, "traffic-signals.json"), signalRuntime);
|
||||
writeJsonAtomic(path.join(staging, "layers", "road_surface.geojson"), compiled.roadSurface);
|
||||
writeJsonAtomic(path.join(staging, "layers", "edge_lines.geojson"), compiled.edgeLines);
|
||||
writeJsonAtomic(path.join(staging, "layers", "sidewalk_surface.geojson"), compiled.sidewalkSurface);
|
||||
writeJsonAtomic(path.join(staging, "layers", "intersection_surface.geojson"), compiled.intersectionSurface);
|
||||
writeJsonAtomic(path.join(staging, "layers", "lane_centerlines.geojson"), compiled.laneCenterlines);
|
||||
writeJsonAtomic(path.join(staging, "layers", "lane_separators.geojson"), compiled.laneSeparators);
|
||||
writeJsonAtomic(path.join(staging, "layers", "center_lines.geojson"), compiled.centerLines);
|
||||
writeJsonAtomic(path.join(staging, "layers", "direction_arrows.geojson"), compiled.directionArrows);
|
||||
writeJsonAtomic(path.join(staging, "layers", "turn_arrows.geojson"), compiled.turnArrows);
|
||||
writeJsonAtomic(path.join(staging, "layers", "crosswalks.geojson"), compiled.crosswalks);
|
||||
writeJsonAtomic(path.join(staging, "layers", "vehicle_stop_lines.geojson"), compiled.vehicleStopLines);
|
||||
writeJsonAtomic(path.join(staging, "layers", "connectors.geojson"), compiled.connectors);
|
||||
fs.rmSync(area.outputs.nativeRoadDir, { recursive: true, force: true });
|
||||
fs.renameSync(staging, area.outputs.nativeRoadDir);
|
||||
return { area, result, comparison };
|
||||
} catch (error) {
|
||||
fs.rmSync(staging, { recursive: true, force: true });
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
function compareOsm2Streets(area, model, compiled) {
|
||||
const source = path.join(area.outputs.geojsonDir, "road_surface.geojson");
|
||||
let featureCount = null;
|
||||
if (fs.existsSync(source)) {
|
||||
const collection = JSON.parse(fs.readFileSync(source, "utf8"));
|
||||
featureCount = Array.isArray(collection.features) ? collection.features.length : null;
|
||||
}
|
||||
const diagnosticsBySeverity = {};
|
||||
const diagnosticsByRule = {};
|
||||
for (const item of compiled.diagnostics) {
|
||||
diagnosticsBySeverity[item.severity] = (diagnosticsBySeverity[item.severity] || 0) + 1;
|
||||
diagnosticsByRule[item.rule] = (diagnosticsByRule[item.rule] || 0) + 1;
|
||||
}
|
||||
const dangling = compiled.diagnostics.filter((item) => item.rule === "unconnected-interior-road-end");
|
||||
const junctions = compiled.intersectionSurface.features;
|
||||
const fallbackJunctions = junctions.filter((feature) => feature.properties.boundary_mode === "connector-convex-fallback");
|
||||
return {
|
||||
schema: "native-road-comparison/v2",
|
||||
nativeRoadCount: model.roads.length,
|
||||
nativeRoadSurfaceFeatures: compiled.roadSurface.features.length,
|
||||
nativeSidewalkSurfaceFeatures: compiled.sidewalkSurface.features.length,
|
||||
nativeJunctionSurfaceFeatures: compiled.intersectionSurface.features.length,
|
||||
nativeApproachEnvelopeJunctions: junctions.length - fallbackJunctions.length,
|
||||
nativeFallbackJunctions: fallbackJunctions.length,
|
||||
nativeMaxJunctionExpansionRatio: junctions.reduce((maximum, feature) => Math.max(maximum, Number(feature.properties.expansion_ratio) || 0), 0),
|
||||
nativeLaneCenterlineFeatures: compiled.laneCenterlines.features.length,
|
||||
nativeLaneSeparatorFeatures: compiled.laneSeparators.features.length,
|
||||
nativeCenterLineFeatures: compiled.centerLines.features.length,
|
||||
nativeDirectionArrowFeatures: compiled.directionArrows.features.length,
|
||||
nativeTurnArrowFeatures: compiled.turnArrows.features.length,
|
||||
nativeCrosswalkFeatures: compiled.crosswalks.features.length,
|
||||
nativeVehicleStopLineFeatures: compiled.vehicleStopLines.features.length,
|
||||
nativeConnectorFeatures: compiled.connectors.features.length,
|
||||
nativeMovementCount: compiled.movements.length,
|
||||
nativePublishedMovementCount: compiled.movements.filter((movement) => movement.geometryPublished).length,
|
||||
nativeConnectionCount: model.connections.length,
|
||||
unconnectedInteriorRoadEnds: dangling.length,
|
||||
unconnectedEndsWithManualCandidates: dangling.filter((item) => item.manualCandidates?.length).length,
|
||||
diagnosticsBySeverity,
|
||||
diagnosticsByRule,
|
||||
osm2streetsRoadSurfaceFeatures: featureCount,
|
||||
osm2streetsAvailable: featureCount !== null,
|
||||
note: "Counts are coverage evidence only; geometry quality requires diagnostic and visual review.",
|
||||
};
|
||||
const compiled = compileInput(toRoadCompilerInput(area));
|
||||
return { ...compiled, area };
|
||||
}
|
||||
|
||||
function main() {
|
||||
|
||||
Reference in New Issue
Block a user