feat(ground): load ground_model.json and apply the pier-centre ground EL
- ground-model.ts: read and check schema 1 of the Python ground model. - ② 지반·지층 card: import the json, show the DTM value beside the excel value, and apply it only when the user presses the button (tracked as an override, revertible). - groundEl becomes an editable, tracked input; the project file keeps the imported model and checks it on open. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
304b350481
commit
c3606011b5
5 files changed
+208
-1
No files matched your search
+57
-1
@@ -16,6 +16,7 @@ import { buildRebar3D, type RebarColorMode, type RebarScene } from './view/rebar
|
||||
import { DEFAULT_MATERIAL, FCK_MEMBERS, fyOf, GRADE_CHOICES, isAssumedMaterial, materialFrom, type BendFactors, type FckByMember, type FckMember, type GradeKey, type Material } from './core/rebar/common';
|
||||
import { buildQuantity, quantityRows, type Quantity, type QuantityLine } from './core/rebar/quantity';
|
||||
import { parseDrawingRows } from './core/drawing-import';
|
||||
import { groundElFor, parseGroundModel, type GroundModelFile } from './core/ground-model';
|
||||
import { parseBearingElevationTable, parseSolplateTable, readDxfTexts } from './core/dxf-table';
|
||||
import { columnDiameterIssues } from './core/column-check';
|
||||
import { findPileSpec, PHC_PILES, STEEL_PILES } from './core/pile-spec';
|
||||
@@ -29,6 +30,9 @@ let bearingRules: BearingRules = { ...BEARING_RULES_DEFAULT };
|
||||
/** 받침 기준 for the project (Q16, 승인 2026-10-23): 'excel' by default. Drawing values per pier name. */
|
||||
let bearingBasis: BearingBasis = 'excel';
|
||||
let bearingDrawing: Record<string, DrawingBearing[]> = {};
|
||||
|
||||
/** 지반 모델 (ground_model.json) for the project. Its values reach a pier's 지반고 only when the user applies them. */
|
||||
let groundModel: GroundModelFile | null = null;
|
||||
import { allVisibleChecked, setVisibleChecked, type TypeFilter } from './core/selection';
|
||||
import { choose, defaultSummary, isSelectable, SHAPE_GROUPS, UNDEFINED_REASON, type MemberId } from './core/shape-options';
|
||||
import { parseProject, SCHEMA_VERSION, serializeProject, type ProjectFile, type ProjectInfo } from './core/project';
|
||||
@@ -74,7 +78,7 @@ const MEMBERS: { id: MemberId; label: string; keys: EditableKey[]; note: string
|
||||
{ id: 'coping', label: '코핑', keys: ['copingWidthMm', 'copingLengthMm', 'copingH1Mm', 'copingH2Mm', 'levelWidthMm'], note: '폭, 길이, 끝단 높이 h1, 테이퍼 높이 h2, Level 폭을 입력합니다. 계단 위치·단차선 입력은 다음 단계에서 추가합니다.' },
|
||||
{ id: 'column', label: '기둥', keys: ['columnDiameterMm'], note: '기둥 직경 D를 입력합니다 (1200–3000 mm, 50 mm 단위). 높이와 위치는 계산값입니다.' },
|
||||
{ id: 'footing', label: '확대기초 · 말뚝', keys: ['footingLengthMm', 'footingWidthMm', 'footingHeightMm', 'blindingMarginMm', 'blindingThicknessMm', 'pileLengthMm'], note: '확대기초 치수, 비림, 말뚝 길이와 규격을 입력합니다. 규칙 미정 항목(모따기, 경사, 복합 말뚝)은 다음 단계에서 추가합니다.' },
|
||||
{ id: 'ground', label: '지반·지층', keys: [], note: '지반고는 입력값을 표시만 합니다. 지층 입력은 다음 단계에서 추가합니다.' },
|
||||
{ id: 'ground', label: '지반·지층', keys: ['groundEl'], note: '지반고는 지반 모델 값을 적용하거나 직접 입력합니다. 지층 입력은 다음 단계에서 추가합니다.' },
|
||||
];
|
||||
|
||||
interface Pending {
|
||||
@@ -1494,6 +1498,54 @@ function renderBearingBasis(wrap: HTMLDivElement, pier: Pier): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** ② 지반 모델 (승인 2026-10-23): the pier-centre ground EL from ground_model.json. Applied only when the user presses the button. */
|
||||
function renderGroundModel(box: HTMLDivElement, pier: Pier): void {
|
||||
const wrap = el('div', undefined, 'ground-model');
|
||||
wrap.appendChild(el('p', '지반 모델 (지반 모델 결과 · ground_model.json)', 'lbl'));
|
||||
const importInput = el('input');
|
||||
importInput.type = 'file';
|
||||
importInput.accept = '.json';
|
||||
importInput.id = 'ground-import';
|
||||
importInput.addEventListener('change', async () => {
|
||||
const file = importInput.files?.[0];
|
||||
if (!file) return;
|
||||
try {
|
||||
groundModel = parseGroundModel(await file.text());
|
||||
setStatus(`${file.name}에서 교각 ${groundModel.piers.length}개의 지반 모델 값을 불러왔습니다. 적용은 교각별로 누르세요.`);
|
||||
renderAll();
|
||||
} catch (err) {
|
||||
setError(`지반 모델을 읽지 못했습니다: ${(err as Error).message}`);
|
||||
} finally {
|
||||
importInput.value = '';
|
||||
}
|
||||
});
|
||||
const importLabel = el('label', '지반 모델 파일 불러오기 (json)');
|
||||
importLabel.appendChild(importInput);
|
||||
wrap.appendChild(importLabel);
|
||||
|
||||
if (!groundModel) {
|
||||
wrap.appendChild(el('p', '지반 모델을 불러오면 이 교각의 지반고를 엑셀 값과 비교하고 적용할 수 있습니다.', 'muted small'));
|
||||
} else {
|
||||
const entry = groundModel.piers.find((q) => q.id === pier.input.name);
|
||||
const center = groundElFor(groundModel, pier.input.name);
|
||||
if (!entry) {
|
||||
wrap.appendChild(el('p', `지반 모델에 ${pier.input.name} 교각이 없습니다.`, 'warn-text'));
|
||||
} else if (center === undefined) {
|
||||
wrap.appendChild(el('p', '지형 자료 범위 밖이라 지반 모델 값이 없습니다. 입력된 지반고를 그대로 씁니다.', 'warn-text'));
|
||||
} else {
|
||||
const diff = entry.excelEl === null ? '—' : `${center - entry.excelEl >= 0 ? '+' : ''}${(center - entry.excelEl).toFixed(3)} m`;
|
||||
wrap.appendChild(el('p', `지형 계산 ${center.toFixed(3)} m · 엑셀 ${entry.excelEl?.toFixed(3) ?? '—'} m · 차이 ${diff}`));
|
||||
const apply = el('button', `지반고 ${center.toFixed(3)} m 적용`, 'btn small');
|
||||
apply.type = 'button';
|
||||
apply.disabled = pier.draft.groundEl === center;
|
||||
apply.addEventListener('click', () => editField(pier.input.name, 'groundEl', String(center)));
|
||||
wrap.appendChild(apply);
|
||||
}
|
||||
wrap.appendChild(el('p', '적용하면 지반고가 변경으로 기록되고, 되돌리기로 엑셀 값으로 돌아갈 수 있습니다. 3D에 반영하려면 다시 생성하세요.', 'muted small'));
|
||||
}
|
||||
box.appendChild(wrap);
|
||||
}
|
||||
|
||||
function renderBearingRef(box: HTMLDivElement, pier: Pier): void {
|
||||
const m = pier.model;
|
||||
const wrap = el('div', undefined, 'bearing-ref');
|
||||
@@ -1671,6 +1723,7 @@ function renderPlan(): void {
|
||||
}
|
||||
if (member.id === 'coping') renderCopingInfo(fields, pier);
|
||||
if (member.id === 'bearing') renderBearingRef(fields, pier);
|
||||
if (member.id === 'ground') renderGroundModel(fields, pier);
|
||||
if (FCK_MEMBERS.includes(member.id as FckMember)) renderMemberFck(fields, member.id as FckMember);
|
||||
regen.textContent = `다시 생성 · 변경 ${pendingKeys(pier).length}`;
|
||||
regen.disabled = pendingKeys(pier).length === 0;
|
||||
@@ -1706,6 +1759,7 @@ function snapshot(): string {
|
||||
fckByMember: state.fckByMember,
|
||||
bearingBasis,
|
||||
bearingDrawing,
|
||||
groundModel,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1724,6 +1778,7 @@ function currentProjectText(): string {
|
||||
fckByMember: state.fckByMember,
|
||||
bearingBasis,
|
||||
bearingDrawing,
|
||||
groundModel: groundModel ?? undefined,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1811,6 +1866,7 @@ function openProjectText(text: string, saved: boolean): void {
|
||||
state.fckByMember = { ...parsed.fckByMember };
|
||||
bearingBasis = parsed.bearingBasis ?? 'excel';
|
||||
bearingDrawing = Object.fromEntries(Object.entries(parsed.bearingDrawing ?? {}).map(([k, v]) => [k, [...v]]));
|
||||
groundModel = parsed.groundModel ?? null;
|
||||
state.piers = parsed.piers.map((p) => ({
|
||||
...buildEntry(p.input, false),
|
||||
defaults: p.defaults ?? p.input,
|
||||
|
||||
Reference in new issue
Block a user