- Engine_Sections `build_sections`(측점 틀 · 지반 · 438 1차 계획선 · 포장 제안 · 파일 안 씀) · run_section_generation = 읽기 → 셈 → 쓰기 - 횡단 이름 규칙 → 새 `B05_Profile_Engine_Sections_Names`(700줄 · 다시 내보냄) - resolve_extra_stations 사본 칸(structures · plus) · 따라감 셈 몫 `followed_structures` · `followed_plus_rows` · `followed_pipes` · `section_seed` · `seat_base_pvi` 뗌 - 체인 · 추천 초기 종단이 같은 조각을 부를 자리 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
83 lines
3.6 KiB
Python
83 lines
3.6 KiB
Python
"""횡단 이름 규칙 한 벌 — 파일 이름 · B07 도면 id · 찾기 · 잔재 거름.
|
|
|
|
`B05_Profile_Engine_Sections` 에서 나눔(700줄) · 그 모듈이 다시 내보냄."""
|
|
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
# ── 횡단 이름 규칙(한 벌) — 파일 이름 · B07 도면 id · 찾기가 모두 이것을 씀 ────────────
|
|
#: 이름 속 측점 글 — 정수 m `00120` · 소수 측점 `00119.80`
|
|
CROSS_KEY_PATTERN = r"(\d+(?:\.\d{2})?)"
|
|
|
|
|
|
def cross_station(chainage_m: float) -> int | float:
|
|
"""측점 → 이름 열쇠 값 — 정수 m(0.005m 안)면 int(옛 이름 · 키 그대로) · 아니면 소수 2자리."""
|
|
value = round(float(chainage_m), 2)
|
|
whole = round(value)
|
|
return int(whole) if abs(value - whole) < 0.005 else value
|
|
|
|
|
|
def cross_key(chainage_m: float) -> str:
|
|
"""이름 속 측점 글 — `00120`(정수 · 옛 이름 그대로) · `00119.80`(소수 측점)."""
|
|
station = cross_station(chainage_m)
|
|
return f"{station:05d}" if isinstance(station, int) else f"{station:08.2f}"
|
|
|
|
|
|
def parse_cross_key(text: str) -> int | float:
|
|
"""`cross_key` 되읽기."""
|
|
return cross_station(float(text))
|
|
|
|
|
|
def cross_filename(chainage_m: float) -> str:
|
|
"""측점 chainage에 대응하는 횡단면 파일명(단일 규칙)."""
|
|
return f"cross_{cross_key(chainage_m)}m.json"
|
|
|
|
|
|
def find_cross_file(cross_dir: Path, chainage_m: float) -> Path:
|
|
"""읽기용 — 지금 이름이 없으면 옛 정수 m 이름(소수 측점이 정수로 붙던 옛 프로젝트)."""
|
|
path = cross_dir / cross_filename(chainage_m)
|
|
legacy = cross_dir / f"cross_{int(round(float(chainage_m))):05d}m.json"
|
|
return path if path.exists() or not legacy.exists() else legacy
|
|
|
|
|
|
def valid_cross_names(cross_dir: Path, stations: list[Any]) -> set[str]:
|
|
"""stations 에 맞는 cross_*.json 파일명 집합 — 읽기 전용(지우지 않음 · GET 이 씀).
|
|
|
|
측점 정보가 비면 빈 집합(호출부는 빈 집합이면 필터를 생략한다).
|
|
"""
|
|
# 옛 프로젝트는 소수 측점 파일이 정수 m 이름이라 찾기 규칙(find_cross_file)으로 가림
|
|
return {
|
|
find_cross_file(cross_dir, station["chainage_m"]).name
|
|
for station in stations
|
|
if isinstance(station, dict) and isinstance(station.get("chainage_m"), (int, float))
|
|
}
|
|
|
|
|
|
def cross_files_in_order(cross_dir: Path, stations: list[Any]) -> list[Path]:
|
|
"""잔재를 걸러 읽을 cross_*.json — **측점 차례**(이름 차례면 `01080.16` 이 `01080` 앞)."""
|
|
valid = valid_cross_names(cross_dir, stations)
|
|
|
|
def chainage(path: Path) -> tuple[float, str]:
|
|
match = re.fullmatch(rf"cross_{CROSS_KEY_PATTERN}m", path.stem)
|
|
return (float(match.group(1)) if match else float("inf"), path.name)
|
|
|
|
files = [path for path in cross_dir.glob("cross_*.json") if not valid or path.name in valid]
|
|
return sorted(files, key=chainage)
|
|
|
|
|
|
def prune_stale_cross_files(cross_dir: Path, stations: list[Any]) -> set[str]:
|
|
"""stations에 없는 잔재 cross_*.json을 삭제하고 유효 파일명 집합을 반환한다.
|
|
|
|
쓰는 쪽(측점 만들기 · 측점 목록 고침)만 부른다 — 조회(GET)는 `valid_cross_names`.
|
|
측점 정보가 비어 있으면 오삭제를 피하기 위해 아무것도 지우지 않고
|
|
빈 집합을 반환한다(호출부는 빈 집합이면 필터를 생략한다).
|
|
"""
|
|
valid = valid_cross_names(cross_dir, stations)
|
|
if not valid:
|
|
return valid
|
|
for path in cross_dir.glob("cross_*.json"):
|
|
if path.name not in valid:
|
|
path.unlink(missing_ok=True)
|
|
return valid
|