교본 6장 3절이 운반처리 위치를 평면도·횡단도에 표시하도록 요구함. - 긴 파선+점(9 3 2 3) · 사토장 색 — **터파기 짧은 점선과 선 종류를 가름** - 이름표 「유용토운반작업장 ○㎡」 를 평상 위에 얹음 - 말풍선에 단면·폭·구간 용량·못 담은 몫·잘림 경고와 **근거 두 줄** (「폭은 노면 끝에서 잼」·「교본 6장 3절이 표시를 요구함」) - 지반을 못 만나 잘린 사토장은 붉게 — 사면 미폐합 경고와 같은 결 - 계획선을 만져도 사토장이 안 사라지게 저장분 폭·구간값을 이어 붙임 (폭은 구간 용량에서 서버가 정한 값이라 브라우저가 다시 풀지 않음) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
92 lines
4.3 KiB
TypeScript
92 lines
4.3 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_UI_Cross_SpoilFill.ts
|
|
* 횡단도에 **사토장(유용토운반작업장) 성토선**을 얹는다.
|
|
*
|
|
* 왜 있나 — 임도기술교본 6장 3절이 **운반처리 위치를 평면도와 횡단도에 표시**하도록
|
|
* 요구한다(지식DB `01_임도/02_상세설계/유용토운반작업장.md` §2). 수량을 세기 전에
|
|
* 도면에 그것이 보여야 한다.
|
|
*
|
|
* ⚠ **터파기 파선과 선 종류를 가른다** — 터파기는 짧은 점선, 사토장은 긴 파선+점.
|
|
* 같은 파선으로 두면 도면에서 둘을 구별할 수 없다(2026-09-09 네 창 확정).
|
|
* ⚠ 값은 설계 결과(`spoil_fill_*`)에서 온다 — 여기서 **다시 세지 않는다**.
|
|
* ========================================================================== */
|
|
|
|
const SVG_NS = "http://www.w3.org/2000/svg";
|
|
|
|
/** 설계 결과에서 사토장 그리기에 쓰는 값만 추려 받는다. */
|
|
export interface SpoilFillDrawing {
|
|
spoil_fill_line?: Array<{ offset_m: number; elevation_m: number }> | null;
|
|
spoil_fill_area_m2?: number | null;
|
|
spoil_fill_width_m?: number | null;
|
|
spoil_fill_unclosed?: boolean | null;
|
|
spoil_fill_capacity_m3?: number | null;
|
|
spoil_fill_placed_m3?: number | null;
|
|
spoil_fill_unplaced_m3?: number | null;
|
|
}
|
|
|
|
/** 말풍선 문구 — 무엇이 얼마나 쌓였는지와 **근거**를 함께 적는다. */
|
|
export function spoilFillTooltip(design: SpoilFillDrawing): string {
|
|
const area = Number(design.spoil_fill_area_m2 ?? 0);
|
|
const width = Number(design.spoil_fill_width_m ?? 0);
|
|
const lines = [
|
|
`유용토운반작업장(구 사토장) · 단면 ${area.toFixed(2)}㎡ · 폭 ${width.toFixed(2)}m`,
|
|
];
|
|
const capacity = design.spoil_fill_capacity_m3;
|
|
if (typeof capacity === "number" && capacity > 0) {
|
|
const placed = Number(design.spoil_fill_placed_m3 ?? 0);
|
|
lines.push(`구간 용량 ${capacity.toFixed(1)}㎥ 중 ${placed.toFixed(1)}㎥ 담김`);
|
|
const unplaced = Number(design.spoil_fill_unplaced_m3 ?? 0);
|
|
if (unplaced > 0) {
|
|
lines.push(`⚠ ${unplaced.toFixed(1)}㎥ 는 못 담음 — 지반 자료가 있는 데까지만 넓힘`);
|
|
}
|
|
}
|
|
if (design.spoil_fill_unclosed) {
|
|
lines.push("⚠ 비탈이 원지반을 못 만나 잘림 — 지반 자료 범위를 넘어감");
|
|
}
|
|
lines.push("폭은 노면 끝(노견이 시작하는 자리)에서 잼 — 그 구간 노견도 이 성토 안에 듦");
|
|
lines.push("교본 6장 3절이 평면도·횡단도 표시를 요구함");
|
|
return lines.join("\n");
|
|
}
|
|
|
|
/**
|
|
* 사토장 성토선을 그린다. 선이 없으면 아무것도 하지 않는다.
|
|
* 돌려주는 값은 그린 폴리라인(없으면 `null`) — 부르는 쪽이 강조에 쓸 수 있다.
|
|
*/
|
|
export function appendSpoilFillOverlay(
|
|
layer: SVGElement,
|
|
design: SpoilFillDrawing | null | undefined,
|
|
x: (offset: number) => number,
|
|
toDisplayY: (elevation: number) => number,
|
|
): SVGElement | null {
|
|
const line = design?.spoil_fill_line;
|
|
if (!design || !Array.isArray(line) || line.length < 2) return null;
|
|
const polyline = document.createElementNS(SVG_NS, "polyline");
|
|
polyline.setAttribute(
|
|
"points",
|
|
line.map((point) => `${x(point.offset_m)},${toDisplayY(point.elevation_m)}`).join(" "),
|
|
);
|
|
polyline.setAttribute(
|
|
"class",
|
|
design.spoil_fill_unclosed ? "b06-chart__spoil-fill is-unclosed" : "b06-chart__spoil-fill",
|
|
);
|
|
const title = document.createElementNS(SVG_NS, "title");
|
|
title.textContent = spoilFillTooltip(design);
|
|
polyline.append(title);
|
|
layer.append(polyline);
|
|
|
|
// 이름표 — 평상 한가운데 위에 얹는다. 선만 있으면 그것이 무엇인지 도면에서 모른다.
|
|
const first = line[0];
|
|
const last = line[line.length - 1];
|
|
const label = document.createElementNS(SVG_NS, "text");
|
|
label.setAttribute("x", String((x(first.offset_m) + x(last.offset_m)) / 2));
|
|
label.setAttribute("y", String(toDisplayY(Math.max(first.elevation_m, last.elevation_m)) - 4));
|
|
label.setAttribute("text-anchor", "middle");
|
|
label.setAttribute("class", "b06-chart__spoil-fill-label");
|
|
label.textContent = `유용토운반작업장 ${Number(design.spoil_fill_area_m2 ?? 0).toFixed(2)}㎡`;
|
|
const labelTitle = document.createElementNS(SVG_NS, "title");
|
|
labelTitle.textContent = spoilFillTooltip(design);
|
|
label.append(labelTitle);
|
|
layer.append(label);
|
|
return polyline;
|
|
}
|