fix(B05): 구조물 측점 ▲▼ 버튼이 두 벌 생기고 가짜 변화점을 만들던 문제

같은 구조물 측점이 두 좌표로 존재했음 — 종단 정본은 원값(`85.594514`), 사이드바 목록은
표기 규칙(`n+ 0.0`)대로 0.1m 로 반올림한 값(`85.6`). 중복 제거가 누가거리 키(소수 3자리)
비교라 서로 다른 측점으로 잡혀 버튼이 두 벌 섰음.

문제는 표시만이 아니었음 — 사이드바 쪽 버튼을 누르면 정본 측점에서 몇 mm 떨어진 자리에
**가짜 변화점**이 승격돼 그 구간 기울기가 수천 %로 튀었음(실측 11,858%).

겹침 판정을 키 비교에서 **거리 비교**(0.1m 이내면 같은 측점)로 바꿈. 사이드바 목록이
0.1m 반올림이라 최대 0.05m 어긋나고, 실제로 그만큼 붙은 별개 측점은 없음.

실측(route 132, 관 11개): ▲ 버튼 75 → 65개, 근접 중복쌍 9건 → 0건.
전체 테스트 183건 통과.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-03 20:23:36 +09:00
co-authored by Claude Opus 5
parent 3d037d869e
commit 0f068a6c1d
+23 -6
View File
@@ -174,6 +174,14 @@ export interface EditOverlayOptions {
onStation: (chainageM: number, delta: number) => void; onStation: (chainageM: number, delta: number) => void;
} }
/**
* 두 측점 목록이 **같은 측점**을 가리키는 것으로 볼 누가거리 차(m).
*
* 사이드바 구조물 목록은 표기 규칙(`n+ 0.0`)대로 0.1m 로 반올림한 값을 들고 있어 종단
* 정본의 원값과 최대 0.05m 어긋난다. 실제로 이만큼 붙은 별개 측점은 없다.
*/
const SAME_STATION_TOLERANCE_M = 0.1;
function overlayButton( function overlayButton(
repeater: { start: (action: () => void) => void }, repeater: { start: (action: () => void) => void },
className: string, className: string,
@@ -241,19 +249,28 @@ export function createEditOverlay(options: EditOverlayOptions): HTMLElement {
* 측점선 자체는 그대로라 버튼만 살짝 비켜선다. * 측점선 자체는 그대로라 버튼만 살짝 비켜선다.
* *
* 확정을 거친 구조물 측점은 종단 정본(alignment.stations)에 **이미 병합**돼 있어 * 확정을 거친 구조물 측점은 종단 정본(alignment.stations)에 **이미 병합**돼 있어
* 사이드바 목록(irregular)과 이중으로 잡힌다 — 같은 chainage(소수 3자리)는 한 번만 * 사이드바 목록(irregular)과 이중으로 잡힌다 — 겹치면 한 번만 버튼을 단다
* 버튼을 단다(2026-08-04 사용자 보고: 구조물 측점 버튼이 2개씩 생김). * (2026-08-04 사용자 보고: 구조물 측점 버튼이 2개씩 생김).
*
* 겹침 판정은 **거리**로 한다. 두 목록의 누가거리가 소수 3자리까지 같지 않기 때문이다 —
* 종단 정본은 원값(`85.594514`), 사이드바 목록은 표기 규칙대로 0.1m 로 반올림한 값
* (`85.6`)을 들고 있어 키 비교로는 서로 다른 측점으로 잡혔다. 그 결과 버튼이 두 벌 생기고,
* 사이드바 쪽 버튼을 누르면 정본 측점에서 몇 mm 떨어진 **가짜 변화점**이 승격돼 그 구간
* 기울기가 수천 %로 튀었다(2026-09-03 사용자 보고 · 실측 11,858%).
*/ */
const alignmentStationKeys = new Set(
alignment.stations.map((station) => chainageKey(station.chainage_m)),
);
const stationPlacements = [ const stationPlacements = [
...alignment.stations.map((station) => ({ ...alignment.stations.map((station) => ({
chainage: station.chainage_m, chainage: station.chainage_m,
plan: station.plan_elevation_m, plan: station.plan_elevation_m,
})), })),
...irregular ...irregular
.filter((station) => !alignmentStationKeys.has(chainageKey(station.chainage_m))) .filter(
(station) =>
!alignment.stations.some(
(merged) =>
Math.abs(merged.chainage_m - station.chainage_m) <= SAME_STATION_TOLERANCE_M,
),
)
.map((station) => ({ .map((station) => ({
chainage: station.chainage_m, chainage: station.chainage_m,
plan: planElevationAtSample(alignment, station.chainage_m), plan: planElevationAtSample(alignment, station.chainage_m),