fix(458 B06 노선 바뀜): 주인 측점 거리 상한 0.51 m · 시종점 밖 시설 안 얹음 · 갈아끼움 grid 지움 · 늘 다시 얹기 · 이름 = 계획 제목
- 관 세트 주인 측점 = 0.51 m 안 가장 가까운 측점(TS attachCulvertSets ↔ 파이썬 attach_culvert_specs) · 넘으면 연동 이웃에도 안 얹고 missed - 형상 얹기 한 벌(attachShapes ↔ attach_shapes) — 시종점 측점 자리인데 좌표가 노선 진행 반대쪽 0.51 m 넘게 바깥 = 「노선 밖」 · 안 얹음 · 안 얹은 것 돌려줌(서버 = 로그 경고) - 측점 갈아끼움 — 격자 아닌 자리면 앞 횡단 grid 표시 지움 · 갈아끼움 뒤 가게 초안 없어도 늘 다시 얹음 · 카드 · 종단 측점 이름 = 계획 제목 · 안 얹은 시설 알림(바뀐 때 한 번) - 시험: test_458_swap_attach(재현 A · D) · test_323_4 거울 moved 경우(상한 · 시종점 밖 · 연동 폭) · test_299 같은 키 견본 = 주인 0.51 m 안으로 · 273_2 흉내 돌려줌 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,8 @@ const SECTION_KEYS: Record<string, string> = {
|
||||
};
|
||||
/** 폭 절반만큼 옆 측점에도 걸치는 종류 — 물넘이포장만. */
|
||||
const SPAN_LINKED_TYPES = new Set(["ford_pavement"]);
|
||||
/** 주인 측점 거리 상한(m · 458) — `design_selection.PIPE_MATCH_M` · 파이썬 `PIPE_MATCH_M` 짝. */
|
||||
const PIPE_MATCH_M = 0.51;
|
||||
|
||||
export const MIN_PIPE_COVER_M = 0.5;
|
||||
export const CHUTE_THICKNESS_CM = 45.0;
|
||||
@@ -587,18 +589,17 @@ function rankBefore(a: readonly number[], b: readonly number[]): boolean {
|
||||
return at >= 0 && a[at] < b[at];
|
||||
}
|
||||
|
||||
/** 측점 자료에 세트를 얹는다(파이썬 `attach_culvert_sets`). 얹은 개수를 돌려준다. */
|
||||
/** 측점 자료에 세트를 얹는다(파이썬 `attach_culvert_specs`). 얹은 개수를 돌려준다 ·
|
||||
* 주인 측점이 상한 밖인 세트는 안 얹고 그 누가거리를 `missed` 에 담음(458). */
|
||||
export function attachCulvertSets(
|
||||
sections: Array<Record<string, unknown>>,
|
||||
sets: ReadonlyMap<number, CulvertSetSpec>,
|
||||
missed?: number[],
|
||||
): number {
|
||||
if (sets.size === 0) return 0;
|
||||
let attached = 0;
|
||||
// ⚠⚠ **관 자리와 측점 자리는 최대 0.5m 어긋난다 — 그것이 설계다**(2026-09-09 실측).
|
||||
// 측점을 만들 때 정수 미터가 같은 격자 측점이 있으면 그리로 스냅한다(횡단 파일명이
|
||||
// 정수 미터라 두 측점이 한 파일을 덮어쓰는 것을 막는 가드). 관 440.241 은 측점 440.0 위에 선다.
|
||||
// ⇒ 0.02m 로만 보면 그런 관은 어느 측점에도 안 붙어 **그림도 수량도 통째로 빠진다.**
|
||||
// ⇒ **가장 가까운 측점 하나**는 거리와 무관하게 그 관의 자리로 본다(짝: 파이썬 `attach_culvert_sets`).
|
||||
// 주인 측점 = 가장 가까운 측점 하나(0.51 m 안 · 옛 정수 m 측점 위 소수 관도 붙음 · 짝: 파이썬
|
||||
// `attach_culvert_specs`) — 더 멀면 그 시설 측점이 없는 것(노선 바뀜) · 연동 이웃에도 안 얹고 경고(458).
|
||||
const owner = new Map<number, number>();
|
||||
for (const [pipeChainage] of sets) {
|
||||
let nearest: number | null = null;
|
||||
@@ -609,7 +610,9 @@ export function attachCulvertSets(
|
||||
nearest = value;
|
||||
}
|
||||
}
|
||||
if (nearest !== null) owner.set(pipeChainage, nearest);
|
||||
if (nearest !== null && Math.abs(nearest - pipeChainage) < PIPE_MATCH_M)
|
||||
owner.set(pipeChainage, nearest);
|
||||
else missed?.push(pipeChainage);
|
||||
}
|
||||
for (const section of sections) {
|
||||
const chainage = num(section.chainage_m, null);
|
||||
@@ -621,6 +624,7 @@ export function attachCulvertSets(
|
||||
let order = 0;
|
||||
for (const [pipeChainage, spec] of sets) {
|
||||
order += 1;
|
||||
if (!owner.has(pipeChainage)) continue;
|
||||
// 연동 대상 종류만 폭의 절반까지 옆 측점에 걸친다.
|
||||
let reach = CHAINAGE_TOLERANCE_M;
|
||||
if (SPAN_LINKED_TYPES.has(String(spec.type))) reach += (num(spec.span_m, 0) ?? 0) / 2;
|
||||
|
||||
@@ -93,16 +93,59 @@ function attachBlanks(
|
||||
}
|
||||
}
|
||||
|
||||
/** 측점마다 그림 자료를 걷고 현황으로 다시 얹음(파이썬 `attach_shapes` 짝 · 제자리 고침). */
|
||||
/** 안 얹은 관 · 시설 누가거리(458) — 시종점 밖으로 밀림 · 주인 측점이 0.51 m 넘게 멂. */
|
||||
export interface AttachMisses {
|
||||
outside: number[];
|
||||
far: number[];
|
||||
}
|
||||
|
||||
/** 주인 측점 거리 상한(m) — `design_selection.PIPE_MATCH_M` · 파이썬 `PIPE_MATCH_M` 짝. */
|
||||
const PIPE_MATCH_M = 0.51;
|
||||
|
||||
/** 시종점 밖으로 밀린 관인가(458) — 시종점 측점 자리인데 좌표가 노선 진행 반대쪽으로 0.51 m 넘게 바깥
|
||||
* (따라감이 누가거리를 0 · 끝에 붙여 둠 · 옆 거리는 안 봄 · 좌표 없으면 아님 · 파이썬 `_outside_route` 짝). */
|
||||
function outsideRoute(
|
||||
pipe: { chainage_m: number; x?: number | null; y?: number | null },
|
||||
sections: ReadonlyArray<Record<string, unknown>>,
|
||||
): boolean {
|
||||
const placed = sections
|
||||
.filter((s) => [s.chainage_m, s.center_x, s.center_y].every(Number.isFinite))
|
||||
.sort((a, b) => Number(a.chainage_m) - Number(b.chainage_m));
|
||||
if (placed.length < 2 || !Number.isFinite(pipe.x) || !Number.isFinite(pipe.y)) return false;
|
||||
const ends: Array<[Record<string, unknown>, Record<string, unknown>]> = [
|
||||
[placed[0], placed[1]],
|
||||
[placed[placed.length - 1], placed[placed.length - 2]],
|
||||
];
|
||||
return ends.some(([end, next]) => {
|
||||
if (Math.abs(Number(end.chainage_m) - pipe.chainage_m) >= PIPE_MATCH_M) return false;
|
||||
const [ex, ey] = [Number(end.center_x), Number(end.center_y)];
|
||||
const [dx, dy] = [Number(next.center_x) - ex, Number(next.center_y) - ey];
|
||||
const span = Math.hypot(dx, dy);
|
||||
return span > 0 && ((pipe.x! - ex) * dx + (pipe.y! - ey) * dy) / span <= -PIPE_MATCH_M;
|
||||
});
|
||||
}
|
||||
|
||||
/** 측점마다 그림 자료를 걷고 현황으로 다시 얹음(파이썬 `attach_shapes` 짝 · 제자리 고침) ·
|
||||
* 시종점 밖 관 · 주인 측점 먼 관은 안 얹고 돌려줌(458 · 경고는 부르는 쪽). */
|
||||
export function attachShapes(
|
||||
sections: Array<Record<string, unknown>>,
|
||||
pipes: ReadonlyArray<PlanPipe>,
|
||||
allPipes: ReadonlyArray<PlanPipe>,
|
||||
structures: ReadonlyArray<PlanStructure>,
|
||||
types: ReadonlyArray<AttachType>,
|
||||
): void {
|
||||
): AttachMisses {
|
||||
for (const section of sections) for (const key of [...SHAPE_KEYS, "blank"]) delete section[key];
|
||||
const misses: AttachMisses = { outside: [], far: [] };
|
||||
const pipes = allPipes.filter((pipe) => {
|
||||
const out = outsideRoute(pipe as Parameters<typeof outsideRoute>[0], sections);
|
||||
if (out) misses.outside.push(pipe.chainage_m);
|
||||
return !out;
|
||||
});
|
||||
const registry = registryDefaults(types);
|
||||
attachCulvertSets(sections, buildCulvertSets(pipes as readonly CulvertPipePoint[], registry));
|
||||
attachCulvertSets(
|
||||
sections,
|
||||
buildCulvertSets(pipes as readonly CulvertPipePoint[], registry),
|
||||
misses.far,
|
||||
);
|
||||
const shaped = (shape: string) => types.filter((t) => catalogRow(t.type_id)?.shape === shape);
|
||||
// 관 세트가 선 측점은 벽을 안 얹음(`attachWallSpecs`) — 차례 = 서버와 같음
|
||||
attachWallSpecs(
|
||||
@@ -121,4 +164,5 @@ export function attachShapes(
|
||||
);
|
||||
attachUnderdrainSpecs(sections, underdrainSpecsFrom(structures as UnderdrainInput[]));
|
||||
attachBlanks(sections, pipes, structures, types);
|
||||
return misses;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user