Files
Aislo/common_util/common_util_station_key.py
T
eomsangdonandClaude Opus 5.5 172ff8170b refactor(511-B B-6 정리): 운영 부름 0 파이썬 짝 걷음 · 남는 짝 머리에 남는 까닭 · 등록부 until 걷음 · 알려진 차 까닭 · 322_3 flow 빨강 고침
걷은 함수(운영 부름 0 · 전수 확인 = 화면 · 서버 · 체인 · 추천 엔진 · B07 · 시험)
- common_util/common_util_excavation.py 통째(126 줄 · pipe_trench_width_m · wall_trench_area_m2 · wall_trench_width_m · 서버 = Node excavation.trench)
- Engine_Grade_Carry.carry_edits(Node carry_seat 겉) · Route_Save_Parts.seat_route_moved(시험 헬퍼 helper_475_1_moved 로 옮김)
- Engine_Tilt.attach_underdrains · Engine_Structures_Wall.attach_wall_structures · load_wall_structures
- Engine_Grade_Alignment.tangent_elevation(부름 0) · station_key.dedupe_station_keys · loose_chainage
줄 수 전 → 후: excavation.py 126 → 0 · Grade_Carry 178 → 157 · Save_Parts 289 → 269 · Wall 221 → 197 · Tilt 115 → 112 · station_key 141 → 122 · Alignment 657 → 654

남는 짝(머리 「남는 까닭」 한 줄) — Grade · Grade_Profile · Alignment · CurveLaw(2~4 순위 폴백 · 추천) · NodeEdits · SampleExtend · Router_Design · Ranges · Engine_Design · Areas · cross_berm(DTM 아닌 지표면 · Node 못 부를 때 · B07 · 추천) · Tilt · Wall · Culvert(B04 B07 B08) · route_follow · structure_station · station_key · route_polyline(745 줄 · 까닭 = 등록부 R1 줄)

등록부 — until 칸 걷음(G1 = py 표본기 남김 · 까닭 note) · C6 pair = attach_underdrain_specs · F8 pair null
알려진 차 — golden KNOWN 셋 그대로 나타남(R7 표본 출처 · W0 저장 때 경고 · X2 입력 선 자릿수 · 까닭 고침) · underdrain 알려진 차 까닭 · 511_b5 KNOWN_ARC 그대로
계산 자리 창 — 45 줄 · 서버만 아닌 41(바꿀 수 있음 40 + F9) · 서버만 넷(G3 D1 D2 N1 줄마다 까닭) · 옛 까닭 「짝 모으는 중 · B-4」 글 걷음
test_322_3_flow 빨강 — 헬퍼가 @config/config_frontend 흉내(511-A design_calc_server import)
시험 기준 — 걷은 짝 거울 = 걷기 전 파이썬 값 고정(excavation · station_key) · Node carry_seat · attach_underdrain_specs · wall_specs_from

검증: 고친 대상 시험 97 파일 562 통과 · 21 건너뜀 · typecheck 0 · 700 줄 넘는 새 파일 없음 — 요구 320-4

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:34:08 +09:00

123 lines
5.0 KiB
Python

