- 좌표 낡음 판정 — 좌표가 그 누가거리를 안 낳으면(마커 · 방향키 · 폼으로 누가거리만 옮김) 노선 위 자리로 다시 적음 · TS placeOnLine ↔ 파이썬 fill_structure_coordinates 짝(보낸 좌표가 누가거리를 낳으면 살림) - 따라감 단계 — 좌표 없는 관 초안은 앞 따라감 좌표 · site 간격 넘김 - 배수 편집기 — 노선 초안 follow 가 다시 매긴 관 자리를 id 짝으로 받음(시설 속성 · 고름 함께) · 초안 버림 = 저장분 다시 읽음 - 관 짝 차례 → id — 관 알림에 고정 id · followPipeReports · shownPipes(movePipeIds 한 번에 옮김) · followedMarks 같은 자리 먼저 - B06 측점 열쇠 — 서버 관 id 도 pipe: (계산 묶음과 같은 갈래) - 시험 test_410_follow_xy · test_411_b_pipe_id_pairing Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
134 lines
5.8 KiB
Python
134 lines
5.8 KiB
Python
"""노선이 바뀔 때 구조물이 땅 위 자리(좌표)를 따라감 — 누가거리를 다시 매김(344-2).
|
|
|
|
사용자 확정(10-16 · 341 결정 2): 관 · 구조물의 자리 정본 = 좌표(x · y) · 누가거리는 그 자리를 지금
|
|
노선에서 읽은 값. 노선이 바뀌면 좌표를 새 노선에 투영해 누가거리를 다시 매기고(정수 m · 234-1),
|
|
새 노선에서 `STRUCTURE_ROUTE_DRIFT_MAX_M` 넘게 멀면 「노선에서 멀어짐」 표시만(버리지 않음).
|
|
|
|
- `fill_structure_coordinates` — 저장 때 노선 위 기준 자리로 x · y 채움(자리가 그대로면 옛 좌표
|
|
지킴 · 보낸 좌표가 그 누가거리를 낳으면 살림 · 410) · 옛 자료는 읽을 때 없는 것만 채움.
|
|
- `follow_structures` — 좌표 → 새 노선 누가거리 · 구간 끝은 같은 만큼 밂 · 노선과의 거리.
|
|
|
|
짝 = TS `common_util/common_util_route_follow.ts` `followStructures`(구조물) · `projectPipePoints`
|
|
(관 — 파이썬 본체 = `common_util_drainage_pipes.project_pipe_points`) · `placeOnLine`(좌표 낡음
|
|
판정) · 거울 시험 = `resources/tester/test_344_2_route_pairs.py` · `test_410_follow_xy.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 (
|
|
SAME_STATION_M,
|
|
route_general_stations,
|
|
snap_existing_station,
|
|
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 _holds_station(item: Any, line: LineString) -> bool:
|
|
"""좌표가 그 기준 측점을 낳는가 — 같은 노선 따라감이 누가거리를 안 옮김(TS `holdsStation`)."""
|
|
at = float(line.project(Point(float(item.x), float(item.y))))
|
|
length = float(line.length)
|
|
target = (
|
|
snap_existing_station(at, route_general_stations(length))
|
|
if getattr(item, "placement", None) == "site"
|
|
else snap_structure_station(at, [length])
|
|
)
|
|
anchor = float(item.chainage_m)
|
|
shift = target - anchor
|
|
return abs(min(max(anchor + shift, 0.0), length) - anchor) <= SAME_STATION_M
|
|
|
|
|
|
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: 항목} — 자리(누가거리 셋)가 그대로면 옛 좌표를 지킨다
|
|
(노선이 바뀌어 멀어진 땅 자리를 다음 저장이 노선 위로 덮지 않게). 보낸 좌표가 그 누가거리를
|
|
낳으면(지금 노선 따라감 = 제자리) 그 좌표를 살린다(410 · 옮긴 자리 · 따라간 땅 자리) — 낡은
|
|
좌표(누가거리만 옮김)는 노선 위로. TS 짝 = `placeOnLine`. `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
|
|
if has_xy and _holds_station(item, line):
|
|
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).
|
|
site 꼴(사토장 · 토취장 · 새 측점 안 심음) = 새 노선 일반 측점 중 가장 가까운 곳(380-3).
|
|
"""
|
|
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))
|
|
at = float(line.project(point))
|
|
target = (
|
|
snap_existing_station(at, route_general_stations(length))
|
|
if getattr(item, "placement", None) == "site"
|
|
else snap_structure_station(at, [length])
|
|
)
|
|
shift = target - 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
|