Files
Pier_v1/pierzainer_ground/pierzainer_ground/pipeline.py
T

122 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""지형 DXF + 교각 + (시추) → 교각별 지반 결과."""
from __future__ import annotations
from dataclasses import dataclass, field
import math
from pathlib import Path
from typing import Optional
from .boreholes import Borehole, read_boreholes
from .checks import Alert, check_pier
from .config import GroundConfig
from .dtm import DTM
from .dxf_terrain import TerrainData, read_terrain_dxf
from .piers import Pier, read_piers
from .strata import SoilColumn, pier_soil_column
@dataclass
class PierResult:
pier: Pier
ground_center: float | None # DTM 중심 지반고
corner_els: list[float | None] # DTM 네 모서리
ground_used: float | None # 쓰는 지반고 (DTM 우선, 없으면 엑셀) — G-5
ground_source: str # 계산(DTM) / 입력(엑셀) / 없음
cover: float | None # 흙덮개 = 지반 최저 − 확대기초 상면
column: SoilColumn | None
alerts: list[Alert] = field(default_factory=list)
dtm_spacing: float | None = None # 중심 지반고를 만든 지형 점 중 가장 먼 거리 (m)
@property
def ground_min(self) -> float | None:
v = [z for z in self.corner_els + [self.ground_center] if z is not None]
return min(v) if v else None
@property
def ground_max(self) -> float | None:
v = [z for z in self.corner_els + [self.ground_center] if z is not None]
return max(v) if v else None
@dataclass
class GroundModel:
terrain: TerrainData
dtm: DTM
boreholes: list[Borehole]
bore_issues: list[str]
results: list[PierResult]
cfg: GroundConfig
@property
def alerts(self) -> list[Alert]:
return [a for r in self.results for a in r.alerts]
def evaluate_pier(p: Pier, dtm: DTM, bores: list[Borehole], cfg: GroundConfig) -> PierResult:
zc, spacing = dtm.z_info(p.X, p.Y)
corners = [dtm.z_at(x, y) for x, y in p.corners()]
if zc is not None:
used, src = zc, "계산"
elif p.excel_ground_el is not None:
used, src = p.excel_ground_el, "입력"
else:
used, src = None, "없음"
known = [z for z in corners if z is not None] or ([used] if used is not None else [])
cover = (min(known) - p.footing_top_el) if (known and p.footing_top_el is not None) else None
column = pier_soil_column(p.X, p.Y, used, bores, cfg) if bores else None
alerts = check_pier(p, zc, corners, column, cfg, has_bore_data=bool(bores), dtm_spacing=spacing)
return PierResult(p, zc, corners, used, src, cover, column, alerts, spacing)
def infer_piers_from_plan(terrain: TerrainData) -> list[Pier]:
"""종평면도에서 교각 번호와 위치를 찾아 치수 없는 교각 목록을 만든다."""
labels = sorted(terrain.pier_labels, key=lambda item: int(item["id"][1:]) if item["id"][1:].isdigit() else 10**9)
labels = list({item["id"]: item for item in labels}.values())
if not labels:
raise ValueError("종평면도에서 교각 번호(P1, P2...)를 찾지 못했습니다. 교각 입력 JSON을 지정하거나 교각 번호 레이어를 확인하세요.")
inferred: list[Pier] = []
for index, label in enumerate(labels):
prev_label = labels[index - 1] if index > 0 else None
next_label = labels[index + 1] if index + 1 < len(labels) else None
dx = dy = 0.0
if prev_label and next_label:
dx = next_label["X"] - prev_label["X"]
dy = next_label["Y"] - prev_label["Y"]
elif next_label:
dx = next_label["X"] - label["X"]
dy = next_label["Y"] - label["Y"]
elif prev_label:
dx = label["X"] - prev_label["X"]
dy = label["Y"] - prev_label["Y"]
length = math.hypot(dx, dy)
axis_dir = (dx / length, dy / length) if length > 1e-9 else (0.0, 1.0)
inferred.append(Pier(label["id"], float(label["X"]), float(label["Y"]), axis_dir=axis_dir))
return inferred
def run_ground_model(terrain_path: str | Path, piers: Optional[str | Path | list[Pier]],
boreholes_xlsx: str | Path | None = None,
cfg: GroundConfig | None = None, work_dir: str | Path | None = None) -> GroundModel:
cfg = cfg or GroundConfig()
terrain = read_terrain_dxf(terrain_path, cfg, work_dir)
if len(terrain.points) < 3:
layer_names = ", ".join(list((terrain.stats.get("layers") or {}).keys())[:12]) or "없음"
raise ValueError(
"종평면도에서 지형점 3개 이상을 읽지 못했습니다. "
f"읽은 점 {len(terrain.points)}개입니다. 표고점/등고선/3D 지형 객체와 EL 범위({cfg.z_min}–{cfg.z_max})를 확인하세요. "
f"발견된 주요 레이어: {layer_names}"
)
dtm = DTM(terrain.points, cfg.tin_max_edge)
if piers is None:
pier_list = infer_piers_from_plan(terrain)
else:
pier_list = read_piers(piers) if isinstance(piers, (str, Path)) else list(piers)
bores: list[Borehole] = []
issues: list[str] = []
if boreholes_xlsx:
pos = {b["id"]: (b["X"], b["Y"]) for b in terrain.boreholes}
bores, issues = read_boreholes(boreholes_xlsx, cfg, dxf_positions=pos)
results = [evaluate_pier(p, dtm, bores, cfg) for p in pier_list]
return GroundModel(terrain, dtm, bores, issues, results, cfg)