feat(B05): 구조물 측점과 겹치는 규칙 측점 제거
구조물은 보조말뚝을 박아 그 자리가 정본 측점이 된다. 규칙 격자(20m)와 0.1m 안에서 만나면 세로선·라벨이 두 겹으로 겹쳐 읽히지 않았다. - 공용 유틸 dropStationsNear 추가(B05_Profile_Util_Station). - 종단 그래프와 3D 측점 띠가 같은 규칙을 쓴다 — 두 화면의 측점이 어긋나지 않는다. 검증: tsc --noEmit 통과, tmp/tests/test_b05_station_merge.mjs 통과. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
import { dropStationsNear } from "./B05_Profile_Util_Station";
|
||||||
import { CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend";
|
import { CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend";
|
||||||
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
|
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
|
||||||
import { showToast } from "@ui/ui_template_elements";
|
import { showToast } from "@ui/ui_template_elements";
|
||||||
@@ -449,7 +450,9 @@ export async function renderB05Route(root: HTMLElement): Promise<void> {
|
|||||||
return best.elevation_m;
|
return best.elevation_m;
|
||||||
};
|
};
|
||||||
// 측점 바 양 끝 램프용 상단측: 사용자 변경분 → solve 자동 판정 순으로 적용.
|
// 측점 바 양 끝 램프용 상단측: 사용자 변경분 → solve 자동 판정 순으로 적용.
|
||||||
const withUphill = [...regular, ...injected].map((station) => {
|
// 구조물 측점과 0.1m 안에서 겹치는 규칙 측점은 지운다 — 3D 측점 띠도 종단 그래프와
|
||||||
|
// 같은 규칙을 써야 두 화면의 측점이 어긋나지 않는다(2026-09-06).
|
||||||
|
const withUphill = [...dropStationsNear(regular, injected), ...injected].map((station) => {
|
||||||
const design = designAt(station.chainage_m);
|
const design = designAt(station.chainage_m);
|
||||||
return {
|
return {
|
||||||
...station,
|
...station,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
* 거친다. 그래프·테이블·유토곡선이 같은 X 매핑을 쓰는 규칙은 그대로다.
|
* 거친다. 그래프·테이블·유토곡선이 같은 X 매핑을 쓰는 규칙은 그대로다.
|
||||||
* ========================================================================== */
|
* ========================================================================== */
|
||||||
|
|
||||||
|
import { dropStationsNear } from "./B05_Profile_Util_Station";
|
||||||
import {
|
import {
|
||||||
createLongitudinalProfile,
|
createLongitudinalProfile,
|
||||||
longitudinalMinimumWidth,
|
longitudinalMinimumWidth,
|
||||||
@@ -237,9 +238,13 @@ export function renderProfile(ctx: ProfileRenderContext): void {
|
|||||||
// 그대로 남는다(2026-08-02 사용자 보고).
|
// 그대로 남는다(2026-08-02 사용자 보고).
|
||||||
const regular = graphData.stations.filter((station) => station.kind !== "irregular");
|
const regular = graphData.stations.filter((station) => station.kind !== "irregular");
|
||||||
const injected = irregularGraphStations(irregularStations, maxChainageOf(longitudinal));
|
const injected = irregularGraphStations(irregularStations, maxChainageOf(longitudinal));
|
||||||
|
// 구조물 측점과 0.1m 안에서 겹치는 규칙 측점은 지운다 — 세로선·라벨이 두 겹으로
|
||||||
|
// 겹쳐 읽히지 않는다(2026-09-06). 남는 쪽은 구조물 측점이다.
|
||||||
const graphLongitudinal = {
|
const graphLongitudinal = {
|
||||||
...graphData,
|
...graphData,
|
||||||
stations: [...regular, ...injected].sort((a, b) => a.chainage_m - b.chainage_m),
|
stations: [...dropStationsNear(regular, injected), ...injected].sort(
|
||||||
|
(a, b) => a.chainage_m - b.chainage_m,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
// 세로 자동 맞춤 — 지금 화면에 보이는 누가거리 구간만 보고 Y 창을 잡는다(2026-09-04
|
// 세로 자동 맞춤 — 지금 화면에 보이는 누가거리 구간만 보고 Y 창을 잡는다(2026-09-04
|
||||||
// 사용자 확정). 가로 스크롤 위치(`scrollLeft`)와 본문 폭이 곧 보이는 구간이다.
|
// 사용자 확정). 가로 스크롤 위치(`scrollLeft`)와 본문 폭이 곧 보이는 구간이다.
|
||||||
|
|||||||
@@ -52,3 +52,24 @@ export function parseStationText(text: string, intervalM: number): number | null
|
|||||||
const direct = Number(trimmed);
|
const direct = Number(trimmed);
|
||||||
return Number.isFinite(direct) && direct >= 0 ? direct : null;
|
return Number.isFinite(direct) && direct >= 0 ? direct : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 구조물 측점과 겹쳤다고 볼 거리(m) — 이보다 가까우면 규칙 측점을 지운다. */
|
||||||
|
export const STATION_MERGE_TOLERANCE_M = 0.1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 구조물 측점과 **겹치는 규칙 측점을 지운다**(2026-09-06).
|
||||||
|
*
|
||||||
|
* 구조물은 보조말뚝을 박아 그 자리가 정본 측점이 된다. 규칙 격자(20m)와 0.1m 안에서
|
||||||
|
* 만나면 세로선·라벨이 두 겹으로 겹쳐 읽히지 않으므로 구조물 쪽만 남긴다.
|
||||||
|
*/
|
||||||
|
export function dropStationsNear<T extends { chainage_m: number }>(
|
||||||
|
stations: readonly T[],
|
||||||
|
anchors: ReadonlyArray<{ chainage_m: number }>,
|
||||||
|
toleranceM: number = STATION_MERGE_TOLERANCE_M,
|
||||||
|
): T[] {
|
||||||
|
if (anchors.length === 0) return [...stations];
|
||||||
|
return stations.filter(
|
||||||
|
(station) =>
|
||||||
|
!anchors.some((anchor) => Math.abs(anchor.chainage_m - station.chainage_m) <= toleranceM),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user