⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt` 가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는 시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음. - `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침** - `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시) - 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q` 옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0 ⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
100 lines
4.2 KiB
Python
100 lines
4.2 KiB
Python
"""미교차 측점이 **얼마나 더 넓히면 닫히는지** — 저장분만으로 잰다(읽기 전용).
|
|
|
|
재는 법 — 저장된 `design_line` 의 바깥 끝(사면이 잘린 자리)에서 시작해, 지반이 **바깥 5m 평균
|
|
기울기**로 이어진다고 보고 설계 사면선과 만나는 거리를 푼다. 지형이 그대로 이어진다는 가정이라
|
|
**하한 추정**이며, 지형이 설계 사면보다 가파르면 「아무리 넓혀도 안 닫힘」으로 잡힌다.
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(PROJECT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
import config.config_db as db # noqa: E402
|
|
from config.config_system import STORAGE_BASE_DIR # noqa: E402
|
|
|
|
ROUTE_ID = 169
|
|
PROJECT = "5601e828-feea-487a-9b25-415e5199f2f5"
|
|
SAMPLES = Path(STORAGE_BASE_DIR) / "1" / "3" / PROJECT / "B06_Section" / "cross_sections"
|
|
|
|
|
|
def ground_of(chainage: float):
|
|
path = SAMPLES / f"cross_{int(round(chainage)):05d}m.json"
|
|
if not path.exists():
|
|
return None
|
|
doc = json.loads(path.read_text(encoding="utf-8"))
|
|
pts = [
|
|
(float(s["offset_m"]), float(s["elevation_m"]))
|
|
for s in doc.get("samples") or []
|
|
if s.get("valid") is not False and s.get("elevation_m") is not None
|
|
]
|
|
return sorted(pts) or None
|
|
|
|
|
|
def need_width(design_line, ground, ratio_fill, ratio_cut):
|
|
"""양 끝에서 필요한 추가 폭(m). 못 닫히면 None."""
|
|
out = {}
|
|
for side, pick in (("left", min), ("right", max)):
|
|
edge = pick(design_line, key=lambda p: p["offset_m"])
|
|
tip_x, tip_z = float(edge["offset_m"]), float(edge["elevation_m"])
|
|
near = [p for p in ground if abs(p[0] - tip_x) <= 5.0]
|
|
if len(near) < 2:
|
|
continue
|
|
run = near[-1][0] - near[0][0]
|
|
if abs(run) < 1e-6:
|
|
continue
|
|
terrain = (near[-1][1] - near[0][1]) / run # 바깥으로 갈수록 (+)면 오름
|
|
gap = tip_z - (near[-1][1] if side == "right" else near[0][1])
|
|
# 절토(사면이 올라감)면 1/cut, 성토(내려감)면 -1/fill. 바깥 방향 부호를 맞춘다.
|
|
rising = gap < 0 # 설계선이 지반보다 낮다 = 절토측
|
|
design = (1.0 / ratio_cut) if rising else (-1.0 / ratio_fill)
|
|
if side == "left":
|
|
design, terrain = -design, -terrain
|
|
closing = design - terrain
|
|
if abs(closing) < 1e-9 or (gap < 0) != (closing > 0):
|
|
out[side] = None # 벌어지기만 함 — 아무리 넓혀도 안 닫힘
|
|
continue
|
|
out[side] = abs(gap / closing)
|
|
return out
|
|
|
|
|
|
async def main() -> None:
|
|
await db.init_db_pool()
|
|
pool = db.get_db_pool()
|
|
rows = []
|
|
async with pool.acquire() as conn, conn.cursor() as cur:
|
|
await cur.execute("SELECT chainage_m, data FROM cross_sections WHERE route_id=%s", (ROUTE_ID,))
|
|
for chainage, data in await cur.fetchall():
|
|
doc = (json.loads(data) if isinstance(data, str) else data) or {}
|
|
design = doc.get("design") or {}
|
|
if not design.get("slope_unclosed"):
|
|
continue
|
|
ground = ground_of(float(chainage))
|
|
line = design.get("design_line") or []
|
|
if not ground or len(line) < 2:
|
|
continue
|
|
need = need_width(
|
|
line, ground, float(design.get("fill_slope_ratio") or 1.2),
|
|
float(design.get("cut_slope_ratio") or 1.0),
|
|
)
|
|
rows.append((float(chainage), need))
|
|
await db.close_db_pool()
|
|
|
|
never = [(c, s) for c, n in rows for s, v in n.items() if v is None]
|
|
finite = [(c, s, v) for c, n in rows for s, v in n.items() if v is not None]
|
|
print(f"미교차 측점 {len(rows)}곳")
|
|
print(f"· 아무리 넓혀도 안 닫히는 측·조합: {len(never)}건")
|
|
if finite:
|
|
finite.sort(key=lambda r: -r[2])
|
|
print(f"· 넓히면 닫히는 조합: {len(finite)}건 — 필요한 추가 폭 최대 {finite[0][2]:.1f}m, "
|
|
f"중앙값 {sorted(v for _, _, v in finite)[len(finite)//2]:.1f}m")
|
|
for row in finite[:6]:
|
|
print(f" {row[0]:8.1f}m {row[1]:5s} +{row[2]:.1f}m")
|
|
|
|
|
|
asyncio.run(main())
|