"""B06 라우터의 횡단 설계 파일 읽기와 프리뷰 계산.""" import json from pathlib import Path from typing import Any from B05_Profile.B05_Profile_Engine_Sections import cross_filename from B06_Section.B06_Section_Engine_Design import compute_cross_design from common_util.common_util_route_profile import design_elevation_from_longitudinal from config.config_system import STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M PREVIEW_DESIGN_FIELDS = ( "ground_type", "roadbed_width_m", "cut_area_m2", "cut_soil_area_m2", "cut_rock_area_m2", "cut_rock_kind", "fill_area_m2", "fill_ground_slope", "design_elevation_m", ) def pavement_suggestions(longitudinal: dict[str, Any]) -> dict[float, bool]: mapping: dict[float, bool] = {} stations = longitudinal.get("stations") for station in stations if isinstance(stations, list) else []: suggested = station.get("pavement_suggested") if isinstance(suggested, bool): mapping[round(float(station.get("chainage_m", 0.0)), 3)] = suggested return mapping def default_section_modes(longitudinal: dict[str, Any]) -> dict[float, str]: mapping: dict[float, str] = {} stations = longitudinal.get("stations") for station in stations if isinstance(stations, list) else []: side = station.get("uphill_side") if side in ("left", "right"): mapping[round(float(station.get("chainage_m", 0.0)), 3)] = f"{side}_cut" return mapping def read_cross_design_inputs( project_root: Path, longitudinal_file_path: str, chainage_m: float ) -> tuple[list[dict], float | None, bool]: root = project_root.resolve() longitudinal_path = (root / longitudinal_file_path).resolve() if root not in longitudinal_path.parents: raise ValueError("종단면 파일 경로가 프로젝트 저장소를 벗어났습니다.") if not longitudinal_path.is_file(): raise FileNotFoundError("종단면 상세 파일을 찾을 수 없습니다.") longitudinal = json.loads(longitudinal_path.read_text(encoding="utf-8")) design_elevation = design_elevation_from_longitudinal(longitudinal, chainage_m) suggested = pavement_suggestions(longitudinal).get(round(chainage_m, 3), False) cross_dir = longitudinal_path.parent.parent / "cross_sections" cross_path = (cross_dir / cross_filename(chainage_m)).resolve() if cross_dir.resolve() not in cross_path.parents or not cross_path.is_file(): raise FileNotFoundError("해당 측점의 횡단 상세 파일을 찾을 수 없습니다.") cross = json.loads(cross_path.read_text(encoding="utf-8")) samples = cross.get("samples") if isinstance(cross, dict) else None if not isinstance(samples, list): raise ValueError("횡단 상세 파일 형식이 올바르지 않습니다.") return samples, design_elevation, suggested def attach_default_designs( longitudinal: dict[str, Any], cross_sections: list[dict[str, Any]] ) -> None: modes = default_section_modes(longitudinal) pavement = pavement_suggestions(longitudinal) for section in cross_sections: if section.get("design"): continue try: chainage = float(section.get("chainage_m", 0.0)) suggested = pavement.get(round(chainage, 3), False) design = compute_cross_design( section.get("samples", []), design_elevation_from_longitudinal(longitudinal, chainage), ground_type="ripping_rock", section_mode=modes.get(round(chainage, 3), "left_cut"), paved=suggested, rock_boundary_offset_m=STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M, ) design.update(status="provisional", pavement_suggested=suggested) section["design"] = design except (ValueError, KeyError): continue def recompute_designs_for_alignment( longitudinal: dict[str, Any], cross_sections: list[dict[str, Any]], stored_designs: list[dict[str, Any]], standard: dict[str, Any] | None, rock_boundary_offsets: dict[str, float] | None = None, ) -> None: modes = default_section_modes(longitudinal) pavement = pavement_suggestions(longitudinal) stored_by_chainage = { round(float(record["chainage_m"]), 3): (record.get("design") or {}) for record in stored_designs } session_offsets: dict[float, float] = {} for raw_key, offset in (rock_boundary_offsets or {}).items(): try: session_offsets[round(float(raw_key), 3)] = float(offset) except (TypeError, ValueError): continue for section in cross_sections: chainage = float(section.get("chainage_m", 0.0)) key = round(chainage, 3) stored = stored_by_chainage.get(key) or {} suggested = pavement.get(key, False) try: design = compute_cross_design( section.get("samples", []), design_elevation_from_longitudinal(longitudinal, chainage), ground_type=str(stored.get("ground_type") or "ripping_rock"), section_mode=str(stored.get("section_mode") or modes.get(key, "left_cut")), ditch_side=stored.get("ditch_side"), ditch_type=str(stored.get("ditch_type") or "standard"), paved=bool(stored.get("paved", suggested)), standard=standard, rock_boundary_offset_m=session_offsets.get( key, stored.get("rock_boundary_offset_m", STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M), ), two_stage_slope=bool(stored.get("two_stage_slope", True)), ditch_enabled=stored.get("ditch_enabled"), ) except (ValueError, KeyError): continue design.update(status="provisional", pavement_suggested=suggested) if stored.get("display_half_width_m") is not None: design["display_half_width_m"] = stored["display_half_width_m"] section["design"] = design