- 상세 창 차례 = 그림 → 표 → CAD 칸 · 칸 머리 한 줄 접기(공용 ui-collapsible) · 열 때 일반 측점 접힘 · 구조물 측점 폄 · 처음 펼 때 칸 만듦 - 미리 넣는 글 = 그 측점 구조물(관 지점 · 구조물 배치 기준 측점)마다 지정된 구조물 조합 이름 줄 · 미지정 = 「이름 조합 미지정」 · 배관 = 원형 마크(본체 줄 없음) · 옛 관보호공 · 관유출부보강 짜 맞춘 글 버림 - B07 짝(CulvertNote.py) 같은 규칙 · 정본 structures.json · 구조물정의 이름까지 읽음 - 시험 test_265_note_cad 넓힘 · test_260 · test_150 기대값 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TNMXVCZXMpHpVvqmFkeChL
481 lines
20 KiB
Python
481 lines
20 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""PLAN 260 — B06 [상세] 표 위 CAD 칸(`structure_note_cad`) → B07 횡단도 표 위.
|
|
|
|
- 260-1 칸: 스키마 · 재계산 남김 · 세션 초안 → [저장] patch.
|
|
- 260-2 미리 넣음: 횡단배수 주인 측점 원형 마크 · 빈 구조물 · 옛 글 옮김
|
|
(`helper_260_note_cad.cjs` 가 실제 TS 를 Node 로 돌림) · 오른쪽 글 = 구조물 조합 줄(265-2 ·
|
|
`test_265_note_cad.py`) — 옛 관보호공 · 관유출부보강 짜 맞춘 글은 버림.
|
|
|
|
./venv/Scripts/python.exe -m pytest resources/tester/test_260_note_cad.py -q
|
|
"""
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(PROJECT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from B06_Section import B06_Section_Router_Design as design_mod # noqa: E402
|
|
from B06_Section.B06_Section_Schema import CrossSectionPatch # noqa: E402
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
TEXT = {
|
|
"id": "t1",
|
|
"type": "Text",
|
|
"lineColor": "#ff7f7f",
|
|
"lineWidth": 1,
|
|
"layerId": "기본",
|
|
"shapeData": {"label": "NO.71+13", "basePoint": {"x": 15, "y": -1.5}, "options": {}},
|
|
}
|
|
CAD = {"entities": [TEXT], "layers": [{"id": "기본", "name": "기본"}]}
|
|
|
|
# ── 260-1 칸 ────────────────────────────────────────────────────────────
|
|
|
|
|
|
def test_CAD_칸은_patch_스키마를_지나_그대로_남는다():
|
|
dumped = CrossSectionPatch(chainage_m=40.0, structure_note_cad=CAD).model_dump()
|
|
merged = {k: v for k, v in dumped.items() if k != "chainage_m" and v is not None}
|
|
assert merged == {"structure_note_cad": CAD}
|
|
# 빈 엔티티 = 비움 — 걸러지지 않고 실림(옛 글 대신 아무것도 안 놓음)
|
|
empty = CrossSectionPatch(chainage_m=40.0, structure_note_cad={"entities": []}).model_dump()
|
|
assert empty["structure_note_cad"] == {"entities": [], "layers": None}
|
|
# 안 보냄 = 안 바꿈
|
|
assert CrossSectionPatch(chainage_m=40.0).model_dump()["structure_note_cad"] is None
|
|
|
|
|
|
def test_CAD_칸은_엔티티_수_상한():
|
|
with pytest.raises(ValueError):
|
|
CrossSectionPatch(chainage_m=40.0, structure_note_cad={"entities": [TEXT] * 2001})
|
|
|
|
|
|
def test_CAD_칸은_재계산에서도_남는다(monkeypatch):
|
|
assert "structure_note_cad" in design_mod.USER_TOUCHED_KEYS
|
|
monkeypatch.setattr(design_mod, "pavement_ranges", lambda root: [(0.0, 100.0)])
|
|
monkeypatch.setattr(design_mod, "ford_surface_drops", lambda root: [])
|
|
monkeypatch.setattr(design_mod, "design_elevation_from_longitudinal", lambda lon, ch: 100.0)
|
|
monkeypatch.setattr(design_mod, "curve_widening_args", lambda section: {})
|
|
monkeypatch.setattr(
|
|
design_mod, "compute_cross_design", lambda samples, elev, **kw: {"paved": kw["paved"]}
|
|
)
|
|
sections = [{"chainage_m": 40.0, "design": {"paved": False, "structure_note_cad": CAD}}]
|
|
assert design_mod.enforce_pavement_ranges({}, sections, Path("."), None) == 1
|
|
assert sections[0]["design"]["structure_note_cad"] == CAD
|
|
|
|
|
|
def test_브라우저가_CAD_칸을_저장_patch_에_싣는다():
|
|
read = lambda rel: (PROJECT_ROOT / rel).read_text(encoding="utf-8") # noqa: E731
|
|
persist = read("B06_Section/B06_Section_UI_Page_Persist.ts")
|
|
assert "readStructureNoteCads(" in persist and ".structure_note_cad = cad" in persist
|
|
state = read("A00_Common/b_page_state.ts")
|
|
assert '"structure-note-cads": { bucket: "draft", scope: "route" }' in state
|
|
# 캐시 설계 → B05 [저장] 길(`crossPatchesFromCache`)도 같은 목록
|
|
assert '"structure_note_cad",' in read("B06_Section/B06_Section_Cross_Refresh.ts")
|
|
# 모달 = 빈 CAD(캔버스만) · 글 상자 없음
|
|
detail = read("B06_Section/B06_Section_UI_Cross_Detail.ts")
|
|
assert "textarea" not in detail and "mountNoteCad(" in detail and "noteCad?.destroy()" in detail
|
|
assert "bare: true" in read("B06_Section/B06_Section_UI_Cross_Detail_Cad.ts")
|
|
|
|
|
|
# ── 260-2 미리 넣음 ─────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def out() -> dict:
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(HERE / "helper_260_note_cad.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def _labels(entities: list[dict]) -> list[str]:
|
|
return [entity["label"] for entity in entities if entity["type"] == "Text"]
|
|
|
|
|
|
def test_관_유입_유출_도수로_한_벌(out):
|
|
"""그림(162-21) 꼴 — 원 안 세 줄 · 오른쪽 측점 · 구조물 조합 줄(265-2)."""
|
|
full = out["full"]
|
|
assert [entity["type"] for entity in full[:2]] == ["Circle", "Line"]
|
|
assert _labels(full) == [
|
|
"배수관",
|
|
"Φ1200m/m",
|
|
"L=22m",
|
|
"NO.71+13",
|
|
"기슭막이 찰쌓기 2.0m",
|
|
"돌붙임 찰붙임",
|
|
]
|
|
# 원 안 = 가운데 맞춤 · 원 중심 x · 오른쪽 = 왼쪽 맞춤 · 줄 3.0 간격 · 붉은 계열 · 기본 층
|
|
inside, right = full[2:5], full[5:]
|
|
assert {(e["align"], e["x"]) for e in inside} == {("center", full[0]["x"])}
|
|
assert [e["y"] for e in right] == [-1.5, -4.5, -7.5]
|
|
assert {e["x"] for e in right} == {15}
|
|
assert {e["align"] for e in right} == {"left"}
|
|
assert {e["color"] for e in full} == {"#ff7f7f"} and {e["layer"] for e in full} == {"기본"}
|
|
|
|
|
|
def test_관_길이_없으면_L_뒤_빈칸(out):
|
|
assert _labels(out["noLength"])[2] == "L="
|
|
# 조합 줄이 없으면 측점 줄까지
|
|
assert _labels(out["noLength"])[3:] == ["NO.71+13"]
|
|
|
|
|
|
def test_유입_집수정_은_형태_높이_길이(out):
|
|
# 265-2 — 보호공 짜 맞춘 글 버림(조합 줄로) · 원형 마크 · 측점만
|
|
assert _labels(out["basin"]) == ["배수관", "Φ1200m/m", "L=22m", "NO.71+13"]
|
|
|
|
|
|
def test_도수로_옵션이_없으면_세트_구조만(out):
|
|
# 265-2 — 관유출부보강 짜 맞춘 글 버림
|
|
assert not any(label.startswith("관유출부보강") for label in _labels(out["chuteFallback"]))
|
|
|
|
|
|
def test_보호공을_안_고르면_관과_측점만(out):
|
|
assert _labels(out["noSides"]) == ["배수관", "Φ1200m/m", "L=22m", "NO.71+13"]
|
|
|
|
|
|
@pytest.mark.parametrize("case", ["none", "ford", "hidden", "neighbour"])
|
|
def test_횡단배수_주인_측점이_아니면_빈값(out, case):
|
|
"""구조물 없음 · 상자형(세월교 · 박스) · 숨은 관 · 이웃 측점 = 빈 도면(줄 없음)."""
|
|
assert out[case] == []
|
|
|
|
|
|
def test_마크_없이_조합_줄만이면_측점부터_왼쪽_끝(out):
|
|
lines = out["linesOnly"]
|
|
assert _labels(lines) == ["NO.71+13", "기슭막이 찰쌓기 2.0m", "돌붙임 찰붙임"]
|
|
assert {e["x"] for e in lines} == {0} and [e["y"] for e in lines] == [-1.5, -4.5, -7.5]
|
|
|
|
|
|
def test_옛_글은_줄마다_Text_로_옮김(out):
|
|
legacy = out["legacy"]
|
|
assert _labels(legacy) == ["L형 측구", "집수정 보강"]
|
|
# 빈 줄은 자리만 — B07 옛 글 줄과 같은 높이(줄 3.0 · 가운데)
|
|
assert [e["y"] for e in legacy] == [-1.5, -7.5] and {e["x"] for e in legacy} == {0}
|
|
assert out["legacyEmpty"] == 0
|
|
|
|
|
|
def test_측점_이름은_B07_꼴(out):
|
|
assert out["station"] == ["NO.71+13", "NO.71", "NO.72"]
|
|
|
|
|
|
# ── 260-3 B07 표 위 ─────────────────────────────────────────────────────
|
|
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import ( # noqa: E402
|
|
CROSS_TABLE_LAYER_ID,
|
|
FRAME_LAYER_ID,
|
|
build_cross_drawing,
|
|
)
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Sheet import section_block_size # noqa: E402
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_Table import ( # noqa: E402
|
|
cross_note_block_height,
|
|
cross_note_cad,
|
|
note_cad_bbox,
|
|
)
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Template import entities_bbox # noqa: E402
|
|
|
|
_SOURCE = {
|
|
"chainage_m": 20.0,
|
|
"samples": [
|
|
{"valid": True, "offset_m": float(x), "elevation_m": 100.0 + 0.3 * x}
|
|
for x in range(-10, 11)
|
|
],
|
|
}
|
|
_QTABLE = {"ground": 100.0, "planned": 99.5}
|
|
|
|
|
|
def _text(label: str, x: float, y: float, align: str = "left", font: float = 2.0) -> dict:
|
|
return {
|
|
"id": label,
|
|
"type": "Text",
|
|
"lineColor": "#ff7f7f",
|
|
"lineWidth": 1,
|
|
"layerId": "기본",
|
|
"shapeData": {
|
|
"label": label,
|
|
"basePoint": {"x": x, "y": y},
|
|
"options": {"textAlign": align, "fontSize": font, "textColor": "#ff7f7f"},
|
|
},
|
|
}
|
|
|
|
|
|
# 미리 넣음과 같은 꼴 — 원(중심 6.5,-9 · 반지름 6.5) · 지시선 · 오른쪽 글(도면 좌표 아무 자리여도 됨)
|
|
_CIRCLE = {
|
|
"id": "c",
|
|
"type": "Circle",
|
|
"lineColor": "#ff7f7f",
|
|
"lineWidth": 1,
|
|
"layerId": "기본",
|
|
"shapeData": {"center": {"x": 106.5, "y": 41.0}, "radius": 6.5},
|
|
}
|
|
_CAD = {
|
|
"structure_note_cad": {
|
|
"entities": [_CIRCLE, _text("NO.1", 115.0, 48.5), _text("관유출부보강", 115.0, 45.5)],
|
|
"layers": [{"id": "기본", "name": "기본"}],
|
|
}
|
|
}
|
|
|
|
|
|
def _draw(design: dict | None) -> dict:
|
|
return build_cross_drawing(_SOURCE, "cross_00020m", None, design, _QTABLE, "No.1")
|
|
|
|
|
|
def _table(drawing: dict) -> dict:
|
|
return next(e for e in drawing["entities"] if e["type"] == "Table")
|
|
|
|
|
|
def _frame_height(drawing: dict) -> float:
|
|
frame = [e for e in drawing["entities"] if e.get("layerId") == FRAME_LAYER_ID]
|
|
_x0, y0, _x1, y1 = entities_bbox(frame)
|
|
return y1 - y0
|
|
|
|
|
|
def test_CAD_칸_범위는_글_높이_폭까지():
|
|
"""글 기준점 = 세로 가운데 · 왼쪽 맞춤 폭 = 한글 글자 크기 · 원 = 중심 ± 반지름."""
|
|
box = note_cad_bbox(_CAD["structure_note_cad"]["entities"])
|
|
assert box == pytest.approx((100.0, 34.5, 115.0 + 6 * 2.0, 49.5))
|
|
assert note_cad_bbox([_text("AB", 10.0, 0.0, "center")]) == pytest.approx((8.8, -1, 11.2, 1))
|
|
assert note_cad_bbox([]) is None
|
|
assert cross_note_cad(None) is None and cross_note_cad({"structure_note": "x"}) is None
|
|
|
|
|
|
def test_CAD_칸은_표_왼쪽_위_맞춤_표는_범위_높이만큼_내려감():
|
|
plain = _draw(None)
|
|
drawn = _draw(_CAD)
|
|
height = 49.5 - 34.5
|
|
assert cross_note_block_height(_CAD) == pytest.approx(height)
|
|
plain_top = _table(plain)["shapeData"]["origin"]["y"]
|
|
drawn_top = _table(drawn)["shapeData"]["origin"]["y"]
|
|
assert plain_top - drawn_top == pytest.approx(height)
|
|
|
|
placed = [e for e in drawn["entities"] if e.get("id") in _placed_ids()]
|
|
left = _table(drawn)["shapeData"]["origin"]["x"]
|
|
box = note_cad_bbox(placed)
|
|
# 좌상 맞춤 — 범위 왼쪽 = 표 왼쪽 · 범위 위 = 옛 표 윗변(글 없을 때 표 자리)
|
|
assert box[0] == pytest.approx(left) and box[3] == pytest.approx(plain_top)
|
|
assert box[1] == pytest.approx(drawn_top)
|
|
# 표 층으로 · 크기 그대로(배율 1) · 글 색 그대로 · id 는 도면마다 새로
|
|
assert {e["layerId"] for e in placed} == {CROSS_TABLE_LAYER_ID}
|
|
circle = next(e for e in placed if e["type"] == "Circle")
|
|
assert circle["shapeData"]["radius"] == 6.5
|
|
assert {e["lineColor"] for e in placed} == {"#ff7f7f"}
|
|
assert "c" not in {e["id"] for e in placed}
|
|
# 블록(테두리) · 시트 칸 크기도 같은 규칙
|
|
assert _frame_height(drawn) - _frame_height(plain) == pytest.approx(height)
|
|
_w, plain_h = section_block_size(_SOURCE, None)
|
|
_w, cad_h = section_block_size(_SOURCE, None, design=_CAD)
|
|
assert cad_h - plain_h == pytest.approx(height)
|
|
_w, scaled_h = section_block_size(_SOURCE, None, design=_CAD, table_scale=2.0)
|
|
_w, plain2_h = section_block_size(_SOURCE, None, table_scale=2.0)
|
|
assert scaled_h - plain2_h == pytest.approx(2 * height)
|
|
|
|
|
|
def _placed_ids() -> set[str]:
|
|
"""옮겨 놓은 CAD 칸 엔티티 id — 결정적 seed `{도면}:notecad:{n}`."""
|
|
from uuid import uuid5
|
|
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad import _ENTITY_NS
|
|
|
|
return {str(uuid5(_ENTITY_NS, f"cross_00020m:notecad:{n}")) for n in range(3)}
|
|
|
|
|
|
def test_CAD_칸이_있으면_옛_글_대신():
|
|
both = _draw({**_CAD, "structure_note": "옛 글"})
|
|
labels = [e["shapeData"]["label"] for e in both["entities"] if e["type"] == "Text"]
|
|
assert "옛 글" not in labels and "NO.1" in labels
|
|
|
|
|
|
def test_CAD_칸이_비면_아무것도_안_놓음_옛_글도_안_씀():
|
|
plain = _draw(None)
|
|
empty = _draw({"structure_note_cad": {"entities": []}, "structure_note": "옛 글"})
|
|
assert _table(plain)["shapeData"] == _table(empty)["shapeData"]
|
|
assert not [e for e in empty["entities"] if e["type"] == "Text"]
|
|
|
|
|
|
def test_CAD_칸이_없으면_옛_글_줄_그대로():
|
|
noted = _draw({"structure_note": "L형 측구\n집수정 보강"})
|
|
labels = [e["shapeData"]["label"] for e in noted["entities"] if e["type"] == "Text"]
|
|
assert labels == ["L형 측구", "집수정 보강"]
|
|
|
|
|
|
# ── 260-4 안 연 측점 = B07 이 파이썬 짝으로 넣음 ─────────────────────────
|
|
|
|
from B06_Section.B06_Section_Engine_Culvert import _culvert_set, pipe_points_file # noqa: E402
|
|
from B07_DesignDetail.B07_DesignDetail_Engine_Cad_CulvertNote import ( # noqa: E402
|
|
load_note_sources,
|
|
note_entities,
|
|
station_no_label,
|
|
structure_note_lines,
|
|
with_structure_note,
|
|
)
|
|
|
|
_OPTIONS = {
|
|
"pipe_diameter_mm": 1200,
|
|
"inlet_type": "기슭막이",
|
|
"inlet_revet_form": "돌쌓기(찰)",
|
|
"inlet_revet_height_m": 2,
|
|
"inlet_revet_length_m": 10,
|
|
"outlet_type": "기슭막이",
|
|
"outlet_revet_form": "돌쌓기(메)",
|
|
"outlet_revet_height_m": 2,
|
|
"outlet_revet_length_m": 10,
|
|
"chute_add_1_structure": "돌붙임",
|
|
"chute_add_1_bond": "찰붙임",
|
|
"chute_add_1_area": 10,
|
|
}
|
|
_NO_CHUTE = {k: v for k, v in _OPTIONS.items() if not k.startswith("chute")}
|
|
|
|
|
|
def _item(key: str, name: str) -> dict:
|
|
return {"layer": "system", "owner": None, "key": key, "name": name}
|
|
|
|
|
|
# 관 조합 — 본체(「관」) · 유입 · 유출 · 도수로(265-2 · 본체 줄은 원형 마크가 대신)
|
|
_PIPE_THING = {
|
|
"name": "배관",
|
|
"pipe": True,
|
|
"at": 1433.0,
|
|
"group_items": {
|
|
"grv6lny": _item("SS000001", "흄관 1200"),
|
|
"gnbf9m9": _item("SS000055", "기슭막이 메쌓기 1.2m 메쌓기 기초무"),
|
|
"gchuteadd1": _item("SS000070", "돌붙임 찰붙임"),
|
|
},
|
|
}
|
|
|
|
|
|
def _case(
|
|
options: dict, pipe_at: float, chainage: float, design: dict | None, things=(), **spec
|
|
) -> dict:
|
|
culvert = {**_culvert_set(options), "chainage_m": pipe_at, **spec}
|
|
return {
|
|
"section": {"chainage_m": chainage, "culvert": culvert, "design": design},
|
|
"station": station_no_label(chainage, 20.0),
|
|
"things": list(things),
|
|
"chainage": chainage,
|
|
"interval": 20.0,
|
|
}
|
|
|
|
|
|
_MIRROR_CASES = [
|
|
_case(_OPTIONS, 1433.0, 1433.0, {"pipe_length_m": 22}, [_PIPE_THING]),
|
|
_case(_NO_CHUTE, 1433.0, 1433.0, {}),
|
|
_case(_NO_CHUTE, 1433.0, 1433.0, {"pipe_length_m": 9.5}),
|
|
_case(_NO_CHUTE, 80, 80, None, [{**_PIPE_THING, "at": 80.0}]),
|
|
_case({**_OPTIONS, "pipe_diameter_mm": 800}, 12.5, 12.5, {}, [{**_PIPE_THING, "at": 12.5}]),
|
|
_case(_OPTIONS, 440.241, 440.0, {"pipe_length_m": 7}, [{**_PIPE_THING, "at": 440.241}]),
|
|
_case(_OPTIONS, 1433.0, 1440.0, {"pipe_length_m": 22}, [_PIPE_THING]),
|
|
_case(_OPTIONS, 1433.0, 1433.0, {}, body="ford"),
|
|
_case(_OPTIONS, 1433.0, 1433.0, {}, hidden_pipe=True),
|
|
]
|
|
|
|
|
|
def _without_id(entities: list[dict]) -> list[dict]:
|
|
return [{k: v for k, v in entity.items() if k != "id"} for entity in entities]
|
|
|
|
|
|
def _mirror(cases: list[dict]) -> list[dict]:
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(HERE / "helper_260_note_cad.cjs"), "mirror"],
|
|
input=json.dumps(cases, ensure_ascii=False),
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def test_거울_TS_와_파이썬_짝은_같은_입력에_같은_엔티티():
|
|
mirrored = _mirror(_MIRROR_CASES)
|
|
assert len(mirrored) == len(_MIRROR_CASES)
|
|
for case, ts in zip(_MIRROR_CASES, mirrored, strict=True):
|
|
section = case["section"]
|
|
lines = structure_note_lines(case["things"], section["chainage_m"])
|
|
assert lines == ts["lines"], case["station"]
|
|
py = note_entities(
|
|
section["chainage_m"], section["culvert"], section["design"], case["station"], lines
|
|
)
|
|
assert _without_id(py) == _without_id(ts["entities"]), case["station"]
|
|
assert station_no_label(case["chainage"], case["interval"]) == ts["station"]
|
|
# 대조가 빈 목록끼리만이 아님 — 주인 측점 여섯은 그림이 있음 · 이웃 · 세월교 · 숨은 관 = 빈값
|
|
assert [bool(item["entities"]) for item in mirrored] == [True] * 6 + [False] * 3
|
|
labels = [e["shapeData"].get("label") for e in mirrored[4]["entities"]]
|
|
assert "Φ800m/m" in labels and "돌붙임 찰붙임" in labels and "흄관 1200" not in labels
|
|
|
|
|
|
def _write_pipes(root: Path, points: list[dict]) -> None:
|
|
path = pipe_points_file(root)
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(json.dumps({"points": points}, ensure_ascii=False), encoding="utf-8")
|
|
|
|
|
|
def test_정본_배수관만_읽음(tmp_path):
|
|
_write_pipes(
|
|
tmp_path,
|
|
[
|
|
{"chainage_m": 1433.0, "lonlat": [0, 0], "facility": "pipe", "options": _OPTIONS},
|
|
{"chainage_m": 80.0, "lonlat": [0, 0], "facility": "pipe", "options": {}},
|
|
{"chainage_m": 90.0, "lonlat": [0, 0], "facility": "ford_bridge", "options": {}},
|
|
],
|
|
)
|
|
pipes, things = load_note_sources(tmp_path)
|
|
assert [pipe[0] for pipe in pipes] == [1433.0] # 관경 안 고름 · 세월교 = 빼
|
|
assert pipes[0][1]["chainage_m"] == 1433.0 and pipes[0][2] == _OPTIONS
|
|
# 글 줄 재료 = 관 지점 모두(측점 차례 · 시설 이름 = 구조물정의 종류 이름)
|
|
assert [(t["name"], t["pipe"], t["at"]) for t in things] == [
|
|
("배관", True, 80.0),
|
|
("세월교", False, 90.0),
|
|
("배관", True, 1433.0),
|
|
]
|
|
assert load_note_sources(tmp_path / "없음") == ([], [])
|
|
|
|
|
|
def test_칸_없음_만_미리_넣음_지운_칸_옛_글_설계없음_이웃은_그대로(tmp_path):
|
|
_write_pipes(
|
|
tmp_path,
|
|
[{"chainage_m": 1433.0, "lonlat": [0, 0], "facility": "pipe", "options": _OPTIONS}],
|
|
)
|
|
sources = load_note_sources(tmp_path)
|
|
design = {"pipe_length_m": 22, "paved": False}
|
|
seeded = with_structure_note(design, 1433.0, sources, "NO.71+13")
|
|
assert seeded is not design and "structure_note_cad" not in design # 저장 설계는 안 바뀜
|
|
labels = [e["shapeData"].get("label") for e in seeded["structure_note_cad"]["entities"]]
|
|
# 관 조합 미지정 = 원형 마크 · 측점만(배관은 글 줄 없음)
|
|
assert labels[2:] == ["배수관", "Φ1200m/m", "L=22m", "NO.71+13"]
|
|
cleared = {**design, "structure_note_cad": {"entities": []}}
|
|
assert with_structure_note(cleared, 1433.0, sources, "x") is cleared
|
|
old = {**design, "structure_note": "옛 글"}
|
|
assert with_structure_note(old, 1433.0, sources, "x") is old
|
|
assert with_structure_note(None, 1433.0, sources, "x") is None
|
|
assert with_structure_note(design, 1440.0, sources, "x") is design
|
|
|
|
|
|
def test_B07_은_안_연_측점에_미리_넣음을_표_위에_그림(tmp_path):
|
|
_write_pipes(
|
|
tmp_path,
|
|
[
|
|
{
|
|
"chainage_m": 20.2,
|
|
"lonlat": [0, 0],
|
|
"facility": "pipe",
|
|
"options": _OPTIONS,
|
|
"group_items": {"g9lq9uk": _item("SS000055", "기슭막이 메쌓기 1.2m")},
|
|
}
|
|
],
|
|
)
|
|
design = with_structure_note({"pipe_length_m": 22}, 20.0, load_note_sources(tmp_path), "NO.1")
|
|
drawn = _draw(design)
|
|
labels = [e["shapeData"]["label"] for e in drawn["entities"] if e["type"] == "Text"]
|
|
assert "NO.1" in labels and "기슭막이 메쌓기 1.2m" in labels
|
|
plain_top = _table(_draw({"pipe_length_m": 22}))["shapeData"]["origin"]["y"]
|
|
drop = plain_top - _table(drawn)["shapeData"]["origin"]["y"]
|
|
assert drop == pytest.approx(cross_note_block_height(design)) and drop > 15
|