- 고정 자료 fixtures/golden_472(검증_랩탑메인 노선 370 · GET · storage 사본 · 2.98 MB) - 길 넷: 정본(GET) · [저장] 서버 재계산 · 노선 저장 체인(DB 없이) · 브라우저 진짜 단계 묶음(vite ssr) - 값 = 측점 · 열쇠 · x · y · 계획고 · 면적 · 층따기 · 누가토량 · 관 길이 · 종단 경고 · 길마다 0 차 - 두 길 사이 = 알려진 차(R7 · W0 · X2 · W1)만 · 안 나타난 알려진 차도 실패 - 값 다시 뜨기 = helper_472_1_dump.py(손으로만) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
155 lines
5.6 KiB
JavaScript
155 lines
5.6 KiB
JavaScript
/* 472-1 헬퍼 — 브라우저 길 기준값(진짜 단계 함수 · vite ssr 묶음 · helper_472_1_browser_entry.ts).
|
|
* node helper_472_1_browser.mjs <묶음.mjs> <고정 자료 폴더> <격자 .bin>
|
|
* 페이지 묶음(`design_pipeline_page`)과 같은 단계 붙임 — 저장 노선 · 초안 없음 · 세션 = 메모리 가짜 · fetch 없음 ·
|
|
* 격자 = 서버 `dtm_grid_bytes` 가 고정 npz 로 지은 이진. 묶음 출력 → 새 노선 상세(`draftSectionDetail`) → 구조물
|
|
* 면적 얹기 → 누가토량. 표준출력 JSON 한 벌(값 꼴 = test_472_1_golden.py `values_of`). */
|
|
import { readFileSync } from "node:fs";
|
|
import { join } from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
|
|
const storage = () => {
|
|
const m = new Map();
|
|
return {
|
|
getItem: (k) => (m.has(k) ? m.get(k) : null),
|
|
setItem: (k, v) => m.set(k, String(v)),
|
|
removeItem: (k) => m.delete(k),
|
|
key: (i) => [...m.keys()][i] ?? null,
|
|
get length() {
|
|
return m.size;
|
|
},
|
|
clear: () => m.clear(),
|
|
};
|
|
};
|
|
globalThis.window = globalThis;
|
|
globalThis.sessionStorage = storage();
|
|
globalThis.localStorage = storage();
|
|
const any = new Proxy(function () {}, {
|
|
get: (_t, k) => (k === Symbol.toPrimitive ? () => 0 : any),
|
|
apply: () => any,
|
|
construct: () => any,
|
|
});
|
|
globalThis.document = any;
|
|
globalThis.requestAnimationFrame = (run) => setTimeout(run, 0);
|
|
globalThis.fetch = () => Promise.reject(new Error("서버 없음"));
|
|
|
|
const [bundle, dir, gridPath] = process.argv.slice(2);
|
|
const m = await import(pathToFileURL(bundle).href);
|
|
const read = (name) => JSON.parse(readFileSync(join(dir, name), "utf8"));
|
|
const detail = read("detail.json");
|
|
const plan = read("plan.json");
|
|
const context = read("context.json");
|
|
const pipes = read("pipes.json").pipe_points.map(({ lonlat: _l, ...pipe }) => pipe);
|
|
const structures = read("structures.json").structures;
|
|
const types = read("structure_types.json").types;
|
|
const plus = read("plus_stations.json").chainages;
|
|
const P = context.project_id;
|
|
const R = context.route_id;
|
|
const bin = readFileSync(gridPath);
|
|
const grid = m.parseDtmGrid(bin.buffer.slice(bin.byteOffset, bin.byteOffset + bin.byteLength));
|
|
|
|
// 페이지가 열 때 세션에 두는 것 — 표준 · 암 경계 기본 · 저장 상세(design_view_profile · 상세 가게)
|
|
localStorage.setItem("frd_current_project_id", P);
|
|
m.writeState("std-cross-default", context.standard_cross_section, P);
|
|
m.writeStateRaw("rock-boundary-default", String(context.rock_boundary_default_offset_m), P);
|
|
m.writeState("section-detail", detail, P, R);
|
|
|
|
const interval = m.SECTION_STATION_INTERVAL_M;
|
|
const enabled = types.filter((type) => type.enabled !== false);
|
|
const savedLine = () => plan.planned.map(([x, y]) => [x, y]);
|
|
const savedCurves = () =>
|
|
(plan.curves ?? []).map(({ start, apex, end, radius_m }) => ({ start, apex, end, radius_m }));
|
|
const savedStations = m.buildStations(savedLine(), savedCurves(), pipes, structures, {
|
|
intervalM: interval,
|
|
types: enabled,
|
|
stationSnap: plan.station_snap ?? false,
|
|
stationSnapStep: plan.station_snap_step,
|
|
});
|
|
const crossSource = m.pipelineCrossSource(P, detail, savedStations, R);
|
|
const edits = m.readAlignment(detail.longitudinal).edits;
|
|
const status = m.shapeStatus(pipes, structures, types);
|
|
const bermAt = (list) => {
|
|
const spans = m.bermSpansFromStructures(Array.isArray(list) ? list : []);
|
|
return (chainageM) => m.bermSpecAt(spans, chainageM);
|
|
};
|
|
const crossStage = m.createCrossStage({ grid: () => ({ grid }) });
|
|
const pipe = m.createDesignPipeline({
|
|
stages: {
|
|
route: m.createRouteStage({
|
|
minRadiusM: () => plan.min_radius_m ?? 0,
|
|
savedLine: () => ({ line: savedLine(), curves: savedCurves() }),
|
|
}),
|
|
follow: m.createFollowStage({
|
|
savedLine,
|
|
saved: () => ({ pipes, structures, alignment: edits }),
|
|
}),
|
|
stations: m.createStationsStage({
|
|
intervalM: interval,
|
|
types: () => enabled,
|
|
saved: () => plan,
|
|
}),
|
|
ground: m.createGroundStage({
|
|
grid: () => ({ grid, design: "las" }),
|
|
remote: () => Promise.reject(new Error("서버 없음")),
|
|
}),
|
|
planline: m.createPlanlineStage({
|
|
savedBase: (at) => m.plantedBase({ base: null, detail }, at, types),
|
|
savedLine,
|
|
}),
|
|
profile: m.profileStage,
|
|
cross: (ctx) =>
|
|
crossStage.stage({
|
|
...ctx,
|
|
inputs: {
|
|
...ctx.inputs,
|
|
cross: m.crossWithContext(
|
|
ctx.inputs.cross,
|
|
ctx.out.ground?.stations ?? [],
|
|
m.shapeStatus(ctx.inputs.pipes, ctx.inputs.structures, types),
|
|
bermAt(ctx.inputs.structures),
|
|
),
|
|
},
|
|
}),
|
|
footprint: m.footprintStage,
|
|
},
|
|
});
|
|
const record = await pipe.start({
|
|
route: null,
|
|
alignment: edits,
|
|
cross: crossSource?.input ?? null,
|
|
pipes,
|
|
structures,
|
|
plusStations: plus,
|
|
});
|
|
await pipe.settled();
|
|
if (record.outcome !== "done") throw new Error(`묶음 ${record.outcome}`);
|
|
|
|
const ground = pipe.output("ground");
|
|
const profile = pipe.output("profile");
|
|
const shown = m.draftSectionDetail(
|
|
detail,
|
|
ground,
|
|
profile,
|
|
pipe.output("cross"),
|
|
crossSource?.specs ?? new Map(),
|
|
interval,
|
|
status,
|
|
);
|
|
m.applyStructureAreaRows(shown.cross_sections, m.structureAreaRows(shown.cross_sections));
|
|
const haul = m.computeMassHaul(
|
|
shown.cross_sections,
|
|
context.earthwork_conversion,
|
|
context.natural_spoil_min_ground_slope ?? undefined,
|
|
);
|
|
process.stdout.write(
|
|
JSON.stringify({
|
|
keys: ground.stations.map((station) => station.key),
|
|
sections: shown.cross_sections,
|
|
mass_haul: haul,
|
|
warnings: profile.warnings ?? [],
|
|
violations: profile.violations ?? [],
|
|
footprint_records: pipe.output("footprint")?.records?.length ?? 0,
|
|
skipped: record.skipped,
|
|
}),
|
|
);
|
|
process.exit(0);
|