"""측점 열쇠 한 규칙(PLAN 344-3) — 노선이 바뀌어도 같은 측점을 같은 이름으로 부름.
일반 = ``grid:k``(누가거리 ÷ 측점 간격 반올림) · 관 = ``pipe:<id>`` · 구조물 = ``structure:<id>`` ·
시종점 = ``end:BP`` · ``end:EP`` · id 를 모르는 구조물 측점 = ``at:<누가거리 0.01 m>`` ·
곡선 측점(MC · 252-2) = ``curve:<누가거리 0.01 m>``.
구간형 구조물 시작 · 끝 측점 = ``structure:<id>:start`` · ``:end``(기준 측점은 ``structure:<id>``).
한 목록에 같은 열쇠가 둘이면 둘째부터 ``at:<누가거리>``(TS ``dedupeStationKeys``).
MC 가 움직여 곡선 열쇠가 바뀌면 넘길 때 가까운 새 곡선 측점으로 이음(``curve_moves`` · 432-3).
⚠ 파이썬·TS 짝 — ``common_util_station_key.ts`` ``stationKey`` ↔ ``station_key``.
남는 까닭 = DTM 아닌 지표면 · Node 못 돌림 때 노선 저장 이월 열쇠(``station_keys``).
한쪽을 고치면 다른 쪽도 고치고 거울 시험 ``resources/tester/test_344_3_station_key_mirror.py``.
"""
from __future__ import annotations
import math
def _half_up(value: float) -> int:
"""JS ``Math.round`` 와 같은 반올림(0.5 는 위로)."""
return math.floor(value + 0.5)
def chainage_key(chainage_m: float) -> str:
"""누가거리 0.01 m — 옛 초안 열쇠(``toFixed(2)``)와 같은 자릿수."""
return f"{_half_up(chainage_m * 100) / 100:.2f}"
def station_key(
kind: str,
chainage_m: float,
interval_m: float | None,
station_id: str | None = None,
id_kind: str = "structure",
role: str = "anchor",
) -> str:
"""측점 열쇠 — irregular 는 id 가 있으면 pipe: · structure:(``id_kind``) · 없으면 at:.
구간형 시작 · 끝은 ``role`` 로 ``:start`` · ``:end``. ``grid`` · ``end`` 는 ``station_id`` 에
원래 격자 번호 · BP/EP(없으면 누가거리 ÷ 간격 반올림 · 0 이면 BP).
"""
loose = f"at:{chainage_key(chainage_m)}"
suffix = "" if role == "anchor" else f":{role}"
if kind == "bp":
return "end:BP"
if kind == "ep":
return "end:EP"
if kind == "end":
return (
f"end:{station_id if station_id is not None else ('BP' if chainage_m <= 0 else 'EP')}"
)
if kind == "curve":
return f"curve:{chainage_key(chainage_m)}"
if kind in ("pipe", "structure"):
return f"{kind}:{station_id}{suffix}" if station_id else loose
if kind == "irregular":
return f"{id_kind}:{station_id}{suffix}" if station_id else loose
if kind == "grid" and station_id is not None:
return f"grid:{station_id}"
if interval_m and interval_m > 0:
return f"grid:{_half_up(chainage_m / interval_m)}"
return loose
#: 옮긴 곡선 측점으로 보는 거리(m) — 옛 MC 자리에서 이 안의 새 MC.
CURVE_NEAR_M = 10.0
def curve_moves(before: list[tuple[str, float]], after: list[tuple[str, float]]) -> dict[str, str]:
"""MC 가 움직여 바뀐 ``curve:`` 열쇠 → 가까운 새 곡선 열쇠.
옛 목록에만 있는 곡선 열쇠 → 새 목록에만 있는 가장 가까운 곡선 열쇠(``CURVE_NEAR_M`` 안 ·
새 열쇠 하나에 옛 열쇠 하나 · 옛 누가거리 차례).
"""
was = {key for key, _ in before}
kept = {key for key, _ in after}
free = [(key, at) for key, at in after if key.startswith("curve:") and key not in was]
out: dict[str, str] = {}
for key, at in sorted(before, key=lambda item: item[1]):
if not key.startswith("curve:") or key in kept:
continue
best = -1
for index, (_, to) in enumerate(free):
gap = abs(to - at)
if gap <= CURVE_NEAR_M and (best < 0 or gap < abs(free[best][1] - at)):
best = index
if best >= 0:
out[key] = free.pop(best)[0]
return out
#: 같은 자리로 보는 중심 차(m) · 축 차 — 브라우저 틀 ↔ 서버 틀 차(467)를 넘김.
SEAT_XY_M = 0.01
SEAT_AXIS = 1e-3
def station_seat(station: dict | None) -> tuple[float, float, tuple[float, float] | None] | None:
"""종단 측점 줄(``center_x`` · ``center_y`` · ``frame.left_xy``) → 자리 · 모르면 None."""
if not isinstance(station, dict):
return None
try:
x, y = float(station["center_x"]), float(station["center_y"])
except (KeyError, TypeError, ValueError):
return None
left = (station.get("frame") or {}).get("left_xy")
axis = (float(left[0]), float(left[1])) if isinstance(left, list) and len(left) == 2 else None
return x, y, axis
def same_seat(a, b) -> bool:
"""노선이 바뀐 뒤 그 측점이 같은 자리인가(요구 275-3) — 짝 ``sameSeat`` · 거울 475-3.
자리 = (x, y, 왼쪽 단위 벡터 | None) · 한쪽을 모르면 같은 자리로 봄(옛 동작).
"""
if a is None or b is None:
return True
if math.hypot(a[0] - b[0], a[1] - b[1]) > SEAT_XY_M:
return False
if a[2] is None or b[2] is None:
return True
return abs(a[2][0] - b[2][0]) <= SEAT_AXIS and abs(a[2][1] - b[2][1]) <= SEAT_AXIS