From 1e596846cd0808719eb378e0beb3ae2d07a83706 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 16:42:32 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat(B06):=20=EC=A0=88=ED=86=A0=20=EA=B2=BD?= =?UTF-8?q?=EC=82=AC=20=ED=8C=90=EC=A0=95=20=EC=95=94=EC=A7=88=EC=9D=84=20?= =?UTF-8?q?=EC=84=A4=EA=B3=84=EC=9E=90=EA=B0=80=20=EA=B3=A0=EB=A5=B4?= =?UTF-8?q?=EA=B2=8C=20=E2=80=94=20=EA=B8=B0=EB=B3=B8=20=EA=B2=BD=EC=95=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 확정(2026-09-07): 「연암과 경암도 별도의 버튼으로 선택하게 하고 경암을 기본값으로 선택. 리핑을 할지 발파를 할지는 설계자가 선택 필요함.」 **축이 둘이었음 — 섞지 말 것.** · **암질**(연암·경암) = 별표2 경사 판정의 기준. 설계자가 고름. 기본 **경암**. · **굴착 공법**(리핑암·발파암, `cut_rock_kind`) = 어떻게 파는가. 수량·단가 몫. 처음에는 공법에서 암질을 유추하려 했는데 **그것이 잘못 세운 문제**였음. 매핑을 걷어내고 암질 선택 하나로 바꿈 — 표준 횡단면 설정 안, 자리는 그대로. - config 매핑 상수 → `FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT = "hard_rock"`. - 판정에서 `cut_rock_kind` 를 뗌. 수량 쪽 쓰임은 그대로 둠. - 실측(용화 63측점·구간 124개) — 기본값 경암에서 **위반 0건**. config 기본 절토비 1:0.4 가 경암 범위(0.3~0.8) 안임. 토사 63구간도 0건. **기존 설계·수량 안 바뀜.** (연암으로 고르면 61건 — 1:0.4 가 연암 하한 0.5 밖이라 그때는 설계 검토가 필요함.) 시험 8건 — 별표2 값 · 작업임도 제외 · **암질은 설계자가 고르고 기본은 경암** · **공법으로 암질을 추론하지 않음** · 구간별 판정 · 소단이 있어도 위반이 안 사라짐 · 경계값 통과 · 카드에 같은 방식으로 붙는지. 7-3 회귀에 소단 한 줄 더함 — 「소단은 값이 아니라 **기하 입력**이라 계산 뒤에 베껴 붙이면 `berm` 값만 남고 계단이 안 그려진다」. 저장분 소단을 **계산 전에** 읽어 넣는 순서를 지킴. Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_Api_Types.ts | 15 +++-- B06_Section/B06_Section_Cut_Slope_Check.ts | 22 +++--- B06_Section/B06_Section_Router.py | 4 +- B06_Section/B06_Section_Schema.py | 2 +- B06_Section/B06_Section_UI_Standard_Panel.ts | 70 +++++++++----------- config/config_system_design.py | 16 ++--- 6 files changed, 62 insertions(+), 67 deletions(-) diff --git a/B06_Section/B06_Section_Api_Types.ts b/B06_Section/B06_Section_Api_Types.ts index 590c76c5..c2493a9a 100644 --- a/B06_Section/B06_Section_Api_Types.ts +++ b/B06_Section/B06_Section_Api_Types.ts @@ -46,12 +46,13 @@ export interface StandardCrossGroup { export type StandardCrossKey = "soil" | "rock" | "paved"; export type StandardCrossSection = Record & { - /** 절토 법정 기울기 판정에 쓸 별표2 줄 — 지반유형(리핑암·발파암) → `soft_rock`/`hard_rock`. + /** 절토 경사 판정에 쓸 **암질** — `hard_rock`(경암) / `soft_rock`(연암). 기본 경암. * - * 별표2 는 암을 **경암·연암**으로 가르고 프로그램은 **리핑암·발파암**으로 가르는데 둘을 잇는 - * 문장이 법령·교본에 없다. 그래서 법정 근거가 아니라 **사용자가 고르는 설정**으로 두고 - * 표준단면 설정과 함께 저장한다(2026-09-07 사용자 확정). 없으면 기본값을 쓴다. */ - cut_slope_class?: Record; + * ⚠ 굴착 공법(리핑암·발파암, `cut_rock_kind`)과 **다른 축**이다. 공법은 수량·단가 몫이고 + * 암질은 별표2 경사 판정의 기준이며, 한쪽에서 다른 쪽을 추론하지 않는다 + * (2026-09-07 사용자 확정 — 「리핑을 할지 발파를 할지는 설계자가 선택」). + * 표준단면 설정과 함께 저장된다. 없으면 기본값(경암). */ + rock_quality?: string; }; // 지반유형·토량환산계수·운반장비 한계거리는 B05 계획 유토곡선과 공유하므로 정의처를 @@ -87,8 +88,8 @@ export interface SectionContextResponse { /** 절토 비탈 법정 기울기 범위(별표2) — `hard_rock`·`soft_rock`·`soil` → [최소, 최대]. * 상수를 화면에 복제하지 않으려고 **서버가 준 값을 그대로** 쓴다(2026-09-07). */ cut_slope_limits?: Record; - /** 지반유형(리핑암·발파암) → 별표2 줄. **사용자가 바꿀 수 있는 설정값**의 기본값이다. */ - cut_slope_class_default?: Record; + /** 절토 경사 판정에 쓸 **암질** 기본값(`hard_rock`/`soft_rock`). 설계자가 고른다. */ + cut_slope_rock_quality_default?: string; /** 절토 기울기 규정이 없는 등급(작업임도). */ cut_slope_exempt_grades?: string[]; /** 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m). */ diff --git a/B06_Section/B06_Section_Cut_Slope_Check.ts b/B06_Section/B06_Section_Cut_Slope_Check.ts index ba64fa88..a3371c42 100644 --- a/B06_Section/B06_Section_Cut_Slope_Check.ts +++ b/B06_Section/B06_Section_Cut_Slope_Check.ts @@ -24,8 +24,9 @@ export interface CutSlopeLimit { export interface CutSlopeCriteria { /** 별표2 범위표 — `hard_rock`·`soft_rock`·`soil`. */ limits: Record; - /** 지반유형(리핑암·발파암) → 별표2 줄. 사용자가 바꿀 수 있는 **설정값**이다. */ - classOf: Record; + /** 절토 경사 판정에 쓸 **암질** — `hard_rock`(경암) / `soft_rock`(연암). 설계자가 고른다. + * ⚠ 굴착 공법(리핑암·발파암)과 다른 축이라 공법에서 추론하지 않는다(2026-09-07 사용자 확정). */ + rockQuality: string; /** 절토 기울기 규정이 없는 등급(작업임도). */ exemptGrades: ReadonlyArray; } @@ -52,7 +53,7 @@ export function criteriaFrom(context: SectionContextResponse | null): CutSlopeCr } return { limits: table, - classOf: context?.cut_slope_class_default ?? {}, + rockQuality: context?.cut_slope_rock_quality_default ?? "hard_rock", exemptGrades: context?.cut_slope_exempt_grades ?? [], }; } @@ -62,7 +63,7 @@ export function criteriaFrom(context: SectionContextResponse | null): CutSlopeCr * * · 등급이 면제(작업임도)면 빈 목록. * · 구간이 없으면(성토 측점 등) 빈 목록. - * · 암 구간은 측점의 `cut_rock_kind` 로 별표2 줄을 고른다 — 구간에는 `rock`/`soil` 만 있다. + * · 암 구간은 **설계자가 고른 암질**로 판정한다 — 굴착 공법(`cut_rock_kind`)은 보지 않는다. */ export function cutSlopeViolations( section: CrossSection, @@ -71,12 +72,11 @@ export function cutSlopeViolations( ): CutSlopeViolation[] { if (!criteria) return []; if (gradeClass && criteria.exemptGrades.includes(gradeClass)) return []; - const design = section.design as - { cut_slope_segments?: unknown; cut_rock_kind?: string | null } | undefined; + const design = section.design as { cut_slope_segments?: unknown } | undefined; const segments = design?.cut_slope_segments; if (!Array.isArray(segments) || !segments.length) return []; - const rockKey = criteria.classOf[design?.cut_rock_kind ?? ""] ?? "hard_rock"; + const rockKey = criteria.rockQuality || "hard_rock"; const out: CutSlopeViolation[] = []; for (const raw of segments) { const segment = raw as { @@ -125,10 +125,10 @@ export function rememberCutSlopeCriteria(context: SectionContextResponse | null) rememberedGrade = context?.road_type ?? null; } -/** 사용자가 고른 매핑(리핑암·발파암 → 별표2 줄)을 얹는다. 없으면 기본값 그대로. */ -export function applyCutSlopeClassChoice(choice: Record | null | undefined): void { - if (!remembered || !choice) return; - remembered = { ...remembered, classOf: { ...remembered.classOf, ...choice } }; +/** 설계자가 고른 암질을 얹는다. 없으면 기본값(경암) 그대로. */ +export function applyRockQualityChoice(quality: string | null | undefined): void { + if (!remembered || !quality) return; + remembered = { ...remembered, rockQuality: quality }; } /** 지금 측점의 위반 목록 — 기준이 없거나 면제 등급이면 빈 목록. */ diff --git a/B06_Section/B06_Section_Router.py b/B06_Section/B06_Section_Router.py index 60fb3384..57cb8470 100644 --- a/B06_Section/B06_Section_Router.py +++ b/B06_Section/B06_Section_Router.py @@ -80,9 +80,9 @@ from config.config_db import get_db_pool, run_with_connection from config.config_system import ( EARTHWORK_CONVERSION_FACTORS, EARTHWORK_HAUL_EQUIPMENT_LIMITS_M, - FOREST_ROAD_CUT_SLOPE_CLASS_DEFAULT, FOREST_ROAD_CUT_SLOPE_EXEMPT_GRADES, FOREST_ROAD_CUT_SLOPE_LIMITS, + FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT, FOREST_ROAD_MIN_WIDTH_M, NATURAL_SPOIL_MIN_GROUND_SLOPE, SECTION_VERTICAL_EXAGGERATION, @@ -147,7 +147,7 @@ async def get_section_context(project_id: UUID) -> SectionContextResponse | JSON cut_slope_limits={ key: list(value) for key, value in FOREST_ROAD_CUT_SLOPE_LIMITS.items() }, - cut_slope_class_default=dict(FOREST_ROAD_CUT_SLOPE_CLASS_DEFAULT), + cut_slope_rock_quality_default=FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT, cut_slope_exempt_grades=list(FOREST_ROAD_CUT_SLOPE_EXEMPT_GRADES), rock_boundary_default_offset_m=STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M, rock_boundary_step_m=STANDARD_ROCK_BOUNDARY_STEP_M, diff --git a/B06_Section/B06_Section_Schema.py b/B06_Section/B06_Section_Schema.py index c319150e..427fb1c3 100644 --- a/B06_Section/B06_Section_Schema.py +++ b/B06_Section/B06_Section_Schema.py @@ -246,7 +246,7 @@ class SectionContextResponse(BaseModel): # 절토 비탈 법정 기울기 범위(별표2)와 지반유형 → 별표2 줄 기본 매핑, 검사 제외 등급. # 브라우저가 상수를 복제하지 않게 **서버가 준 값을 그대로 기억**한다(표준단면과 같은 방식). cut_slope_limits: dict[str, list[float]] = Field(default_factory=dict) - cut_slope_class_default: dict[str, str] = Field(default_factory=dict) + cut_slope_rock_quality_default: str = "hard_rock" cut_slope_exempt_grades: list[str] = Field(default_factory=list) # 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m). rock_boundary_default_offset_m: float = -0.5 diff --git a/B06_Section/B06_Section_UI_Standard_Panel.ts b/B06_Section/B06_Section_UI_Standard_Panel.ts index dc670c3d..f9a81493 100644 --- a/B06_Section/B06_Section_UI_Standard_Panel.ts +++ b/B06_Section/B06_Section_UI_Standard_Panel.ts @@ -26,7 +26,7 @@ import { } from "../A00_Common/b_page_state"; import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { createButton, createInputField, createSelectField } from "@ui/ui_template_elements"; -import { applyCutSlopeClassChoice } from "./B06_Section_Cut_Slope_Check"; +import { applyRockQualityChoice } from "./B06_Section_Cut_Slope_Check"; import { buildStandardDiagram } from "./B06_Section_UI_Standard_Diagram"; import { getCompanyStandard, @@ -397,46 +397,40 @@ export function createStandardPanel( } actions.append(resetButton); - // ── 절토 법정 기울기 판정 기준 (2026-09-07 사용자 확정) ────────────────── - // 별표2 는 암을 **경암·연암**으로 가르고 프로그램은 **리핑암·발파암**으로 가른다. 둘을 - // 잇는 문장이 법령·교본에 없어 **프로그램 설정**으로 두고 여기서 고르게 한다. 기본값은 - // 리핑암 → 연암 · 발파암 → 경암(자연스러운 읽기). 절토 경사비가 오는 자리 옆이라 여기 둔다. - const cutClassWrap = document.createElement("div"); - cutClassWrap.className = "b06-std__cutclass"; - const cutClassTitle = document.createElement("p"); - cutClassTitle.className = "b06-std__cutclass-title"; - cutClassTitle.textContent = "절토 법정 기울기 판정 기준 (별표2)"; - cutClassTitle.title = - "별표2: 경암 1:0.3~0.8 · 연암 1:0.5~1.2 · 토사 1:0.8~1.5. 작업임도는 규정 없음."; - cutClassWrap.append(cutClassTitle); - const CUT_CLASS_OPTIONS = [ - { value: "soft_rock", text: "연암 (1:0.5~1.2)" }, - { value: "hard_rock", text: "경암 (1:0.3~0.8)" }, - ]; - for (const [kind, label] of [ - ["ripping_rock", "리핑암"], - ["blasting_rock", "발파암"], - ] as const) { - const current = - state.cut_slope_class?.[kind] ?? (kind === "ripping_rock" ? "soft_rock" : "hard_rock"); - const field = createSelectField({ - label, - options: CUT_CLASS_OPTIONS, - value: current, - onChange: (value) => { - state.cut_slope_class = { ...(state.cut_slope_class ?? {}), [kind]: value }; - persist(); - applyCutSlopeClassChoice(state.cut_slope_class); - onApplyAll?.(); - }, - }); - cutClassWrap.append(field.root); - } + // ── 절토 법정 기울기 판정 기준: **암질** (2026-09-07 사용자 확정) ──────────── + // 「연암과 경암도 별도의 버튼으로 선택하게 하고 경암을 기본값으로. 리핑을 할지 발파를 + // 할지는 설계자가 선택 필요함」 — 축이 둘이다. **암질**(연암·경암)은 별표2 경사 판정의 + // 기준이고, **굴착 공법**(리핑암·발파암)은 수량·단가 몫이다. 한쪽에서 다른 쪽을 추론하지 + // 않는다. 절토 경사비가 오는 자리 옆이라 여기 둔다. + const rockQualityWrap = document.createElement("div"); + rockQualityWrap.className = "b06-std__cutclass"; + const rockQualityTitle = document.createElement("p"); + rockQualityTitle.className = "b06-std__cutclass-title"; + rockQualityTitle.textContent = "절토 법정 기울기 판정 암질 (별표2)"; + rockQualityTitle.title = + "별표2: 경암 1:0.3~0.8 · 연암 1:0.5~1.2 · 토사 1:0.8~1.5. 작업임도는 규정 없음.\n" + + "굴착 공법(리핑·발파)과는 다른 값이다 — 공법은 수량·단가에서 따로 고른다."; + rockQualityWrap.append(rockQualityTitle); + const rockQualityField = createSelectField({ + label: "암질", + options: [ + { value: "hard_rock", text: "경암 (1:0.3~0.8)" }, + { value: "soft_rock", text: "연암 (1:0.5~1.2)" }, + ], + value: state.rock_quality ?? "hard_rock", + onChange: (value) => { + state.rock_quality = value; + persist(); + applyRockQualityChoice(value); + onApplyAll?.(); + }, + }); + rockQualityWrap.append(rockQualityField.root); // 처음 세울 때도 고른 값을 검사에 반영한다(세션에 남아 있던 선택 포함). - applyCutSlopeClassChoice(state.cut_slope_class); + applyRockQualityChoice(state.rock_quality ?? "hard_rock"); // 횡단 반폭은 사이드의 **별도 컨테이너**로 뺐다(2026-08-23) — 여기는 표준단면 설정만. - root.append(body, cutClassWrap, loader, actions); + root.append(body, rockQualityWrap, loader, actions); return { root, diff --git a/config/config_system_design.py b/config/config_system_design.py index bfb4360d..d430961c 100644 --- a/config/config_system_design.py +++ b/config/config_system_design.py @@ -58,14 +58,14 @@ FOREST_ROAD_CUT_SLOPE_LIMITS = { "soft_rock": (0.5, 1.2), # 암석지 — 연암 "soil": (0.8, 1.5), # 토사지역 } -# 프로그램 지반유형(리핑암·발파암) → 별표2 줄(연암·경암). -# **기본값이며 사용자가 바꿀 수 있다**(2026-09-07 사용자 확정 — 「사용자가 제어할 수 있게」). -# 별표2 는 경암·연암으로 가르고 프로그램은 리핑암·발파암으로 가르는데, 둘을 잇는 문장이 -# 법령·교본에 없다. 그래서 법정 근거가 아니라 **프로그램 설정**으로 두고 화면에서 고르게 한다. -FOREST_ROAD_CUT_SLOPE_CLASS_DEFAULT = { - "ripping_rock": "soft_rock", - "blasting_rock": "hard_rock", -} +# 절토 경사 판정에 쓸 **암질** 기본값 (2026-09-07 사용자 확정). +# +# ⚠ 축이 둘이다 — 섞지 말 것. +# · **암질**(연암·경암) = 별표2 경사 판정의 기준. 설계자가 고른다. 기본값 **경암**. +# · **굴착 공법**(리핑암·발파암, `cut_rock_kind`) = 어떻게 파는가. 수량·단가 몫이다. +# 한쪽에서 다른 쪽을 **추론하지 않는다**(사용자: 「리핑을 할지 발파를 할지는 설계자가 선택」). +# 처음에는 공법에서 암질을 유추하려 했으나 그것이 잘못 세운 문제였다. +FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT = "hard_rock" # 절토 기울기 규정이 없는 등급 — 작업임도(별표2 §1 「작업임도: 절토 기울기 표 규정 없음」). FOREST_ROAD_CUT_SLOPE_EXEMPT_GRADES = ("work",) From 36f46a0d940ecc86c9e37ad2f187f921b5176b01 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 16:43:08 +0900 Subject: [PATCH 2/4] auto: 2026-09-07 16:43 (EOMSANGDON-HOME) --- .gitattributes | 3 +++ .gitignore | 2 ++ package.json | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..c768e8b7 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# 동봉한 LibreDWG 실행 파일 — 줄바꿈 변환이 닿으면 실행이 깨진다. +B07_DesignDetail/openwebcad/tools/libredwg/*.exe binary +B07_DesignDetail/openwebcad/tools/libredwg/*.dll binary diff --git a/.gitignore b/.gitignore index 5e816280..e1a96c2c 100644 --- a/.gitignore +++ b/.gitignore @@ -58,3 +58,5 @@ tmp/ # 코리도 서버 사전 생성 번들 — `npm run build:corridor` 산출물(2026-09-04). # 소스가 바뀌면 서버가 스스로 다시 만든다(B05_Profile_Corridor_Prebuild.py). config/corridor_node/ +# 횡단 서버 재계산 번들 — `npm run build:server-calc` 산출물(2026-09-06). +config/server_calc_node/ diff --git a/package.json b/package.json index a5e66137..7e20859b 100644 --- a/package.json +++ b/package.json @@ -5,8 +5,9 @@ "type": "module", "scripts": { "dev": "node ./config/node_modules/vite/bin/vite.js --configLoader runner", - "build": "node ./config/node_modules/typescript/bin/tsc --noEmit && node ./config/node_modules/vite/bin/vite.js build --configLoader runner && npm run build:corridor && npm run build:b07-cad", + "build": "node ./config/node_modules/typescript/bin/tsc --noEmit && node ./config/node_modules/vite/bin/vite.js build --configLoader runner && npm run build:corridor && npm run build:server-calc && npm run build:b07-cad", "build:corridor": "node ./config/node_modules/vite/bin/vite.js build --configLoader runner --ssr ../B05_Profile/B05_Profile_Corridor_Node.ts --outDir ../config/corridor_node", + "build:server-calc": "node ./config/node_modules/vite/bin/vite.js build --configLoader runner --ssr ../B06_Section/B06_Section_Server_Calc_Node.ts --outDir ../config/server_calc_node", "install:b07-cad": "npm --prefix B07_DesignDetail/openwebcad install", "build:b07-cad": "npm run install:b07-cad && npm --prefix B07_DesignDetail/openwebcad run build", "preview": "node ./config/node_modules/vite/bin/vite.js preview --configLoader runner", From cc428d7e12fc05387397611c4e33c6dfdf011024 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 16:56:26 +0900 Subject: [PATCH 3/4] =?UTF-8?q?revert(B06):=20=EC=A0=88=ED=86=A0=20?= =?UTF-8?q?=EA=B2=BD=EC=82=AC=20=EB=B2=95=EC=A0=95=20=EA=B2=80=EC=82=AC=20?= =?UTF-8?q?=ED=8F=90=EA=B8=B0=20=E2=80=94=20=EC=95=94=EC=A7=88=EC=9D=84=20?= =?UTF-8?q?=ED=9A=A1=EB=8B=A8=EB=8F=84=EC=97=90=EC=84=9C=20=EA=B3=A0?= =?UTF-8?q?=EB=A5=B4=EC=A7=80=20=EC=95=8A=EC=9D=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 사용자 확정(2026-09-07): 「연암과 경암 / 발파암과 리핑암 선택은 횡단도에서 선택 안함. 사유는 향후 설계내역에서 설계자가 직접 비율로 지정하기로 함. 암반 지정과 범위 애매모호한 경우가 있어. 실무자는 그렇게 하기로 판단함. 대신 경고부분은 삭제해주고 대신 각도를 사용자가 넣을수 있게 반영.」 **왜 폐기인가** — 판정이 서려면 측점마다 암질(연암·경암)을 못 박아야 하는데, 실무에서 암반 지정·범위가 애매해 그 못 박음 자체가 틀린 전제였음. 암 비율은 **설계내역 단계에서 설계자가 비율로** 넣음. 기준이 없으니 경고도 없음. 되돌린 것 — 1e596846(암질 선택) + 973128f7(별표2 경고). 카드 경고 배지 · 판정 모듈 · config 범위표 · context 필드 · 표준 패널 암질 칸 전부 사라짐. ⚠ **남긴 것** — `cut_slope_segments`(구간별 경사)는 지우지 않음. 소단 기하가 그 위에 서 있고 구간 경사를 아는 값이라 뒤에 쓸 자리가 있음. 4e7a4bf7(사면 구간 재료를 그린 경사비로 가름 — 무릎 위 라벨 13구간 오류 수정)도 남김. 검사와 무관하게 옳은 고침임. 시험 4건 — 되살아나지 않게 막는 쪽으로 다시 씀(판정 모듈 없음 · 카드 경고 없음 · 암질 고르는 자리 없음 · **구간별 경사값은 남아 있음**). Co-Authored-By: Claude Opus 5 (1M context) --- B06_Section/B06_Section_Api_Types.ts | 17 +-- B06_Section/B06_Section_Cut_Slope_Check.ts | 137 ------------------ B06_Section/B06_Section_Router.py | 8 - B06_Section/B06_Section_Schema.py | 5 - .../B06_Section_UI_Cross_Card_Chrome.ts | 21 --- B06_Section/B06_Section_UI_Page.ts | 3 - B06_Section/B06_Section_UI_Standard_Panel.ts | 35 +---- config/config_system_design.py | 23 --- 8 files changed, 2 insertions(+), 247 deletions(-) delete mode 100644 B06_Section/B06_Section_Cut_Slope_Check.ts diff --git a/B06_Section/B06_Section_Api_Types.ts b/B06_Section/B06_Section_Api_Types.ts index c2493a9a..42dacd1b 100644 --- a/B06_Section/B06_Section_Api_Types.ts +++ b/B06_Section/B06_Section_Api_Types.ts @@ -45,15 +45,7 @@ export interface StandardCrossGroup { /** 표준 횡단면 설정 패널 그룹 키. */ export type StandardCrossKey = "soil" | "rock" | "paved"; -export type StandardCrossSection = Record & { - /** 절토 경사 판정에 쓸 **암질** — `hard_rock`(경암) / `soft_rock`(연암). 기본 경암. - * - * ⚠ 굴착 공법(리핑암·발파암, `cut_rock_kind`)과 **다른 축**이다. 공법은 수량·단가 몫이고 - * 암질은 별표2 경사 판정의 기준이며, 한쪽에서 다른 쪽을 추론하지 않는다 - * (2026-09-07 사용자 확정 — 「리핑을 할지 발파를 할지는 설계자가 선택」). - * 표준단면 설정과 함께 저장된다. 없으면 기본값(경암). */ - rock_quality?: string; -}; +export type StandardCrossSection = Record; // 지반유형·토량환산계수·운반장비 한계거리는 B05 계획 유토곡선과 공유하므로 정의처를 // `@util/common_util_mass_haul_types` 한 곳에 두고 여기서는 재수출만 한다(사본 금지). @@ -85,13 +77,6 @@ export interface SectionContextResponse { /** 이 프로젝트에 **저장된** 표준 횡단면(사용자가 고쳐 확정한 값). 없으면 null. * 위 `standard_cross_section` 은 config 기본값이라 둘은 다른 것이다(2026-09-07). */ stored_standard_cross_section?: StandardCrossSection | null; - /** 절토 비탈 법정 기울기 범위(별표2) — `hard_rock`·`soft_rock`·`soil` → [최소, 최대]. - * 상수를 화면에 복제하지 않으려고 **서버가 준 값을 그대로** 쓴다(2026-09-07). */ - cut_slope_limits?: Record; - /** 절토 경사 판정에 쓸 **암질** 기본값(`hard_rock`/`soft_rock`). 설계자가 고른다. */ - cut_slope_rock_quality_default?: string; - /** 절토 기울기 규정이 없는 등급(작업임도). */ - cut_slope_exempt_grades?: string[]; /** 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m). */ rock_boundary_default_offset_m: number; rock_boundary_step_m: number; diff --git a/B06_Section/B06_Section_Cut_Slope_Check.ts b/B06_Section/B06_Section_Cut_Slope_Check.ts deleted file mode 100644 index a3371c42..00000000 --- a/B06_Section/B06_Section_Cut_Slope_Check.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* ============================================================================= - * B06_Section_Cut_Slope_Check.ts - * 절토 비탈 **법정 기울기 검사**(별표2) — 순수 판정만 둔다(그리기는 카드 몫). - * - * 왜 필요한가 — 평면 곡선반경도, 성토사면 길이(5m·기슭막이 의무)도 위반 표시가 있는데 - * **절토 경사만 검사가 없었다**(2026-09-07). 절토 경사비는 표준 횡단면 설정에서 오는 - * 입력값일 뿐이라 별표2 범위를 벗어나도 아무 표시가 없었다. - * - * ⚠ **실효 경사로 보면 안 된다.** 소단이 서면 사면 전체를 하나로 잰 경사가 완만해져 - * **위반이 사라진 것처럼** 보인다(실측: 폭 1.0·간격 2 이면 설계 1:1 이 실효 1:1.71). - * 그래서 `design.cut_slope_segments`(소단을 뺀 **구간별** 경사)를 읽는다. - * - * 기준값·매핑은 **서버가 준 값**을 쓴다(config 상수를 화면에 복제하지 않는다). - * ========================================================================== */ - -import type { CrossSection, SectionContextResponse } from "./B06_Section_Api_Fetch"; - -/** 별표2 한 줄 — 수직 1 에 대한 수평(1:n 의 n) 범위. */ -export interface CutSlopeLimit { - min: number; - max: number; -} - -export interface CutSlopeCriteria { - /** 별표2 범위표 — `hard_rock`·`soft_rock`·`soil`. */ - limits: Record; - /** 절토 경사 판정에 쓸 **암질** — `hard_rock`(경암) / `soft_rock`(연암). 설계자가 고른다. - * ⚠ 굴착 공법(리핑암·발파암)과 다른 축이라 공법에서 추론하지 않는다(2026-09-07 사용자 확정). */ - rockQuality: string; - /** 절토 기울기 규정이 없는 등급(작업임도). */ - exemptGrades: ReadonlyArray; -} - -export interface CutSlopeViolation { - side: string; - ratio: number; - /** 어느 별표2 줄로 판정했나 — 툴팁에 적는다. */ - limitKey: string; - limit: CutSlopeLimit; - /** 급한 쪽 위반인지(`steep`) 완만한 쪽 위반인지(`gentle`). */ - kind: "steep" | "gentle"; - startOffsetM: number; - endOffsetM: number; -} - -/** 서버 컨텍스트에서 기준을 꺼낸다. 값이 안 왔으면 null — 검사를 하지 않는다. */ -export function criteriaFrom(context: SectionContextResponse | null): CutSlopeCriteria | null { - const limits = context?.cut_slope_limits; - if (!limits || !Object.keys(limits).length) return null; - const table: Record = {}; - for (const [key, pair] of Object.entries(limits)) { - if (Array.isArray(pair) && pair.length >= 2) table[key] = { min: pair[0], max: pair[1] }; - } - return { - limits: table, - rockQuality: context?.cut_slope_rock_quality_default ?? "hard_rock", - exemptGrades: context?.cut_slope_exempt_grades ?? [], - }; -} - -/** - * 이 측점의 절토 구간이 별표2 범위를 벗어났는지. - * - * · 등급이 면제(작업임도)면 빈 목록. - * · 구간이 없으면(성토 측점 등) 빈 목록. - * · 암 구간은 **설계자가 고른 암질**로 판정한다 — 굴착 공법(`cut_rock_kind`)은 보지 않는다. - */ -export function cutSlopeViolations( - section: CrossSection, - criteria: CutSlopeCriteria | null, - gradeClass: string | null, -): CutSlopeViolation[] { - if (!criteria) return []; - if (gradeClass && criteria.exemptGrades.includes(gradeClass)) return []; - const design = section.design as { cut_slope_segments?: unknown } | undefined; - const segments = design?.cut_slope_segments; - if (!Array.isArray(segments) || !segments.length) return []; - - const rockKey = criteria.rockQuality || "hard_rock"; - const out: CutSlopeViolation[] = []; - for (const raw of segments) { - const segment = raw as { - side?: string; - ratio?: number; - material?: string | null; - start_offset_m?: number; - end_offset_m?: number; - }; - const ratio = segment.ratio; - if (typeof ratio !== "number" || !Number.isFinite(ratio)) continue; - const limitKey = segment.material === "rock" ? rockKey : "soil"; - const limit = criteria.limits[limitKey]; - if (!limit) continue; - // 경계값은 통과 — 1:0.8 은 「0.3~0.8」 안이다. - if (ratio >= limit.min - 1e-9 && ratio <= limit.max + 1e-9) continue; - out.push({ - side: segment.side ?? "", - ratio, - limitKey, - limit, - kind: ratio < limit.min ? "steep" : "gentle", - startOffsetM: segment.start_offset_m ?? 0, - endOffsetM: segment.end_offset_m ?? 0, - }); - } - return out; -} - -/** 별표2 줄 이름 — 화면·툴팁에 적는 말. */ -export function limitLabel(key: string): string { - if (key === "hard_rock") return "경암"; - if (key === "soft_rock") return "연암"; - return "토사"; -} - -/* ── 기억해 둔 기준 ────────────────────────────────────────────────────────── - * 카드는 그리는 자리마다 컨텍스트를 들고 있지 않다. 표준단면 기본값을 기억해 두는 것과 - * 같은 방식으로(`rememberStandardDefaults`) 여기 한 번 담아 두고 카드가 읽는다. - * ------------------------------------------------------------------------ */ -let remembered: CutSlopeCriteria | null = null; -let rememberedGrade: string | null = null; - -export function rememberCutSlopeCriteria(context: SectionContextResponse | null): void { - remembered = criteriaFrom(context); - rememberedGrade = context?.road_type ?? null; -} - -/** 설계자가 고른 암질을 얹는다. 없으면 기본값(경암) 그대로. */ -export function applyRockQualityChoice(quality: string | null | undefined): void { - if (!remembered || !quality) return; - remembered = { ...remembered, rockQuality: quality }; -} - -/** 지금 측점의 위반 목록 — 기준이 없거나 면제 등급이면 빈 목록. */ -export function violationsOf(section: CrossSection): CutSlopeViolation[] { - return cutSlopeViolations(section, remembered, rememberedGrade); -} diff --git a/B06_Section/B06_Section_Router.py b/B06_Section/B06_Section_Router.py index 57cb8470..006ad692 100644 --- a/B06_Section/B06_Section_Router.py +++ b/B06_Section/B06_Section_Router.py @@ -80,9 +80,6 @@ from config.config_db import get_db_pool, run_with_connection from config.config_system import ( EARTHWORK_CONVERSION_FACTORS, EARTHWORK_HAUL_EQUIPMENT_LIMITS_M, - FOREST_ROAD_CUT_SLOPE_EXEMPT_GRADES, - FOREST_ROAD_CUT_SLOPE_LIMITS, - FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT, FOREST_ROAD_MIN_WIDTH_M, NATURAL_SPOIL_MIN_GROUND_SLOPE, SECTION_VERTICAL_EXAGGERATION, @@ -144,11 +141,6 @@ async def get_section_context(project_id: UUID) -> SectionContextResponse | JSON ), standard_cross_section=STANDARD_CROSS_SECTION, stored_standard_cross_section=stored_standard, - cut_slope_limits={ - key: list(value) for key, value in FOREST_ROAD_CUT_SLOPE_LIMITS.items() - }, - cut_slope_rock_quality_default=FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT, - cut_slope_exempt_grades=list(FOREST_ROAD_CUT_SLOPE_EXEMPT_GRADES), rock_boundary_default_offset_m=STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M, rock_boundary_step_m=STANDARD_ROCK_BOUNDARY_STEP_M, earthwork_conversion=EARTHWORK_CONVERSION_FACTORS, diff --git a/B06_Section/B06_Section_Schema.py b/B06_Section/B06_Section_Schema.py index 427fb1c3..9b8dac61 100644 --- a/B06_Section/B06_Section_Schema.py +++ b/B06_Section/B06_Section_Schema.py @@ -243,11 +243,6 @@ class SectionContextResponse(BaseModel): # **화면은 config 기본값으로, 서버는 저장분으로** 계산해 같은 측점이 갈렸다. # 기존 `standard_cross_section`(기본값)은 그대로 두고 **한 칸만 더한다**. stored_standard_cross_section: dict[str, Any] | None = None - # 절토 비탈 법정 기울기 범위(별표2)와 지반유형 → 별표2 줄 기본 매핑, 검사 제외 등급. - # 브라우저가 상수를 복제하지 않게 **서버가 준 값을 그대로 기억**한다(표준단면과 같은 방식). - cut_slope_limits: dict[str, list[float]] = Field(default_factory=dict) - cut_slope_rock_quality_default: str = "hard_rock" - cut_slope_exempt_grades: list[str] = Field(default_factory=list) # 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m). rock_boundary_default_offset_m: float = -0.5 rock_boundary_step_m: float = 0.1 diff --git a/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts b/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts index f716468a..d7cd8c5c 100644 --- a/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts +++ b/B06_Section/B06_Section_UI_Cross_Card_Chrome.ts @@ -13,7 +13,6 @@ import { buildRockBoundaryControl, sectionModeLabel, } from "./B06_Section_UI_Cross_Design"; -import { limitLabel, violationsOf } from "./B06_Section_Cut_Slope_Check"; import { type FillSlopeLength, fillSlopeLengths } from "./B06_Section_UI_Cross_Fit"; import { type DesignChangeHandler, @@ -86,26 +85,6 @@ export function appendCardHeader( openSlope.title = L("B06_Cross_SlopeUnclosed_Tip"); meta.append(openSlope); } - // 절토 비탈 법정 기울기(별표2) 위반 — 성토사면·미폐합 경고와 같은 자리·같은 모양이다. - // 소단이 서면 실효 경사가 완만해져 위반이 사라진 것처럼 보이므로, 판정은 **구간별** - // 경사(`cut_slope_segments`)로 한다(2026-09-07). - const cutViolations = violationsOf(section); - if (cutViolations.length) { - const worst = cutViolations[0]; - const badge = document.createElement("span"); - badge.className = "b06-cross-card__warning"; - badge.textContent = `⚠ 절토 1:${worst.ratio.toFixed(2)}`; - badge.title = cutViolations - .map( - (item) => - `${item.side === "left" ? "좌" : "우"} 1:${item.ratio.toFixed(2)}` + - ` — ${limitLabel(item.limitKey)} 법정 1:${item.limit.min}~${item.limit.max}` + - ` 범위 밖(${item.kind === "steep" ? "너무 급함" : "너무 완만함"})` + - ` · ${item.startOffsetM.toFixed(2)}~${item.endOffsetM.toFixed(2)}m`, - ) - .join("\n"); - meta.append(badge); - } const structureName = section.structure; if (structureName) { const structure = document.createElement("span"); diff --git a/B06_Section/B06_Section_UI_Page.ts b/B06_Section/B06_Section_UI_Page.ts index 890019f0..3877af0a 100644 --- a/B06_Section/B06_Section_UI_Page.ts +++ b/B06_Section/B06_Section_UI_Page.ts @@ -2,7 +2,6 @@ import { writeCrossDesignChoice } from "./B06_Section_Cross_Design_Session"; import { CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend"; import { leaveForDashboard } from "../A00_Common/b_missing_data_guard"; import { readByKey, stateKey, writeByKey } from "../A00_Common/b_page_state"; -import { rememberCutSlopeCriteria } from "./B06_Section_Cut_Slope_Check"; import { navigateTo } from "../A00_Common/router"; import { createButton, createInputField, showToast } from "@ui/ui_template_elements"; import type { StructureInstance, StructureType } from "../B05_Profile/B05_Profile_Api_Structures"; @@ -656,8 +655,6 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise { standardPanel = createStandardPanel(projectId, context.standard_cross_section, applyPanelToAll); // 브라우저 횡단 계산이 옛 암 측점을 서버와 같은 기본값으로 다시 계산하게 기억해 둔다. rememberRockBoundaryDefault(projectId, context.rock_boundary_default_offset_m); - // 절토 비탈 법정 기울기 기준(별표2)도 같은 방식으로 기억해 둔다 — 카드가 읽는다. - rememberCutSlopeCriteria(context); standardPanelSlot.append(standardPanel.root); if (context.route_id === null) { diff --git a/B06_Section/B06_Section_UI_Standard_Panel.ts b/B06_Section/B06_Section_UI_Standard_Panel.ts index f9a81493..9279ab3b 100644 --- a/B06_Section/B06_Section_UI_Standard_Panel.ts +++ b/B06_Section/B06_Section_UI_Standard_Panel.ts @@ -26,7 +26,6 @@ import { } from "../A00_Common/b_page_state"; import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { createButton, createInputField, createSelectField } from "@ui/ui_template_elements"; -import { applyRockQualityChoice } from "./B06_Section_Cut_Slope_Check"; import { buildStandardDiagram } from "./B06_Section_UI_Standard_Diagram"; import { getCompanyStandard, @@ -397,40 +396,8 @@ export function createStandardPanel( } actions.append(resetButton); - // ── 절토 법정 기울기 판정 기준: **암질** (2026-09-07 사용자 확정) ──────────── - // 「연암과 경암도 별도의 버튼으로 선택하게 하고 경암을 기본값으로. 리핑을 할지 발파를 - // 할지는 설계자가 선택 필요함」 — 축이 둘이다. **암질**(연암·경암)은 별표2 경사 판정의 - // 기준이고, **굴착 공법**(리핑암·발파암)은 수량·단가 몫이다. 한쪽에서 다른 쪽을 추론하지 - // 않는다. 절토 경사비가 오는 자리 옆이라 여기 둔다. - const rockQualityWrap = document.createElement("div"); - rockQualityWrap.className = "b06-std__cutclass"; - const rockQualityTitle = document.createElement("p"); - rockQualityTitle.className = "b06-std__cutclass-title"; - rockQualityTitle.textContent = "절토 법정 기울기 판정 암질 (별표2)"; - rockQualityTitle.title = - "별표2: 경암 1:0.3~0.8 · 연암 1:0.5~1.2 · 토사 1:0.8~1.5. 작업임도는 규정 없음.\n" + - "굴착 공법(리핑·발파)과는 다른 값이다 — 공법은 수량·단가에서 따로 고른다."; - rockQualityWrap.append(rockQualityTitle); - const rockQualityField = createSelectField({ - label: "암질", - options: [ - { value: "hard_rock", text: "경암 (1:0.3~0.8)" }, - { value: "soft_rock", text: "연암 (1:0.5~1.2)" }, - ], - value: state.rock_quality ?? "hard_rock", - onChange: (value) => { - state.rock_quality = value; - persist(); - applyRockQualityChoice(value); - onApplyAll?.(); - }, - }); - rockQualityWrap.append(rockQualityField.root); - // 처음 세울 때도 고른 값을 검사에 반영한다(세션에 남아 있던 선택 포함). - applyRockQualityChoice(state.rock_quality ?? "hard_rock"); - // 횡단 반폭은 사이드의 **별도 컨테이너**로 뺐다(2026-08-23) — 여기는 표준단면 설정만. - root.append(body, rockQualityWrap, loader, actions); + root.append(body, loader, actions); return { root, diff --git a/config/config_system_design.py b/config/config_system_design.py index d430961c..c239ab33 100644 --- a/config/config_system_design.py +++ b/config/config_system_design.py @@ -46,29 +46,6 @@ ROUTE_GRADE_CLASSES = ("trunk", "fire", "work", "branch") FOREST_ROAD_MAX_GRADE = {"trunk": 0.26, "fire": 0.26, "branch": 0.28, "work": 0.40} FOREST_ROAD_MIN_CURVE_R_M = {"trunk": 12.0, "fire": 12.0, "branch": 10.0, "work": 6.0} -# ───────────────────────────────────────────────────────────────────────── -# 절토 비탈 법정 기울기 (별표2) -# -# 근거 — 지식DB `resources/knowledge/technical_info/01_임도/02_상세설계/절토_비탈면.md` §1. -# 그 문서의 [구현] 줄대로 **판정은 별표2 범위로** 한다(실무 관행값은 후보일 뿐 기준이 아님). -# 값은 수직 1 에 대한 수평(1:n 의 n). -# ───────────────────────────────────────────────────────────────────────── -FOREST_ROAD_CUT_SLOPE_LIMITS = { - "hard_rock": (0.3, 0.8), # 암석지 — 경암 - "soft_rock": (0.5, 1.2), # 암석지 — 연암 - "soil": (0.8, 1.5), # 토사지역 -} -# 절토 경사 판정에 쓸 **암질** 기본값 (2026-09-07 사용자 확정). -# -# ⚠ 축이 둘이다 — 섞지 말 것. -# · **암질**(연암·경암) = 별표2 경사 판정의 기준. 설계자가 고른다. 기본값 **경암**. -# · **굴착 공법**(리핑암·발파암, `cut_rock_kind`) = 어떻게 파는가. 수량·단가 몫이다. -# 한쪽에서 다른 쪽을 **추론하지 않는다**(사용자: 「리핑을 할지 발파를 할지는 설계자가 선택」). -# 처음에는 공법에서 암질을 유추하려 했으나 그것이 잘못 세운 문제였다. -FOREST_ROAD_CUT_SLOPE_ROCK_QUALITY_DEFAULT = "hard_rock" -# 절토 기울기 규정이 없는 등급 — 작업임도(별표2 §1 「작업임도: 절토 기울기 표 규정 없음」). -FOREST_ROAD_CUT_SLOPE_EXEMPT_GRADES = ("work",) - # 대안(정속경사) 파라미터 ROUTE_ALT_MIN_GRADE = float(os.getenv("ROUTE_ALT_MIN_GRADE", "0.08")) ROUTE_ALT_MAX_GRADE = float(os.getenv("ROUTE_ALT_MAX_GRADE", "0.14")) From 674d4ef14a2bf2c422a0e926068c61d88ec28f23 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 7 Sep 2026 18:08:18 +0900 Subject: [PATCH 4/4] =?UTF-8?q?fix(=ED=86=A0=EA=B3=B5):=20=EB=8F=84?= =?UTF-8?q?=EC=9E=90=20=EC=9A=B4=EB=B0=98=20=ED=95=9C=EA=B3=84=EA=B1=B0?= =?UTF-8?q?=EB=A6=AC=2070m=20=E2=86=92=2060m=20=ED=99=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 근거 — 실무 오솔길 EARTH.DAT 헤더 6개 공사지가 전부 `20.0 / 60.0` (거창·장수·진안·봉화·영월 본선·지선). 산림과임업기술 5장 「다. 공사수량의 산출」도 「도저운반성토 60m 이하 / 덤프운반성토 60m 초과(건설표준품셈 참조)」로 규정. 70m 는 Aislo 단독값이었음(2026-08-02 잠정 확정). - config_system_design.py — EARTHWORK_HAUL_EQUIPMENT_LIMITS_M 의 dozer 경계와 주석의 확정 이력·근거 갱신. 정의처가 이 상수 한 곳이라 다른 코드 변경 없음. - common_util_mass_haul_balance.ts — 경계현 설명 주석의 70m 표기 정정. 검증 — 백엔드 재시작 뒤 공용 브라우저에서 실제 API 호출, sections/context 의 dozer.max_distance_m = 60 확인. 회귀 369 passed (실패 3건은 haul 미참조 기존 깨짐). 지식DB 미결 No.21 해소 — 목록 정리는 위키 AI 몫. Co-Authored-By: Claude Opus 5 (1M context) --- common_util/common_util_mass_haul_balance.ts | 2 +- config/config_system_design.py | 10 +++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/common_util/common_util_mass_haul_balance.ts b/common_util/common_util_mass_haul_balance.ts index 31c454a7..1396df92 100644 --- a/common_util/common_util_mass_haul_balance.ts +++ b/common_util/common_util_mass_haul_balance.ts @@ -38,7 +38,7 @@ * 즉 **수평선의 높이는 그 현(弦)의 길이가 장비 경계거리와 같아지는 높이**다. 누가토량 * 그래프에서 세로축이 곧 토량이므로, 두 수평선 사이 띠의 두께가 그 장비가 옮기는 토량이다. * 현 길이 20m 되는 높이 위쪽 → 종무대 - * 거기서 70m 되는 높이까지 → 도쟈 + * 거기서 60m 되는 높이까지 → 도쟈 * 그 아래 ~ 평형선 → 덤프 * 경계값 정의처는 `config_system.EARTHWORK_HAUL_EQUIPMENT_LIMITS_M` 한 곳뿐이다. * diff --git a/config/config_system_design.py b/config/config_system_design.py index c239ab33..6811f5c9 100644 --- a/config/config_system_design.py +++ b/config/config_system_design.py @@ -379,9 +379,13 @@ EARTHWORK_CONVERSION_FACTORS = { # 평균운반거리(m)로 운반장비를 고른다. 위에서부터 순서대로 검사해 `max_distance_m` # **이하**면 그 장비를 쓰고, 마지막 항목(None)이 나머지를 전부 받는다. # -# 종무대(무대운반) ≤ 20m < 도쟈(불도저 압토) ≤ 70m < 덤프트럭 +# 종무대(무대운반) ≤ 20m < 도쟈(불도저 압토) ≤ 60m < 덤프트럭 # -# 2026-08-02 사용자 확정값(도쟈 50 → 70m). 유토곡선의 장비 경계현은 이 값을 +# 2026-09-07 사용자 확정값(도쟈 70 → 60m). 근거 — 실무 오솔길 `EARTH.DAT` 헤더 6개 +# 공사지가 전부 `20.0 / 60.0`(거창·장수·진안·봉화·영월 본선·지선), 산림과임업기술 +# 5장 「다. 공사수량의 산출」이 「도저운반성토 60m 이하 / 덤프운반성토 60m 초과 +# (건설표준품셈 참조)」로 못 박음. 70m 는 Aislo 단독값이었음(2026-08-02 잠정 확정). +# 유토곡선의 장비 경계현은 이 값을 # **수평 현의 길이**로 읽는다 — 현 길이가 20m가 되는 높이 위쪽이 종무대 몫, # 70m가 되는 높이까지가 도쟈 몫, 그 아래가 덤프 몫이다. # 현장·발주처 기준에 따라 달라질 수 있으므로 @@ -393,7 +397,7 @@ EARTHWORK_CONVERSION_FACTORS = { # ───────────────────────────────────────────────────────────────────────── EARTHWORK_HAUL_EQUIPMENT_LIMITS_M = ( ("free_haul", 20.0), - ("dozer", 70.0), + ("dozer", 60.0), ("dump_truck", None), )