feat(380-3): 노선 따라간 사토장 · 토취장 = 새 노선 일반 측점 맞춤 · test_322_2 마스터 판 독립
- follow_structures ↔ followStructures(intervalM): site 꼴 = 새 노선 일반 측점(격자 · 끝) 중 가장 가까운 곳 · 그 밖 정수 m 그대로 - 파이썬 snap_existing_station ↔ TS snapExistingStation · general_stations ↔ gridStations 짝 - 브라우저 노선 초안 따라감(Pipeline_Grade) = 측점 간격 넘김 · 서버 노선 저장 따라감 = 서버 간격 - test_322_2: B05 목록 고정 수 23 대신 both 목록 수와 같음(M02 켬끔 판마다 다름) - 시험 test_380_3_site_follow(거울 · 가운데면 앞 측점 · 좌표 없음 그대로) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
This commit is contained in:
@@ -19,7 +19,11 @@ from typing import Any, Iterable, Sequence
|
||||
|
||||
from shapely.geometry import LineString, Point
|
||||
|
||||
from common_util.common_util_structure_station import snap_structure_station
|
||||
from common_util.common_util_structure_station import (
|
||||
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")
|
||||
@@ -79,6 +83,7 @@ def follow_structures(
|
||||
|
||||
기준 자리 = 투영 → 정수 m(노선 끝 안) · 구간 끝(`start_m` · `end_m`)은 같은 만큼 밀고 0 ~ 연장
|
||||
안으로 자름. 좌표가 없는 옛 자료는 그대로(거리 None).
|
||||
site 꼴(사토장 · 토취장 · 새 측점 안 심음) = 새 노선 일반 측점 중 가장 가까운 곳(380-3).
|
||||
"""
|
||||
items = list(items)
|
||||
line = _line(xy)
|
||||
@@ -93,7 +98,13 @@ def follow_structures(
|
||||
result.append((None, False))
|
||||
continue
|
||||
point = Point(float(x), float(y))
|
||||
shift = snap_structure_station(float(line.project(point)), [length]) - float(anchor)
|
||||
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:
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
* 거울 시험 = resources/tester/test_344_2_route_pairs.py.
|
||||
* ========================================================================== */
|
||||
|
||||
import { SAME_STATION_M, snapStructureStation } from "./common_util_structure_station";
|
||||
import {
|
||||
gridStations,
|
||||
SAME_STATION_M,
|
||||
snapExistingStation,
|
||||
snapStructureStation,
|
||||
} from "./common_util_structure_station";
|
||||
|
||||
/** 「노선에서 멀어짐」 거리(m) — 파이썬 `config_system.STRUCTURE_ROUTE_DRIFT_MAX_M` 짝(거울 시험이 대조). */
|
||||
export const STRUCTURE_ROUTE_DRIFT_MAX_M = 20;
|
||||
@@ -146,6 +151,7 @@ export function projectPipePoints<P extends FollowingPoint>(
|
||||
}
|
||||
|
||||
export interface FollowingStructure {
|
||||
placement?: string | null;
|
||||
chainage_m?: number | null;
|
||||
start_m?: number | null;
|
||||
end_m?: number | null;
|
||||
@@ -153,18 +159,24 @@ export interface FollowingStructure {
|
||||
y?: number | null;
|
||||
}
|
||||
|
||||
/** 구조물이 좌표를 따라감 — 사본 · 노선과 거리(좌표 없으면 null) · 멀어짐(차례는 그대로). */
|
||||
/** 구조물이 좌표를 따라감 — 사본 · 노선과 거리(좌표 없으면 null) · 멀어짐(차례는 그대로) ·
|
||||
* `intervalM` 주면 site 꼴(사토장 · 토취장) = 새 노선 일반 측점 중 가장 가까운 곳(380-3 · 파이썬은 서버 간격). */
|
||||
export function followStructures<S extends FollowingStructure>(
|
||||
items: ReadonlyArray<S>,
|
||||
line: ReadonlyArray<XY>,
|
||||
maxDriftM: number = STRUCTURE_ROUTE_DRIFT_MAX_M,
|
||||
intervalM: number | null = null,
|
||||
): Array<{ item: S; drift_m: number | null; far: boolean }> {
|
||||
return items.map((item) => {
|
||||
const hit =
|
||||
item.x != null && item.y != null ? projectOnPolyline(line, { x: item.x, y: item.y }) : null;
|
||||
if (hit === null || item.chainage_m == null)
|
||||
return { item: { ...item }, drift_m: null, far: false };
|
||||
const shift = snapStructureStation(hit.chainage_m, [hit.length_m]) - item.chainage_m;
|
||||
const target =
|
||||
intervalM && item.placement === "site"
|
||||
? snapExistingStation(hit.chainage_m, gridStations(hit.length_m, intervalM))
|
||||
: snapStructureStation(hit.chainage_m, [hit.length_m]);
|
||||
const shift = target - item.chainage_m;
|
||||
const clamp = (value: number): number => Math.min(Math.max(value + shift, 0), hit.length_m);
|
||||
return {
|
||||
item: {
|
||||
|
||||
@@ -45,6 +45,14 @@ def snap_structure_station(chainage_m: float, stations: Sequence[float]) -> floa
|
||||
return max(0.0, min(whole, float(math.floor(end + _EPS))))
|
||||
|
||||
|
||||
def snap_existing_station(chainage_m: float, stations: Sequence[float]) -> float:
|
||||
"""가장 가까운 기존 측점(가운데면 앞) · 비면 정수 m — TS `snapExistingStation` 짝."""
|
||||
value = float(chainage_m)
|
||||
if not math.isfinite(value) or not stations:
|
||||
return snap_structure_station(value, stations)
|
||||
return float(min(stations, key=lambda at: (round(abs(float(at) - value), 6), float(at))))
|
||||
|
||||
|
||||
def route_general_stations(total_m: float) -> list[float]:
|
||||
"""노선 길이의 일반 측점 — 서버 측점 간격(`SECTION_STATION_INTERVAL_M`)."""
|
||||
return general_stations(total_m, SECTION_STATION_INTERVAL_M)
|
||||
|
||||
@@ -115,6 +115,17 @@ export function snapExistingStation(
|
||||
});
|
||||
}
|
||||
|
||||
/** 격자 측점 + 끝 측점 — 파이썬 `general_stations` 짝(노선 따라감 site 맞춤 · 380-3). */
|
||||
export function gridStations(totalM: number, intervalM: number): number[] {
|
||||
if (!(totalM > 0 && intervalM > 0)) return [0];
|
||||
const values = Array.from(
|
||||
{ length: Math.floor(totalM / intervalM) + 1 },
|
||||
(_, i) => i * intervalM,
|
||||
);
|
||||
if (totalM - values[values.length - 1] > EPS) values.push(totalM);
|
||||
return values;
|
||||
}
|
||||
|
||||
/** 놓는 꼴 따라 맞춤 — site = 가장 가까운 기존 측점 · 그 밖 = 정수 m. */
|
||||
export const snapStationFor = (chainageM: number, placement: string | null | undefined): number =>
|
||||
placement === "site" ? snapExistingStation(chainageM) : snapStructureStation(chainageM);
|
||||
|
||||
Reference in New Issue
Block a user