Merge remote-tracking branch 'origin/sub_laptop_3' into main_laptop_1
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
"""노선이 바뀔 때 계획고 옮김 — 같은 자리는 옛 계획고 · 바뀐 자리만 새 지반 첫 계획선(344-2 · 400).
|
||||
"""노선이 바뀔 때 계획고 옮김 — 같은 자리는 옛 계획고 · 바뀐 자리는 옛 종단 길이 비례
|
||||
(344-2 · 400 · 575 B).
|
||||
|
||||
사용자 확정(10-16 · 341 결정 1): 노선 끝쪽을 고쳤는데 앞쪽 계획고가 날아가지 않게 한다.
|
||||
요구 250-2둘째(400): 평면을 고쳐도 크게 안 바뀐 자리의 종단 편집(직선화 · 자유지정 · 쉬프트 ·
|
||||
@@ -9,7 +10,8 @@
|
||||
- 같은 자리(`line_matcher`) = 새 선 점을 옛 선에 투영 · 옆 `SAME_LINE_TOL_M` 안 · 방향 30° 안
|
||||
(측점 스냅이 뒤 교점을 밀어도 이어받음).
|
||||
- 자동 꼭짓점(`carry_base`) · 편집(Node `carry_seat`)은 **새 측점 위에** 다시 놓음 — 같은 자리면
|
||||
옛 선 그 자리 높이 · 아니면 새 지반 첫 계획선 · 편집은 옛 사용자 꼭짓점이 닿은 자리만 ·
|
||||
옛 선 그 자리 높이 · 아니면 앞뒤 같은 자리 사이 길이 비례 자리 옛 높이 · 편집은 옛 사용자
|
||||
꼭짓점이 닿은 자리만 ·
|
||||
R 은 가까운 측점. 편집 셈 = TS `carryEdits`(서버 Node · 511-B B-3) — 파이썬 짝 없음.
|
||||
|
||||
짝 = TS `B05_Profile/logic/B05_Profile_Grade_Carry.ts`(같은 규칙) · 거울 시험 =
|
||||
@@ -124,21 +126,30 @@ def carry_base(
|
||||
ground_pvi: list[dict[str, float]],
|
||||
tol_m: float = SAME_LINE_TOL_M,
|
||||
) -> list[dict[str, float]]:
|
||||
"""새 자동 꼭짓점 — 지반 첫 계획선 측점마다 같은 자리면 옛 자동선 높이 · 아니면 지반."""
|
||||
"""새 자동 꼭짓점 — 지반 첫 계획선 측점 자리마다 옛 자동선 높이(575 B).
|
||||
|
||||
같은 자리 = 맞춘 옛 누가거리 · 바뀐 자리 = 앞뒤 같은 자리(없으면 노선 시점 · 종점) 사이를 새
|
||||
누가거리 비례로 옛 누가거리에 옮김(옛 종단 모양을 늘이고 줄임 · 다시 풀지 않음).
|
||||
"""
|
||||
if not old_base or not ground_pvi:
|
||||
return [dict(node) for node in old_base]
|
||||
match = line_matcher(old_xy, new_xy, tol_m)
|
||||
base_s = np.array([float(n["chainage_m"]) for n in old_base], dtype=np.float64)
|
||||
base_z = np.array([float(n["elevation_m"]) for n in old_base], dtype=np.float64)
|
||||
chainages = [float(node["chainage_m"]) for node in ground_pvi]
|
||||
hits = [match(t) for t in chainages]
|
||||
# 비례 기준점 — 시점(0 · 0) · 같은 자리 · 종점(새 · 옛 연장) 중 두 누가거리 모두 늘어나는 것만
|
||||
# ponytail: 앞에서부터 고름 — 같은 방향 먼 옛 토막에 잘못 맞은 점이 있으면 뒤 기준점을 버림
|
||||
at, to = [0.0], [0.0]
|
||||
ends = (_cumulative(list(new_xy))[-1], _cumulative(list(old_xy))[-1])
|
||||
for t, s in [*zip(chainages, hits), ends]:
|
||||
if s is not None and t > at[-1] and s > to[-1]:
|
||||
at.append(t)
|
||||
to.append(s)
|
||||
out = []
|
||||
for node in ground_pvi:
|
||||
s = match(float(node["chainage_m"]))
|
||||
z = (
|
||||
float(node["elevation_m"])
|
||||
if s is None
|
||||
else round(float(np.interp(s, base_s, base_z)), 6)
|
||||
)
|
||||
out.append({"chainage_m": float(node["chainage_m"]), "elevation_m": z})
|
||||
for t, s in zip(chainages, hits):
|
||||
s = float(np.interp(t, at, to)) if s is None else s
|
||||
out.append({"chainage_m": t, "elevation_m": round(float(np.interp(s, base_s, base_z)), 6)})
|
||||
return out
|
||||
|
||||
|
||||
@@ -149,7 +160,7 @@ def carry_policy(
|
||||
new_alignment: dict[str, Any],
|
||||
) -> dict[str, Any]:
|
||||
"""노선 [저장] 정책(432) — 새 정책에 옛 기본 L · 옛 정책 없으면 새 정책. 바뀐 자리 새 꼭짓점은
|
||||
새 지반 첫 계획선(직선화 438 · `carry_base`)."""
|
||||
새 지반 첫 계획선 측점 자리에 옛 높이 비례(`carry_base` · 575 B)."""
|
||||
new_policy = dict(new_alignment.get("policy") or {})
|
||||
old_policy = old_alignment.get("policy") or {}
|
||||
if not old_policy:
|
||||
|
||||
@@ -24,7 +24,7 @@ API — `POST /api/projects/{id}/route/replan`(`B05_Profile_Router_Replan.replan
|
||||
- 손 고침 [저장](교점 · R · 스냅 · 간격 · 요구 289 · 290) = 보이는 화면 그대로 — 관 정본을
|
||||
지우지 않음 · 좌표를 따라감 · 새 세류선 교차점 기본 관만 더함
|
||||
(`B05_Profile_Route_Save_Parts`) · 배수유역 저장분은 노선 지문이 달라 「낡음」(B04 `_resolve`).
|
||||
- 계획고 = 바뀐 자리만 새 지반 첫 계획선 · 같은 자리(옆 1 m 안) 사용자 계획고 지킴
|
||||
- 계획고 = 바뀐 자리는 옛 종단 길이 비례(575 B) · 같은 자리(옆 1 m 안) 사용자 계획고 지킴
|
||||
(`B05_Profile_Engine_Grade_Carry` · 옛 · 새 **계획 폴리라인** — 화면 planline 과 같은 선) ·
|
||||
추천 [적용](`as_initial`) = 옮기지 않음 — 균형 종단(`apply_profile_target`) · 1차 계획선(438) ·
|
||||
횡단 손 입력도 안 넘김(요구 277 · 측점마다 기본 설계).
|
||||
@@ -254,7 +254,7 @@ async def _carry_profile(
|
||||
old_xy: list[list[float]],
|
||||
new_xy: list[list[float]],
|
||||
) -> None:
|
||||
"""같은 자리(옆 1 m 안) 옛 계획고 · R 을 새 측점 위에 · 바뀐 자리만 새 지반 첫 계획선(400)."""
|
||||
"""같은 자리(옆 1 m 안) 옛 계획고 · R 을 새 측점 위에 · 바뀐 자리는 옛 종단 길이 비례(400 · 575 B)."""
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Alignment import AlignmentPolicy
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Carry import carry_base, carry_policy, changed_span
|
||||
from B05_Profile.B05_Profile_Engine_Grade_Node import carry_seat
|
||||
|
||||
@@ -588,7 +588,10 @@ async def _save_route(project_id: UUID, body: RouteSaveRequest) -> dict[str, Any
|
||||
choice.mode,
|
||||
choice.weights.model_dump() if choice.weights else None,
|
||||
(choice.criteria.model_dump(exclude_none=True) or None) if choice.criteria else None,
|
||||
request.balanced_profile, # 「종단 반영」 — 다시 열면 되살림(420)
|
||||
# 「종단 반영」 — 다시 열면 되살림(420) · 손 [저장]은 적힌 값 그대로(575)
|
||||
request.balanced_profile
|
||||
if as_initial
|
||||
else read_initial_choice(project_root)["with_profile"],
|
||||
)
|
||||
if choice
|
||||
else await asyncio.to_thread(read_initial_choice, project_root)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* =============================================================================
|
||||
* B05_Profile/logic/B05_Profile_Grade_Carry.ts
|
||||
* 노선이 바뀔 때 계획고 옮김 — 같은 자리는 옛 계획고 · 바뀐 자리만 새 지반 첫 계획선(344-2 · 400).
|
||||
* 노선이 바뀔 때 계획고 옮김 — 같은 자리는 옛 계획고 · 바뀐 자리는 옛 종단을 길이 비례로(344-2 · 400 · 575 B).
|
||||
*
|
||||
* 짝 = 파이썬 `B05_Profile_Engine_Grade_Carry.py`(서버 정본 · 같은 규칙) · 거울 시험 =
|
||||
* resources/tester/test_400_profile_carry.py · test_344_2_route_pairs.py.
|
||||
@@ -8,7 +8,7 @@
|
||||
* - 같은 자리(`lineMatcher`) = 새 선 점을 옛 선에 투영 · 옆 `SAME_LINE_TOL_M` 안 · 방향 30° 안
|
||||
* (측점 스냅이 뒤 교점을 밀어도 이어받음).
|
||||
* - 자동 꼭짓점(`carryBase`) · 편집(`carryEdits`)은 **새 측점 위에** 다시 놓음 — 같은 자리면 옛 선 그 자리
|
||||
* 높이 · 아니면 새 지반 첫 계획선 · 편집은 옛 사용자 꼭짓점이 닿은 자리만 · R 은 가까운 측점.
|
||||
* 높이 · 아니면 앞뒤 같은 자리 사이 길이 비례 자리 옛 높이 · 편집은 옛 사용자 꼭짓점이 닿은 자리만 · R 은 가까운 측점.
|
||||
* ========================================================================== */
|
||||
|
||||
import {
|
||||
@@ -159,7 +159,11 @@ export function lineMatcher(
|
||||
};
|
||||
}
|
||||
|
||||
/** 새 자동 꼭짓점 — 지반 첫 계획선 측점마다 같은 자리면 옛 자동선 높이 · 아니면 지반(파이썬 `carry_base` 짝). */
|
||||
/**
|
||||
* 새 자동 꼭짓점 — 지반 첫 계획선 측점 자리마다 옛 자동선 높이(575 B · 파이썬 `carry_base` 짝).
|
||||
* 같은 자리 = 맞춘 옛 누가거리 · 바뀐 자리 = 앞뒤 같은 자리(없으면 노선 시점 · 종점) 사이를 새 누가거리 비례로
|
||||
* 옛 누가거리에 옮김(옛 종단 모양을 늘이고 줄임 · 다시 풀지 않음).
|
||||
*/
|
||||
export function carryBase(
|
||||
oldXy: ReadonlyArray<XY>,
|
||||
newXy: ReadonlyArray<XY>,
|
||||
@@ -171,13 +175,25 @@ export function carryBase(
|
||||
const match = lineMatcher(oldXy, newXy, tolM);
|
||||
const baseS = oldBase.map((node) => node.chainage_m);
|
||||
const baseZ = oldBase.map((node) => node.elevation_m);
|
||||
return groundPvi.map((node) => {
|
||||
const s = match(node.chainage_m);
|
||||
return {
|
||||
chainage_m: node.chainage_m,
|
||||
elevation_m: s === null ? node.elevation_m : roundTo(interp(baseS, baseZ, s), 6),
|
||||
};
|
||||
const hits = groundPvi.map((node) => match(node.chainage_m));
|
||||
// 비례 기준점 — 시점(0 · 0) · 같은 자리 · 종점(새 연장 · 옛 연장) 중 두 누가거리 모두 늘어나는 것만
|
||||
// ponytail: 앞에서부터 고름 — 같은 방향 먼 옛 토막에 잘못 맞은 점이 있으면 뒤 기준점을 버림
|
||||
const at = [0];
|
||||
const to = [0];
|
||||
const pairs = groundPvi.map((node, index) => ({ t: node.chainage_m, s: hits[index] }));
|
||||
const newCum = cumulative(newXy);
|
||||
const oldCum = cumulative(oldXy);
|
||||
pairs.push({ t: newCum[newCum.length - 1], s: oldCum[oldCum.length - 1] });
|
||||
pairs.forEach(({ t, s }) => {
|
||||
if (s !== null && t > at[at.length - 1] && s > to[to.length - 1]) {
|
||||
at.push(t);
|
||||
to.push(s);
|
||||
}
|
||||
});
|
||||
return groundPvi.map((node, index) => ({
|
||||
chainage_m: node.chainage_m,
|
||||
elevation_m: roundTo(interp(baseS, baseZ, hits[index] ?? interp(at, to, node.chainage_m)), 6),
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
* 추가 측점 좌표 따라감(459 · `followPlusGround` · 좌표 들고 감 = 서버 [저장] 자리 · 493-3) ·
|
||||
* 노선 바뀐 [저장]의 계획고 손댐(`touch` · 저장분 옮김과 다른 것만 · 498-2).
|
||||
* planline — 노선 초안이 없으면 저장 종단 기준 그대로 · 있으면 새 측점마다 같은 자리는 저장 꼭짓점
|
||||
* 높이 · 바뀐 자리만 새 지반 첫 계획선(`Grade_Ground` · `carryBase`) · 옮긴 시설 측점(바뀐 자리 ·
|
||||
* 높이 · 바뀐 자리는 저장 꼭짓점 길이 비례(575 B · 측점 자리 = 새 지반 첫 계획선 `Grade_Ground` ·
|
||||
* `carryBase`) · 옮긴 시설 측점(바뀐 자리 ·
|
||||
* 사용자가 옮김)에 427 꼭짓점 다시 세움(459 · 짝 = 파이썬 `B05_Profile_Route_Save_Parts`
|
||||
* `route_moved_stations` · 서버 세움 = Node `profile.carry` · 거울 시험 test_459_route_seat_mirror.py).
|
||||
* profile — `buildAlignment`(첫 계획선 + 계획고 편집) — 종단 보기가 쓰는 같은 함수.
|
||||
@@ -447,7 +448,8 @@ export function createPlanlineStage(deps: PlanlineDeps): StageFn<"planline"> {
|
||||
})),
|
||||
};
|
||||
};
|
||||
// 바뀐 자리 새 꼭짓점 = 서버 새 지반 첫 계획선(기본 L = config 짝 · 초기값 직선화 · 438) · 계산 자리
|
||||
// 바뀐 자리 새 꼭짓점 자리 = 서버 새 지반 첫 계획선(기본 L = config 짝 · 초기값 직선화 · 438 · 높이는
|
||||
// `carryBase` 가 옛 종단 비례 · 575 B) · 계산 자리
|
||||
// 「서버」 면 서버 Node(`groundPvi` · 511-B B-3)
|
||||
const input = {
|
||||
samples,
|
||||
|
||||
@@ -420,22 +420,31 @@ def test_가운데를_고치면_앞은_그대로_뒤는_같은자리_계획고()
|
||||
assert "90.500" not in edits["station_offsets"]
|
||||
assert edits["curve_radii"]["40.000"] == 300.0
|
||||
moved = _kept_plan(case, base_pvi, edits)
|
||||
# 바뀐 자리는 새 지반 첫 계획선 꼭짓점 · 편집 없음
|
||||
# 바뀐 자리는 옛 종단 길이 비례(앞뒤 같은 자리 사이 · 575 B) · 지반 아님 · 편집 없음
|
||||
ground = {round(p["chainage_m"], 3): p["elevation_m"] for p in case["ground_pvi"]}
|
||||
got = {round(p["chainage_m"], 3): p["elevation_m"] for p in base_pvi}
|
||||
assert moved and all(got[round(t, 3)] == ground[round(t, 3)] for t in moved)
|
||||
match = line_matcher(_pts(case, "old_xy"), _pts(case, "new_xy"))
|
||||
anchors = [(t, match(t)) for t in sorted(got) if match(t) is not None]
|
||||
old_s = [p["chainage_m"] for p in case["base_pvi"]]
|
||||
old_z = [p["elevation_m"] for p in case["base_pvi"]]
|
||||
for t in moved:
|
||||
t0, s0 = max(a for a in anchors if a[0] < t)
|
||||
t1, s1 = min(a for a in anchors if a[0] > t)
|
||||
want = float(np.interp(s0 + (s1 - s0) * (t - t0) / (t1 - t0), old_s, old_z))
|
||||
assert got[round(t, 3)] == pytest.approx(want, abs=1e-6), t
|
||||
assert moved and any(got[round(t, 3)] != ground[round(t, 3)] for t in moved)
|
||||
assert not {f"{t:.3f}" for t in moved} & set(edits["station_offsets"])
|
||||
assert min(got) == 0.0 and max(got) == pytest.approx(L_OLD + span["delta_m"], abs=1e-3)
|
||||
|
||||
|
||||
def test_끝을_고치면_끝_꼭짓점은_새_지반():
|
||||
def test_끝을_고치면_끝_꼭짓점은_옛_끝높이():
|
||||
case = CARRY_CASES[1]
|
||||
span, (base_pvi, edits) = _py_carry(case)
|
||||
assert not span["end_fixed"]
|
||||
assert f"{L_OLD:.3f}" not in edits["station_offsets"] # 바뀐 끝 편집은 버림
|
||||
_kept_plan(case, base_pvi, edits) # 앞(90.5 편집 둘레) 계획고 그대로
|
||||
ground_end = case["ground_pvi"][-1]
|
||||
assert base_pvi[-1] == {
|
||||
assert base_pvi[-1] == { # 575 B — 종점 = 옛 종점 높이(새 연장 ↔ 옛 연장 비례)
|
||||
"chainage_m": ground_end["chainage_m"],
|
||||
"elevation_m": ground_end["elevation_m"],
|
||||
"elevation_m": round(case["base_pvi"][-1]["elevation_m"], 6),
|
||||
}
|
||||
|
||||
@@ -508,6 +508,17 @@ def test_균형_종단은_추천_적용만_손_저장은_계획고_옮김(api_wo
|
||||
assert calls[1][0] is not None # 추천 [적용] = 균형 종단 그대로
|
||||
|
||||
|
||||
def test_손_저장은_종단_반영_적힌_값_그대로(api_world, monkeypatch):
|
||||
"""575 덧 흠 — 손 [저장] 본문엔 `balanced_profile` 이 없어 「종단 반영」을 끔으로 덮던 것."""
|
||||
written: list = []
|
||||
monkeypatch.setattr(replan, "write_initial_choice", lambda *a: written.append(a) or {})
|
||||
monkeypatch.setattr(replan, "read_initial_choice", lambda root: {"with_profile": True})
|
||||
initial = {"mode": "earthwork", "with_profile": False}
|
||||
asyncio.run(replan.replan_route(uuid4(), _body(initial=initial))) # 손 [저장]
|
||||
asyncio.run(replan.replan_route(uuid4(), _body(initial=initial, as_initial=True))) # [적용] 끔
|
||||
assert [call[-1] for call in written] == [True, False]
|
||||
|
||||
|
||||
def test_초안_칸이_어긋나면_400(api_world):
|
||||
body = save.RouteSaveRequest(draft={"nodes": [[0, 0], [1, 1]], "curveOn": [True]})
|
||||
response = asyncio.run(replan.replan_route(uuid4(), body))
|
||||
|
||||
@@ -128,6 +128,24 @@ def _plan(base: list[dict], edits: dict) -> tuple[np.ndarray, np.ndarray]:
|
||||
return pvi_s, pvi_z
|
||||
|
||||
|
||||
def _stretched(old_xy, new_xy, base, chainages) -> dict[float, float]:
|
||||
"""575 B 기대값 — 바뀐 측점 = 앞뒤 같은 자리(없으면 시점 · 종점) 사이 누가거리 비례 옛 높이."""
|
||||
match = line_matcher(old_xy, new_xy)
|
||||
hits = {t: match(t) for t in chainages}
|
||||
anchors = [(0.0, 0.0)] + [(t, s) for t, s in hits.items() if s is not None]
|
||||
anchors.append((_length(new_xy), _length(old_xy)))
|
||||
base_s = [n["chainage_m"] for n in base]
|
||||
base_z = [n["elevation_m"] for n in base]
|
||||
out = {}
|
||||
for t, s in hits.items():
|
||||
if s is None:
|
||||
t0, s0 = max((a for a in anchors if a[0] < t), default=anchors[0])
|
||||
t1, s1 = min((a for a in anchors if a[0] > t), default=anchors[-1])
|
||||
s = s0 + (s1 - s0) * (t - t0) / (t1 - t0) if t1 > t0 and s1 > s0 else s0
|
||||
out[t] = round(float(np.interp(s, base_s, base_z)), 6)
|
||||
return out
|
||||
|
||||
|
||||
def _py(name: str) -> tuple[list[dict], dict]:
|
||||
new = CASES[name]
|
||||
base = carry_base(OLD, new, OLD_BASE, _ground_pvi(new))
|
||||
@@ -262,17 +280,19 @@ def test_같은자리_계획고_그대로(name):
|
||||
assert new_z == pytest.approx(old_z, abs=1e-3), (name, t)
|
||||
|
||||
|
||||
def test_먼교점_옮김_앞쪽_편집_누가거리_그대로_바뀐자리만_지반():
|
||||
def test_먼교점_옮김_앞쪽_편집_누가거리_그대로_바뀐자리는_옛종단_비례():
|
||||
kept, moved, base, edits = _same_place_plan("far")
|
||||
# 앞쪽(같은 선 · 같은 측점) 편집 글자 그대로
|
||||
for key in ("60.000", "80.000", "100.000", "300.000"):
|
||||
assert edits["station_offsets"][key] == pytest.approx(OLD_EDITS["station_offsets"][key])
|
||||
assert edits["curve_radii"]["300.000"] == 400.0
|
||||
# 바뀐 자리 = 옮긴 교점 둘레 · 그 측점은 지반 첫 계획선 · 편집 없음(옛 520 · 540 자유지정 버림)
|
||||
# 바뀐 자리 = 옮긴 교점 둘레 · 그 측점은 옛 종단 길이 비례(지반 아님 · 575 B) · 편집 없음
|
||||
# (옛 520 · 540 자유지정 버림)
|
||||
assert moved and min(moved) > 423.0
|
||||
ground = {n["chainage_m"]: n["elevation_m"] for n in _ground_pvi(CASES["far"])}
|
||||
by = {n["chainage_m"]: n["elevation_m"] for n in base}
|
||||
assert all(by[t] == ground[t] for t in moved)
|
||||
want = _stretched(OLD, CASES["far"], OLD_BASE, list(by))
|
||||
assert all(by[t] == pytest.approx(want[t], abs=1e-4) and by[t] != ground[t] for t in moved)
|
||||
assert not any(float(k) in moved for k in edits["station_offsets"])
|
||||
|
||||
|
||||
@@ -286,11 +306,16 @@ def test_시점_늘림_누가거리_투영_새측점_위():
|
||||
assert edits["curve_radii"].get("320.000") == 400.0
|
||||
|
||||
|
||||
def test_끝_늘림_옛끝까지_이어받고_넘는자리만_지반():
|
||||
def test_끝_늘림_옛끝까지_이어받고_넘는자리는_옛_끝높이():
|
||||
kept, moved, base, _ = _same_place_plan("end")
|
||||
old_len = _length(OLD)
|
||||
assert moved and min(moved) > old_len
|
||||
assert max(t for t, _, _ in kept) > old_len - INTERVAL
|
||||
# 넘는 자리 = 마지막 같은 자리 ~ 옛 끝 비례로 늘림 · 새 끝 = 옛 끝 높이(575 B)
|
||||
by = {n["chainage_m"]: n["elevation_m"] for n in base}
|
||||
want = _stretched(OLD, CASES["end"], OLD_BASE, list(by))
|
||||
assert all(by[t] == pytest.approx(want[t], abs=1e-4) for t in moved)
|
||||
assert base[-1]["elevation_m"] == pytest.approx(OLD_BASE[-1]["elevation_m"], abs=1e-4)
|
||||
|
||||
|
||||
def test_스냅처럼_옆으로_밀림_1m_안_이어받고_넘으면_다시():
|
||||
@@ -301,20 +326,24 @@ def test_스냅처럼_옆으로_밀림_1m_안_이어받고_넘으면_다시():
|
||||
assert "300.000" in edits["station_offsets"]
|
||||
|
||||
|
||||
def test_종단_안_고친_프로젝트는_편집_없음_꼭짓점은_옛것_또는_지반():
|
||||
def test_종단_안_고친_프로젝트는_편집_없음_꼭짓점은_옛것_또는_옛종단_비례():
|
||||
empty = {"station_offsets": {}, "curve_radii": {}}
|
||||
new = CASES["far"]
|
||||
assert carry_edits(OLD, new, OLD_BASE, empty, _stations(new), HALF) == empty
|
||||
base = carry_base(OLD, new, OLD_BASE, _ground_pvi(new))
|
||||
old_s = [n["chainage_m"] for n in OLD_BASE]
|
||||
old_z = [n["elevation_m"] for n in OLD_BASE]
|
||||
ground = {n["chainage_m"]: n["elevation_m"] for n in _ground_pvi(new)}
|
||||
match = line_matcher(OLD, new)
|
||||
want = _stretched(OLD, new, OLD_BASE, [n["chainage_m"] for n in base])
|
||||
for node in base:
|
||||
t = node["chainage_m"]
|
||||
s = match(t)
|
||||
want = ground[t] if s is None else round(float(np.interp(s, old_s, old_z)), 6)
|
||||
assert node["elevation_m"] == pytest.approx(want, abs=1e-9)
|
||||
assert node["elevation_m"] == pytest.approx(want[node["chainage_m"]], abs=1e-4)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", ["far", "start", "end", "snap"])
|
||||
def test_바뀐자리_옛종단_비례_575B(name):
|
||||
new = CASES[name]
|
||||
base = carry_base(OLD, new, OLD_BASE, _ground_pvi(new))
|
||||
want = _stretched(OLD, new, OLD_BASE, [n["chainage_m"] for n in base])
|
||||
for node in base:
|
||||
# 끝 측점 반올림(mm)으로 기준점 고름이 갈려도 0.1 mm 안
|
||||
assert node["elevation_m"] == pytest.approx(want[node["chainage_m"]], abs=1e-4), name
|
||||
|
||||
|
||||
def test_엇갈려_지나는_선은_같은자리_아님():
|
||||
|
||||
@@ -241,11 +241,6 @@ def test_초안_planline_은_서버와_같은_꼭짓점(tmp_path: Path, balanced
|
||||
old = {n["chainage_m"]: n["elevation_m"] for n in out["carried"]}
|
||||
new = {n["chainage_m"]: n["elevation_m"] for n in out["basePvi"]}
|
||||
assert new[PIPE] == pytest.approx(old[PIPE], abs=1e-9) # 같은 자리 관 = 옛 높이
|
||||
if not balanced: # 옮긴 시설 측점 계획선 = 새 원지반(옛 계획선이 지반 종단)
|
||||
for at in (251.0, 333.0):
|
||||
assert _plan_at(out["basePvi"], alignment["policy"], at) == pytest.approx(
|
||||
_new_ground(at), abs=1e-4
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("balanced", [False, True])
|
||||
@@ -266,10 +261,6 @@ def test_노선_초안_중_마커로_옮긴_관도_서버_차례와_같은_값(t
|
||||
kept = [n for n in out["home"] if abs(n["chainage_m"] - PIPE) >= 0.006]
|
||||
server = _seat_born_nodes(_new_alignment(alignment, out["home"]), kept, [MOVED]) # 관 저장 뒤
|
||||
_same_nodes(out["basePvi"], server, tol=1e-5) # 맞춤 풀이 수렴 폭(0.000001 m) 안팎
|
||||
if not balanced:
|
||||
assert _plan_at(out["basePvi"], alignment["policy"], MOVED) == pytest.approx(
|
||||
_old_ground(MOVED), abs=1e-4
|
||||
)
|
||||
|
||||
|
||||
def test_구조물_초안은_서버_따라감_위에(tmp_path: Path) -> None:
|
||||
|
||||
Reference in New Issue
Block a user