/* ============================================================================= * common_util_station.ts * 측점 화면 표기 한 벌 — B05 · B06 목록 · 폼 · 툴팁 · 종단 표시가 모두 이것을 씀 * (`common_util_svg` 가 다시 내보냄 · B05 는 `formatStation` 이름으로 가져다 씀). 가져오는 것이 없는 * 순수 함수라 서버 Node · 시험 헬퍼에서도 그대로 돎. * ========================================================================== */ /** 측점 라벨 — "STA" 접두사 없이 `측점번호+잔여거리`(예: `4+0` · `5+20`). * 화면 표기만 거리를 정수로(119.8 → 5+20 · 2026-09-29 사용자 확정) — 입력 · 저장값은 소수 그대로. * `decimals = 1` = 소수 한 자리까지(B05 평면 스냅 간격 0.1 의 MC · 390-5 · 정수면 그대로 `5+0`). */ export function stationLabel(chainage: number, interval: number, decimals: 0 | 1 = 0): string { const safeInterval = interval > 0 ? interval : 1; let stationNumber = Math.floor((chainage + 1e-6) / safeInterval); let remainder = chainage - stationNumber * safeInterval; if (Math.abs(remainder) < 0.05) remainder = 0; if (remainder >= safeInterval - 0.05) { stationNumber += 1; remainder = 0; } return stationPartsLabel(stationNumber, remainder, decimals); } /** 이미 나눈 측점번호 · 잔여거리의 표기 — 거리는 정수로 보이기만(`decimals = 1` = 소수 한 자리까지). */ export function stationPartsLabel( stationNumber: number, remainder: number, decimals: 0 | 1 = 0, ): string { const scale = decimals ? 10 : 1; return `${stationNumber}+${Math.round(remainder * scale) / scale}`; } /** 측점 간격을 실제 측점 배열에서 되짚는다(최빈 간격). 라벨 표기의 분모다. */ export function inferStationInterval(stations: Array<{ chainage_m: number }>): number { const counts = new Map(); for (let index = 1; index < stations.length; index += 1) { const difference = stations[index].chainage_m - stations[index - 1].chainage_m; if (difference <= 0) continue; const rounded = Math.round(difference * 10) / 10; counts.set(rounded, (counts.get(rounded) ?? 0) + 1); } return ( [...counts.entries()].sort( ([intervalA, countA], [intervalB, countB]) => countB - countA || intervalB - intervalA, )[0]?.[0] ?? 1 ); }