fix(B05): 세월교 요구 여유 = 배수관 산식 + 물넘이 몫, 물넘이포장은 여유 없음

세월교는 배관을 여러 개 묶어 다리 형태로 만든 것이라 배수관과 같은 산식을 쓰되,
그 위에 물넘이가 얹히므로 0.5m를 더 얹는다(2026-08-23 사용자 확정).
물넘이포장은 도로 위에 그대로 만드는 시설이라 들어 올릴 이유가 없다.

- 세월교 Ø1000 → 1.0 + 0.5(토피) + 0.5(물넘이) = 2.0
- 물넘이포장 → 0.0
- 앞서 쓰던 월류 높이(ford_height_m) 기준은 폐기

검증: 계획선 재생성 실측 — 세월교 지점 계획고-지반고 2.00 일치
(배수관 1.50/1.30, BOX 2.50도 유지). pytest 6/6, tsc 통과

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 14:19:57 +09:00
co-authored by Claude Opus 5
parent e1d0d6dda3
commit ea55ad1c73
2 changed files with 22 additions and 19 deletions
+11 -12
View File
@@ -10,7 +10,8 @@
* 산식(사용자 확정 예시 그대로):
* 배수관 Ø1000 → 지반고 + 1.0(관경) + 0.5(토피) = +1.5
* BOX암거 2.0×2.0 → 지반고 + 2.0(구체 높이) + 0.5(토피) = +2.5
* 물넘이·세월교 → 지반고 + 월류 높이(좌측 패널이 설계유량으로 산출한 `ford_height_m`)
* 세월교 Ø1000 → 지반고 + 1.0(관경) + 0.5(토피) + 0.5(물넘이 몫) = +2.0
* 물넘이포장 → 도로에 그대로 앉히는 시설이라 요구 여유 없음
* 토피 0.5m는 B06 배수관 엔진의 `MIN_PIPE_COVER_M`과 같은 값이다(교차 확인).
*
* **정본 산식은 백엔드** `common_util_drainage_pipes.facility_clearance_m`이다 —
@@ -33,7 +34,8 @@ export const MIN_COVER_M = 0.5;
/** 시설 제원이 비었을 때 쓰는 기본값 — 화면 기본 선택과 맞춘다. */
const DEFAULT_PIPE_DIAMETER_MM = 1000;
const DEFAULT_BOX_HEIGHT_M = 2;
const DEFAULT_FORD_HEIGHT_M = 0.3;
/** 세월교 추가 여유(m) — 배관 다발 위에 물넘이가 얹힌다(2026-08-23 사용자 확정). */
const FORD_BRIDGE_EXTRA_M = 0.5;
export interface MinCoverPoint {
chainage_m: number;
@@ -64,20 +66,17 @@ export function minCoverPoints(pipes: MinCoverPipe[]): MinCoverPoint[] {
clearance_m: heightM + MIN_COVER_M,
label: `BOX암거 H${heightM.toFixed(1)}`,
});
} else if (facility === "ford_pavement" || facility === "ford_bridge") {
// 월류 구조 — 토피가 아니라 월류 높이만큼 노면이 올라간다(좌측 패널 산출값).
const heightM = numberOf(options.ford_height_m, DEFAULT_FORD_HEIGHT_M);
result.push({
chainage_m: pipe.chainage_m,
clearance_m: heightM,
label: `${facility === "ford_bridge" ? "세월교" : "물넘이포장"} 월류 ${heightM.toFixed(2)}m`,
});
} else if (facility === "ford_pavement") {
// 물넘이포장은 도로 위에 그대로 만든다 — 들어 올릴 이유가 없다.
continue;
} else {
// 세월교는 배관 다발이라 배수관과 같은 산식 + 물넘이 몫 0.5m.
const diameterM = numberOf(options.pipe_diameter_mm, DEFAULT_PIPE_DIAMETER_MM) / 1000;
const extra = facility === "ford_bridge" ? FORD_BRIDGE_EXTRA_M : 0;
result.push({
chainage_m: pipe.chainage_m,
clearance_m: diameterM + MIN_COVER_M,
label: `배수관 Ø${Math.round(diameterM * 1000)}`,
clearance_m: diameterM + MIN_COVER_M + extra,
label: `${facility === "ford_bridge" ? "세월교" : "배수관"} Ø${Math.round(diameterM * 1000)}`,
});
}
}
+11 -7
View File
@@ -273,12 +273,15 @@ def save_detail_basins(stored_path: str, features: list[dict[str, Any]]) -> Path
# 두면 시설이 들어갈 자리가 없다 — 시설 제원만큼 계획고를 들어 올려야 한다.
# 배수관 Ø1000 → 지반고 + 1.0(관경) + 0.5(토피) = +1.5
# BOX암거 2×2 → 지반고 + 2.0(구체 높이) + 0.5(토피) = +2.5
# 물넘이·세월교 → 지반고 + 월류 높이(좌측 패널이 설계유량으로 산출한 값)
# 토피 0.5m는 B06 배수관 엔진(`MIN_PIPE_COVER_M`)과 같은 값이다.
# 세월교 Ø1000 → 지반고 + 1.0(관경) + 0.5(토피) + 0.5(물넘이 몫) = +2.0
# 물넘이포장 → 도로에 그대로 앉히는 시설이라 요구 여유 없음 (2026-08-23 사용자)
# 세월교는 배관을 여러 개 묶어 다리 형태로 만든 것이라 배수관과 같은 산식을 쓰되,
# 그 위에 물넘이가 얹히므로 0.5m를 더 얹는다. 토피 0.5m는 B06 배수관
# 엔진(`MIN_PIPE_COVER_M`)과 같은 값이다.
MIN_PIPE_COVER_M = 0.5
FORD_BRIDGE_EXTRA_M = 0.5
DEFAULT_PIPE_DIAMETER_MM = 1000.0
DEFAULT_BOX_HEIGHT_M = 2.0
DEFAULT_FORD_HEIGHT_M = 0.3
def _positive(value: Any, fallback: float) -> float:
@@ -294,11 +297,12 @@ def facility_clearance_m(facility: str, options: dict[str, Any] | None) -> float
values = options or {}
if facility == PIPE_FACILITY_BOX:
return _positive(values.get("body_height_m"), DEFAULT_BOX_HEIGHT_M) + MIN_PIPE_COVER_M
if facility in (PIPE_FACILITY_FORD_PAVEMENT, PIPE_FACILITY_FORD_BRIDGE):
# 월류 구조라 토피가 아니라 월류 높이만큼 노면이 올라간다.
return _positive(values.get("ford_height_m"), DEFAULT_FORD_HEIGHT_M)
if facility == PIPE_FACILITY_FORD_PAVEMENT:
# 물넘이포장은 도로 위에 그대로 만든다 — 들어 올릴 이유가 없다.
return 0.0
diameter_m = _positive(values.get("pipe_diameter_mm"), DEFAULT_PIPE_DIAMETER_MM) / 1000.0
return diameter_m + MIN_PIPE_COVER_M
extra = FORD_BRIDGE_EXTRA_M if facility == PIPE_FACILITY_FORD_BRIDGE else 0.0
return diameter_m + MIN_PIPE_COVER_M + extra
def pipe_anchor_clearances(points: list[PipePoint]) -> list[tuple[float, float]]: