"""사면이 안 닫히는 측점 — **얼마나 더 넓히면 닫히나**를 잰다(읽기 전용). 왜 — 계산 반폭(±20m) 안에서 사면이 원지반을 못 만나면 절·성토 면적이 그 자리에서 잘린다. 「경고로 대체」가 2026-09-03 사용자 확정이지만, 데스크탑 창이 **수량이 조용히 적게 나온다**는 점을 짚었다. 반폭을 넓힐지 정하려면 **얼마나 넓혀야 닫히는지**를 알아야 한다. 재는 법 — 저장된 지반 샘플의 **바깥 5m 평균 기울기**로 지형이 계속 이어진다고 보고, 설계 사면선과 만나는 거리를 푼다. 지형이 그대로 이어진다는 가정이라 **하한 추정**이다. """ 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)) from config.config_system import STORAGE_BASE_DIR # noqa: E402 ROOT = Path(STORAGE_BASE_DIR) def outer_trend(samples, side): """바깥 5m 구간의 지반 기울기(수직/수평) — 양수면 바깥으로 갈수록 높아진다.""" pts = sorted( ((float(s["offset_m"]), float(s["elevation_m"])) for s in samples if s.get("valid") is not False), key=lambda p: p[0], ) if side == "left": pts = [p for p in pts if p[0] <= pts[0][0] + 5.0] pts = pts[::-1] else: pts = [p for p in pts if p[0] >= pts[-1][0] - 5.0] if len(pts) < 2: return None, None run = abs(pts[-1][0] - pts[0][0]) if run <= 0: return None, None return (pts[-1][1] - pts[0][1]) / run, pts[-1] def main(project: str, route_hint: str = "") -> None: base = ROOT / "1" / "3" / project / "B06_Section" / "cross_sections" files = sorted(base.glob("cross_*.json")) print(f"측점 파일 {len(files)}개") rows = [] for path in files: doc = json.loads(path.read_text(encoding="utf-8")) samples = doc.get("samples") or [] if len(samples) < 4: continue for side in ("left", "right"): slope, edge = outer_trend(samples, side) if slope is None or edge is None: continue # 절토 사면은 1:0.4(암)~1:1.0(토사) — 바깥으로 갈수록 오르는 기울기 1/n. # 지형이 사면보다 가파르면 영원히 안 만난다. for ratio, label in ((0.4, "암 1:0.4"), (1.0, "토사 1:1.0")): design_rise = 1.0 / ratio if slope >= design_rise: rows.append((path.stem, side, label, None)) break never = [r for r in rows if r[3] is None] print(f"지형이 설계 사면보다 가팔라 **영원히 못 만나는** 측점·측 조합: {len(never)}건") for row in never[:12]: print(" ", row[0], row[1], row[2]) if __name__ == "__main__": main(sys.argv[1] if len(sys.argv) > 1 else "5601e828-feea-487a-9b25-415e5199f2f5")