Files
Aislo/common_util/common_util_route_follow.py
T
eomsangdonandClaude Opus 5.5 bb63b30aae feat(344-2): 첫 계획선 · 관 자리 투영 · 구조물 좌표 따라감 · 계획고 옮김 짝
- 첫 계획선(계획고 = 지반고) base_pvi TS 짝 = B05_Profile/logic/B05_Profile_Grade_Ground.ts(1순위만 · 못 서는 땅은 null = 서버로 다시 셈)
- 관 자리 투영 TS 짝 · 구조물 따라감 py · ts 짝 = common_util_route_follow(정수 m · 구간 끝 밂 · 0 ~ 연장 자름 · 20 m 넘으면 멀어짐 표시 · 상수 STRUCTURE_ROUTE_DRIFT_MAX_M)
- 구조물 x · y 저장(저장 때 노선 위 자리 · 자리가 그대로면 옛 좌표 지킴 · 옛 자료는 읽을 때 채움) · 노선 [확인] 때 구조물 정본이 좌표를 따라감
- 노선 바뀜 때 계획고 옮김 = 바뀐 구간만 새 지반 첫 계획선 · 앞 그대로 · 뒤 길이 차만큼 밂(B05_Profile_Engine_Grade_Carry.py ↔ logic/B05_Profile_Grade_Carry.ts)
- 거울 시험 test_344_2_route_pairs(+ helper cjs) · buildCurves · evaluateAt 내보냄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
2026-10-16 11:19:54 +09:00

104 lines
4.4 KiB
Python

"""노선이 바뀔 때 구조물이 땅 위 자리(좌표)를 따라감 — 누가거리를 다시 매김(344-2).
사용자 확정(10-16 · 341 결정 2): 관 · 구조물의 자리 정본 = 좌표(x · y) · 누가거리는 그 자리를 지금
노선에서 읽은 값. 노선이 바뀌면 좌표를 새 노선에 투영해 누가거리를 다시 매기고(정수 m · 234-1),
새 노선에서 `STRUCTURE_ROUTE_DRIFT_MAX_M` 넘게 멀면 「노선에서 멀어짐」 표시만(버리지 않음).
- `fill_structure_coordinates` — 저장 때 노선 위 기준 자리로 x · y 채움(자리가 그대로면 옛 좌표
지킴) · 옛 자료는 읽을 때 없는 것만 채움.
- `follow_structures` — 좌표 → 새 노선 누가거리 · 구간 끝은 같은 만큼 밂 · 노선과의 거리.
짝 = TS `common_util/common_util_route_follow.ts` `followStructures`(구조물) · `projectPipePoints`
(관 — 파이썬 본체 = `common_util_drainage_pipes.project_pipe_points`) · 거울 시험 =
`resources/tester/test_344_2_route_pairs.py`.
"""
from __future__ import annotations
from typing import Any, Iterable, Sequence
from shapely.geometry import LineString, Point
from common_util.common_util_structure_station import snap_structure_station
from config.config_system import STRUCTURE_ROUTE_DRIFT_MAX_M
_POSITION_KEYS = ("chainage_m", "start_m", "end_m")
def _line(xy: Sequence[Sequence[float]]) -> LineString | None:
points = [(float(point[0]), float(point[1])) for point in xy]
if len(points) < 2:
return None
line = LineString(points)
return line if line.length > 0 else None
def _positions(item: Any) -> tuple[Any, ...]:
return tuple(getattr(item, key, None) for key in _POSITION_KEYS)
def fill_structure_coordinates(
items: Iterable[Any],
xy: Sequence[Sequence[float]],
*,
previous: dict[str, Any] | None = None,
only_missing: bool = False,
) -> int:
"""구조물 x · y 를 노선 위 기준 자리(`chainage_m`)로 채운다(제자리) · 채운 수.
`previous` = 저장 전 정본 {structure_id: 항목} — 자리(누가거리 셋)가 그대로면 옛 좌표를 지킨다
(노선이 바뀌어 멀어진 땅 자리를 다음 저장이 노선 위로 덮지 않게). `only_missing` = 읽을 때.
"""
line = _line(xy)
if line is None:
return 0
filled = 0
for item in items:
anchor = getattr(item, "chainage_m", None)
if anchor is None:
continue
has_xy = getattr(item, "x", None) is not None and getattr(item, "y", None) is not None
if only_missing and has_xy:
continue
old = (previous or {}).get(getattr(item, "structure_id", None) or "")
if old is not None and _positions(old) == _positions(item) and old.x is not None:
item.x, item.y = old.x, old.y
continue
point = line.interpolate(float(anchor))
item.x, item.y = round(float(point.x), 4), round(float(point.y), 4)
filled += 1
return filled
def follow_structures(
items: Iterable[Any],
xy: Sequence[Sequence[float]],
max_drift_m: float = STRUCTURE_ROUTE_DRIFT_MAX_M,
) -> list[tuple[float | None, bool]]:
"""좌표를 새 노선에 투영해 누가거리를 다시 매긴다(제자리) → 항목마다 (노선과 거리, 멀어짐).
기준 자리 = 투영 → 정수 m(노선 끝 안) · 구간 끝(`start_m` · `end_m`)은 같은 만큼 밀고 0 ~ 연장
안으로 자름. 좌표가 없는 옛 자료는 그대로(거리 None).
"""
items = list(items)
line = _line(xy)
if line is None:
return [(None, False)] * len(items)
length = float(line.length)
result: list[tuple[float | None, bool]] = []
for item in items:
x, y = getattr(item, "x", None), getattr(item, "y", None)
anchor = getattr(item, "chainage_m", None)
if x is None or y is None or anchor is None:
result.append((None, False))
continue
point = Point(float(x), float(y))
shift = snap_structure_station(float(line.project(point)), [length]) - float(anchor)
for key in _POSITION_KEYS:
value = getattr(item, key, None)
if value is not None:
setattr(item, key, min(max(float(value) + shift, 0.0), length))
drift = float(line.distance(point))
result.append((drift, drift > max_drift_m))
return result