diff --git a/B05_Profile/B05_Profile_Engine_Grade_Carry.py b/B05_Profile/B05_Profile_Engine_Grade_Carry.py index 5eb8d852c..a0bf18900 100644 --- a/B05_Profile/B05_Profile_Engine_Grade_Carry.py +++ b/B05_Profile/B05_Profile_Engine_Grade_Carry.py @@ -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: diff --git a/B05_Profile/B05_Profile_Route_Save.py b/B05_Profile/B05_Profile_Route_Save.py index 9b1ca473c..3a1941e61 100644 --- a/B05_Profile/B05_Profile_Route_Save.py +++ b/B05_Profile/B05_Profile_Route_Save.py @@ -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 diff --git a/B05_Profile/B05_Profile_Router_Replan.py b/B05_Profile/B05_Profile_Router_Replan.py index e772dbac6..5e4acb4e7 100644 --- a/B05_Profile/B05_Profile_Router_Replan.py +++ b/B05_Profile/B05_Profile_Router_Replan.py @@ -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) diff --git a/B05_Profile/logic/B05_Profile_Grade_Carry.ts b/B05_Profile/logic/B05_Profile_Grade_Carry.ts index 2c0a7bc4b..31a79dbdf 100644 --- a/B05_Profile/logic/B05_Profile_Grade_Carry.ts +++ b/B05_Profile/logic/B05_Profile_Grade_Carry.ts @@ -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, newXy: ReadonlyArray, @@ -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), + })); } /** diff --git a/B05_Profile/logic/B05_Profile_Pipeline_Grade.ts b/B05_Profile/logic/B05_Profile_Pipeline_Grade.ts index 74b016409..1eb1eac94 100644 --- a/B05_Profile/logic/B05_Profile_Pipeline_Grade.ts +++ b/B05_Profile/logic/B05_Profile_Pipeline_Grade.ts @@ -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, diff --git a/resources/tester/test_344_2_route_pairs.py b/resources/tester/test_344_2_route_pairs.py index 0868d368f..ffdf52da9 100644 --- a/resources/tester/test_344_2_route_pairs.py +++ b/resources/tester/test_344_2_route_pairs.py @@ -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), } diff --git a/resources/tester/test_346_3_route_save.py b/resources/tester/test_346_3_route_save.py index c7a050b63..d14947565 100644 --- a/resources/tester/test_346_3_route_save.py +++ b/resources/tester/test_346_3_route_save.py @@ -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)) diff --git a/resources/tester/test_400_profile_carry.py b/resources/tester/test_400_profile_carry.py index 615313b38..f04c801d5 100644 --- a/resources/tester/test_400_profile_carry.py +++ b/resources/tester/test_400_profile_carry.py @@ -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_엇갈려_지나는_선은_같은자리_아님(): diff --git a/resources/tester/test_459_route_seat_mirror.py b/resources/tester/test_459_route_seat_mirror.py index e35076c18..4d8dd758e 100644 --- a/resources/tester/test_459_route_seat_mirror.py +++ b/resources/tester/test_459_route_seat_mirror.py @@ -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: