refactor(ground): remove the unversioned-file fallback

- A file without a schema number is refused again; the example output in
  RAG/pierzainer_ground now has schema 2, so the temporary path is not needed.
- The legacy fixture is removed; tests cover the missing and null schema
  numbers and a pier without a ground object.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-23 18:09:50 +09:00
1 parent bfd41d7c8d
commit 0e70cfb969
3 files changed
+9 -417

No files matched your search

+2 -7
View File
@@ -37,16 +37,11 @@ function numOrNull(v: unknown): number | null {
return typeof v === 'number' && Number.isFinite(v) ? v : null;
}
/**
* Checks the shape of a parsed ground_model.json. Throws with the first problem named in Korean.
* A file with no `schema` key is the layout written before the number was added: it is read as schema 1, and the
* `ground` object is required so that a renamed field is refused instead of loading as empty values.
* TODO: remove the no-schema branch once the 옛 파일 (examples/out) is regenerated with schema 1.
*/
/** Checks the shape of a parsed ground_model.json. Throws with the first problem named in Korean. */
export function checkGroundModel(raw: unknown): GroundModelFile {
const obj = raw as Partial<GroundModelFile> | null;
if (!obj || typeof obj !== 'object') throw new Error('지반 모델 파일 형식이 아닙니다.');
if (obj.schema !== undefined && !READABLE_SCHEMAS.includes(obj.schema)) {
if (!READABLE_SCHEMAS.includes(obj.schema as number)) {
throw new Error(`지반 모델 형식 번호 ${String(obj.schema)}을(를) 읽을 수 없습니다 (지원: ${READABLE_SCHEMAS.join(', ')}).`);
}
if (!Array.isArray(obj.piers)) throw new Error('지반 모델 파일에 교각 목록(piers)이 없습니다.');
-393
View File
@@ -1,393 +0,0 @@
{
"generated": "2026-10-23",
"coord": "측량 X(북) · Y(동), m. 치수 mm",
"terrain": {
"file": "C0050202-001.dxf",
"spot_blocks": 433,
"spot_texts": 190,
"contours": 67,
"line3d_vertices": 0,
"points_used": 3429,
"by_source": {
"spot": 431,
"text": 5,
"contour": 2993,
"line3d": 0
},
"removed": 1,
"boreholes": 18,
"extent": [
259779.6342279386,
179066.3432863709,
260208.0132279157,
179261.3784074617
]
},
"removed_points": [
{
"X": 260155.762358268,
"Y": 179189.228890184,
"Z": -18.115,
"source": "spot",
"reason": "EL 범위 밖 (0.0–200.0)"
}
],
"boreholes_dxf": [
{
"id": "BB-24",
"X": 260110.5200305235,
"Y": 179142.87077588285
},
{
"id": "BB-28",
"X": 259832.7395636612,
"Y": 179078.6772609205
},
{
"id": "NBB-21",
"X": 260169.7415555437,
"Y": 179217.2592038325
},
{
"id": "NBB-22",
"X": 260119.0843895569,
"Y": 179207.447663092
},
{
"id": "NBB-23",
"X": 260063.7209158014,
"Y": 179200.7170868495
},
{
"id": "NBB-24",
"X": 260013.7365954409,
"Y": 179193.2691158374
},
{
"id": "NBB-25",
"X": 259959.8230206913,
"Y": 179182.585023065
},
{
"id": "NBB-26",
"X": 259908.1691879086,
"Y": 179170.4141072329
},
{
"id": "NBB-27",
"X": 259862.0303893618,
"Y": 179162.6491102049
},
{
"id": "NBB-28",
"X": 259804.6978413376,
"Y": 179143.6022597326
},
{
"id": "NBB-29",
"X": 260162.3349768046,
"Y": 179177.2095564581
},
{
"id": "NBB-30",
"X": 260115.11201831407,
"Y": 179163.3224655297
},
{
"id": "NBB-31",
"X": 260052.92384203407,
"Y": 179154.4466218247
},
{
"id": "NBB-32",
"X": 260004.4464295526,
"Y": 179155.3905176037
},
{
"id": "NBB-33",
"X": 259953.9248062317,
"Y": 179147.6435384383
},
{
"id": "NBB-34",
"X": 259903.7958026133,
"Y": 179135.2767765673
},
{
"id": "NBB-35",
"X": 259855.9217242609,
"Y": 179124.5131579286
},
{
"id": "NBB-36",
"X": 259804.9274191266,
"Y": 179111.1866481418
}
],
"borehole_issues": [],
"piers": [
{
"id": "P1",
"X": 259865.676,
"Y": 179138.094,
"axis_dir": [
0.16722,
-0.98592
],
"ground": {
"center": 29.355,
"corners": [
29.536,
29.441,
29.18,
29.27
],
"min": 29.18,
"max": 29.536,
"used": 29.355,
"source": "계산",
"dtm_spacing": 19.0,
"excel": 28.931,
"diff_vs_excel": 0.424
},
"footing": {
"L_mm": 10400.0,
"W_mm": 8150.0,
"top_el": 28.208,
"bottom_el": 26.208,
"cover": 0.972
},
"pile": {
"tip_el": 2.208,
"d_mm": 609.6
},
"soil": null,
"alerts": [
{
"code": "W09",
"level": "경고",
"pier": "P1",
"message": "엑셀 지반고 28.931와 지형 29.355의 차이 +0.424 m가 허용 0.3 m를 넘습니다."
},
{
"code": "I05",
"level": "정보",
"pier": "P1",
"message": "시추 자료가 없어 지층을 판정하지 않았습니다."
}
]
},
{
"id": "P2",
"X": 259914.439,
"Y": 179149.112,
"axis_dir": [
0.167151,
-0.985931
],
"ground": {
"center": 28.167,
"corners": [],
"min": 28.167,
"max": 28.167,
"used": 28.167,
"source": "계산",
"dtm_spacing": 19.5,
"excel": null,
"diff_vs_excel": null
},
"footing": {
"L_mm": null,
"W_mm": null,
"top_el": null,
"bottom_el": null,
"cover": null
},
"pile": {
"tip_el": null,
"d_mm": null
},
"soil": null,
"alerts": [
{
"code": "I05",
"level": "정보",
"pier": "P2",
"message": "시추 자료가 없어 지층을 판정하지 않았습니다."
}
]
},
{
"id": "P3",
"X": 259963.44,
"Y": 179159.017,
"axis_dir": [
0.167117,
-0.985937
],
"ground": {
"center": 28.12,
"corners": [],
"min": 28.12,
"max": 28.12,
"used": 28.12,
"source": "계산",
"dtm_spacing": 48.6,
"excel": null,
"diff_vs_excel": null
},
"footing": {
"L_mm": null,
"W_mm": null,
"top_el": null,
"bottom_el": null,
"cover": null
},
"pile": {
"tip_el": null,
"d_mm": null
},
"soil": null,
"alerts": [
{
"code": "I07",
"level": "정보",
"pier": "P3",
"message": "지반고를 만든 지형 점이 최대 48.6 m 떨어져 있습니다 (기준 30 m). 현장 측량값을 확인하세요."
},
{
"code": "I05",
"level": "정보",
"pier": "P3",
"message": "시추 자료가 없어 지층을 판정하지 않았습니다."
}
]
},
{
"id": "P4",
"X": 260012.654,
"Y": 179167.802,
"axis_dir": [
0.167117,
-0.985937
],
"ground": {
"center": 28.608,
"corners": [],
"min": 28.608,
"max": 28.608,
"used": 28.608,
"source": "계산",
"dtm_spacing": 44.3,
"excel": null,
"diff_vs_excel": null
},
"footing": {
"L_mm": null,
"W_mm": null,
"top_el": null,
"bottom_el": null,
"cover": null
},
"pile": {
"tip_el": null,
"d_mm": null
},
"soil": null,
"alerts": [
{
"code": "I07",
"level": "정보",
"pier": "P4",
"message": "지반고를 만든 지형 점이 최대 44.3 m 떨어져 있습니다 (기준 30 m). 현장 측량값을 확인하세요."
},
{
"code": "I05",
"level": "정보",
"pier": "P4",
"message": "시추 자료가 없어 지층을 판정하지 않았습니다."
}
]
},
{
"id": "P5",
"X": 260062.055,
"Y": 179175.464,
"axis_dir": [
0.1671,
-0.98594
],
"ground": {
"center": 27.242,
"corners": [],
"min": 27.242,
"max": 27.242,
"used": 27.242,
"source": "계산",
"dtm_spacing": 2.7,
"excel": null,
"diff_vs_excel": null
},
"footing": {
"L_mm": null,
"W_mm": null,
"top_el": null,
"bottom_el": null,
"cover": null
},
"pile": {
"tip_el": null,
"d_mm": null
},
"soil": null,
"alerts": [
{
"code": "I05",
"level": "정보",
"pier": "P5",
"message": "시추 자료가 없어 지층을 판정하지 않았습니다."
}
]
},
{
"id": "P6",
"X": 260116.582,
"Y": 179182.59,
"axis_dir": [
0.167062,
-0.985946
],
"ground": {
"center": 28.766,
"corners": [],
"min": 28.766,
"max": 28.766,
"used": 28.766,
"source": "계산",
"dtm_spacing": 14.3,
"excel": null,
"diff_vs_excel": null
},
"footing": {
"L_mm": null,
"W_mm": null,
"top_el": null,
"bottom_el": null,
"cover": null
},
"pile": {
"tip_el": null,
"d_mm": null
},
"soil": null,
"alerts": [
{
"code": "I05",
"level": "정보",
"pier": "P6",
"message": "시추 자료가 없어 지층을 판정하지 않았습니다."
}
]
}
]
}
+7 -17
View File
@@ -1,4 +1,3 @@
import { readFileSync } from 'node:fs';
import { describe, expect, it } from 'vitest';
import { groundElFor, parseGroundModel } from '../src/core/ground-model';
import { changedKeys, overrideKeys, revertDraft, type PierValues } from '../src/core/changes';
@@ -89,26 +88,17 @@ describe('지형 격자 (schema 2)', () => {
});
});
describe('번호 없는 옛 지반 모델 (B′: 임시 경로)', () => {
// examples/out/ground_model.json as written before the schema number existed (P1–P6, same values as the report).
const LEGACY = readFileSync(new URL('./fixtures/ground-model-legacy.json', import.meta.url), 'utf-8');
it('reads the unversioned file and reports it as schema 1', () => {
const m = parseGroundModel(LEGACY);
expect(m.schema).toBe(2); // normalised to the current schema
expect(m.piers.map((p) => p.id)).toEqual(['P1', 'P2', 'P3', 'P4', 'P5', 'P6']);
expect(groundElFor(m, 'P1')).toBe(29.355);
expect(m.piers[0].excelEl).toBe(28.931);
describe('형식 번호 검사', () => {
it('refuses a file with no schema number (the B′ fallback is removed)', () => {
expect(() => parseGroundModel(JSON.stringify({ piers: [{ id: 'P1', ground: { center: 29.355, excel: 28.931 } }] }))).toThrow('형식 번호 undefined');
});
it('refuses a pier that has no ground object (a renamed field must not load as empty values)', () => {
const data = JSON.parse(LEGACY);
delete data.piers[1].ground;
expect(() => parseGroundModel(JSON.stringify(data))).toThrow('P2: 지반 값(ground)이 없습니다');
it('refuses a null schema number', () => {
expect(() => parseGroundModel('{"schema": null, "piers": []}')).toThrow('형식 번호 null');
});
it('refuses a null schema number instead of treating it as missing', () => {
expect(() => parseGroundModel(LEGACY.replace('{', '{"schema": null,'))).toThrow('형식 번호 null');
it('refuses a pier that has no ground object', () => {
expect(() => parseGroundModel(JSON.stringify({ schema: 1, piers: [{ id: 'P2' }] }))).toThrow('P2: 지반 값(ground)이 없습니다');
});
});