- 짝 `common_util_structure_attach.ts` attachShapes ↔ `B06_Section_Engine_Attach.py` attach_shapes — 열쇠 걷고 관 세트 → 벽 → 포장 → 맹암거 → 빈칸 경고(측점 계획 자리 · 「옹벽 높이」 · 「배관 관경」 · 「맹암거 암거 높이」 · 「물넘이포장 월류 높이」) - 서버 `_read_section_detail` · 재생성 응답 = attach_saved_shapes 하나 · 화면 `withDraftShapes`(현황 = 초안 ?? 저장분 · 초안 없어지면 저장분으로 한 번) — decorate 관 세트 · withDraftPipeKinds · 맹암거 · 포장 · 벽 블록 걷음 · 구조물 초안 없을 때 미리 뜬 횡단에 포장 · 벽 · 맹암거 빠지던 것 · 반폭 넓힘 뒤 날 상세(초안 빠짐) 고침 - 파이썬 얹기 = 목록 함수(culvert_sets_from · attach_culvert_specs · wall_specs_from · attach_wall_specs · underdrains_from · attach_underdrain_specs) + 옛 파일 함수는 감쌈 - 3D 지문 = SHAPE_KEYS(`common_util_structure_shape_keys.ts`) + 조작값 통째(손 해시 걷음 · 벽 기초 · 단 · 포장 · 맹암거 빠짐) · C군 벽 = 기준 측점과 가장 가까운 측점에서만 시작 ~ 끝 스윕(`wallSweepAt`) - 소단 = 표 형상 berm(BERM_TYPE_ID 걷음 · 화면 isBermType · 서버 catalog_row) - 시험: test_323_4_attach_mirror(42종 + 빈칸 · 서버 = 화면 · 옛 자리 걷힘 · 벽 한 번 · 지문) · 전수 표 ★ 둘(물넘이포장 걸친 격자 100 = 서버와 같음 · STAGE 4) · 옛 도움 파일 = 측점 → 형상 차례 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
106 lines
4.9 KiB
Python
106 lines
4.9 KiB
Python
"""형상 얹기 한 벌(323-4) — 측점(횡단 dict) + 현황(관 · 구조물) + 구조물 표 → 그 측점 그림 자료.
|
|
|
|
구조물 열쇠(`SHAPE_KEYS` · 빈칸 경고 `blank`)를 걷고 차례대로 다시 얹음 — 관 세트 → 벽 → 포장 → 맹암거 →
|
|
빈칸 경고. 고르기 = 표 형상(322-1). 서버 상세 읽기(`_read_section_detail`) · 재생성 응답이 이것 하나를 씀.
|
|
TS 짝 = `common_util/common_util_structure_attach.ts` `attachShapes`(화면 = 초안 ?? 저장분 현황) ·
|
|
거울 시험 = `resources/tester/test_323_4_attach_mirror.py`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
from B05_Profile.B05_Profile_Structures_Repository import load_structures
|
|
from B05_Profile.B05_Profile_Structures_Schema import structure_type_map
|
|
from B06_Section.B06_Section_Engine_Culvert import (
|
|
attach_culvert_specs,
|
|
culvert_sets_from,
|
|
pipe_points_file,
|
|
)
|
|
from B06_Section.B06_Section_Engine_Structures_Wall import attach_wall_specs, wall_specs_from
|
|
from B06_Section.B06_Section_Engine_Tilt import attach_underdrain_specs, underdrains_from
|
|
from common_util.common_util_drainage_pipes import read_pipe_points_file
|
|
from common_util.common_util_structure_catalog import catalog_row, field_name, type_ids_with
|
|
from common_util.common_util_structure_pavement import (
|
|
attach_pavement_specs,
|
|
pavement_specs_from,
|
|
)
|
|
from common_util.common_util_structure_station import (
|
|
SAME_STATION_M,
|
|
blank_keys,
|
|
plan_stations,
|
|
)
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
#: 측점 그림 자료 열쇠 — TS `common_util_structure_shape_keys.SHAPE_KEYS` 짝.
|
|
SHAPE_KEYS = ("culvert", "ford", "box", "ford_pavement", "revetment", "pavement", "underdrain")
|
|
|
|
|
|
def _blank_labels(type_id: str, options: dict | None, types: dict[str, Any]) -> list[str]:
|
|
"""빈 필수 칸 → 「종류 이름 칸 이름」(TS `blankLabels` 짝)."""
|
|
name = (catalog_row(type_id) or {}).get("name") or type_id
|
|
ordered = [types[type_id]] if type_id in types else []
|
|
ordered += list(types.values())
|
|
out = []
|
|
for key in blank_keys(type_id, options):
|
|
label = next(
|
|
(o.label for t in ordered for o in t.options if o.key == key and o.label), None
|
|
)
|
|
out.append(f"{name} {label or field_name(key) or key}")
|
|
return out
|
|
|
|
|
|
def _attach_blanks(sections: list[dict[str, Any]], pipes: list[Any], structures: list[Any]) -> None:
|
|
"""빈칸 경고 — 측점 계획 자리마다 덮는 관 · 구조물의 빈 필수 칸(TS `attachBlanks` 짝)."""
|
|
types = structure_type_map()
|
|
owners: dict[str, tuple[str, dict | None]] = {}
|
|
for pipe in pipes:
|
|
owner = getattr(pipe, "id", None) or f"pipe-{float(pipe.chainage_m):.2f}"
|
|
owners[owner] = (pipe.facility or "pipe", pipe.options)
|
|
for item in structures:
|
|
if item.structure_id:
|
|
owners[item.structure_id] = (item.type_id, item.options)
|
|
for station in plan_stations(pipes, structures):
|
|
if not station["blank"]:
|
|
continue
|
|
labels: list[str] = []
|
|
for owner in station["owners"]:
|
|
hit = owners.get(owner)
|
|
for label in _blank_labels(hit[0], hit[1], types) if hit else []:
|
|
if label not in labels:
|
|
labels.append(label)
|
|
for section in sections:
|
|
at = section.get("chainage_m")
|
|
if isinstance(at, (int, float)) and abs(at - station["chainage_m"]) < SAME_STATION_M:
|
|
section["blank"] = labels
|
|
|
|
|
|
def attach_shapes(sections: list[dict[str, Any]], pipes: list[Any], structures: list[Any]) -> None:
|
|
"""측점마다 그림 자료를 걷고 현황으로 다시 얹음(TS `attachShapes` 짝 · 제자리 고침)."""
|
|
for section in sections:
|
|
for key in (*SHAPE_KEYS, "blank"):
|
|
section.pop(key, None)
|
|
attach_culvert_specs(sections, culvert_sets_from(pipes))
|
|
# 관 세트가 선 측점은 벽을 안 얹음(`attach_wall_specs`) — 차례 = 화면과 같음
|
|
attach_wall_specs(sections, wall_specs_from(structures))
|
|
attach_pavement_specs(
|
|
sections,
|
|
pavement_specs_from([s.model_dump() for s in structures], type_ids_with(shape="pavement")),
|
|
)
|
|
attach_underdrain_specs(sections, underdrains_from(structures))
|
|
_attach_blanks(sections, pipes, structures)
|
|
|
|
|
|
def attach_saved_shapes(project_root: Path, sections: list[dict[str, Any]]) -> None:
|
|
"""저장분 현황(관 지점 · 구조물 정본)으로 얹음 — 구조물 정본을 못 읽으면 관만(횡단 조회는 이어 감)."""
|
|
pipes = read_pipe_points_file(pipe_points_file(project_root))
|
|
try:
|
|
structures = load_structures(str(project_root))[1]
|
|
except Exception: # noqa: BLE001 — 정본을 못 읽어도 횡단 조회는 이어 간다
|
|
logger.exception("B06 구조물 정본을 읽지 못했습니다 (관만 얹음)")
|
|
structures = []
|
|
attach_shapes(sections, pipes, structures)
|