diff --git a/A00_Common/group_workflow.ts b/A00_Common/group_workflow.ts index 0fa1d7e21..1d6c0eabe 100644 --- a/A00_Common/group_workflow.ts +++ b/A00_Common/group_workflow.ts @@ -31,8 +31,15 @@ const FLOWS: readonly GroupFlow[] = [ }, { routes: STAB_STEP_ROUTES, - labels: ["DE_Step_StabGround", "DE_Step_StabSection", "DE_Step_Stab3D", "DE_Step_StabReport"], - icons: ["๐Ÿชจ", "๐Ÿ“", "๐ŸงŠ", "๐Ÿ“„"], + labels: [ + "DE_Step_StabGround", + "DE_Step_StabSection", + "DE_Step_Stab3D", + "DE_Step_StabReport", + "DE_Step_StabRock", + "DE_Step_StabArea", + ], + icons: ["๐Ÿชจ", "๐Ÿ“", "๐ŸงŠ", "๐Ÿ“„", "โ›ฐ๏ธ", "๐Ÿ—บ๏ธ"], }, ]; diff --git a/E01_StabRegister/E01_StabRegister_Router.py b/E01_StabRegister/E01_StabRegister_Router.py index a8f553d30..c356b8d75 100644 --- a/E01_StabRegister/E01_StabRegister_Router.py +++ b/E01_StabRegister/E01_StabRegister_Router.py @@ -72,8 +72,8 @@ async def post_stab_project( request: Request, payload: CreateStabRequest, session: dict[str, Any] = Depends(require_company), -) -> dict[str, str]: - """์•ˆ์ •์„ฑํ‰๊ฐ€ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ โ€” ์›๋ณธ B06 ๋ฏธ์™„๋ฃŒ๋ฉด 400.""" +) -> dict[str, Any]: + """์•ˆ์ •์„ฑํ‰๊ฐ€ ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ โ€” ์›๋ณธ B06 ๋ฏธ์™„๋ฃŒ๋ฉด 400 ยท ํšก๋‹จ ์—†๋Š” ์ธก์ ์€ warnings.""" company_id = session.get("company_id") rows = await _sources(company_id, payload.source_project_id) if company_id else [] if not rows: @@ -102,7 +102,11 @@ async def post_stab_project( logger.exception("E01 ๋ณต์‚ฌ ์‹คํŒจ โ€” ๋งŒ๋“  ํ”„๋กœ์ ํŠธ๋ฅผ ์ง€์›€: %s", project_id) await hard_delete_project(project_id, int(session["user_id"]), request) raise HTTPException(status_code=500, detail="์›๋ณธ ์ž๋ฃŒ ๋ณต์‚ฌ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.") from None - return {"project_id": project_id, "source_fingerprint": manifest["source_fingerprint"]} + return { + "project_id": project_id, + "source_fingerprint": manifest["source_fingerprint"], + "warnings": manifest.get("warnings", []), + } async def _copy_source(project_id: str, source_id: str) -> dict[str, Any]: diff --git a/E01_StabRegister/E01_StabRegister_Source.py b/E01_StabRegister/E01_StabRegister_Source.py index ba4895d65..70bb98bb7 100644 --- a/E01_StabRegister/E01_StabRegister_Source.py +++ b/E01_StabRegister/E01_StabRegister_Source.py @@ -18,6 +18,7 @@ from common_util.common_util_project_copy import ( MANIFEST_NAME, STABILITY_DB_NAME, STABILITY_SOURCE_DIR, + resolve_cross_file, ) from common_util.common_util_storage import resolve_stored_project_path from config.config_db import get_db_pool @@ -69,16 +70,25 @@ def db_snapshot(root: Path) -> dict[str, Any]: def _cross_rows(root: Path) -> list[dict[str, Any]]: - rows = db_snapshot(root)["cross_sections"] - return sorted(rows, key=lambda row: (float(row["chainage_m"] or 0.0), row["sequence_num"] or 0)) + rows = [row for row in db_snapshot(root)["cross_sections"] if row["chainage_m"] is not None] + return sorted(rows, key=lambda row: (float(row["chainage_m"]), row.get("sequence_num") or 0)) + + +def _row_data(row: dict[str, Any]) -> dict[str, Any]: + data = row.get("data") + return (json.loads(data) if isinstance(data, str) else data) or {} def stations(root: Path) -> list[dict[str, Any]]: - """์ธก์  ๋ชฉ๋ก(๋ˆ„๊ฐ€ ์ฐจ๋ก€) โ€” chainage_m ยท label ยท ์ค‘์‹ฌ ์ขŒํ‘œ ยท ๋ฐฉ์œ„๊ฐ ยท ์˜ค๋ฅด๋ง‰ ์ชฝ ยท ์ง€๋ฐ˜ ์œ ํ˜•.""" + """์ธก์  ๋ชฉ๋ก(๋ˆ„๊ฐ€ ์ฐจ๋ก€) โ€” chainage_m ยท label ยท ์ค‘์‹ฌ ์ขŒํ‘œ ยท ๋ฐฉ์œ„๊ฐ ยท ์˜ค๋ฅด๋ง‰ ์ชฝ ยท ์ง€๋ฐ˜ ์œ ํ˜•. + + ํšก๋‹จ ํŒŒ์ผ์„ ๋ชป ์ฐพ์€ ์ธก์ (`missing_stations`)์€ ใ€Œํšก๋‹จ ์—†์Œใ€์œผ๋กœ ๋น ์ง โ€” ์ „์ฒด๋Š” ์•ˆ ๊นจ์ง.""" out = [] for row in _cross_rows(root): - data = json.loads(row["data"]) if isinstance(row["data"], str) else row["data"] - cross = _json(root / row["cross_section_file_path"]) + path = resolve_cross_file(root, row) + if path is None: + continue + cross = _json(path) out.append( { "chainage_m": float(row["chainage_m"]), @@ -89,27 +99,39 @@ def stations(root: Path) -> list[dict[str, Any]]: "center_z": cross.get("center_z"), "azimuth_deg": cross.get("azimuth_deg"), "uphill_side": cross.get("uphill_side"), - "ground_type": (data.get("design") or {}).get("ground_type"), + "ground_type": (_row_data(row).get("design") or {}).get("ground_type"), } ) return out +def missing_stations(root: Path) -> list[float]: + """ํšก๋‹จ ์—†์Œ์œผ๋กœ ๋น ์ง„ ์ธก์  ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ โ€” ํ™”๋ฉด ์•Œ๋ฆผ์šฉ(๊ฐœ์ˆ˜ = len).""" + return [ + float(row["chainage_m"]) + for row in _cross_rows(root) + if resolve_cross_file(root, row) is None + ] + + def cross_section(root: Path, chainage_m: float) -> dict[str, Any]: """ํ•œ ์ธก์  ํšก๋‹จ โ€” ์ง€๋ฐ˜์„ (samples: offset_m ยท x ยท y ยท z ยท valid) ยท ์ขŒํ‘œํ‹€ ยท B06 ์„ค๊ณ„๊ฐ’ (`design`: design_line ยท road_edges ยท section_mode ยท ์ ˆ์„ฑํ†  ๊ฒฝ์‚ฌ ยท ์†Œ๋‹จ ยท ground_type ยท - rock_boundary_offset_m(์•” ๊ฒฝ๊ณ„ = ์ง€๋ฐ˜์„ ์„ ๊ทธ๋งŒํผ ๋‚ด๋ฆฐ ์„  ยท ์•” ์ธก์ ๋งŒ) โ€ฆ).""" + rock_boundary_offset_m(์•” ๊ฒฝ๊ณ„ = ์ง€๋ฐ˜์„ ์„ ๊ทธ๋งŒํผ ๋‚ด๋ฆฐ ์„  ยท ์•” ์ธก์ ๋งŒ) โ€ฆ). + ์ธก์ ์ด ์—†๊ฑฐ๋‚˜ ํšก๋‹จ ์—†์Œ์ด๋ฉด KeyError.""" for row in _cross_rows(root): if abs(float(row["chainage_m"]) - float(chainage_m)) < 1e-6: - data = json.loads(row["data"]) if isinstance(row["data"], str) else row["data"] - cross = _json(root / row["cross_section_file_path"]) + path = resolve_cross_file(root, row) + if path is None: + raise KeyError(f"ํšก๋‹จ ์—†์Œ: {chainage_m}") + cross = _json(path) return { "chainage_m": float(row["chainage_m"]), "label": cross.get("label"), "frame": cross.get("frame"), "uphill_side": cross.get("uphill_side"), "samples": cross.get("samples") or [], - "design": data.get("design") or {}, + "design": _row_data(row).get("design") or {}, } raise KeyError(f"์ธก์  ์—†์Œ: {chainage_m}") diff --git a/E01_StabRegister/E01_StabRegister_UI_Missing.css b/E01_StabRegister/E01_StabRegister_UI_Missing.css new file mode 100644 index 000000000..0e54a15cf --- /dev/null +++ b/E01_StabRegister/E01_StabRegister_UI_Missing.css @@ -0,0 +1,21 @@ +/* ๋น ์ง„ ํšก๋‹จ ๋  โ€” E02 ยท E03 ํ™”๋ฉด ์œ„(E01_StabRegister_UI_Missing.ts) */ +.e-missing { + margin: 0 0 8px; + padding: 6px 10px; + border: 1px solid var(--color-warning); + border-left-width: 4px; + border-radius: var(--radius-sm); + background: var(--color-surface-raised); + color: var(--color-text); + font-size: 13px; +} + +.e-missing > summary { + cursor: pointer; + font-weight: 600; +} + +.e-missing > p { + margin: 6px 0 0; + color: var(--color-text-secondary); +} diff --git a/E01_StabRegister/E01_StabRegister_UI_Missing.ts b/E01_StabRegister/E01_StabRegister_UI_Missing.ts new file mode 100644 index 000000000..252d0ccc9 --- /dev/null +++ b/E01_StabRegister/E01_StabRegister_UI_Missing.ts @@ -0,0 +1,24 @@ +/* ============================================================================= + * E01_StabRegister_UI_Missing.ts + * ๋น ์ง„ ํšก๋‹จ ๋ (์š”๊ตฌ 351 ยท PLAN 532-5) โ€” E02 ยท E03 ํ™”๋ฉด ์œ„ ใ€Œํšก๋‹จ ์—†๋Š” ์ธก์  N ๊ฐœ โ€” ํ•ด์„์—์„œ ๋น ์งใ€. + * ๋ชฉ๋ก = ์„œ๋ฒ„ `missing_stations`(E01 ๋ณต์‚ฌ๋ณธ์— ํšก๋‹จ ํŒŒ์ผ์ด ์—†๋Š” ์ธก์  ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ) ยท ๋ˆ„๋ฅด๋ฉด ํŽผ์นจ. + * ========================================================================== */ + +import "./E01_StabRegister_UI_Missing.css"; +import { el } from "@ui/ui_template_elements"; +import { t } from "@ui/ui_template_locale"; + +/** ๋น ์ง„ ์ธก์ ์ด ์—†์œผ๋ฉด ๋นˆ ์นธ(๋  ์—†์Œ) */ +export function buildMissingBanner(missing: number[] | undefined): HTMLElement { + const list = missing ?? []; + if (!list.length) return el("div"); + const box = el("details", { + className: "e-missing", + attrs: { "data-role": "missing-stations", "data-count": String(list.length) }, + }); + box.append( + el("summary", { text: t("E_Missing_Head").replace("{n}", String(list.length)) }), + el("p", { text: list.map((m) => `${Math.round(m * 100) / 100} m`).join(" ยท ") }), + ); + return box; +} diff --git a/E02_StabGround/E02_StabGround_Router.py b/E02_StabGround/E02_StabGround_Router.py index 1b85d0c70..aee593430 100644 --- a/E02_StabGround/E02_StabGround_Router.py +++ b/E02_StabGround/E02_StabGround_Router.py @@ -1,6 +1,7 @@ """E02 ์ง€๋ฐ˜ ์กฐ๊ฑด API (์š”๊ตฌ 347-2 ยท 3 ยท 4 ยท PLAN 532-3). - `GET /api/projects/{id}/stab/ground` โ€” ์ž‘์—…๋ณธ(์—†์œผ๋ฉด ์ดˆ๊ธฐ๊ฐ’) + ๊ธฐ๋ณธ๊ฐ’ + ๊ณ ์น  ์ˆ˜ ์žˆ๋Š”์ง€ + + ํšก๋‹จ ์—†๋Š” ์ธก์ (`missing_stations` ยท ํ™”๋ฉด ๋ ) - `PUT โ€ฆ/stab/ground` โ€” [์ €์žฅ] ๊ฒ€์‚ฌ ๋’ค ์ž‘์—…๋ณธ `E02_StabGround/ground.json` ยท ์ธก์ (B06 ์•” ๊ฒฝ๊ณ„)์€ ์„œ๋ฒ„ ๊ฐ’ - `POST โ€ฆ/stab/ground/reset` โ€” [์ดˆ๊ธฐํ™”] ์ดˆ๊ธฐ๊ฐ’์„ ์ž‘์—…๋ณธ์— ๋ฎ์–ด์”€(์žฌ๊ณ„์‚ฐ ์•„๋‹˜) @@ -27,7 +28,7 @@ from common_util.common_util_project_copy import MANIFEST_NAME from common_util.common_util_storage import resolve_stored_project_path from config.config_db import get_db_pool from config.config_system_design import STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M -from E01_StabRegister.E01_StabRegister_Source import db_snapshot, source_dir +from E01_StabRegister.E01_StabRegister_Source import db_snapshot, missing_stations, source_dir from E02_StabGround import E02_StabGround_Model as model router = APIRouter(prefix="/api/projects", tags=["E02 Stab Ground"]) @@ -96,6 +97,12 @@ async def seed_initial(project_id: str) -> None: ensure_initial(root) +def _missing(project_root: Path) -> list[float]: + """ํšก๋‹จ ์—†์–ด ํ•ด์„์—์„œ ๋น ์ง„ ์ธก์  โ€” ๋ณต์‚ฌ๋ณธ์ด ์—†์œผ๋ฉด ๋นˆ ๋ชฉ๋ก.""" + root = source_dir(project_root) + return missing_stations(root) if (root / MANIFEST_NAME).is_file() else [] + + def _read_working(project_root: Path) -> dict[str, Any] | None: path = model.ground_path(project_root) return json.loads(path.read_text(encoding="utf-8")) if path.is_file() else None @@ -119,6 +126,7 @@ async def get_ground( "saved": working is not None, "defaults": defaults, "editable": _editable(session, project), + "missing_stations": _missing(root), } diff --git a/E02_StabGround/E02_StabGround_UI_Page.ts b/E02_StabGround/E02_StabGround_UI_Page.ts index e0e985ef4..b45bb974f 100644 --- a/E02_StabGround/E02_StabGround_UI_Page.ts +++ b/E02_StabGround/E02_StabGround_UI_Page.ts @@ -20,6 +20,7 @@ import { import { attachHoverInfo } from "@ui/ui_template_hover_info"; import { t, type LocaleKey } from "@ui/ui_template_locale"; import { mountGroupStep } from "../A00_Common/group_workflow"; +import { buildMissingBanner } from "../E01_StabRegister/E01_StabRegister_UI_Missing"; import { buildCards, type E02View } from "./E02_StabGround_UI_Tables"; import { checkGround, @@ -33,6 +34,7 @@ interface GroundResponse { saved?: boolean; defaults?: GroundDefaults; editable?: boolean; + missing_stations?: number[]; } const cacheKey = (projectId: string): string => `e02_ground_cache:${projectId}`; @@ -95,6 +97,7 @@ export function renderE02StabGround(root: HTMLElement): void { let view: E02View | null = null; let refreshStations = (): void => {}; let dirty = false; + let missing: number[] = []; const paintStatus = (errors: CheckError[]): void => { const lines: string[] = []; @@ -127,7 +130,7 @@ export function renderE02StabGround(root: HTMLElement): void { if (!view) return; const cards = buildCards(view); refreshStations = cards.refreshStations; - main.replaceChildren(cards.root); + main.replaceChildren(buildMissingBanner(missing), cards.root); mark(); }; @@ -221,6 +224,7 @@ export function renderE02StabGround(root: HTMLElement): void { void (async () => { try { const body = await request(`/projects/${projectId}/stab/ground`); + missing = body.missing_stations ?? []; view = { ground: body.ground, defaults: body.defaults as GroundDefaults, diff --git a/E03_StabSection/E03_StabSection_Router.py b/E03_StabSection/E03_StabSection_Router.py index 573c922a9..d7da920a5 100644 --- a/E03_StabSection/E03_StabSection_Router.py +++ b/E03_StabSection/E03_StabSection_Router.py @@ -2,7 +2,7 @@ - `POST /api/projects/{id}/stab/analyze2d` โ€” ์ „ ์ธก์  ํ•ด์„(๋’ค์—์„œ ยท ๋ฐ”๋€ ์ธก์ ๋งŒ) ยท ๊ฐ™์€ ์ง€๋ฌธ = ์•ˆ ์…ˆ - `GET โ€ฆ/stab/progress` โ€” ์ง„ํ–‰๋ฅ  -- `GET โ€ฆ/stab/result2d` โ€” ์š”์•ฝ + ์ธก์  ๋ชฉ๋ก(ํŒ์ • ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด) +- `GET โ€ฆ/stab/result2d` โ€” ์š”์•ฝ + ์ธก์  ๋ชฉ๋ก(ํŒ์ • ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด ยท ๋ฒฝ ์ˆ˜ ยท ์ง€๋ฐฐ ๋น„ํƒˆ) + ํšก๋‹จ ์—†๋Š” ์ธก์  - `GET โ€ฆ/stab/section/{station_key}` โ€” ๊ทธ ์ธก์  ์ง€์ธต ํด๋ฆฌ๊ณค ยท ์ง€ํ•˜์ˆ˜์„  ยท ์ž„๊ณ„๋ฉด ยท ํ›„๋ณด ยท ์ ˆํŽธ ยท ๊ฒฉ์ž - `PUT โ€ฆ/stab/representative` โ€” ๋Œ€ํ‘œ ๋‹จ๋ฉด ์ผฌ/๋” โ†’ ์ž‘์—…๋ณธ `E03_StabSection/representative.json` - `GET โ€ฆ/stab/corridor3d` โ€” ๋ณต์‚ฌ๋ณธ ๋…ธ์„ ๋ฉด(1 m ์†Ž์Œ ยท 3D ์ƒ‰์น  ๋ณด๊ธฐ์šฉ) @@ -10,8 +10,8 @@ ์ž…๋ ฅ = E01 ๋ณต์‚ฌ๋ณธ(์ธก์  ยท ํšก๋‹จ ยท DTM) + E02 ์ง€๋ฐ˜ ์กฐ๊ฑด(์ž‘์—…๋ณธ ยท ์—†์œผ๋ฉด ์ดˆ๊ธฐ๊ฐ’) ๊ณ„์‚ฐ ์ž๋ฆฌ = โ‘ข ์„œ๋ฒ„ ๋‹จ๋…. ๊ฒฐ๊ณผ = ํŒŒ์ƒ ์ €์žฅ๋ณธ `_derived/stab2d_station/<์ธก์  ์ง€๋ฌธ>` ยท `_derived/stab2d_summary/<์ „์ฒด ์ง€๋ฌธ>` โ€” -์ธก์  ์ง€๋ฌธ = ์—”์ง„ ์ฝ”๋“œ ยท ๋ณต์‚ฌ ์ง€๋ฌธ ยท ๋ฌผ์„ฑ ยท ์กฐ๊ฑด ยท ํ•˜์ค‘ ยท ๊ทธ ์ธก์  ์ง€์ธต. ๊ณต์œ  ํšŒ์‚ฌ(๋ณด๊ธฐ๋งŒ)๋Š” -`require_project_access` ๊ฐ€ POST ยท PUT ๋ฅผ ๋ง‰์Œ. +์ธก์  ์ง€๋ฌธ = ์—”์ง„ ์ฝ”๋“œ ยท ๋ณต์‚ฌ ์ง€๋ฌธ ยท ๋ฌผ์„ฑ ยท ์กฐ๊ฑด ยท ํ•˜์ค‘ ยท ์ง€ํ•˜์ˆ˜ ๊ฐˆ๋ž˜ ยท ๊ทธ ์ธก์  ์ง€์ธต ยท ๋ฒฝ. +๊ณต์œ  ํšŒ์‚ฌ(๋ณด๊ธฐ๋งŒ)๋Š” `require_project_access` ๊ฐ€ POST ยท PUT ๋ฅผ ๋ง‰์Œ. """ from __future__ import annotations @@ -49,6 +49,7 @@ from E01_StabRegister import E01_StabRegister_Source as src from E02_StabGround import E02_StabGround_Model as ground_model from E02_StabGround import E02_StabGround_Router as e02 from E03_StabSection import E03_StabSection_View as view +from E03_StabSection import E03_StabSection_Walls as walls_mod from E03_StabSection import E03_StabSection_Worker as worker from E03_StabSection.E03_StabSection_Engine_Model import ( ground_from_cross, @@ -94,8 +95,9 @@ def engine_hash() -> str: def _ground(root: Path) -> dict[str, Any]: - """E02 ์ง€๋ฐ˜ ์กฐ๊ฑด โ€” ์ž‘์—…๋ณธ ยท ์—†์œผ๋ฉด ์ดˆ๊ธฐ๊ฐ’.""" - return e02._read_working(root) or e02.ensure_initial(root) + """E02 ์ง€๋ฐ˜ ์กฐ๊ฑด โ€” ์ž‘์—…๋ณธ ยท ์—†์œผ๋ฉด ์ดˆ๊ธฐ๊ฐ’(์˜› ์ •๋ณธ = ๋น ์ง„ ์ง€ํ•˜์ˆ˜ ์นธ ๊ธฐ๋ณธ๊ฐ’ ยท E02 ํ™”๋ฉด๊ณผ ๊ฐ™์Œ).""" + ground = e02._read_working(root) or e02.ensure_initial(root) + return ground_model.upgrade(ground, ground_model.load_defaults()) def _layers(resolved: dict[str, Any] | None) -> list | None: @@ -109,6 +111,12 @@ def _layers(resolved: dict[str, Any] | None) -> list | None: return [[resolved["upper"], None]] +@lru_cache(maxsize=4) +def _walls(source: str, _copy_fp: str) -> dict[str, dict[str, Any]]: + """์ธก์ ๋ณ„ ๋ฒฝ(532-4 ยท Node ํ•œ ๋ฒˆ ์•ฝ 2 s) โ€” ๋ณต์‚ฌ๋ณธ์€ ๋™๊ฒฐ์ด๋ผ ๋ณต์‚ฌ ์ง€๋ฌธ์œผ๋กœ ๊ธฐ์–ต.""" + return walls_mod.walls_by_station(source) + + def plan_run(root: Path, ground: dict[str, Any]) -> dict[str, Any]: """์ธก์ ๋งˆ๋‹ค ์ž…๋ ฅ ยท ์ง€๋ฌธ + ์ „์ฒด ์ง€๋ฌธ(ํŒ์ • ๊ธฐ์ค€ ํฌํ•จ) โ€” ๋ณต์‚ฌ๋ณธ์ด ์—†์œผ๋ฉด 400.""" try: @@ -124,12 +132,19 @@ def plan_run(root: Path, ground: dict[str, Any]) -> dict[str, Any]: "conditions": conditions, "seismic_kh": float(seismic.get("kh") or 0.0) if seismic.get("enabled") else 0.0, "road_surcharge_kpa": float(ground.get("road_surcharge_kpa") or 0.0), + "groundwater": ground.get("groundwater"), } resolved = {chainage_key(r["chainage_m"]): r for r in ground_model.resolve_stations(ground)} + try: + found = _walls(str(source), copy_fp) + except RuntimeError as error: + raise HTTPException(status_code=500, detail=str(error)) from error items = [] for station in stations: key = chainage_key(station["chainage_m"]) layers = _layers(resolved.get(key)) + entry = found.get(key) + parts = [engine_hash(), copy_fp, shared, key, layers] + ([entry] if entry else []) items.append( { "key": key, @@ -138,12 +153,20 @@ def plan_run(root: Path, ground: dict[str, Any]) -> dict[str, Any]: "ground_type": station.get("ground_type"), "center": [station.get("center_x"), station.get("center_y")], "layers": layers, - "fp": derived_key(engine_hash(), copy_fp, shared, key, layers), + "walls": entry, + "fp": derived_key(*parts), # ๋ฒฝ ์—†๋Š” ์ธก์  = ์ข…์ „ ์ง€๋ฌธ ๊ผด } ) factors = ground.get("safety_factors") or {} overall = derived_key([item["fp"] for item in items], factors) - return {"source": source, "shared": shared, "items": items, "factors": factors, "fp": overall} + return { + "source": source, + "ground": ground, + "shared": shared, + "items": items, + "factors": factors, + "fp": overall, + } def _station_input(source: Path, item: dict[str, Any], sampler: Any) -> dict[str, Any]: @@ -153,7 +176,12 @@ def _station_input(source: Path, item: dict[str, Any], sampler: Any) -> dict[str ground = widen_ground(cross, sampler) if sampler else ground_from_cross(cross) except (KeyError, TypeError, ValueError): ground = ground_from_cross(cross) - return {"cross": cross, "design": cross["design"], "ground": [list(p) for p in ground]} + return { + "cross": cross, + "design": walls_mod.design_with_walls(cross["design"], item.get("walls")), + "walls": walls_mod.walls_of(item.get("walls")), + "ground": [list(p) for p in ground], + } def _sampler(source: Path) -> Any: @@ -166,28 +194,38 @@ def _sampler(source: Path) -> Any: def _meta(item: dict[str, Any], design: dict[str, Any]) -> dict[str, Any]: keys = ("key", "chainage_m", "label", "ground_type", "center", "fp") - return {**{k: item[k] for k in keys}, "section_mode": design.get("section_mode")} + walls = len(walls_mod.walls_of(item.get("walls")) or []) + return { + **{k: item[k] for k in keys}, + "section_mode": design.get("section_mode"), + "walls": walls, + } def _run_worker(job: dict[str, Any], emit: Any) -> None: - """์ผ๊พผ ํ”„๋กœ์„ธ์Šค(`E03_StabSection_Worker`) โ€” ํ•œ ์ค„๋งˆ๋‹ค emit ยท ์‹คํŒจ๋ฉด RuntimeError.""" + """์ผ๊พผ ํ”„๋กœ์„ธ์Šค(`E03_StabSection_Worker`) โ€” ํ•œ ์ค„๋งˆ๋‹ค emit ยท ์‹คํŒจ๋ฉด RuntimeError. + + stderr ๋Š” ํŒŒ์ผ๋กœ(ํŒŒ์ดํ”„๋กœ ๋‘๋ฉด ๊ฒฝ๊ณ ๊ฐ€ ์Œ“์—ฌ ์ผ๊พผ์ด ๋ฉˆ์ถค).""" with tempfile.TemporaryDirectory(prefix="aislo_e03_") as folder: path = Path(folder) / "job.json" path.write_text(json.dumps(job, ensure_ascii=False), encoding="utf-8") - process = subprocess.Popen( - [sys.executable, "-m", "E03_StabSection.E03_StabSection_Worker", str(path)], - cwd=PROJECT_ROOT, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - encoding="utf-8", - ) - for line in process.stdout: - if line.startswith("{"): - emit(json.loads(line).get("done")) - error = process.stderr.read() - if process.wait() != 0: - raise RuntimeError(f"ํ•ด์„ ์ผ๊พผ ์‹คํŒจ: {error.strip().splitlines()[-1:]}") + log = Path(folder) / "stderr.txt" + with log.open("w", encoding="utf-8") as errors: + process = subprocess.Popen( + [sys.executable, "-m", "E03_StabSection.E03_StabSection_Worker", str(path)], + cwd=PROJECT_ROOT, + stdout=subprocess.PIPE, + stderr=errors, + text=True, + encoding="utf-8", + ) + for line in process.stdout: + if line.startswith("{"): + emit(json.loads(line).get("done")) + code = process.wait() + if code != 0: + tail = log.read_text(encoding="utf-8", errors="replace").strip().splitlines()[-1:] + raise RuntimeError(f"ํ•ด์„ ์ผ๊พผ ์‹คํŒจ: {tail}") def run_all(root: Path, plan: dict[str, Any], report: Any) -> int: @@ -205,6 +243,7 @@ def run_all(root: Path, plan: dict[str, Any], report: Any) -> int: "root": str(root), "keep": max(3 * len(items), 6), "workers": WORKERS or max(1, (os.cpu_count() or 2) - 2), + "conditions_ground": plan["ground"], "kwargs": { "materials": shared["materials"], "conditions": list(shared["conditions"]), @@ -318,9 +357,11 @@ async def result2d( summary, fresh = _latest_summary(root, plan["fp"]) editable = e02._editable(session, project) running = bool((_JOBS.get(str(project_id)) or {}).get("running")) + missing = src.missing_stations(plan["source"]) # ํšก๋‹จ ์—†์–ด ํ•ด์„์—์„œ ๋น ์ง„ ์ธก์ (ํ™”๋ฉด ๋ ) if not summary: - rows = [view.station_row(item, None, plan["factors"]) for item in plan["items"]] + rows = [view.station_row(_meta(item, {}), None, plan["factors"]) for item in plan["items"]] view.mark_representative(rows, _overrides(root)) + rows = [{**r, **view.governing_row(r)} for r in rows] return { "status": "none", "fresh": False, @@ -329,9 +370,11 @@ async def result2d( "conditions": plan["shared"]["conditions"], "factors": plan["factors"], "stations": rows, + "missing_stations": missing, } rows = summary["stations"] view.mark_representative(rows, _overrides(root)) + rows = [{**r, **view.governing_row(r)} for r in rows] return { "status": "ok", "fresh": fresh, @@ -339,6 +382,7 @@ async def result2d( "running": running, **{k: v for k, v in summary.items() if k != "stations"}, "stations": rows, + "missing_stations": missing, } @@ -355,19 +399,25 @@ async def section(project_id: UUID, station_key: str) -> dict[str, Any]: raise HTTPException(status_code=404, detail="๊ทธ ์ธก์  ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.") value = saved["value"] cross = src.cross_section(plan["source"], row["chainage_m"]) + entry = next((it["walls"] for it in plan["items"] if it["key"] == station_key), None) + design = walls_mod.design_with_walls(cross["design"], entry) kw = { "materials": plan["shared"]["materials"], "layers": value["inputs"]["layers"], "ground": [tuple(p) for p in value["inputs"]["ground"]], "road_surcharge_kpa": plan["shared"]["road_surcharge_kpa"], + "walls": walls_mod.walls_of(entry), } - dry = section_model_from_b06(cross, cross["design"], **kw) - wet = section_model_from_b06(cross, cross["design"], wet=True, **kw) - water = { - cond: (wet["groundwater"].get("line") if cond == "wet" else None) - for cond in summary["conditions"] - } + dry = section_model_from_b06(cross, design, **kw) + water, rules = {}, {} + for cond in summary["conditions"]: + # ์ˆ˜์œ„์„  = E02 ๊ณ ๋ฅธ ๊ฐˆ๋ž˜(r_u ยท ์—†์Œ = ์„  ์—†์Œ) + model = section_model_from_b06(cross, design, wet=cond == "wet", **kw) + gw = ground_model.station_groundwater(ground, cond, model) + water[cond] = gw.get("line") if gw.get("mode") == "line" else None + rules[cond] = {"mode": gw.get("mode"), "ru": gw.get("ru")} out = view.section_view(row, value["out"], dry, water) + out["water_rule"] = rules out["fresh"] = fresh return out diff --git a/E03_StabSection/E03_StabSection_Router_WhatIf.py b/E03_StabSection/E03_StabSection_Router_WhatIf.py new file mode 100644 index 000000000..85ae4723f --- /dev/null +++ b/E03_StabSection/E03_StabSection_Router_WhatIf.py @@ -0,0 +1,345 @@ +"""E03 ๋ณด๊ฐ• ๊ฒ€ํ†  API โ€” ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ • ์žฌํ•ด์„ ยท ๋ฒฝ ์™ธ์  ์•ˆ์ • (์š”๊ตฌ 345 ยท 351 ยท PLAN 536-3). + +- `POST /api/projects/{id}/stab/whatif/{station_key}` โ€” ๊ฐ€์ •(๋น„ํƒˆ ยท 1:n ยท ์†Œ๋‹จ ํญ ยท ๋†’์ด ยท ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ) + โ†’ ํ˜•์ƒ๋งŒ ๋ฐ”๊พผ ๋ชจํ˜•์œผ๋กœ `analyze_station` ๋‹ค์‹œ ยท ์ „ํ›„ ์•ˆ์ „์œจ ยท ํŒ์ • ยท ๋ฐ”๋€ ๋‹จ๋ฉด(ํ™”๋ฉด ๊ผด) +- `POST โ€ฆ/stab/wallcheck/{station_key}` โ€” ๊ทธ ์ธก์  ๋ฒฝ๋งˆ๋‹ค ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ(ํ‰์ƒ์‹œ ยท ์ง€์ง„ ์‹œ) +- `GET โ€ฆ/stab/whatif` โ€” ์ง€๊ธˆ 2D ๊ฒฐ๊ณผ์— ๋ถ™์€ ๊ฐ€์ • ยท ๋ฒฝ ๊ฒ€ํ†  ๋ชฉ๋ก(๋‹จ๋ฉด ๊ผด ๋บŒ ยท E05 ใ€Œ๋ณด๊ฐ•๊ฒ€ํ† ใ€ ์‹œํŠธ) + +๊ฐ€์ • ๊ฒฐ๊ณผ = ํŒŒ์ƒ ์ €์žฅ๋ณธ `_derived/stab2d_whatif/<์ง€๋ฌธ>` + ๋‹จ๋ฉด ๊ผด `stab2d_whatif_view/` (์›๋ณธ ๊ฒฐ๊ณผ ๊ทธ๋Œ€๋กœ) ยท +์ง€๋ฌธ = ์›๋ณธ ์ธก์  ์ง€๋ฌธ + ๊ฐ€์ •. ์—”์ง„ = ๋ถ€๋ฅด๊ธฐ๋งŒ(`analyze_station` ยท `section_model_from_b06`). +๊ณ„์‚ฐ ์ž๋ฆฌ = โ‘ข ์„œ๋ฒ„ ๋‹จ๋…. +""" + +from __future__ import annotations + +import asyncio +import hashlib +import math +import time +from pathlib import Path +from typing import Any, Literal +from functools import lru_cache +from uuid import UUID + +from fastapi import APIRouter, HTTPException +from pydantic import BaseModel, Field +from shapely.geometry import Point, Polygon + +from common_util.common_util_derived_store import ( + derived_dir, + derived_key, + read_derived, + write_derived, +) +from E01_StabRegister import E01_StabRegister_Source as src +from E02_StabGround import E02_StabGround_Model as ground_model +from E02_StabGround import E02_StabGround_Router as e02 +from E03_StabSection import E03_StabSection_Router as e03 +from E03_StabSection import E03_StabSection_View as view +from E03_StabSection import E03_StabSection_Walls as walls_mod +from E03_StabSection import E03_StabSection_WallCheck as wallcheck +from E03_StabSection import E03_StabSection_WhatIf as whatif +from E03_StabSection.E03_StabSection_Engine_Model import WALL_MATERIAL, section_model_from_b06 +from E03_StabSection.E03_StabSection_Engine_Station import analyze_station + +router = APIRouter(prefix="/api/projects", tags=["E03 Stab What-if"]) +KIND = "stab2d_whatif" +KIND_VIEW = "stab2d_whatif_view" # ๋ฐ”๋€ ๋‹จ๋ฉด ํ™”๋ฉด ๊ผด(๋ฌด๊ฑฐ์›€ ยท ๋ชฉ๋ก์€ ์•ˆ ์ฝ์Œ) +KEEP = 300 + + +@lru_cache(maxsize=1) +def code_hash() -> str: + """๊ฐ€์ • ยท ๋ฒฝ ๊ฒ€ํ†  ์ฝ”๋“œ ์ง€๋ฌธ โ€” ๋ฐ”๋€Œ๋ฉด ์ €์žฅ๋ณธ์„ ๋‹ค์‹œ ์…ˆ(์—”์ง„ ์ง€๋ฌธ์€ ์›๋ณธ ์ธก์  ์ง€๋ฌธ์— ์žˆ์Œ).""" + digest = hashlib.sha1() + for name in ("E03_StabSection_WhatIf.py", "E03_StabSection_WallCheck.py", Path(__file__).name): + digest.update((Path(__file__).parent / name).read_bytes()) + return digest.hexdigest()[:12] + + +class WhatIfRequest(BaseModel): + side: Literal["left", "right"] + ratio: float | None = Field(None, gt=0.05, le=whatif.MAX_RATIO) + bench_w: float = Field(0.0, ge=0.0, le=10.0) + bench_h: float = Field(0.0, ge=0.0, le=30.0) + search: bool = False + #: ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ ๋ชฉํ‘œ โ€” ์กฐ๊ฑด ํ•˜๋‚˜(None = ๋ชจ๋“  ์กฐ๊ฑด) ยท ๋ชฉํ‘œ ์•ˆ์ „์œจ(None = ๊ทธ ์กฐ๊ฑด ๊ธฐ์ค€์•ˆ์ „์œจ) + condition: Literal["dry", "wet", "seismic"] | None = None + target_fs: float | None = Field(None, gt=0.5, le=5.0) + + +class WallRequest(BaseModel): + method: Literal["coulomb", "rankine"] = "coulomb" + water_ratio: float = Field(0.0, ge=0.0, le=1.0) + kh: float | None = Field(None, ge=0.0, le=0.5) + + +def _context(root: Path, station_key: str) -> dict[str, Any]: + """์ธก์  ํ•˜๋‚˜ โ€” ์ง€๊ธˆ ์ž…๋ ฅ๊ณผ ๊ฐ™์€ 2D ๊ฒฐ๊ณผ ยท ์›๋ณธ ์ €์žฅ๋ณธ ยท ์—”์ง„ ์ž…๋ ฅ(์ผ๊พผ๊ณผ ๊ฐ™์€ ๊ฐ’).""" + ground = e03._ground(root) + plan = e03.plan_run(root, ground) + summary, fresh = e03._latest_summary(root, plan["fp"]) + if not summary or not fresh: + raise HTTPException(status_code=409, detail="2D ๊ฒฐ๊ณผ๊ฐ€ ์ง€๊ธˆ ์ž…๋ ฅ๊ณผ ๋‹ค๋ฆ…๋‹ˆ๋‹ค โ€” ๋จผ์ € [ํ•ด์„].") + row = next((r for r in summary["stations"] if r["key"] == station_key), None) + saved = read_derived(root, e03.KIND_STATION, row["fp"]) if row else None + if not saved or not saved["value"]["out"]: + raise HTTPException(status_code=404, detail="๊ทธ ์ธก์  ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.") + value = saved["value"] + cross = src.cross_section(plan["source"], row["chainage_m"]) + entry = next((it["walls"] for it in plan["items"] if it["key"] == station_key), None) + shared = plan["shared"] + kw = { + "materials": shared["materials"], + "conditions": list(shared["conditions"]), + "seismic_kh": shared["seismic_kh"], + "road_surcharge_kpa": shared["road_surcharge_kpa"], + "layers": value["inputs"]["layers"], + "ground": [tuple(p) for p in value["inputs"]["ground"]], + } + walls = walls_mod.walls_of(entry) + if walls: + kw["walls"] = walls + return { + "ground": ground, + "plan": plan, + "row": row, + "value": value, + "cross": cross, + "design": walls_mod.design_with_walls(cross["design"], entry), + "walls": walls or [], + "kw": kw, + } + + +def _wall_side(wall: dict[str, Any]) -> str: + return "left" if Polygon(wall["polygon"]).centroid.x > 0 else "right" + + +def _section(ctx: dict[str, Any], design: dict[str, Any], row: dict, out: dict) -> dict: + """๋ฐ”๋€ ๋‹จ๋ฉด ํ™”๋ฉด ๊ผด โ€” `GET โ€ฆ/section` ๊ณผ ๊ฐ™์€ ๊ผด(์ง€ํ•˜์ˆ˜์„  = E02 ๊ณ ๋ฅธ ๊ฐˆ๋ž˜).""" + kw = {k: ctx["kw"][k] for k in ("materials", "layers", "ground", "road_surcharge_kpa")} + kw["walls"] = ctx["kw"].get("walls") + dry = section_model_from_b06(ctx["cross"], design, **kw) + water = {} + for cond in ctx["kw"]["conditions"]: + model = section_model_from_b06(ctx["cross"], design, wet=cond == "wet", **kw) + gw = ground_model.station_groundwater(ctx["ground"], cond, model) + water[cond] = gw.get("line") if gw.get("mode") == "line" else None + return view.section_view(row, out, dry, water) + + +def _side_cases(row: dict[str, Any], side: str) -> dict[str, Any]: + return {cond: by[side] for cond, by in (row.get("cases") or {}).items() if side in by} + + +def run_whatif(root: Path, station_key: str, req: WhatIfRequest) -> dict[str, Any]: + ctx = _context(root, station_key) + row, design, ground = ctx["row"], ctx["design"], ctx["kw"]["ground"] + role = {s["side"]: s["role"] for s in row["sides"]}.get(req.side) + if role is None: + raise HTTPException(status_code=422, detail="๊ทธ ์ชฝ ๋น„ํƒˆ์ด ์—†์Šต๋‹ˆ๋‹ค.") + if any(_wall_side(w) == req.side for w in ctx["walls"]): + raise HTTPException(status_code=422, detail="๋ฒฝ์ด ์„  ๋น„ํƒˆ โ€” ๋ฒฝ ์™ธ์  ์•ˆ์ •์œผ๋กœ ๊ฒ€ํ† ํ•˜์„ธ์š”.") + before_ratio = whatif.current_ratio(design, req.side, role) + assume = { + "side": req.side, + "role": role, + "ratio": float(req.ratio or before_ratio), + "bench_w": req.bench_w if req.bench_h > 0 else 0.0, + "bench_h": req.bench_h if req.bench_w > 0 else 0.0, + } + goal = {"condition": req.condition, "target_fs": req.target_fs} if req.search else None + key = derived_key(code_hash(), row["fp"], assume, goal) + cached, shown = read_derived(root, KIND, key), read_derived(root, KIND_VIEW, key) + if cached and shown: + return {**cached["value"], "view": shown["value"]} + t0 = time.perf_counter() + meta, factors = ctx["value"]["meta"], ctx["plan"]["factors"] + + memo: dict[float, tuple[dict, dict] | None] = {} + + def run(n: float) -> tuple[dict, dict] | None: + n = round(n, 4) + if n not in memo: + variant = whatif.design_variant(design, ground, {**assume, "ratio": n}) + # None = ํ•ด์„ ํญ ์•ˆ์—์„œ ์›์ง€๋ฐ˜์„ ๋ชป ๋งŒ๋‚จ(์ž์—ฐ ๋น„ํƒˆ๋ณด๋‹ค ์™„๋งŒ) + memo[n] = variant and (analyze_station(ctx["cross"], variant, **ctx["kw"]), variant) + return memo[n] + + reached: list[float] = [] # ์›์ง€๋ฐ˜์„ ๋งŒ๋‚œ ๊ธฐ์šธ๊ธฐ + + def passes(n: float) -> bool: + done = run(n) # ์›๋ณธ๊ณผ ๊ฐ™์€ ๋ฐฉ๋ฒ• ยท ์กฐ๊ฑด(๋ฐฉ๋ฒ•์„ ์ค„์ด๋ฉด ํƒ์ƒ‰ ์”จ์•—์ด ๋‹ฌ๋ผ ๊ฐ’์ด ๋ฐ”๋€œ) + if done is None: + return False + reached.append(n) + cases = _side_cases(view.station_row(meta, done[0], factors), req.side) + if req.condition: + cases = {k: v for k, v in cases.items() if k == req.condition} + need = (lambda c: c["required"]) if req.target_fs is None else (lambda c: req.target_fs) + return bool(cases) and all( + c["fs"] is not None and need(c) is not None and c["fs"] >= need(c) + for c in cases.values() + ) + + search = None + if req.search: + top = whatif.gentlest_ratio(design, ground, assume, before_ratio) or before_ratio + search = {**whatif.search_ratio(passes, before_ratio, max_ratio=top), "gentlest": top} + # ๋ชป ์ฐพ์œผ๋ฉด ์›์ง€๋ฐ˜์„ ๋งŒ๋‚œ ๊ฐ€์žฅ ์™„๋งŒํ•œ ๊ธฐ์šธ๊ธฐ๋กœ ๋ณด์—ฌ ์คŒ + assume["ratio"] = search["found"] or max(reached, default=before_ratio) + assume["ratio"] = round(assume["ratio"], 4) + done = run(assume["ratio"]) + if done is None: + raise HTTPException( + status_code=422, + detail=f"๊ฐ€์ • ๋น„ํƒˆ(1:{assume['ratio']:g} ยท ์†Œ๋‹จ ํฌํ•จ)์ด ํ•ด์„ ํญ ์•ˆ์—์„œ ์›์ง€๋ฐ˜์„ ๋ชป ๋งŒ๋‚ฉ๋‹ˆ๋‹ค" + " โ€” ์ž์—ฐ ๋น„ํƒˆ๋ณด๋‹ค ์™„๋งŒ.", + ) + out, variant = done + after = view.station_row(meta, out, factors) + record = { + "type": "slope", + "station_key": station_key, + "chainage_m": row["chainage_m"], + "label": row.get("label"), + "base_fp": row["fp"], + "assume": {**assume, "ratio_before": before_ratio}, + "search": search and {**search, **goal}, + "before": _side_cases(row, req.side), + "after": _side_cases(after, req.side), + "computed_at": time.strftime("%Y-%m-%dT%H:%M:%S"), + "elapsed_s": round(time.perf_counter() - t0, 2), + } + shown = _section(ctx, variant, after, out) + write_derived(root, KIND_VIEW, key, shown, keep=KEEP) + write_derived(root, KIND, key, record, keep=KEEP) + return {**record, "view": shown} + + +def _material_at(model: dict[str, Any], x: float, z: float, skip: str = "wall") -> str | None: + pt = Point(x, z) + for region in model["regions"]: + if region["material_id"] != skip and Polygon(region["polygon"]).buffer(1e-6).contains(pt): + return region["material_id"] + return None + + +def _surface_z(surface: list, x: float) -> float: + pts = sorted(surface) + for (xa, za), (xb, zb) in zip(pts, pts[1:]): + if xa <= x <= xb: + return za + (zb - za) * ((x - xa) / (xb - xa) if xb > xa else 0.0) + return pts[0][1] if x < pts[0][0] else pts[-1][1] + + +def run_wallcheck(root: Path, station_key: str, req: WallRequest) -> dict[str, Any]: + ctx = _context(root, station_key) + if not ctx["walls"]: + raise HTTPException(status_code=404, detail="๊ทธ ์ธก์ ์— ๋ฒฝ์ด ์—†์Šต๋‹ˆ๋‹ค.") + row = ctx["row"] + seismic = ctx["ground"].get("seismic") or {} + kh = ( + req.kh + if req.kh is not None + else float(seismic.get("kh") or 0.0) * bool(seismic.get("enabled")) + ) + params = {"method": req.method, "water_ratio": req.water_ratio, "kh": kh} + key = derived_key(code_hash(), row["fp"], "wall", params) + cached = read_derived(root, KIND, key) + if cached: + return cached["value"] + kw = {k: ctx["kw"][k] for k in ("materials", "layers", "ground", "road_surcharge_kpa", "walls")} + model = section_model_from_b06(ctx["cross"], ctx["design"], **kw) + mats = {m["id"]: m for m in model["materials"]} + roles = {s["side"]: s["role"] for s in row["sides"]} + walls = [] + for wall in ctx["walls"]: + poly = Polygon(wall["polygon"]) + side = _wall_side(wall) + role = roles.get(side, "fill") + outward = 1 if side == "left" else -1 + back = -outward if role == "fill" else outward # ์Œ“๊ธฐ = ๋…ธ๋ฉด ์ชฝ ํ™ ยท ๊นŽ๊ธฐ = ์‚ฐ ์ชฝ ํ™ + x0, z0, x1, z1 = poly.bounds + H = z1 - z0 + heel = x1 if back > 0 else x0 + toe = x0 if back > 0 else x1 + fill_id = _material_at(model, heel + back * 0.3, z0 + 0.5 * H) or "fill" + found_id = _material_at(model, 0.5 * (x0 + x1), z0 - 0.2) or fill_id + backfill, foundation = mats.get(fill_id, mats.get("fill")), mats.get(found_id) + surface = model["surface"] + dz = _surface_z(surface, heel + back * max(H, 1.0)) - _surface_z(surface, heel) + beta = math.degrees(math.atan2(dz, max(H, 1.0))) + beta = min(max(beta, 0.0), float(backfill["phi"]) - 1.0) + common = { + "backfill": backfill, + "foundation": foundation, + "method": req.method, + "beta_deg": beta, + "surcharge_kpa": ctx["kw"]["road_surcharge_kpa"] if role == "fill" else 0.0, + "water_ratio": req.water_ratio, + "wall_gamma": float(WALL_MATERIAL["gamma_t"]), + "embed_m": max(_surface_z(surface, toe - back * 0.05) - z0, 0.0), + } + cases = [wallcheck.check_wall(wall["polygon"], back, **common)] + if kh > 0: + cases.append(wallcheck.check_wall(wall["polygon"], back, kh=kh, **common)) + walls.append( + { + "name": wall.get("name"), + "kind": wall.get("kind"), + "side": side, + "role": role, + "backfill": fill_id, + "foundation": found_id, + "cases": cases, + } + ) + record = { + "type": "wall", + "station_key": station_key, + "chainage_m": row["chainage_m"], + "label": row.get("label"), + "base_fp": row["fp"], + "params": params, + "walls": walls, + "computed_at": time.strftime("%Y-%m-%dT%H:%M:%S"), + } + write_derived(root, KIND, key, record, keep=KEEP) + return record + + +def records(root: Path) -> list[dict[str, Any]]: + """๊ฐ€์žฅ ์ตœ๊ทผ 2D ์š”์•ฝ ์ธก์ ์— ๋ถ™์€ ๊ฐ€์ • ยท ๋ฒฝ ๊ฒ€ํ†  โ€” (์ธก์  ยท ์ข…๋ฅ˜ ยท ๋น„ํƒˆ)๋งˆ๋‹ค ๊ฐ€์žฅ ์ตœ๊ทผ ํ•˜๋‚˜ ยท + ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ์ˆœ.""" + summary, _ = e03._latest_summary(root, "") + fps = {r["fp"] for r in (summary or {}).get("stations") or []} + latest: dict[tuple, dict[str, Any]] = {} + for path in derived_dir(root, KIND).glob("*.json"): + saved = read_derived(root, KIND, path.stem) + value = (saved or {}).get("value") + if not value or value.get("base_fp") not in fps: + continue + slot = (value["station_key"], value["type"], (value.get("assume") or {}).get("side")) + if slot not in latest or value["computed_at"] > latest[slot]["computed_at"]: + latest[slot] = value + return sorted(latest.values(), key=lambda v: (float(v["chainage_m"]), v["type"])) + + +@router.post("/{project_id}/stab/whatif/{station_key}", response_model=None) +async def post_whatif(project_id: UUID, station_key: str, payload: WhatIfRequest) -> dict: + root, _ = await e02._project(project_id) + return await asyncio.to_thread(run_whatif, root, station_key, payload) + + +@router.post("/{project_id}/stab/wallcheck/{station_key}", response_model=None) +async def post_wallcheck(project_id: UUID, station_key: str, payload: WallRequest) -> dict: + root, _ = await e02._project(project_id) + return await asyncio.to_thread(run_wallcheck, root, station_key, payload) + + +@router.get("/{project_id}/stab/whatif", response_model=None) +async def get_whatif(project_id: UUID) -> dict: + root, _ = await e02._project(project_id) + return {"records": await asyncio.to_thread(records, root)} diff --git a/E03_StabSection/E03_StabSection_UI_List.ts b/E03_StabSection/E03_StabSection_UI_List.ts index 52c86ea45..6a810878a 100644 --- a/E03_StabSection/E03_StabSection_UI_List.ts +++ b/E03_StabSection/E03_StabSection_UI_List.ts @@ -82,16 +82,27 @@ export function buildList( onToggle(row.key); }); tr.append( - el("td", { text: row.label?.replace(/^STA\./, "") ?? row.key }), el("td", { - text: row.sides.length ? roleText(row, { cut: t("E03_Cut"), fill: t("E03_Fill") }) : "-", + text: row.label?.replace(/^STA\./, "").replace(/\.000$/, "") ?? row.key, + children: row.walls + ? [el("span", { className: "e03-wall-tag", text: "โ–ฆ", attrs: { title: t("E03_Walls") } })] + : [], + }), + el("td", { + text: row.sides.length + ? roleText(row, { cut: t("E03_CutShort"), fill: t("E03_FillShort") }) + : "-", }), ...draw.conditions.map((c) => el("td", { className: "is-num", text: fmt(row.min[c]) })), el("td", { children: [ el("span", { className: `e03-verdict e03-verdict--${v}`, - text: row.error ? t("E03_Error") : t(`E03_Verdict_${v}` as LocaleKey), + text: row.error + ? t("E03_Error") + : v === "none" + ? "-" + : t(`E03_Verdict_${v}` as LocaleKey), }), ], }), diff --git a/E03_StabSection/E03_StabSection_UI_Page.ts b/E03_StabSection/E03_StabSection_UI_Page.ts index 4287bfa09..0d0846373 100644 --- a/E03_StabSection/E03_StabSection_UI_Page.ts +++ b/E03_StabSection/E03_StabSection_UI_Page.ts @@ -5,6 +5,7 @@ * ์œ„ = ํ‰๋ฉด ์œ„ํ—˜ ๋  ยท ๊ฐ€์šด๋ฐ = ํšก๋‹จ(์ž„๊ณ„๋ฉด ยท ๋“ฑ์•ˆ์ „์œจ ยท ํ›„๋ณด ยท ์ ˆํŽธ) ยท ์šฐ = ๊ฒฐ๊ณผ ยท ์ ˆํŽธํ‘œ ยท ์ž์œ ๋ฌผ์ฒด๋„ ยท ๊ทธ๋ž˜ํ”„ * ๊ณ„์‚ฐ = ์„œ๋ฒ„ ๋‹จ๋…([ํ•ด์„] โ†’ POST โ€ฆ/stab/analyze2d ยท ์ž ๊ธˆํŒ ์ง„ํ–‰๋ฅ  = GET โ€ฆ/stab/progress) ยท * ๋Œ€ํ‘œ ๋‹จ๋ฉด ์ผฌ/๋” = sessionStorage ์บ์‹œ โ†’ [๋Œ€ํ‘œ ๋‹จ๋ฉด ์ €์žฅ] = PUT(์ž๋™์ €์žฅ ์—†์Œ). + * ใ€Œ๋ณด๊ฐ• ๊ฒ€ํ† ใ€ ํƒญ = ์šฐ์ธก์„ ๊ฐ€์ • ์žฌํ•ด์„ ยท ๋ฒฝ ์™ธ์  ์•ˆ์ •์œผ๋กœ ๋ฐ”๊ฟˆ(`UI_Reinforce` ยท ์„œ๋ฒ„ ๋”ฐ๋กœ ์ €์žฅ๋ณธ). * ========================================================================== */ import "./E03_StabSection_UI_Style.css"; @@ -20,15 +21,24 @@ import { } from "@ui/ui_template_elements"; import { t, type LocaleKey } from "@ui/ui_template_locale"; import { mountGroupStep } from "../A00_Common/group_workflow"; +import { buildMissingBanner } from "../E01_StabRegister/E01_StabRegister_UI_Missing"; import { buildList } from "./E03_StabSection_UI_List"; import { buildPlan } from "./E03_StabSection_UI_Plan"; import { buildResult } from "./E03_StabSection_UI_Result"; import { buildLegends, createSectionCanvas, + WALL_ID, type SectionToggles, } from "./E03_StabSection_UI_Section"; import { openView3d } from "./E03_StabSection_UI_View3D"; +import { + buildReinforce, + type SlopeAsk, + type WallAsk, + type WallRecord, + type WhatIfRecord, +} from "./E03_StabSection_UI_Reinforce"; import type { Analysis, Condition, @@ -46,6 +56,8 @@ interface Result2d { computed_at?: string; summary?: Record; stations: StationRow[]; + /** ํšก๋‹จ ์—†์–ด ํ•ด์„์—์„œ ๋น ์ง„ ์ธก์  ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ(E01 ๋ณต์‚ฌ๋ณธ) */ + missing_stations?: number[]; } interface Progress { @@ -97,6 +109,10 @@ export function renderE03StabSection(root: HTMLElement): void { let side: string | null = null; let picked: number | null = null; const toggles: SectionToggles = { map: true, centers: false, candidates: true }; + let mode: "result" | "reinforce" = "result"; + let busy = false; + const slopeRecs = new Map(); // ์ธก์  ์—ด์‡  โ†’ ๋งˆ์ง€๋ง‰ ๊ฐ€์ • + const wallRecs = new Map(); let repCache: Record = JSON.parse( sessionStorage.getItem(repKey(pid)) ?? "{}", ); @@ -156,6 +172,7 @@ export function renderE03StabSection(root: HTMLElement): void { panel.append(controls, ngBox, status, listHost, actions); // ---- ๋ณธ๋ฌธ ---- + const missingHost = el("div"); const planHost = el("div"); const toolbar = el("div", { className: "e03-card__head" }); const sectionCard = el("div", { className: "e03-card" }); @@ -168,7 +185,7 @@ export function renderE03StabSection(root: HTMLElement): void { const resultHost = el("div", { className: "e03-result", attrs: { style: "min-height: 0" } }); const body = el("div", { className: "e03-body" }); body.append(sectionCard, resultHost); - main.append(planHost, body); + main.append(missingHost, planHost, body); const repOf = (row: StationRow): { on: boolean; user: boolean | null } => { const server = row.representative ?? { auto: [], user: null, on: false }; @@ -287,13 +304,54 @@ export function renderE03StabSection(root: HTMLElement): void { (v) => ((toggles.candidates = v), paintSection()), ), ); - toolbar.append(title, sideField.root, flags); + const modeField = createSelectField({ + options: [ + { value: "result", text: t("E03_Tab_Result") }, + { value: "reinforce", text: t("E03_Tab_Reinforce") }, + ], + value: mode, + compact: true, + ariaLabel: t("E03_Tab"), + onChange: (v) => { + mode = v as typeof mode; + paintSection(); + }, + }); + modeField.select.dataset.role = "mode"; + toolbar.append(title, sideField.root, modeField.root, flags); const draw = { view, analysis, toggles, picked }; canvas.render(draw); - legendHost.append( - buildLegends(draw), - el("p", { className: "e03-muted", text: t("E03_NoWalls") }), - ); + legendHost.append(buildLegends(draw)); + const rule = view.water_rule?.[condition]; + if (rule?.mode === "ru") + legendHost.append( + el("p", { + className: "e03-muted", + text: t("E03_Water_Ru").replace("{v}", String(rule.ru)), + attrs: { "data-role": "water-ru" }, + }), + ); + if (view.regions.some((r) => r.material_id === WALL_ID)) + legendHost.append(el("p", { className: "e03-muted", text: t("E03_Walls_Hint") })); + const row = currentRow(); + if (mode === "reinforce" && row) { + if (row.walls && !wallRecs.has(row.key) && !busy && result?.editable) + queueMicrotask(() => void runWall(row.key, { method: "coulomb", water_ratio: 0 })); + resultHost.append( + buildReinforce({ + row, + condition, + side, + editable: !!result?.editable, + busy, + slope: slopeRecs.get(row.key), + wall: wallRecs.get(row.key), + runSlope: (ask) => void runSlope(row.key, ask), + runWall: (ask) => void runWall(row.key, ask), + }), + ); + return; + } resultHost.append( buildResult(view, analysis, picked, (i) => { picked = i; @@ -302,6 +360,33 @@ export function renderE03StabSection(root: HTMLElement): void { ); }; + /** ๊ฐ€์ • ์žฌํ•ด์„ ยท ๋ฒฝ ๊ฒ€ํ†  โ€” ์„œ๋ฒ„ ๊ณ„์‚ฐ(์ˆ˜์‹ญ ์ดˆ) ๋™์•ˆ ๋‹จ์ถ”๋งŒ ์ž ๊ธˆ */ + async function reinforce( + path: string, + body: unknown, + done: (value: T) => void, + ): Promise { + busy = true; + paintSection(); + try { + done(await api(path, { method: "POST", body: JSON.stringify(body) })); + } catch (error) { + showToast(error instanceof Error ? error.message : t("Common_Status_Error"), "error"); + } finally { + busy = false; + paintSection(); + } + } + const runSlope = (key: string, ask: SlopeAsk): Promise => + reinforce(`${base}/whatif/${encodeURIComponent(key)}`, ask, (rec) => { + slopeRecs.set(key, rec); + side = rec.assume.side; // ์ „ ๊ทธ๋ฆผ = ๊ฐ€์ •ํ•œ ๋น„ํƒˆ + }); + const runWall = (key: string, ask: WallAsk): Promise => + reinforce(`${base}/wallcheck/${encodeURIComponent(key)}`, ask, (rec) => + wallRecs.set(key, rec), + ); + const paintPlan = (): void => { planHost.replaceChildren( result ? buildPlan(result.stations, condition, active, (key) => void select(key)) : el("div"), @@ -331,8 +416,11 @@ export function renderE03StabSection(root: HTMLElement): void { } async function load(): Promise { + const before = result?.status; result = await api(`${base}/result2d`); + if (before === "none" && result.status === "ok") active = null; // ์ฒซ ํ•ด์„ ๋’ค = ๊ฐ€์žฅ ๋‚ฎ์€ ์ธก์ ์œผ๋กœ sections.clear(); + missingHost.replaceChildren(buildMissingBanner(result.missing_stations)); const conds = result.conditions; if (!conds.includes(condition)) condition = conds[0] ?? "dry"; condField.setOptions( diff --git a/E03_StabSection/E03_StabSection_UI_Reinforce.ts b/E03_StabSection/E03_StabSection_UI_Reinforce.ts new file mode 100644 index 000000000..0b30092bd --- /dev/null +++ b/E03_StabSection/E03_StabSection_UI_Reinforce.ts @@ -0,0 +1,314 @@ +/* ============================================================================= + * E03_StabSection_UI_Reinforce.ts + * E03 ใ€Œ๋ณด๊ฐ• ๊ฒ€ํ† ใ€ ํƒญ(PLAN 536-3) โ€” ๊ณ ๋ฅธ ์ธก์  ๋น„ํƒˆ ๊ฐ€์ •(1:n ์™„ํ™” ยท ์†Œ๋‹จ ํญ ยท ๋†’์ด ยท ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ) + * โ†’ ์„œ๋ฒ„ ์žฌํ•ด์„(POST โ€ฆ/stab/whatif/{์ธก์ }) ยท ์ „ํ›„ ์•ˆ์ „์œจ ยท ํŒ์ • ํ‘œ ยท ํ›„ ๋‹จ๋ฉด ๊ทธ๋ฆผ ยท + * ๋ฒฝ ์žˆ๋Š” ์ธก์  = ๋ฒฝ ์™ธ์  ์•ˆ์ • ํ‘œ(POST โ€ฆ/stab/wallcheck/{์ธก์ } ยท ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ). + * ๊ฐ€์ •์€ ํ˜•์ƒ๋งŒ ๋ฐ”๊พผ ๋”ฐ๋กœ ์ €์žฅ๋ณธ(์›๋ณธ ๊ฒฐ๊ณผ ๊ทธ๋Œ€๋กœ) ยท ๊ณ„์‚ฐ์€ ์„œ๋ฒ„. + * ========================================================================== */ + +import { createButton, createInputField, createSelectField, el } from "@ui/ui_template_elements"; +import { t, type LocaleKey } from "@ui/ui_template_locale"; +import { createSectionCanvas } from "./E03_StabSection_UI_Section"; +import { verdictChip } from "./E03_StabSection_UI_Result"; +import type { Condition, SectionView, SideCase, StationRow } from "./logic/E03_StabSection_Logic"; + +export interface WhatIfRecord { + type: "slope"; + station_key: string; + assume: { + side: string; + role: "cut" | "fill"; + ratio: number; + ratio_before: number; + bench_w: number; + bench_h: number; + }; + search: { + found: number | null; + gentlest: number; + tried: [number, boolean][]; + condition: Condition | null; + } | null; + before: Partial>; + after: Partial>; + elapsed_s: number; + view: SectionView; +} + +type Check = "sliding" | "overturning" | "bearing"; +interface WallCase { + case: "normal" | "seismic"; + fs: Record; + required: Record; + ok: Record; + coefficients: { ka: number; kae: number | null }; + base: { e: number; q_max: number; q_ult: number }; + geometry: { H: number; B: number }; +} +export interface WallRecord { + type: "wall"; + params: { method: string; water_ratio: number; kh: number }; + walls: { name: string | null; side: string; role: string; cases: WallCase[] }[]; +} + +export interface SlopeAsk { + side: string; + ratio: number | null; + bench_w: number; + bench_h: number; + search: boolean; + /** ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ ๋ชฉํ‘œ ์กฐ๊ฑด(๊ทธ ์กฐ๊ฑด ๊ธฐ์ค€์•ˆ์ „์œจ) */ + condition: Condition | null; +} +export interface WallAsk { + method: string; + water_ratio: number; +} + +export interface ReinforceOptions { + row: StationRow; + condition: Condition; + side: string | null; + editable: boolean; + busy: boolean; + slope: WhatIfRecord | undefined; + wall: WallRecord | undefined; + runSlope: (ask: SlopeAsk) => void; + runWall: (ask: WallAsk) => void; +} + +const fmt = (v: number | null | undefined, d = 2): string => + v === null || v === undefined || !Number.isFinite(v) ? "-" : v.toFixed(d); +const CONDS: Condition[] = ["dry", "wet", "seismic"]; +const CHECKS: Check[] = ["sliding", "overturning", "bearing"]; + +function num(input: HTMLInputElement): number | null { + const v = Number(input.value); + return input.value.trim() === "" || !Number.isFinite(v) ? null : v; +} + +function slopeTable(rec: WhatIfRecord): HTMLElement { + const table = el("table", { className: "e03-table", attrs: { "data-role": "whatif-table" } }); + const head = ["E03_Condition", "E03_Rf_Before", "E03_Rf_After", "E03_Required"] as LocaleKey[]; + table.append( + el("thead", { + children: [ + el("tr", { + children: [ + ...head.map((k, i) => el("th", { className: i ? "is-num" : "", text: t(k) })), + el("th", { text: t("E03_Rf_VerdictBefore") }), + el("th", { text: t("E03_Rf_VerdictAfter") }), + ], + }), + ], + }), + ); + const body = el("tbody"); + for (const cond of CONDS) { + const a = rec.after[cond]; + if (!a) continue; + const b = rec.before[cond]; + const tr = el("tr", { attrs: { "data-cond": cond } }); + tr.append( + el("td", { text: t(`E03_Cond_${cond}` as LocaleKey) }), + el("td", { className: "is-num", text: fmt(b?.fs, 3) }), + el("td", { className: "is-num", text: fmt(a.fs, 3), attrs: { "data-role": "fs-after" } }), + el("td", { className: "is-num", text: fmt(a.required) }), + el("td", { children: [verdictChip(b?.ok ?? null)] }), + el("td", { children: [verdictChip(a.ok)] }), + ); + body.append(tr); + } + table.append(body); + return table; +} + +function assumeText(rec: WhatIfRecord): string { + const a = rec.assume; + let text = `1:${a.ratio_before} โ†’ 1:${a.ratio}`; + if (a.bench_w && a.bench_h) text += ` ยท ${t("E03_Rf_Bench")} ${a.bench_w} m / ${a.bench_h} m`; + if (rec.search) + text += rec.search.found + ? ` ยท ${t("E03_Rf_Found")} (${rec.search.tried.length}${t("E03_Iter")})` + : ` ยท ${t("E03_Rf_NotFound").replace("{n}", String(rec.search.gentlest))}`; + return `${text} ยท ${rec.elapsed_s} s`; +} + +function wallTable(rec: WallRecord): HTMLElement { + const box = el("div", { attrs: { "data-role": "wall-table" } }); + for (const wall of rec.walls) { + for (const c of wall.cases) { + box.append( + el("p", { + className: "e03-muted", + text: + `${wall.name ?? t("E03_Rf_Wall")} ยท ${t(`E03_Rf_Case_${c.case}` as LocaleKey)} ยท ` + + `H ${fmt(c.geometry.H)} m ยท B ${fmt(c.geometry.B)} m ยท Ka ${fmt(c.coefficients.ka, 3)}` + + (c.coefficients.kae ? ` ยท KAE ${fmt(c.coefficients.kae, 3)}` : "") + + ` ยท e ${fmt(c.base.e)} m ยท q_max ${fmt(c.base.q_max, 1)} kPa`, + }), + ); + const table = el("table", { className: "e03-table" }); + table.append( + el("thead", { + children: [ + el("tr", { + children: [ + el("th", { text: t("E03_Rf_Check") }), + el("th", { className: "is-num", text: t("E03_Rf_Fs") }), + el("th", { className: "is-num", text: t("E03_Required") }), + el("th", { text: t("E03_Verdict") }), + ], + }), + ], + }), + ); + const body = el("tbody"); + for (const k of CHECKS) + body.append( + el("tr", { + attrs: { "data-check": k, "data-case": c.case }, + children: [ + el("td", { text: t(`E03_Rf_${k}` as LocaleKey) }), + el("td", { className: "is-num", text: fmt(c.fs[k]) }), + el("td", { className: "is-num", text: fmt(c.required[k], 1) }), + el("td", { children: [verdictChip(c.ok[k])] }), + ], + }), + ); + table.append(body); + box.append(table); + } + } + return box; +} + +export function buildReinforce(o: ReinforceOptions): HTMLElement { + const host = el("div", { className: "e03-card", attrs: { "data-role": "reinforce" } }); + const sides = o.row.sides; + const sideField = createSelectField({ + label: t("E03_Side"), + options: sides.map((s) => ({ + value: s.side, + text: `${t(s.side === "left" ? "E03_Left" : "E03_Right")} ยท ${t(s.role === "cut" ? "E03_Cut" : "E03_Fill")}`, + })), + value: o.slope?.assume.side ?? o.side ?? undefined, + compact: true, + }); + const ratio = createInputField({ + label: t("E03_Rf_Ratio"), + type: "number", + min: 0.1, + max: 4, + compact: true, + value: o.slope ? String(o.slope.assume.ratio) : "", + placeholder: t("E03_Rf_RatioNow"), + }); + const benchW = createInputField({ + label: t("E03_Rf_BenchW"), + type: "number", + min: 0, + compact: true, + value: o.slope?.assume.bench_w ? String(o.slope.assume.bench_w) : "", + }); + const benchH = createInputField({ + label: t("E03_Rf_BenchH"), + type: "number", + min: 0, + compact: true, + value: o.slope?.assume.bench_h ? String(o.slope.assume.bench_h) : "", + }); + for (const f of [ratio, benchW, benchH]) f.input.step = "0.05"; + const ask = (search: boolean): SlopeAsk => ({ + side: sideField.select.value, + ratio: search ? null : num(ratio.input), + bench_w: num(benchW.input) ?? 0, + bench_h: num(benchH.input) ?? 0, + search, + condition: search ? o.condition : null, + }); + const run = createButton({ + label: o.busy ? t("E03_Rf_Busy") : t("E03_Rf_Run"), + variant: "filled", + onClick: () => o.runSlope(ask(false)), + }); + const find = createButton({ + label: t("E03_Rf_Search"), + variant: "ghost", + onClick: () => o.runSlope(ask(true)), + }); + run.disabled = find.disabled = o.busy || !o.editable; + run.dataset.role = "whatif-run"; + find.dataset.role = "whatif-search"; + const fields = el("div", { className: "e03-panel__row" }); + fields.append(sideField.root, ratio.root, benchW.root, benchH.root); + const buttons = el("div", { className: "e03-panel__row" }); + buttons.append(find, run); + host.append( + el("h3", { className: "e03-card__title", text: t("E03_Rf_Title") }), + el("p", { className: "e03-muted", text: t("E03_Rf_Hint") }), + el("p", { + className: "e03-muted", + text: t("E03_Rf_Goal").replace("{c}", t(`E03_Cond_${o.condition}` as LocaleKey)), + }), + fields, + buttons, + ); + if (o.slope) { + host.append( + el("p", { + className: "e03-muted", + text: assumeText(o.slope), + attrs: { "data-role": "assume" }, + }), + slopeTable(o.slope), + el("h4", { className: "e03-card__title", text: t("E03_Rf_AfterSection") }), + ); + const canvas = createSectionCanvas(() => undefined); + canvas.root.style.height = "260px"; + canvas.root.style.flex = "none"; + canvas.root.dataset.role = "whatif-section"; + host.append(canvas.root); + const analyses = o.slope.view.analyses.filter((a) => a.condition === o.condition); + const analysis = analyses.find((a) => a.side === o.slope!.assume.side) ?? analyses[0] ?? null; + const draw = { + view: o.slope.view, + analysis, + toggles: { map: false, centers: false, candidates: false }, + picked: null, + }; + requestAnimationFrame(() => canvas.render(draw)); + } + if (o.row.walls) { + const method = createSelectField({ + label: t("E03_Rf_Method"), + options: [ + { value: "coulomb", text: "Coulomb" }, + { value: "rankine", text: "Rankine" }, + ], + value: o.wall?.params.method, + compact: true, + }); + const water = createInputField({ + label: t("E03_Rf_Water"), + type: "number", + min: 0, + max: 1, + compact: true, + value: String(o.wall?.params.water_ratio ?? 0), + }); + water.input.step = "0.1"; + const check = createButton({ + label: t("E03_Rf_WallRun"), + variant: "ghost", + onClick: () => o.runWall({ method: method.select.value, water_ratio: num(water.input) ?? 0 }), + }); + check.disabled = o.busy || !o.editable; + check.dataset.role = "wall-run"; + const row = el("div", { className: "e03-panel__row" }); + row.append(method.root, water.root, check); + host.append(el("h3", { className: "e03-card__title", text: t("E03_Rf_WallTitle") }), row); + if (o.wall) host.append(wallTable(o.wall)); + } + return host; +} diff --git a/E03_StabSection/E03_StabSection_UI_Result.ts b/E03_StabSection/E03_StabSection_UI_Result.ts index a9225f2aa..d8b7fb5d0 100644 --- a/E03_StabSection/E03_StabSection_UI_Result.ts +++ b/E03_StabSection/E03_StabSection_UI_Result.ts @@ -17,7 +17,7 @@ import { type Point, type SectionView, } from "./logic/E03_StabSection_Logic"; -import { materialClass, svgEl } from "./E03_StabSection_UI_Section"; +import { WALL_ID, materialClass, svgEl } from "./E03_StabSection_UI_Section"; const fmt = (v: number | null | undefined, d = 2): string => v === null || v === undefined || !Number.isFinite(v) ? "-" : v.toFixed(d); @@ -32,7 +32,7 @@ export function verdictChip(ok: boolean | null): HTMLElement { }); } -function kpi(analysis: Analysis): HTMLElement { +function kpi(analysis: Analysis, walls: number): HTMLElement { const c = analysis.critical; const dl = el("dl", { className: "e03-kpi" }); const row = (label: string, value: HTMLElement | string, big = false): void => { @@ -54,6 +54,8 @@ function kpi(analysis: Analysis): HTMLElement { : "-", ); row(t("E03_Height"), `${fmt(analysis.height_m)} m`); + if (walls) + row(t("E03_Walls"), el("span", { text: String(walls), attrs: { "data-role": "walls" } })); if (c?.center) { row(t("E03_Center"), `${fmt(c.center[0])}, ${fmt(c.center[1])}`); row(t("E03_Radius"), `${fmt(c.radius)} m`); @@ -174,11 +176,18 @@ function freeBody(view: SectionView, analysis: Analysis, picked: number | null): "data-value": String(a.value), }), ); - const tip = a.name.startsWith("E") || a.name.startsWith("X") || a.name === "Q" ? p0 : p1; + // ๊ธ€ = ํ™”์‚ด ๋ฐ”๊นฅ ๋(๋“ค์–ด์˜ค๋Š” ํž˜ = ๊ผฌ๋ฆฌ ยท ๋‚˜๊ฐ€๋Š” ํž˜ = ๋จธ๋ฆฌ)์—์„œ ํ™”์‚ด ๋ฐฉํ–ฅ์œผ๋กœ ์กฐ๊ธˆ ๋” + const len = Math.hypot(p1[0] - p0[0], p1[1] - p0[1]) || 1; + const d: Point = [(p1[0] - p0[0]) / len, (p1[1] - p0[1]) / len]; + const ending = a.name !== "W"; + const out: Point = ending ? [-d[0], -d[1]] : d; + const at: Point = ending ? p0 : p1; + const anchor = out[0] > 0.3 ? "start" : out[0] < -0.3 ? "end" : "middle"; + const label = { x: at[0] + out[0] * 6, y: at[1] + out[1] * 6 + (out[1] > 0.3 ? 10 : 0) }; svg.append( svgEl( "text", - { x: tip[0] + 4, y: tip[1] - 4, class: "e03-label" }, + { ...label, "text-anchor": anchor, class: "e03-label" }, `${a.name} ${fmt(a.value, 1)}`, ), ); @@ -277,7 +286,8 @@ export function buildResult( } const head = (key: LocaleKey): HTMLElement => el("h3", { className: "e03-card__title", text: t(key) }); - root.append(head("E03_Result"), kpi(analysis), methodTable(analysis)); + const walls = view.regions.filter((r) => r.material_id === WALL_ID).length; + root.append(head("E03_Result"), kpi(analysis, walls), methodTable(analysis)); root.append(head("E03_Slices"), sliceTable(analysis, picked, onPick)); root.append(head("E03_Fbd"), freeBody(view, analysis, picked)); root.append(head("E03_Graph"), stressGraph(analysis, picked)); diff --git a/E03_StabSection/E03_StabSection_UI_Section.ts b/E03_StabSection/E03_StabSection_UI_Section.ts index 7960d5042..8bbd73618 100644 --- a/E03_StabSection/E03_StabSection_UI_Section.ts +++ b/E03_StabSection/E03_StabSection_UI_Section.ts @@ -1,11 +1,12 @@ /* ============================================================================= * E03_StabSection_UI_Section.ts - * E03 ๊ฐ€์šด๋ฐ ํšก๋‹จ(PLAN 532-3) โ€” ์ง€์ธต ์ƒ‰ ยท ์ง€ํ‘œ ยท ์ง€ํ•˜์ˆ˜์„  ยท ๋…ธ๋ฉด ํ•˜์ค‘ ยท ๋“ฑ์•ˆ์ „์œจ ์ƒ‰ ์ง€๋„(์›ํ˜ธ ์ค‘์‹ฌ ๊ฒฉ์ž + * E03 ๊ฐ€์šด๋ฐ ํšก๋‹จ(PLAN 532-3) โ€” ์ง€์ธต ์ƒ‰ ยท ๋ฒฝ(ํšŒ์ƒ‰ ๋น—๊ธˆ ยท 532-5) ยท ์ง€ํ‘œ ยท ์ง€ํ•˜์ˆ˜์„  ยท ๋…ธ๋ฉด ํ•˜์ค‘ ยท ๋“ฑ์•ˆ์ „์œจ ์ƒ‰ ์ง€๋„(์›ํ˜ธ ์ค‘์‹ฌ ๊ฒฉ์ž * ์ตœ์†Œ ์•ˆ์ „์œจ) ยท ์›ํ˜ธ ์ค‘์‹ฌ ์  ยท ๋‹ค๋ฅธ ํ›„๋ณด(์˜…๊ฒŒ) ยท ์ž„๊ณ„ ํ™œ๋™๋ฉด(๊ตต๊ฒŒ ยท ์ค‘์‹ฌ ยท ๋ฐ˜๊ฒฝ) ยท ์ ˆํŽธ(๋ˆ„๋ฅด๋ฉด ๊ณ ๋ฆ„). * ๋„ํ˜• ์ขŒํ‘œ = ๊ฒฐ๊ณผ ์ขŒํ‘œ ๊ทธ๋Œ€๋กœ ยท ๋ฌถ์Œ transform(`viewMatrix`) ํ•˜๋‚˜ โ€” ๊ณ„์‚ฐ์€ logic. * ์คŒ ยท ํŒฌ = ๊ณต์šฉ `attachSvgZoomPan`(ํœ  = ์คŒ ยท ๊ฐ€์šด๋ฐ ๋Œ๊ธฐ = ํŒฌ ยท ๋‘ ๋ฒˆ ๋ˆ„๋ฆ„ = ์›๋ฐฐ์œจ). * ========================================================================== */ +import "./E03_StabSection_UI_Walls.css"; import { t } from "@ui/ui_template_locale"; import { el } from "@ui/ui_template_elements"; import { attachSvgZoomPan, type ZoomPanState } from "../A00_Common/b_svg_zoom_pan"; @@ -13,6 +14,7 @@ import { bandIndex, bandLegend, fitView, + focusHome, pointsText, sliceShape, toModel, @@ -25,6 +27,9 @@ import { const SVG_NS = "http://www.w3.org/2000/svg"; const PAD = 28; +const MAX_SCALE = 30; +/** ๋ฒฝ(์˜น๋ฒฝ ยท ์„์ถ•) ๋ฌผ์„ฑ id โ€” ์„œ๋ฒ„ `WALL_MATERIAL` */ +export const WALL_ID = "wall"; export function svgEl( tag: K, @@ -85,7 +90,9 @@ export function buildLegends(draw: SectionDraw): HTMLElement { for (const m of draw.view.materials.filter((x) => used.has(x.id))) { const item = el("span", { className: "e03-legend__item" }); item.append( - el("span", { className: `e03-legend__swatch ${materialClass(draw.view, m.id)}` }), + el("span", { + className: `e03-legend__swatch ${m.id === WALL_ID ? "e03-wall-swatch" : materialClass(draw.view, m.id)}`, + }), m.name, ); box.append(item); @@ -119,10 +126,34 @@ export function createSectionCanvas(onPick: (index: number | null) => void): Sec const ns = { "vector-effect": "non-scaling-stroke" }; const unit = 1 / fit.k; // 1 px ์˜ ๊ฒฐ๊ณผ ์ขŒํ‘œ ๊ธธ์ด + // ๋ฒฝ ๋น—๊ธˆ โ€” ๊ฒฐ๊ณผ ์ขŒํ‘œ ํŒจํ„ด(ํ™”๋ฉด 6 px ๊ฐ„๊ฒฉ) + const hatch = svgEl("pattern", { + id: "e03-wall-hatch", + patternUnits: "userSpaceOnUse", + width: 6 * unit, + height: 6 * unit, + patternTransform: "rotate(45)", + }); + hatch.append( + svgEl("rect", { width: 6 * unit, height: 6 * unit, class: "e03-wall-hatch__bg" }), + svgEl("line", { + x1: 0, + y1: 0, + x2: 0, + y2: 6 * unit, + "stroke-width": 1.5 * unit, + class: "e03-wall-hatch__line", + }), + ); + const defs = svgEl("defs"); + defs.append(hatch); + world.append(defs); for (const region of view.regions) { + const wall = region.material_id === WALL_ID; const ring = svgEl("polygon", { points: pointsText(region.polygon), - class: `e03-region ${materialClass(view, region.material_id)}`, + class: wall ? "e03-wall" : `e03-region ${materialClass(view, region.material_id)}`, + ...(wall ? { fill: "url(#e03-wall-hatch)", "data-role": "wall" } : {}), ...ns, }); world.append(ring); @@ -274,7 +305,14 @@ export function createSectionCanvas(onPick: (index: number | null) => void): Sec labels.append( svgEl( "text", - { x: ax + 8, y: ay - 10, class: "e03-label e03-label--fs", "data-role": "fs-label" }, + { + x: ax, + y: ay, + dx: "0.6em", + dy: "-0.7em", + class: "e03-label e03-label--fs", + "data-role": "fs-label", + }, `Fs = ${fmt(critical.fs)}`, ), ); @@ -282,7 +320,7 @@ export function createSectionCanvas(onPick: (index: number | null) => void): Sec labels.append( svgEl( "text", - { x: ax + 8, y: ay + 6, class: "e03-label" }, + { x: ax, y: ay, dx: "0.7em", dy: "0.5em", class: "e03-label" }, `Xc ${fmt(critical.center[0], 2)} ยท Zc ${fmt(critical.center[1], 2)} ยท R ${fmt(critical.radius, 2)} m`, ), ); @@ -307,7 +345,7 @@ export function createSectionCanvas(onPick: (index: number | null) => void): Sec }); svg.addEventListener("mouseleave", () => (tip.style.display = "none")); - const key = `${view.key}|${width}x${height}`; + const key = `${view.key}|${analysis?.condition}|${analysis?.side}|${width}x${height}`; if (key !== zoomKey) zoom = undefined; zoomKey = key; attachSvgZoomPan({ @@ -317,9 +355,13 @@ export function createSectionCanvas(onPick: (index: number | null) => void): Sec heightPx: height, pad: { left: 0, right: 0, top: 0, bottom: 0 }, wheelZoom: true, - maxScale: 20, + maxScale: MAX_SCALE, + home: focusHome(fit, view, analysis, width, height, MAX_SCALE), initial: zoom, - onChange: (state) => (zoom = state), + onChange: (state) => { + zoom = state; + labels.style.setProperty("--e03-label-k", String(1 / state.scale)); // ๊ธ€์ž๋Š” ํ™•๋Œ€ ์•ˆ ํ•จ + }, }); }; diff --git a/E03_StabSection/E03_StabSection_UI_Style.css b/E03_StabSection/E03_StabSection_UI_Style.css index 95b8a2665..c7bbb4419 100644 --- a/E03_StabSection/E03_StabSection_UI_Style.css +++ b/E03_StabSection/E03_StabSection_UI_Style.css @@ -80,7 +80,7 @@ display: grid; grid-template-rows: auto minmax(0, 1fr); gap: var(--spacing-8); - height: 100%; + height: calc(100dvh - var(--app-header-h)); padding: var(--spacing-8) var(--spacing-12); box-sizing: border-box; min-height: 0; @@ -89,6 +89,7 @@ .e03-body { display: grid; grid-template-columns: minmax(0, 1fr) minmax(320px, 420px); + grid-template-rows: minmax(0, 1fr); gap: var(--spacing-8); min-height: 0; } @@ -214,9 +215,10 @@ } .e03-list tbody tr.is-active { - background: var(--color-lavender-wash); - outline: 1px solid var(--color-accent); - outline-offset: -1px; + background: var(--color-surface-sunken); + outline: 2px solid var(--color-accent); + outline-offset: -2px; + font-weight: var(--font-weight-medium); } .e03-verdict { @@ -418,14 +420,14 @@ .e03-label { fill: var(--color-text); - font-size: 12px; + font-size: calc(12px * var(--e03-label-k, 1)); paint-order: stroke; stroke: var(--e03-plot); - stroke-width: 3px; + stroke-width: calc(3px * var(--e03-label-k, 1)); } .e03-label--fs { - font-size: 14px; + font-size: calc(14px * var(--e03-label-k, 1)); font-weight: 700; } @@ -508,7 +510,9 @@ } .e03-table tbody tr.is-picked { - background: var(--color-lavender-wash); + background: var(--color-surface-sunken); + outline: 2px solid var(--color-accent); + outline-offset: -2px; } .e03-slices { @@ -576,7 +580,7 @@ .e03-view3d { position: fixed; inset: 5vh 5vw; - z-index: 50; + z-index: 800; display: flex; flex-direction: column; border-radius: var(--radius-sm); diff --git a/E03_StabSection/E03_StabSection_UI_View3D.ts b/E03_StabSection/E03_StabSection_UI_View3D.ts index 4f69b9229..4042d0d22 100644 --- a/E03_StabSection/E03_StabSection_UI_View3D.ts +++ b/E03_StabSection/E03_StabSection_UI_View3D.ts @@ -149,7 +149,7 @@ export async function openView3d( const camera = new THREE.PerspectiveCamera(45, 1, 0.5, Math.max(sphere.radius * 20, 1000)); camera.position .copy(sphere.center) - .add(new THREE.Vector3(0, sphere.radius * 1.4, sphere.radius * 1.2)); + .add(new THREE.Vector3(0, sphere.radius * 1.9, sphere.radius * 1.6)); const controls = new OrbitControls(camera, renderer.domElement); controls.target.copy(sphere.center); controls.update(); diff --git a/E03_StabSection/E03_StabSection_UI_Walls.css b/E03_StabSection/E03_StabSection_UI_Walls.css new file mode 100644 index 000000000..13c7771c5 --- /dev/null +++ b/E03_StabSection/E03_StabSection_UI_Walls.css @@ -0,0 +1,23 @@ +/* E03 ํšก๋‹จ ๋ฒฝ(์˜น๋ฒฝ ยท ์„์ถ•) ํšŒ์ƒ‰ ๋น—๊ธˆ ยท ๋ชฉ๋ก ํ‘œ์‹œ(532-5) */ +.e03-wall { + stroke: var(--e03-surface-line); + stroke-width: 1; +} + +.e03-wall-hatch__bg { + fill: #9a9a9a; + fill-opacity: 0.35; +} + +.e03-wall-hatch__line { + stroke: #6b6b6b; +} + +.e03-wall-swatch { + background: repeating-linear-gradient(45deg, #6b6b6b 0 2px, #c8c8c8 2px 5px); +} + +.e03-wall-tag { + margin-left: 4px; + color: var(--color-text-secondary); +} diff --git a/E03_StabSection/E03_StabSection_View.py b/E03_StabSection/E03_StabSection_View.py index a52d20470..6e94d152e 100644 --- a/E03_StabSection/E03_StabSection_View.py +++ b/E03_StabSection/E03_StabSection_View.py @@ -76,6 +76,7 @@ def station_row( "ground_type": meta.get("ground_type"), "center": meta.get("center"), "fp": meta.get("fp"), + "walls": meta.get("walls") or 0, "sides": list(sides.values()), "cases": cases, "min": minimum, @@ -129,6 +130,36 @@ def mark_representative(rows: list[dict[str, Any]], overrides: dict[str, bool]) } +def governing_row(row: dict[str, Any]) -> dict[str, Any]: + """์ธก์  ์ค„ ๋ง์นธ(E04 ยท E05 ๊ฐ™์€ ๊ทœ์น™) โ€” ์ง€๋ฐฐ = ์กฐ๊ฑด ร— ๋น„ํƒˆ ์ค‘ ๊ธฐ์ค€ ๋Œ€๋น„ ๊ฐ€์žฅ ๋‚ฎ์€ ๊ฒƒ(fs / ๊ธฐ์ค€) ยท + fs_min = ๊ทธ fs ยท ์กฐ๊ฑด๋ณ„ ์•ˆ์ „์œจ = ๊ทธ ๋น„ํƒˆ ยท ํŒ์ • = ์กฐ๊ฑด ํ•˜๋‚˜๋ผ๋„ N.G ๋ฉด ng.""" + roles = {s["side"]: s["role"] for s in row.get("sides") or []} + cases = row.get("cases") or {} + worst = min( + ( + (c["fs"] / c["required"], side, c["fs"]) + for sides in cases.values() + for side, c in sides.items() + if c.get("fs") is not None and c.get("required") + ), + default=None, + ) + side = worst[1] if worst else None + verdicts = set((row.get("verdict") or {}).values()) + values = [v for v in (row.get("min") or {}).values() if v is not None] # ๊ธฐ์ค€ ์—†์„ ๋•Œ + return { + "station_key": row["key"], + "chainage_m": row["chainage_m"], + "side": roles.get(side), + "fs_min": worst[2] if worst else min(values, default=None), + "fs_by_condition": { + cond: sides[side]["fs"] for cond, sides in cases.items() if side in sides + }, + "method": PRIMARY, + "verdict_overall": "ng" if "ng" in verdicts else "ok" if verdicts == {"ok"} else None, + } + + def summarize(rows: list[dict[str, Any]], conditions: list[str]) -> dict[str, Any]: """๋จธ๋ฆฌ ์š”์•ฝ โ€” ์กฐ๊ฑด๋ณ„ ์ตœ์†Œ ์•ˆ์ „์œจ ยท N.G ์ธก์  ์ˆ˜.""" out: dict[str, Any] = {"stations": len(rows), "errors": sum(1 for r in rows if r["error"])} diff --git a/E03_StabSection/E03_StabSection_WallCheck.py b/E03_StabSection/E03_StabSection_WallCheck.py new file mode 100644 index 000000000..e02dc0ce9 --- /dev/null +++ b/E03_StabSection/E03_StabSection_WallCheck.py @@ -0,0 +1,220 @@ +"""E03 ๋ฒฝ(์˜น๋ฒฝ ยท ์„์ถ•) ์™ธ์  ์•ˆ์ • โ€” ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ (์š”๊ตฌ 345 ยท 351 ยท PLAN 536-3). + +storage ๋ฅผ ๋ชจ๋ฅด๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜. ๋ฒฝ ํด๋ฆฌ๊ณค(2D ๋ชจํ˜• `walls` ยท x = B06 offset ยท z = ํ‘œ๊ณ ) + ๋ฐฐ๋ฉด ยท ๊ธฐ์ดˆ ๋ฌผ์„ฑ +โ†’ ํ•˜์ค‘ ยท ์•ˆ์ „์œจ ยท ํŒ์ •. ๊ธฐ์ค€ = KDS 11 80 05(์ง€์‹DB `๊ตฌ์กฐ๋ฌผ_์•ˆ์ •๊ณ„์‚ฐ.md` ยง2 ยท `original/์•ˆ์ •์„ฑํ‰๊ฐ€/04`). + +- ํ† ์•• = Coulomb(๋ฒฝ ๋ฐฐ๋ฉด = ๋’ท๊ตฝ ๋ฐ”๋‹ฅ ~ ๋ฐฐ๋ฉด ๊ผญ๋Œ€๊ธฐ ๊ฐ€์ƒ๋ฉด ยท ๋ฒฝ๋งˆ์ฐฐ ฮด) ๋˜๋Š” Rankine(๋’ท๊ตฝ ์—ฐ์ง ๊ฐ€์ƒ๋ฉด ยท + ฮด = ฮฒ) ยท ์ง€์ง„ ์‹œ = Mononobe-Okabe(์ •์  H/3 + ์ฆ๋ถ„ 0.6H ยท Seed-Whitman) + ๋ฒฝ ๊ด€์„ฑ khยทW. +- ๊ฐ€์ƒ๋ฉด๊ณผ ๋ฒฝ ์‚ฌ์ด ํ™ = ๋ฒฝ ๋ฌด๊ฒŒ์— ๋”ํ•จ ยท ๋ฐฐ๋ฉด ์ ์ฐฉ = 0(๋ณด์ˆ˜ ์ชฝ). +- ์ƒ์žฌํ•˜์ค‘ q = ๋ฐฐ๋ฉด ๋“ฑ๋ถ„ํฌ(๋…ธ๋ฉด ํ•˜์ค‘) ยท ์ˆ˜์•• = ๋ฐฐ๋ฉด ์ˆ˜์œ„ hw(๋ฐ”๋‹ฅ๋ถ€ํ„ฐ) ์ •์ˆ˜์•• + ๋ฐ”๋‹ฅ ์‚ผ๊ฐ ์–‘์••๋ ฅ ยท + ์ˆ˜์œ„ ์•„๋ž˜ ํ† ์•• = ์ˆ˜์ค‘ ๋‹จ์œ„์ค‘๋Ÿ‰. +- ํ™œ๋™ = (Vยทฮผ + c_bยทB) / ฮฃH(์ˆ˜๋™ํ† ์•• ๋นผ๊ณ ) ยท ์ „๋„ = ์•ž๊ตฝ ๊ธฐ์ค€ ์ €ํ•ญ / ์ „๋„ ๋ชจ๋ฉ˜ํŠธ ยท + ์ง€์ง€๋ ฅ = Meyerhof ๊ทนํ•œ(์œ ํšจํญ B' ยท ๊ฒฝ์‚ฌ ๊ณ„์ˆ˜) / ์ตœ๋Œ€ ์ง€๋ฐ˜๋ฐ˜๋ ฅ(์‚ฌ๋‹ค๋ฆฌ๊ผด ยท ์‚ผ๊ฐ). +""" + +from __future__ import annotations + +import math +from typing import Any + +import numpy as np +from shapely.geometry import LineString, Polygon, box +from shapely.ops import unary_union + +GAMMA_W = 9.81 +#: KDS 11 80 05 ๊ธฐ์ค€์•ˆ์ „์œจ โ€” ํ‰์ƒ์‹œ ยท ์ง€์ง„ ์‹œ(ํ™œ๋™์€ ์ˆ˜๋™ํ† ์•• ๋บ€ ๊ฐ’) +REQUIRED = { + "normal": {"sliding": 1.5, "overturning": 2.0, "bearing": 3.0}, + "seismic": {"sliding": 1.2, "overturning": 1.5, "bearing": 2.0}, +} +#: ๋ฒฝ ๋‹จ์œ„์ค‘๋Ÿ‰(kN/mยณ) โ€” ์ฝ˜ํฌ๋ฆฌํŠธ ยท ์ฐฐ์Œ“๊ธฐ 23 ยท ๋ฉ”์Œ“๊ธฐ(๊ณต๊ทน) 20 +WALL_GAMMA = {"default": 23.0, "dry_masonry": 20.0} +_N = 400 # ํ† ์•• ์ ๋ถ„ ์นธ + + +def ka_coulomb(phi: float, delta: float, theta: float, beta: float, psi: float = 0.0) -> float: + """Coulomb ์ฃผ๋™ํ† ์••๊ณ„์ˆ˜(ฯˆ > 0 = Mononobe-Okabe KAE ยท kv = 0). ๊ฐ = ๋„. + + ฮธ = ๋ฐฐ๋ฉด์ด ์—ฐ์ง์—์„œ ์•ž์ชฝ(๋ฒฝ ์ชฝ)์œผ๋กœ ๋ˆ„์šด ๊ฐ(์๊ธฐ๊ฐ€ ์ปค์ง€๋Š” ์ชฝ +) ยท ฮฒ = ๋ฐฐ๋ฉด ํ™ ๊ฒฝ์‚ฌ ยท ฮด = ๋ฒฝ๋งˆ์ฐฐ.""" + f, d, t, b, p = (math.radians(v) for v in (phi, delta, theta, beta, psi)) + under = math.sin(f + d) * math.sin(f - b - p) / (math.cos(d + t + p) * math.cos(b - t)) + root = math.sqrt(max(under, 0.0)) + return math.cos(f - t - p) ** 2 / ( + math.cos(p) * math.cos(t) ** 2 * math.cos(d + t + p) * (1.0 + root) ** 2 + ) + + +def ka_rankine(phi: float, beta: float = 0.0) -> float: + """Rankine ์ฃผ๋™ํ† ์••๊ณ„์ˆ˜(๊ฒฝ์‚ฌ ๋ฐฐ๋ฉด ฮฒ ยท ํ•ฉ๋ ฅ์€ ฮฒ ๋กœ ๊ธฐ์šบ).""" + f, b = math.radians(phi), math.radians(beta) + root = math.sqrt(max(math.cos(b) ** 2 - math.cos(f) ** 2, 0.0)) + return math.cos(b) * (math.cos(b) - root) / (math.cos(b) + root) + + +def bearing_factors(phi: float) -> tuple[float, float, float]: + """Meyerhof ์ง€์ง€๋ ฅ ๊ณ„์ˆ˜(Nc ยท Nq ยท Nฮณ) โ€” ฯ† ๋„.""" + f = math.radians(phi) + if f < 1e-9: + return 5.14, 1.0, 0.0 + nq = math.exp(math.pi * math.tan(f)) * math.tan(math.pi / 4 + f / 2) ** 2 + return (nq - 1.0) / math.tan(f), nq, (nq - 1.0) * math.tan(1.4 * f) + + +def _pressure( + ka: float, H: float, gamma: float, gamma_sat: float, q: float, hw: float +) -> tuple[float, float]: + """์—ฐ์ง ๋†’์ด H ๊ฐ€์ƒ๋ฉด ํ† ์•• ํ•ฉ๋ ฅ ยท ๋ฐ”๋‹ฅ์—์„œ ์ž‘์šฉ ๋†’์ด โ€” ฯƒ' = ์ˆ˜์œ„ ์œ„ ฮณ ยท ์•„๋ž˜ ฮณ' ๋ˆ„์  + q.""" + z = np.linspace(0.0, H, _N + 1) # ๊ผญ๋Œ€๊ธฐ์—์„œ ์•„๋ž˜๋กœ + dry = np.minimum(z, H - hw) + wet = np.maximum(z - (H - hw), 0.0) + p = ka * (gamma * dry + (gamma_sat - GAMMA_W) * wet + q) + total = float(np.trapz(p, z)) + arm = float(np.trapz(p * (H - z), z)) / total if total > 0 else H / 3 + return total, arm + + +def check_wall( + polygon: list, + back: int, + *, + backfill: dict[str, float], + foundation: dict[str, float], + method: str = "coulomb", + delta_ratio: float = 2.0 / 3.0, + beta_deg: float = 0.0, + surcharge_kpa: float = 10.0, + water_ratio: float = 0.0, + kh: float = 0.0, + wall_gamma: float = WALL_GAMMA["default"], + base_friction: float | None = None, + base_adhesion: float = 0.0, + embed_m: float = 0.0, +) -> dict[str, Any]: + """๋ฒฝ ํ•˜๋‚˜ ์™ธ์  ์•ˆ์ •. back = ๋ฐฐ๋ฉด(ํ™) ์ชฝ x ๋ฐฉํ–ฅ(+1 ยท โˆ’1). ๋ฌผ์„ฑ = {gamma_t, gamma_sat, c, phi}. + + kh > 0 ์ด๋ฉด ์ง€์ง„ ์‹œ(๊ธฐ์ค€ `REQUIRED["seismic"]`) ยท ์•„๋‹ˆ๋ฉด ํ‰์ƒ์‹œ. ๋Œ๋ ค์คŒ = ํ•˜์ค‘ ยท ์•ˆ์ „์œจ ยท ํŒ์ •.""" + wall = Polygon(polygon) + xs = np.array([p[0] for p in polygon], dtype=float) + zs = np.array([p[1] for p in polygon], dtype=float) + zb, zt = float(zs.min()), float(zs.max()) + H = zt - zb + base = xs[zs <= zb + 1e-3] + heel = float(base.max() if back > 0 else base.min()) + toe = float(base.min() if back > 0 else base.max()) + B = abs(heel - toe) + phi, gamma = float(backfill["phi"]), float(backfill["gamma_t"]) + gamma_sat = float(backfill.get("gamma_sat") or gamma) + hw = min(max(water_ratio, 0.0), 1.0) * H + + if method == "rankine": + x_top, theta, delta = heel, 0.0, beta_deg + else: + top = xs[zs >= zt - 1e-3] + x_top = float(top.max() if back > 0 else top.min()) # ๋ฐฐ๋ฉด ๊ผญ๋Œ€๊ธฐ ๋ชจ์„œ๋ฆฌ + theta = math.degrees(math.atan2(-(x_top - heel) * back, H)) + delta = delta_ratio * phi + # ๊ฐ€์ƒ๋ฉด๊ณผ ๋ฒฝ ์‚ฌ์ด ํ™ โ€” ๋ฒฝ ์ชฝ ๋ฐ˜ํ‰๋ฉด โˆฉ ๋ฒฝ ์ƒ์ž โˆ’ ๋ฒฝ ยท ๊ฐ€์ƒ๋ฉด์— ๋ถ™์€ ์กฐ๊ฐ๋งŒ(์•ž๋ฉด ์•ž ํ™ ๋นผ๊ณ ) + reach = 10.0 * (H + B + 1.0) + side = [(heel, zb), (x_top, zt), (x_top - back * reach, zt), (heel - back * reach, zb)] + plane = LineString([(heel, zb), (x_top, zt)]) + rest = Polygon(side).intersection(box(*wall.bounds)).difference(wall) + parts = [g for g in getattr(rest, "geoms", [rest]) if isinstance(g, Polygon)] + soil = unary_union([g for g in parts if g.area > 1e-9 and g.distance(plane) < 1e-6]) + soil_w = soil.area * gamma + soil_x = soil.centroid.x if soil_w > 0 else heel + soil_z = soil.centroid.y if soil_w > 0 else zb + + ka = ( + ka_rankine(phi, beta_deg) + if method == "rankine" + else ka_coulomb(phi, delta, theta, beta_deg) + ) + q_eff = surcharge_kpa * ( + 1.0 + if method == "rankine" + else math.cos(math.radians(theta)) / math.cos(math.radians(theta - beta_deg)) + ) + Pa, ya = _pressure(ka, H, gamma, gamma_sat, q_eff, hw) + loads = [("earth", Pa, ya)] + kae = None + if kh > 0: + psi = math.degrees(math.atan(kh)) + kae = ka_coulomb(phi, delta, theta, beta_deg, psi) + Pae, _ = _pressure(kae, H, gamma, gamma_sat, q_eff, hw) + loads.append(("seismic_earth", max(Pae - Pa, 0.0), 0.6 * H)) + incline = math.radians(delta + theta) + + def plane_x(y: float) -> float: # ๊ฐ€์ƒ๋ฉด ์œ„ ๋†’์ด y ์ž๋ฆฌ x + return heel + (x_top - heel) * (y / H if H > 0 else 0.0) + + def arm(x: float) -> float: # ์•ž๊ตฝ์—์„œ ๋ฐฐ๋ฉด ์ชฝ ์ˆ˜ํ‰ ํŒ” ๊ธธ์ด + return (x - toe) * back + + W = wall.area * wall_gamma + xw, zw = wall.centroid.x, wall.centroid.y + Ph = sum(P for _, P, _ in loads) * math.cos(incline) + Pv = sum(P for _, P, _ in loads) * math.sin(incline) + m_res = W * arm(xw) + soil_w * arm(soil_x) + m_over = 0.0 + for _, P, y in loads: + m_res += P * math.sin(incline) * arm(plane_x(y)) + m_over += P * math.cos(incline) * y + Pw = 0.5 * GAMMA_W * hw**2 + U = 0.5 * GAMMA_W * hw * B + m_over += Pw * hw / 3.0 + U * (2.0 * B / 3.0) + inertia = kh * (W + soil_w) + m_over += kh * W * (zw - zb) + kh * soil_w * (soil_z - zb) + H_total = Ph + Pw + inertia + V = W + soil_w + Pv - U + + phi_f = float(foundation["phi"]) + mu = base_friction if base_friction is not None else math.tan(math.radians(2.0 / 3.0 * phi_f)) + sliding = (V * mu + base_adhesion * B) / H_total if H_total > 0 else math.inf + overturning = m_res / m_over if m_over > 0 else math.inf + xbar = (m_res - m_over) / V if V > 0 else -math.inf + e = B / 2.0 - xbar + if xbar <= 0: + q_max, b_eff = math.inf, 0.0 + elif abs(e) <= B / 6.0: + q_max, b_eff = V / B * (1.0 + 6.0 * abs(e) / B), B - 2.0 * abs(e) + else: + q_max, b_eff = 2.0 * V / (3.0 * min(xbar, B - xbar)), B - 2.0 * abs(e) + nc, nq, ng = bearing_factors(phi_f) + alpha = math.degrees(math.atan2(H_total, V)) if V > 0 else 90.0 + i_cq = (1.0 - alpha / 90.0) ** 2 + i_g = (1.0 - alpha / phi_f) ** 2 if phi_f > 0 and alpha < phi_f else 0.0 + gf = float(foundation["gamma_t"]) + q_ult = ( + float(foundation.get("c") or 0.0) * nc * i_cq + + gf * max(embed_m, 0.0) * nq * i_cq + + 0.5 * gf * max(b_eff, 0.0) * ng * i_g + ) + bearing = q_ult / q_max if q_max > 0 and math.isfinite(q_max) else 0.0 + case = "seismic" if kh > 0 else "normal" + fs = {"sliding": sliding, "overturning": overturning, "bearing": bearing} + need = REQUIRED[case] + return { + "case": case, + "method": method, + "geometry": {"H": H, "B": B, "toe_x": toe, "heel_x": heel, "theta_deg": theta}, + "coefficients": {"ka": ka, "kae": kae, "delta_deg": delta, "beta_deg": beta_deg}, + "loads": { + "W": W, + "W_soil": soil_w, + "Pa": Pa, + "Pa_arm": ya, + "Ph": Ph, + "Pv": Pv, + "Pw": Pw, + "U": U, + "inertia": inertia, + "V": V, + "H": H_total, + "M_resist": m_res, + "M_overturn": m_over, + }, + "base": {"e": e, "q_max": q_max, "q_ult": q_ult, "B_eff": b_eff, "mu": mu, "alpha": alpha}, + "fs": fs, + "required": dict(need), + "ok": {k: bool(fs[k] >= need[k]) for k in fs}, + } diff --git a/E03_StabSection/E03_StabSection_WhatIf.py b/E03_StabSection/E03_StabSection_WhatIf.py new file mode 100644 index 000000000..9593dbf87 --- /dev/null +++ b/E03_StabSection/E03_StabSection_WhatIf.py @@ -0,0 +1,199 @@ +"""E03 ๋ณด๊ฐ• ๊ฐ€์ • โ€” ๋น„ํƒˆ ๊ธฐ์šธ๊ธฐ ์™„ํ™” ยท ์†Œ๋‹จ ๋ง๋ถ™์ž„ โ†’ ์„ค๊ณ„์„  ๋‹ค์‹œ ๊ทธ๋ฆผ ยท ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ (PLAN 536-3). + +storage ๋ฅผ ๋ชจ๋ฅด๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜. ๊ฐ€์ • = ๋ชจํ˜• ํ˜•์ƒ๋งŒ ๋ฐ”๊ฟˆ(์„ค๊ณ„ ยท ์ž„๋„ ์ชฝ์œผ๋กœ ์•ˆ ๋Œ๋ ค ์”€ ยท ์š”๊ตฌ 345 E). +- `reshape_side` = ํ•œ ์ชฝ ๋น„ํƒˆ์„ ๋น„ํƒˆ ์‹œ์ž‘์ ์—์„œ 1:n(+ ๋†’์ด h ๋งˆ๋‹ค ํญ w ์†Œ๋‹จ)์œผ๋กœ ์›์ง€๋ฐ˜๊นŒ์ง€ ๋‹ค์‹œ ๊ทธ๋ฆผ ยท + ์•ˆ์ชฝ(๋…ธ๋ฉด ยท ์ธก๊ตฌ)์€ ๊ทธ๋Œ€๋กœ ยท 2๋‹จ(์•” ยท ํ† ์‚ฌ) ๊ฒฝ์‚ฌ๋Š” ํ•œ ๊ฒฝ์‚ฌ๋กœ ๊ฐˆ์Œ. +- `slope_start` = ๋น„ํƒˆ ์‹œ์ž‘์  โ€” ๊นŽ๊ธฐ = ์ธก๊ตฌ ๋ฐ”๊นฅ(`cut_slope_segments` ์‹œ์ž‘) ยท ์Œ“๊ธฐ = ๋…ธ๋ฉด ๋. +- `search_ratio` = ๋ชฉํ‘œ ์•ˆ์ „์œจ์„ ๋งž์ถ”๋Š” ๊ฐ€์žฅ ๊ธ‰ํ•œ 1:n(์ด๋ถ„ ยท `step` ๋‹จ์œ„ ์˜ฌ๋ฆผ) โ€” ํ•ด์„์€ ๋ถ€๋ฅด๋Š” ์ชฝ ํ•จ์ˆ˜. +์ขŒํ‘œ = B06 offset(์ขŒ +) ยท ํ‘œ๊ณ . +""" + +from __future__ import annotations + +import math +from typing import Any, Callable + +import numpy as np + +#: ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ โ€” ๊ฐ€์žฅ ์™„๋งŒํ•œ ๊ธฐ์šธ๊ธฐ ยท ์ฐพ๋Š” ๋‹จ์œ„ +MAX_RATIO = 4.0 +RATIO_STEP = 0.05 + + +def current_ratio(design: dict[str, Any], side: str, role: str) -> float: + """์ง€๊ธˆ ์„ค๊ณ„ ๊ธฐ์šธ๊ธฐ 1:n ์˜ n โ€” ๊นŽ๊ธฐ = ๊ทธ ์ชฝ ์ฒซ ๊ตฌ๊ฐ„ ยท ์Œ“๊ธฐ = ์Œ“๊ธฐ ๊ธฐ์šธ๊ธฐ.""" + if role == "fill": + return float(design.get("fill_slope_ratio") or 1.5) + segs = [s for s in design.get("cut_slope_segments") or [] if s.get("side") == side] + if segs: + return float(min(segs, key=lambda s: abs(s["start_offset_m"]))["ratio"]) + return float(design.get("cut_slope_ratio") or 1.0) + + +def slope_start(design: dict[str, Any], side: str, role: str) -> float: + """๋น„ํƒˆ ์‹œ์ž‘ offset.""" + edge = float(((design.get("road_edges") or {}).get(side) or {}).get("offset_m", 0.0)) + if role == "cut": + segs = [s for s in design.get("cut_slope_segments") or [] if s.get("side") == side] + if segs: + return float(min(segs, key=lambda s: abs(s["start_offset_m"]))["start_offset_m"]) + return edge + + +def _profile( + x0: float, z0: float, sign: float, up: float, ratio: float, bench_w: float, bench_h: float +) -> list[tuple[float, float]]: + """๋น„ํƒˆ ๊ผญ์ง“์ (์‹œ์ž‘ โ†’ ๋ฐ”๊นฅ) โ€” ์›์ง€๋ฐ˜์„ ๋„‰๋„‰ํžˆ ์ง€๋‚˜๋„๋ก ๊ธธ๊ฒŒ(๋’ค์—์„œ ์ž๋ฆ„).""" + pts = [(x0, z0)] + x, z = x0, z0 + while abs(x - x0) < 200.0: + rise = bench_h if bench_w > 0 and bench_h > 0 else 200.0 + x, z = x + sign * rise * ratio, z + up * rise + pts.append((x, z)) + if bench_w > 0 and bench_h > 0: + x += sign * bench_w + pts.append((x, z)) + return pts + + +def _daylight( + pts: list[tuple[float, float]], ground: list, sign: float, up: float +) -> list[tuple[float, float]] | None: + """๋น„ํƒˆ์ด ์›์ง€๋ฐ˜๊ณผ ์ฒ˜์Œ ๋งŒ๋‚˜๋Š” ์ ๊นŒ์ง€ โ€” ๋ชป ๋งŒ๋‚˜๋ฉด None(์ง€ํ˜• ๋ฐ–). ์‹œ์ž‘๋ถ€ํ„ฐ ์›์ง€๋ฐ˜ ๋ฐ–์ด๋ฉด ์‹œ์ž‘์ ๋งŒ.""" + gx = np.array([p[0] for p in ground], dtype=float) + gz = np.array([p[1] for p in ground], dtype=float) + out = [pts[0]] + for (xa, za), (xb, zb) in zip(pts, pts[1:]): + inside = gx[(gx > min(xa, xb)) & (gx < max(xa, xb))] + xs = np.unique(np.r_[xa, xb, inside]) + xs = xs[np.argsort(sign * xs)] + zl = za + (xs - xa) / (xb - xa) * (zb - za) if xb != xa else np.full(len(xs), zb) + f = up * (zl - np.interp(xs, gx, gz)) # โ‰ฅ 0 = ์›์ง€๋ฐ˜ ๋ฐ–์œผ๋กœ ๋‚˜์˜ด + if len(out) == 1 and f[0] >= 0: + return out + for k in range(1, len(xs)): + if not gx[0] <= xs[k] <= gx[-1]: + return None + if f[k] >= 0: + s = f[k - 1] / (f[k - 1] - f[k]) + x = float(xs[k - 1] + s * (xs[k] - xs[k - 1])) + return out + [(x, float(zl[k - 1] + s * (zl[k] - zl[k - 1])))] + out.append((xb, zb)) + return None + + +def reshape_side( + design_line: list, + ground: list, + *, + side: str, + role: str, + start_x: float, + ratio: float, + bench_w: float = 0.0, + bench_h: float = 0.0, +) -> list[list[float]] | None: + """ํ•œ ์ชฝ ๋น„ํƒˆ๋งŒ ๋‹ค์‹œ ๊ทธ๋ฆฐ ์„ค๊ณ„์„ ([[offset, z] โ€ฆ]) โ€” ์›์ง€๋ฐ˜์„ ๋ชป ๋งŒ๋‚˜๋ฉด None.""" + line = sorted((float(x), float(z)) for x, z in design_line) + sign = 1.0 if side == "left" else -1.0 + up = 1.0 if role == "cut" else -1.0 + lx = np.array([p[0] for p in line]) + lz = np.array([p[1] for p in line]) + z0 = float(np.interp(start_x, lx, lz)) + inner = [p for p in line if sign * p[0] < sign * start_x - 1e-9] + slope = _daylight(_profile(start_x, z0, sign, up, ratio, bench_w, bench_h), ground, sign, up) + if slope is None: + return None + return [list(p) for p in sorted(inner + slope)] + + +def design_variant( + design: dict[str, Any], ground: list, assume: dict[str, Any] +) -> dict[str, Any] | None: + """๊ฐ€์ • ํ•˜๋‚˜(side ยท role ยท ratio ยท bench_w ยท bench_h) โ†’ ์„ค๊ณ„์„ ๋งŒ ๋ฐ”๊พผ ์„ค๊ณ„ ์‚ฌ๋ณธ(None = ์›์ง€๋ฐ˜ ๋ชป ๋งŒ๋‚จ).""" + side, role = assume["side"], assume["role"] + pts = [(p["offset_m"], p["elevation_m"]) for p in design.get("design_line") or []] + line = reshape_side( + pts, + ground, + side=side, + role=role, + start_x=slope_start(design, side, role), + ratio=float(assume["ratio"]), + bench_w=float(assume.get("bench_w") or 0.0), + bench_h=float(assume.get("bench_h") or 0.0), + ) + if line is None: + return None + return {**design, "design_line": [{"offset_m": x, "elevation_m": z} for x, z in line]} + + +def gentlest_ratio( + design: dict[str, Any], + ground: list, + assume: dict[str, Any], + start: float, + step: float = RATIO_STEP, +) -> float | None: + """์›์ง€๋ฐ˜์„ ๋งŒ๋‚˜๋Š” ๊ฐ€์žฅ ์™„๋งŒํ•œ n(start ~ MAX_RATIO ยท step ๋‹จ์œ„ ยท ํ˜•์ƒ๋งŒ ยท ์ด๋ถ„) โ€” ์—†์œผ๋ฉด None.""" + + def meets(n: float) -> bool: + return design_variant(design, ground, {**assume, "ratio": n}) is not None + + lo = math.ceil(start / step - 1e-9) * step + hi = math.floor(MAX_RATIO / step + 1e-9) * step + if not meets(lo): + return None + if meets(hi): + return round(hi, 4) + while hi - lo > step + 1e-9: # lo = ๋งŒ๋‚จ ยท hi = ๋ชป ๋งŒ๋‚จ + mid = round(round((lo + hi) / 2 / step) * step, 6) + if mid in (lo, hi): + break + lo, hi = (mid, hi) if meets(mid) else (lo, mid) + return round(lo, 4) + + +def search_ratio( + passes: Callable[[float], bool], + start: float, + *, + max_ratio: float = MAX_RATIO, + step: float = RATIO_STEP, + coarse: int = 5, +) -> dict[str, Any]: + """๋ชฉํ‘œ๋ฅผ ๋งž์ถ”๋Š” ๊ฐ€์žฅ ๊ธ‰ํ•œ n(start โ‰ค n โ‰ค max_ratio ยท step ๋‹จ์œ„). passes(n) = ํ•ด์„ ยท ํŒ์ •. + + ์™„๋งŒํ• ์ˆ˜๋ก ๋Š˜ ์•ˆ์ „ํ•˜์ง€ ์•Š์Œ(๊ธ‰ํ•œ ์ž์—ฐ ๋น„ํƒˆ = ์™„ํ™”๊ฐ€ ๊นŽ๊ธฐ ๋†’์ด๋ฅผ ํ‚ค์›€) โ€” ์„ฑ๊ธด ๊ฑธ์Œ(์ตœ๋Œ€ `coarse` ์นธ)์œผ๋กœ + ์ฒ˜์Œ ๋งž๋Š” ์นธ์„ ์ฐพ๊ณ  ๊ทธ ์•ž ์นธ ์‚ฌ์ด๋ฅผ ์ด๋ถ„์œผ๋กœ ์ขํž˜ ยท ๋๊นŒ์ง€ ๋ชป ๋งž์ถ”๋ฉด found None.""" + tried: list[tuple[float, bool]] = [] + + def test(n: float) -> bool: + ok = passes(n) + tried.append((round(n, 4), ok)) + return ok + + def snap(n: float) -> float: + return round(round(n / step) * step, 6) + + lo = snap(math.ceil(start / step - 1e-9) * step) + top = snap(math.floor(max_ratio / step + 1e-9) * step) + if test(lo): + return {"found": lo, "tried": tried} + gap = max(2 * step, snap((top - lo) / coarse)) + hi = None + n = lo + while n < top - 1e-9: + n = min(snap(n + gap), top) + if test(n): + hi = n + break + lo = n + if hi is None: + return {"found": None, "tried": tried} + while hi - lo > step + 1e-9: # lo = ๋ชป ๋งž์ถค ยท hi = ๋งž์ถค + mid = snap((lo + hi) / 2) + if mid in (lo, hi): + break + lo, hi = (lo, mid) if test(mid) else (mid, hi) + return {"found": round(hi, 4), "tried": tried} diff --git a/E03_StabSection/E03_StabSection_Worker.py b/E03_StabSection/E03_StabSection_Worker.py index 9dcc4b2d4..72d43a14b 100644 --- a/E03_StabSection/E03_StabSection_Worker.py +++ b/E03_StabSection/E03_StabSection_Worker.py @@ -3,7 +3,8 @@ `python -m E03_StabSection.E03_StabSection_Worker ` โ€” ์„œ๋ฒ„(`Router.run_all`)๊ฐ€ ๋„์›€. ์ธก์ ๋งˆ๋‹ค ํ”„๋กœ์„ธ์Šค๋กœ ๋‚˜๋ˆ  ๋Œ๋ฆฌ๊ณ  ๋๋‚  ๋•Œ๋งˆ๋‹ค ํŒŒ์ƒ ์ €์žฅ๋ณธ์„ ์“ฐ๊ณ  ํ•œ ์ค„(JSON)์„ ์ฐ์Œ โ†’ ์„œ๋ฒ„ ์ง„ํ–‰๋ฅ . ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค์—์„œ ๋ฐ”๋กœ ํ”„๋กœ์„ธ์Šค ํ’€์„ ์—ด๋ฉด ์ž์‹๋งˆ๋‹ค `main.py` ๋ฅผ ๋‹ค์‹œ ์ฝ์–ด ์ด ๋ชจ๋“ˆ์„ ์ž…๊ตฌ๋กœ ๋‘ . -job = {root, keep, kwargs, workers, items: [{fp, meta, layers, cross, design, ground}]}. +job = {root, keep, kwargs, workers, conditions_ground, items: [{fp, meta, layers, cross, design, +walls, ground}]} ยท conditions_ground = E02 ์ง€๋ฐ˜ ์กฐ๊ฑด(์ง€ํ•˜์ˆ˜ ๊ฐˆ๋ž˜ โ†’ ์ธก์  ์ˆ˜์œ„). """ from __future__ import annotations @@ -22,8 +23,12 @@ KIND_STATION = "stab2d_station" logger = logging.getLogger(__name__) -def _analyze(item: dict[str, Any], kwargs: dict[str, Any]) -> dict[str, Any]: +def _analyze( + item: dict[str, Any], kwargs: dict[str, Any], conditions_ground: dict | None = None +) -> dict[str, Any]: kw = {**kwargs, "layers": item["layers"], "ground": [tuple(p) for p in item["ground"]]} + if item.get("walls"): + kw["walls"] = item["walls"] return analyze_station(item["cross"], item["design"], **kw) @@ -39,6 +44,7 @@ def _value(item: dict[str, Any], out: dict | None, error: str | None) -> dict[st def run_job(job: dict[str, Any], emit: Callable[[str], None]) -> None: """์ธก์ ๋งˆ๋‹ค ํ•ด์„ โ†’ ์ €์žฅ๋ณธ ยท emit(์—ด์‡ ). ํ•œ ์ธก์  ์‹คํŒจ๋Š” ๊ทธ ์ค„์— ๊นŒ๋‹ญ๋งŒ ๋‚จ๊ธฐ๊ณ  ์ด์–ด ๊ฐ.""" root, keep, kwargs = Path(job["root"]), int(job["keep"]), job["kwargs"] + conditions_ground = job.get("conditions_ground") items = job["items"] def save(item: dict[str, Any], out: dict | None, error: str | None) -> None: @@ -49,13 +55,13 @@ def run_job(job: dict[str, Any], emit: Callable[[str], None]) -> None: if workers == 1: for item in items: try: - save(item, _analyze(item, kwargs), None) + save(item, _analyze(item, kwargs, conditions_ground), None) except Exception as error: # noqa: BLE001 logger.exception("E03 ์ธก์  ํ•ด์„ ์‹คํŒจ: %s", item["meta"]["key"]) save(item, None, str(error)) return with ProcessPoolExecutor(max_workers=workers) as pool: - futures = {pool.submit(_analyze, item, kwargs): item for item in items} + futures = {pool.submit(_analyze, item, kwargs, conditions_ground): item for item in items} for future in as_completed(futures): item = futures[future] try: diff --git a/E03_StabSection/logic/E03_StabSection_Logic.ts b/E03_StabSection/logic/E03_StabSection_Logic.ts index 9bf1903dd..bbab80681 100644 --- a/E03_StabSection/logic/E03_StabSection_Logic.ts +++ b/E03_StabSection/logic/E03_StabSection_Logic.ts @@ -31,6 +31,8 @@ export interface StationRow { min: Partial>; verdict: Partial>; error: string | null; + /** ๊ทธ ์ธก์  ๋ฒฝ(์˜น๋ฒฝ ยท ์„์ถ•) ์ˆ˜ โ€” ์˜› ์ €์žฅ๋ณธ์€ ์—†์Œ */ + walls?: number; representative?: { auto: string[]; user: boolean | null; on: boolean }; } @@ -104,6 +106,8 @@ export interface SectionView { bottom_z: number | null; surcharge: { x0: number; x1: number; q: number }[]; water: Partial>; + /** ์กฐ๊ฑด๋ณ„ ์ง€ํ•˜์ˆ˜ ๊ผด(E02 ๊ฐˆ๋ž˜) โ€” r_u ๋ฉด ์ˆ˜์œ„์„  ์—†์Œ */ + water_rule?: Partial>; analyses: Analysis[]; } @@ -243,6 +247,43 @@ export function fitView(view: SectionView, width: number, height: number, pad: n return { k, tx: width / 2 + (k * (x0 + x1)) / 2, ty: height / 2 + (k * (z0 + z1)) / 2 }; } +/** + * ์ฒ˜์Œ ๋ณด๊ธฐ(์คŒ ยท ํŒฌ) โ€” ๊ทธ ๋น„ํƒˆ ํƒ์ƒ‰ ๋ฒ”์œ„ ยท ์ž„๊ณ„๋ฉด ยท ๊ฒฉ์ž ์ค‘์‹ฌ์„ ํŒ ๊ฐ€์šด๋ฐ ํฌ๊ฒŒ(์—ฌ๋ฐฑ 25 %). + * ํŒ ์ „์ฒด(scale 1) = ๋ชจํ˜• ์ „์ฒด ยท ๋‘ ๋ฒˆ ๋ˆ„๋ฆ„ = ์ด ๋ณด๊ธฐ ยท ๋ฐฐ์œจ์€ 1~maxScale. + */ +export function focusHome( + fit: ViewFit, + view: SectionView, + analysis: Analysis | null, + width: number, + height: number, + maxScale: number, +): { scale: number; tx: number; ty: number } { + if (!analysis) return { scale: 1, tx: 0, ty: 0 }; + const pts: Point[] = []; + const [x0, x1] = analysis.limits; + view.surface.filter((p) => p[0] >= x0 && p[0] <= x1).forEach((p) => pts.push(p)); + analysis.critical?.points.forEach((p) => pts.push(p)); + if (analysis.grid) { + const g = analysis.grid; + pts.push([Math.min(...g.x), Math.min(...g.z)], [Math.max(...g.x), Math.max(...g.z)]); + } + if (!pts.length) return { scale: 1, tx: 0, ty: 0 }; + const scr = pts.map((p) => toScreen(fit, p)); + const [sx0, sx1] = [Math.min(...scr.map((p) => p[0])), Math.max(...scr.map((p) => p[0]))]; + const [sy0, sy1] = [Math.min(...scr.map((p) => p[1])), Math.max(...scr.map((p) => p[1]))]; + const raw = Math.min( + width / Math.max((sx1 - sx0) * 1.25, 1), + height / Math.max((sy1 - sy0) * 1.25, 1), + ); + const scale = Math.min(Math.max(raw, 1), maxScale); + return { + scale, + tx: width / 2 - (scale * (sx0 + sx1)) / 2, + ty: height / 2 - (scale * (sy0 + sy1)) / 2, + }; +} + /** ๊ฒฐ๊ณผ ์ขŒํ‘œ โ†’ ํ™”๋ฉด px */ export function toScreen(fit: ViewFit, p: Point): Point { return [-fit.k * p[0] + fit.tx, -fit.k * p[1] + fit.ty]; @@ -336,9 +377,9 @@ export function forceArrows( return [ arrow("W", slice.weight, centroid, [0, -1]), arrow("Q", slice.surcharge, midTop, [0, -1], true), - arrow("N", slice.N_eff, midBase, n), - arrow("U", slice.U, midBase, n), - arrow("S", slice.S, midBase, [-s * t[0], -s * t[1]]), + arrow("N", slice.N_eff, midBase, n, true), + arrow("U", slice.U, midBase, n, true), + arrow("S", slice.S, midBase, [-s * t[0], -s * t[1]], true), arrow("EL", slice.E_left, nearFace, [s, 0], true), arrow("ER", slice.E_right, farFace, [-s, 0], true), arrow("XL", slice.X_left, nearFace, [0, 1], true), diff --git a/E04_Stab3D/E04_Stab3D_Engine_Search.py b/E04_Stab3D/E04_Stab3D_Engine_Search.py index 8a2c1426d..1716245f1 100644 --- a/E04_Stab3D/E04_Stab3D_Engine_Search.py +++ b/E04_Stab3D/E04_Stab3D_Engine_Search.py @@ -22,6 +22,7 @@ from __future__ import annotations import math import time +from collections.abc import Callable from dataclasses import dataclass, field import numpy as np @@ -61,6 +62,9 @@ class SearchParams: refine_below: float = 2.0 groundwater: dict | None = None seismic_kh: float = 0.0 + # ํ™”๋ฉด ๊ณ ๋ฆฌ(533-3) โ€” ๊ฒฐ๊ณผ ๋ถˆ๋ณ€ ยท on_progress(percent, message) ยท should_stop() ์ฐธ์ด๋ฉด InterruptedError + on_progress: Callable[[int, str], None] | None = None + should_stop: Callable[[], bool] | None = None @dataclass @@ -264,6 +268,13 @@ def search(strata: Strata, params: SearchParams | None = None) -> SearchResult: best = {"governing": np.inf} seen: set[int] = set() counters = {"trials": 0, "evaluated": 0} + stage = [0, 40, "๊ฑฐ์นœ ํƒ์ƒ‰"] # ์ง„ํ–‰๋ฅ  โ€” ์‹œ์ž‘ % ยท ํญ % ยท ์ด๋ฆ„ + + def tick(done: float) -> None: + if p.should_stop and p.should_stop(): + raise InterruptedError + if p.on_progress: + p.on_progress(int(stage[0] + stage[1] * done), str(stage[2])) def node_xyz(ids: np.ndarray) -> np.ndarray: ix = ids % nxs @@ -290,9 +301,11 @@ def search(strata: Strata, params: SearchParams | None = None) -> SearchResult: dirs = np.column_stack([fall_x[cy, cx], fall_y[cy, cx]]) r_lo = np.maximum(p.dr, np.floor(0.5 * h / p.dr) * p.dr) r_hi = h + p.max_depth - for ratio in ratios: + for k_ratio, ratio in enumerate(ratios): done = np.zeros(len(ids), bool) - for r in np.arange(r_lo.min(), r_hi.max() + 1e-9, p.dr): + radii = np.arange(r_lo.min(), r_hi.max() + 1e-9, p.dr) + for k_r, r in enumerate(radii): + tick((k_ratio + k_r / len(radii)) / len(ratios)) live = np.flatnonzero(~done & (r_lo <= r + 1e-9) & (r <= r_hi + 1e-9)) if not len(live): continue @@ -365,11 +378,13 @@ def search(strata: Strata, params: SearchParams | None = None) -> SearchResult: ids = (grid[:, 0] * nys + grid[:, 1]) * nxs + grid[:, 2] seen.update(ids.tolist()) seeds = run(ids) - rounds = 0 + rounds = passes = 0 while seeds and (step > 1 or rounds < p.refine_rounds): if step == 1: rounds += 1 step = max(1, step // 2) + passes += 1 + stage[:] = [min(40 + 8 * (passes - 1), 77), 8, f"๊ณ ์šด ํƒ์ƒ‰ {passes}"] cand = set() for nid in seeds: ih, rem = divmod(nid, nys * nxs) @@ -394,6 +409,7 @@ def search(strata: Strata, params: SearchParams | None = None) -> SearchResult: limit = max(p.refine_below, 1.2 * best["governing"]) crit = np.unique(node_map[(node_map >= 0) & (f_min < limit)]) if ell and len(crit): + stage[:] = [85, 14, "ํƒ€์›์ฒด"] run(crit, ell) f_out = np.where(np.isfinite(f_min), f_min, np.nan) diff --git a/E04_Stab3D/E04_Stab3D_Router.py b/E04_Stab3D/E04_Stab3D_Router.py index 56a21b515..2416a95e8 100644 --- a/E04_Stab3D/E04_Stab3D_Router.py +++ b/E04_Stab3D/E04_Stab3D_Router.py @@ -49,8 +49,8 @@ _IDLE = { class SearchSettings(BaseModel): """ํƒ์ƒ‰ ์กฐ๊ฑด โ€” ๊ธธ์ด m ยท ๋ถ€ํ”ผ mยณ (Scoops3D ๋ฐฉ์‹ ยท ์š”๊ตฌ์„œ 4-2).""" - shape: Literal["sphere", "ellipsoid", "both"] = "both" - ellipsoid_ratio: float = Field(2.0, ge=1.0, le=5.0) # ํƒ€์›์ฒด = ํšŒ์ „์ถ• ๋ฐฉํ–ฅ ๋ฐ˜์ถ• ๋ฐฐ์ˆ˜ + shape: Literal["sphere", "both"] = "both" # both = ๊ตฌ + ๊ตฌ ์ž„๊ณ„ ๋…ธ๋“œ์—์„œ ํƒ€์›์ฒด + ellipsoid_ratio: float = Field(2.0, ge=1.1, le=5.0) # ํƒ€์›์ฒด = ํšŒ์ „์ถ• ๋ฐฉํ–ฅ ๋ฐ˜์ถ• ๋ฐฐ์ˆ˜ box_half_width_m: float = Field(30.0, ge=5.0, le=100.0) # ํƒ์ƒ‰ ์ƒ์ž = ์ค‘์‹ฌ์„  ยฑ ์ด ํญ cell_m: float = Field(1.0, ge=0.5, le=5.0) # ๊ธฐ๋‘ฅ(์…€) ํฌ๊ธฐ spacing_m: float = Field(2.0, ge=0.5, le=10.0) # ๊ตฌ ์ค‘์‹ฌ ์ˆ˜ํ‰ ๊ฐ„๊ฒฉ(๊ฐ€์žฅ ๊ณ ์šด) @@ -67,7 +67,7 @@ class Analyze3dRequest(BaseModel): start_m: float = Field(..., ge=0.0) end_m: float = Field(..., ge=0.0) search: SearchSettings = SearchSettings() - method: Literal["bishop", "hovland"] = "bishop" # bishop = Hungr 3D Bishop(+Janbu ๋ณด์ •) + condition: Literal["dry", "wet"] = "dry" # ์šฐ๊ธฐ = ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”(KDS ์šฐ๊ธฐ ๊ทœ์น™) force: bool = False @@ -108,7 +108,7 @@ def _settings(payload: Analyze3dRequest) -> dict[str, Any]: return { "start_m": round(payload.start_m, 2), "end_m": round(payload.end_m, 2), - "method": payload.method, + "condition": payload.condition, **search.model_dump(), } @@ -184,7 +184,6 @@ def _overview(project_root: Path, root: Path) -> dict[str, Any]: for item in saved: current = run.fingerprint(finger_source, ground, item.pop("settings")) item["stale"] = item.pop("fingerprint") != current - points = source.db_snapshot(root).get("route_points") or [] return { "sections": run.auto_sections(stations), "stations": [ @@ -196,9 +195,10 @@ def _overview(project_root: Path, root: Path) -> dict[str, Any]: } for s in stations ], + # ์ค‘์‹ฌ์„  = ์ธก์  ์ค‘์‹ฌ(B06 ๋ˆ„๊ฐ€ ๊ทธ๋Œ€๋กœ ยท route_points ๋Š” ์ ‘์„  ๊บพ์€์„ ์ด๋ผ ๋ˆ„๊ฐ€๊ฐ€ ๋‹ค๋ฆ„) "line": [ - [float(p["model_x"]), float(p["model_y"]), float(p["chainage_m"])] - for p in sorted(points, key=lambda p: p["sequence_num"]) + [float(s["center_x"]), float(s["center_y"]), float(s["chainage_m"])] + for s in sorted(stations, key=lambda s: float(s["chainage_m"])) ], "saved": saved, "defaults": SearchSettings().model_dump(), diff --git a/E04_Stab3D/E04_Stab3D_Run.py b/E04_Stab3D/E04_Stab3D_Run.py index 2a0e1a003..599886ce9 100644 --- a/E04_Stab3D/E04_Stab3D_Run.py +++ b/E04_Stab3D/E04_Stab3D_Run.py @@ -8,7 +8,9 @@ grid = {x0, y0, dx, nx, ny} โ€” ์…€ (iy, ix) ์ค‘์‹ฌ = (x0 + ixยทdx, y0 + iyยทdx) ยท ์ค„ ์šฐ์„  ํ‰ํƒ„ ๋ฐฐ์—ด z = ํ•ฉ์„ฑ ์ง€ํ‘œ(์›์ง€ํ˜• + ๋…ธ์„ ๋ฉด) ยท f = ์…€๋ณ„ ์ตœ์†Œ ์•ˆ์ „์œจ ยท v = ๊ทธ ์ž„๊ณ„ ์ฒด์  mยณ ยท m = ๋ฐฉ๋ฒ•(0 Bishop ยท 1 Janbu ๋ณด์ •) ยท ๋‹ฟ์ง€ ์•Š์€ ์…€ = null - best = ์ „์ฒด ์ตœ์†Œ ํ™œ๋™๋ฉด {x, y, z(์ค‘์‹ฌ), r, ratio, azimuth_deg, f, volume, method, ix, iy} + best = ์ „์ฒด ์ตœ์†Œ ํ™œ๋™๋ฉด {x, y, z(์ค‘์‹ฌ), r, ratio, azimuth_deg(ํ™œ๋™ ๋ฐฉํ–ฅ), f, volume, method, ix, iy, + hovland ยท bishop ยท janbu_corrected(๊ฐ™์€ ๋ฉด ๋ฐฉ๋ฒ•๋ณ„ F) ยท truncated_ratio ยท floor([์…€, ๋ชป ๋šซ๋Š” ์ธต ์œ—๋ฉด])} + ํƒ€์›์ฒด = ํšŒ์ „์ถ•(ํ™œ๋™ ๋ฐฉํ–ฅ์— ์ˆ˜์ง) ๋ฐ˜์ถ•๋งŒ ratioยทr โ€” 533-2 `trial_columns` ์™€ ๊ฐ™์€ ๊ผด. """ from __future__ import annotations @@ -23,11 +25,15 @@ from pathlib import Path from typing import Any import numpy as np +from scipy.spatial import cKDTree -from common_util.common_util_surface_sampler import DtmGridSampler from E01_StabRegister import E01_StabRegister_Source as source +from E02_StabGround.E02_StabGround_Model import load_defaults as load_ground_defaults +from E03_StabSection.E03_StabSection_Types import DEFAULT_ROAD_SURCHARGE_KPA +from E04_Stab3D import E04_Stab3D_Engine_Grid as grid_engine +from E04_Stab3D import E04_Stab3D_Engine_Search as search_engine -ENGINE_VERSION = "e04-preview-1" # ์—”์ง„ ํŒ โ€” ๋ฐ”๋€Œ๋ฉด ์ง€๋ฌธ์ด ๋‹ฌ๋ผ์ ธ ๋‹ค์‹œ ์…ˆ +ENGINE_VERSION = "e04-3" # ์—”์ง„ ํŒ โ€” ๋ฐ”๋€Œ๋ฉด ์ง€๋ฌธ์ด ๋‹ฌ๋ผ์ ธ ๋‹ค์‹œ ์…ˆ STAGE_DIR = "E04_Stab3D" GROUND_FILE = Path("E02_StabGround") / "ground.json" SECTION_LENGTH_M = 100.0 # ์ž๋™ ๊ตฌ๊ฐ„ ๊ธธ์ด @@ -103,59 +109,71 @@ def bbox_of( # --------------------------------------------------------------------------- -# ์—”์ง„ โ€” 533-1 ยท 2 ๊ฒฐ๊ณผ ๊ผด์ด ์˜ค๊ธฐ ์ „ = ๋ฏธ๋ฆฌ๋ณด๊ธฐ(์ง€ํ˜• ๊ฒฝ์‚ฌ๋กœ ๋งŒ๋“  ๊ฐ€์งœ ๋ž˜์Šคํ„ฐ ยท ํ‹€ ยท ๋ฐฐ์น˜ ํ™•์ธ์šฉ) +# ์—”์ง„ โ€” ํ•ฉ์„ฑ DEM ยท ์ง€์ธต(533-1 Grid) โ†’ ๊ตฌ ยท ํƒ€์›์ฒด ํƒ์ƒ‰ + ๊ธฐ๋‘ฅ ์†”๋ฒ„(533-2 Search ยท Column) # --------------------------------------------------------------------------- -def _preview_engine( - root: Path, +def _engine_station(root: Path, station: dict[str, Any]) -> dict[str, Any]: + """๋ณต์‚ฌ๋ณธ ์ธก์  โ†’ 533-1 ์ธก์  ํ•œ ์ค„(์ค‘์‹ฌ ยท ์ขŒํ‘œํ‹€ ยท ์ง€๋ฐ˜์„  ยท B06 ์„ค๊ณ„์„  ยท ์•” ๊นŠ์ด ยท ๋…ธ๋ฉด).""" + cross = source.cross_section(root, station["chainage_m"]) + row = { + "chainage_m": station["chainage_m"], + "center_x": station["center_x"], + "center_y": station["center_y"], + "frame": cross["frame"], + "samples": cross["samples"], + } + return grid_engine.station_from_b06(row, cross["design"]) + + +def _node_mask( stations: list[dict[str, Any]], - settings: dict[str, Any], - on_progress: Progress, - should_stop: StopFlag, -) -> dict[str, Any]: - dem = DtmGridSampler.open_cached(source.terrain_grid_path(root)) - x0, x1, y0, y1 = bbox_of(stations, float(settings["box_half_width_m"])) - dx = float(settings["cell_m"]) - xs = np.arange(x0, x1 + dx / 2, dx) - ys = np.arange(y0, y1 + dx / 2, dx) - xx, yy = np.meshgrid(xs, ys) - z, valid = dem.sample_xy(np.column_stack([xx.ravel(), yy.ravel()])) - z = np.where(valid, z, np.nan).reshape(xx.shape) - gy, gx = np.gradient(z, dx) - slope = np.hypot(gx, gy) - f = np.full(z.shape, np.nan) - rows = z.shape[0] - for iy in range(rows): - if should_stop(): - raise InterruptedError - f[iy] = 0.8 + 2.4 / (1.0 + 2.5 * slope[iy] ** 2) - if iy % max(1, rows // 20) == 0: - on_progress(int(100 * iy / rows), f"๋ฏธ๋ฆฌ๋ณด๊ธฐ {iy}/{rows} ์ค„") - volume = np.where(np.isfinite(f), 50.0 + 400.0 * slope**2, np.nan) - iy, ix = np.unravel_index(int(np.nanargmin(f)), f.shape) - r = 10.0 - best = { - "x": float(xs[ix]), - "y": float(ys[iy]), - "z": float(z[iy, ix] + 0.6 * r), - "r": r, - "ratio": 1.0, - "azimuth_deg": 0.0, - "f": float(f[iy, ix]), - "volume": float(volume[iy, ix]), - "method": 0, - } - return { - "grid": {"x0": float(xs[0]), "y0": float(ys[0]), "dx": dx, "nx": len(xs), "ny": len(ys)}, - "z": z, - "f": f, - "v": volume, - "m": np.where(np.isfinite(f), 0.0, np.nan), - "best": best, - "trials": int(np.isfinite(f).sum()), - "preview": True, - } + grid: dict[str, Any], + start_m: float, + end_m: float, + half_width_m: float, +) -> np.ndarray: + """ํƒ์ƒ‰ ๋…ธ๋“œ ์ž๋ฆฌ = ์ค‘์‹ฌ์„ ์—์„œ half_width ์•ˆ ยท ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ค‘์‹ฌ์„  ์ ์˜ ๋ˆ„๊ฐ€๊ฐ€ ๊ตฌ๊ฐ„ ์•ˆ + ยท ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ ์ด ์ค‘์‹ฌ์„  ์–‘ ๋์ ์ธ ์…€(์‹œ์  ์•ž ยท ์ข…์  ๋’ค)์€ ๋บŒ. + + ์ค‘์‹ฌ์„  = ์ธก์  ์ค‘์‹ฌ์„ ์ด์€ ์„ (B06 ๋ˆ„๊ฐ€ ๊ทธ๋Œ€๋กœ ยท route_points ๋Š” ์ ‘์„  ๊บพ์€์„ ์ด๋ผ ๋ˆ„๊ฐ€๊ฐ€ ๋‹ค๋ฆ„).""" + line = np.array( + [[float(s["center_x"]), float(s["center_y"]), float(s["chainage_m"])] for s in stations] + ) + # 0.5 m ๋กœ ์ด˜์ด˜ํžˆ โ€” ๊บพ์ธ ์  ์‚ฌ์ด ์…€๋„ ๋ฐ”๋ฅธ ๋ˆ„๊ฐ€ + dense = [line[:1]] + for a, b in zip(line[:-1], line[1:]): + n = max(1, int(math.ceil(math.hypot(*(b[:2] - a[:2])) / 0.5))) + t = np.linspace(0, 1, n + 1)[1:, None] + dense.append(a + (b - a) * t) + line = np.vstack(dense) + xx, yy = np.meshgrid(grid["x"], grid["y"]) + dist, at = cKDTree(line[:, :2]).query(np.column_stack([xx.ravel(), yy.ravel()])) + chain = line[at, 2] + keep = (dist <= half_width_m) & (chain >= start_m - 1e-6) & (chain <= end_m + 1e-6) + keep &= (at > 0) & (at < len(line) - 1) + return keep.reshape(xx.shape) + + +def _floor(strata: Any, best: dict[str, Any]) -> list[list[float]]: + """์ž„๊ณ„๋ฉด์ด ๋ชป ๋šซ๋Š” ์ธต์— ์ž˜๋ฆฐ ์…€ โ€” [์…€ ๋ฒˆํ˜ธ, ์ธต ์œ—๋ฉด] (ํ™”๋ฉด ํ™œ๋™๋ฉด ๋ฉ”์‰ฌ๊ฐ€ ๊ทธ ๋†’์ด๋กœ ์ž๋ฆ„).""" + xx, yy = np.meshgrid(strata.x, strata.y) + cx, cy, cz = best["center"] + d = np.asarray(best.get("direction") or [0.0, 1.0], float) + pp = (xx - cx) * d[0] + (yy - cy) * d[1] + qq = (-(xx - cx) * d[1] + (yy - cy) * d[0]) / max(float(best["ratio"]), 1e-9) + rest = float(best["radius"]) ** 2 - pp**2 - qq**2 + slip = np.where(rest >= 0, cz - np.sqrt(np.maximum(rest, 0)), np.nan) + hard = strata.hard_top + hit = np.isfinite(slip) & (slip < strata.z_top) & (hard > slip) & (hard < strata.z_top) + return [[int(i), round(float(hard.ravel()[i]), 2)] for i in np.flatnonzero(hit.ravel())] + + +def reference_fs(ground: dict[str, Any] | None, condition: str) -> float: + """๊ธฐ์ค€์•ˆ์ „์œจ(E02 ยท ์—†์œผ๋ฉด ๋งˆ์Šคํ„ฐ ๊ธฐ๋ณธ) โ€” ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ์ค‘ ํฐ ๊ฐ’(๋ณด์ˆ˜).""" + factors = (ground or load_ground_defaults()).get("safety_factors") or {} + row = factors.get(condition) or {} + return float(max(row.get("cut", 1.5), row.get("fill", 1.5))) def run_engine( @@ -166,8 +184,80 @@ def run_engine( on_progress: Progress, should_stop: StopFlag, ) -> dict[str, Any]: - """์—”์ง„ ํ•œ ๋ฒˆ โ†’ {grid, z, f, v, m(๋„˜ํŒŒ์ด (ny, nx)), best, trials}.""" - return _preview_engine(root, stations, settings, on_progress, should_stop) + """์—”์ง„ ํ•œ ๋ฒˆ โ†’ {grid, z, f, v, m(๋„˜ํŒŒ์ด (ny, nx)), best, trials}. + + stations = ๊ตฌ๊ฐ„ ์ธก์  + ์•ž๋’ค ์ด์›ƒ ํ•˜๋‚˜์”ฉ(๊ตฌ๊ฐ„ ๋ ํ•ฉ์„ฑ ์ง€ํ˜•์ด ์ด์–ด์ง€๊ฒŒ). + """ + on_progress(1, "ํ•ฉ์„ฑ ์ง€ํ˜•") + half = float(settings["box_half_width_m"]) + margin = half + float(settings["max_depth_m"]) + 10.0 # ํ™œ๋™์ฒด๊ฐ€ ๊ฒฉ์ž ๋์— ๋‹ฟ์ง€ ์•Š๊ฒŒ + with np.load(source.terrain_grid_path(root)) as npz: + dem = {k: npz[k] for k in ("x", "y", "z", "valid_mask")} + grid = grid_engine.build_grid( + dem, + [_engine_station(root, s) for s in stations], + bbox=bbox_of(stations, margin), + road_q_kpa=float((ground or {}).get("road_surcharge_kpa", DEFAULT_ROAD_SURCHARGE_KPA)), + cell_m=float(settings["cell_m"]), + ) + wet = settings["condition"] == "wet" + strata = grid_engine.strata_from_grid( + grid, (ground or {}).get("materials"), water_depth_m=0.0 if wet else None + ) + mask = _node_mask(stations, grid, settings["start_m"], settings["end_m"], half) + ratios = (1.0,) if settings["shape"] == "sphere" else (1.0, float(settings["ellipsoid_ratio"])) + params = search_engine.SearchParams( + node_mask=mask, + spacing=float(settings["spacing_m"]), + h_min=float(settings["h_min_m"]), + h_max=float(settings["h_max_m"]), + h_step=float(settings["h_step_m"]), + dr=float(settings["dr_m"]), + max_depth=float(settings["max_depth_m"]), + v_min=float(settings["v_min_m3"]), + v_max=float(settings["v_max_m3"]), + ratios=ratios, + groundwater={"mode": "line"} if wet else None, # ์ง€ํ•˜์ˆ˜์œ„๋ฉด = strata.z_water + on_progress=lambda pct, msg: on_progress(2 + int(pct * 0.97), msg), + should_stop=should_stop, + ) + res = search_engine.search(strata, params) + best = res.best + if not math.isfinite(float(best.get("governing", math.inf))): + raise ValueError("์กฐ๊ฑด์— ๋งž๋Š” ํ™œ๋™๋ฉด์ด ์—†์Šต๋‹ˆ๋‹ค โ€” ๋ถ€ํ”ผ ยท ๋†’์ด ๋ฒ”์œ„๋ฅผ ๋„“ํžˆ์„ธ์š”.") + d = best.get("direction") or [0.0, 1.0] + cx, cy, cz = best["center"] + return { + "grid": { + "x0": float(strata.x[0]), + "y0": float(strata.y[0]), + "dx": float(strata.dx), + "nx": len(strata.x), + "ny": len(strata.y), + }, + "z": strata.z_top, + "f": res.f_min, + "v": res.volume, + "m": np.where(res.method >= 0, res.method, np.nan), + "best": { + "x": float(cx), + "y": float(cy), + "z": float(cz), + "r": float(best["radius"]), + "ratio": float(best["ratio"]), + "azimuth_deg": math.degrees(math.atan2(float(d[0]), float(d[1]))) % 360.0, + "f": float(best["governing"]), + "volume": float(best["volume"]), + "method": int(best["method"]), + "hovland": float(best["hovland"]), + "bishop": float(best["bishop"]), + "janbu_corrected": float(best["janbu_corrected"]), + "truncated_ratio": float(best["truncated_ratio"]), + "floor": _floor(strata, best), + }, + "trials": int(res.trials), + "reference_fs": reference_fs(ground, settings["condition"]), + } # --------------------------------------------------------------------------- @@ -201,7 +291,6 @@ def pack( return { "fingerprint": finger, "engine": ENGINE_VERSION, - "preview": bool(raw.get("preview")), "settings": settings, "computed_at": datetime.now().isoformat(timespec="seconds"), "elapsed_s": round(elapsed_s, 2), @@ -217,6 +306,12 @@ def pack( "f_min": round(float(best["f"]), 3), "volume": round(float(best["volume"]), 1), "method": METHOD_NAMES[int(best.get("method") or 0)], + "condition": settings["condition"], + "reference_fs": raw["reference_fs"], + "verdict": "ok" if best["f"] >= raw["reference_fs"] else "ng", + "by_method": { + k: round(float(best[k]), 3) for k in ("hovland", "bishop", "janbu_corrected") + }, "chainage_m": float(_nearest_chainage(stations, best["x"], best["y"])), "cells": int(np.isfinite(f).sum()), "trials": int(raw.get("trials") or 0), @@ -247,7 +342,6 @@ def saved_summaries(project_root: Path) -> list[dict[str, Any]]: "key": path.stem, "fingerprint": doc["fingerprint"], "settings": doc["settings"], - "preview": doc.get("preview", False), **doc["summary"], } ) @@ -264,12 +358,14 @@ def analyze( ) -> dict[str, Any]: """ํ•œ ๊ตฌ๊ฐ„ 3D ํ•ด์„ โ†’ ์ €์žฅ๋ณธ ์“ฐ๊ณ  ์š”์•ฝ ๋Œ๋ ค์คŒ.""" started = time.perf_counter() - stations = source.stations(root) + stations = sorted(source.stations(root), key=lambda s: float(s["chainage_m"])) picked = section_stations(stations, settings["start_m"], settings["end_m"]) if len(picked) < 2: raise ValueError("๊ตฌ๊ฐ„ ์•ˆ ์ธก์ ์ด ๋‘˜ ๋ฏธ๋งŒ์ž…๋‹ˆ๋‹ค.") + first, last = stations.index(picked[0]), stations.index(picked[-1]) + around = stations[max(first - 1, 0) : last + 2] # ์•ž๋’ค ์ด์›ƒ โ€” ๊ตฌ๊ฐ„ ๋ ํ•ฉ์„ฑ ์ง€ํ˜• ์ด์Œ ground = read_ground(project_root) - raw = run_engine(root, picked, ground, settings, on_progress, should_stop) + raw = run_engine(root, around, ground, settings, on_progress, should_stop) result = pack(raw, picked, settings, finger, time.perf_counter() - started) save(project_root, section_key(settings["start_m"], settings["end_m"]), result) return result["summary"] diff --git a/E04_Stab3D/E04_Stab3D_UI_Map.ts b/E04_Stab3D/E04_Stab3D_UI_Map.ts index 85b8d5ebe..407511ee3 100644 --- a/E04_Stab3D/E04_Stab3D_UI_Map.ts +++ b/E04_Stab3D/E04_Stab3D_UI_Map.ts @@ -7,7 +7,7 @@ import { el } from "@ui/ui_template_elements"; import { t } from "@ui/ui_template_locale"; -import { cutCells } from "./logic/E04_Stab3D_Mesh"; +import { cutCells, footprintCenter } from "./logic/E04_Stab3D_Mesh"; import { FS_STEPS, VOLUME_STEPS, @@ -40,6 +40,8 @@ interface View { scale: number; // px / m } +const RANGE_COLOR = "#2563eb"; // ๊ณ ๋ฅธ ๊ตฌ๊ฐ„ โ€” 3D ๋…ธ์„  ๊ด€๊ณผ ๊ฐ™์€ ์ƒ‰ ยท ์•ˆ์ „์œจ ์ƒ‰ ๋‹จ๊ณ„์™€ ๊ฒน์น˜์ง€ ์•Š์Œ + const shortLabel = (label: string): string => label.replace(/^STA\./, "").replace(/\.0+$/, ""); const css = (name: string, fallback: string): string => @@ -61,6 +63,7 @@ export function createPlanMap(): PlanMap { let layer: MapLayer = "fs"; let image: HTMLCanvasElement | null = null; let outline: Set = new Set(); + let marker: { x: number; y: number } | null = null; let view: View = { cx: 0, cy: 0, scale: 1 }; let width = 0; let height = 0; @@ -118,7 +121,7 @@ export function createPlanMap(): PlanMap { if (line.length < 2) return; ctx.lineJoin = ctx.lineCap = "round"; ctx.strokeStyle = text; - ctx.lineWidth = 2; + ctx.lineWidth = 1.5; ctx.beginPath(); line.forEach((p, i) => i ? ctx.lineTo(...toScreen(p[0], p[1])) : ctx.moveTo(...toScreen(p[0], p[1])), @@ -184,9 +187,9 @@ export function createPlanMap(): PlanMap { } function drawMarker(text: string): void { - if (!result) return; + if (!result || !marker) return; const { best } = result; - const [px, py] = toScreen(best.x, best.y); + const [px, py] = toScreen(marker.x, marker.y); ctx.lineWidth = 2; ctx.strokeStyle = "#ffffff"; ctx.beginPath(); @@ -245,7 +248,7 @@ export function createPlanMap(): PlanMap { ctx.setTransform(dpr, 0, 0, dpr, 0, 0); ctx.clearRect(0, 0, width, height); const text = css("--color-text", "#1f2937"); - const accent = css("--color-primary", "#6d28d9"); + const accent = RANGE_COLOR; if (result && image) { const g = result.grid; const [left, top] = toScreen(g.x0 - g.dx / 2, g.y0 + (g.ny - 0.5) * g.dx); @@ -335,6 +338,8 @@ export function createPlanMap(): PlanMap { return { x: rect.left + px, y: rect.top + py }; }, result: () => result, + marker: () => marker, + outline: () => [...outline], layer: () => layer, }; (window as unknown as { __e04Map?: typeof probe }).__e04Map = probe; @@ -354,7 +359,9 @@ export function createPlanMap(): PlanMap { }, setResult(next) { result = next; - outline = new Set(next ? cutCells(next.grid, next.z, next.best).keys() : []); + const cut = next ? cutCells(next.grid, next.z, next.best) : new Map(); + outline = new Set(cut.keys()); + marker = next ? footprintCenter(next.grid, cut, next.best) : null; buildImage(); fit(); draw(); diff --git a/E04_Stab3D/E04_Stab3D_UI_Page.ts b/E04_Stab3D/E04_Stab3D_UI_Page.ts index 8cad3b057..858502ba9 100644 --- a/E04_Stab3D/E04_Stab3D_UI_Page.ts +++ b/E04_Stab3D/E04_Stab3D_UI_Page.ts @@ -35,7 +35,6 @@ interface SavedSummary { volume: number; method: string; stale: boolean; - preview: boolean; } interface Overview { @@ -302,10 +301,19 @@ export function renderE04Stab3D(root: HTMLElement): void { ); const rows: [LocaleKey, string, string?][] = [ ["E04_Result_F3d", s.f_min.toFixed(3), stepColor(FS_STEPS, s.f_min)], + [ + "E04_Result_Verdict", + `${t(s.verdict === "ok" ? "E04_Verdict_ok" : "E04_Verdict_ng")} (${t( + `E04_Condition_${s.condition}` as LocaleKey, + )} โ‰ฅ ${s.reference_fs.toFixed(2)})`, + ], ["E04_Result_F2d", f2d === null ? t("E04_No2D") : f2d.toFixed(3)], ["E04_Result_Ratio", ratio === null ? "-" : ratio.toFixed(3)], ["E04_Result_Volume", `${Math.round(s.volume).toLocaleString()} mยณ`], ["E04_Result_Method", t(`E04_MethodName_${s.method}` as LocaleKey)], + ["E04_Result_Bishop", s.by_method.bishop.toFixed(3)], + ["E04_Result_Janbu", s.by_method.janbu_corrected.toFixed(3)], + ["E04_Result_Hovland", s.by_method.hovland.toFixed(3)], ["E04_Result_Where", station?.label ?? `${s.chainage_m.toFixed(1)} m`], ["E04_Result_Cells", s.cells.toLocaleString()], ["E04_Result_Trials", s.trials.toLocaleString()], @@ -314,6 +322,7 @@ export function renderE04Stab3D(root: HTMLElement): void { const list = el("dl", { className: "e04-result__list" }); for (const [key, value, color] of rows) { const dd = el("dd", { text: value, attrs: { "data-field": key } }); + if (key === "E04_Result_Verdict") dd.classList.add(`is-${s.verdict}`); if (color) { const chip = el("i", { className: "e04-section__chip" }); chip.style.background = color; @@ -323,8 +332,7 @@ export function renderE04Stab3D(root: HTMLElement): void { } const notes: HTMLElement[] = []; if (result.stale) notes.push(el("p", { className: "e04-warn", text: t("E04_Result_Stale") })); - if (result.preview) - notes.push(el("p", { className: "e04-note", text: t("E04_Result_Preview") })); + notes.push(el("p", { className: "e04-note", text: t("E04_Result_Note") })); resultBody.replaceChildren(list, ...notes); } @@ -371,11 +379,11 @@ export function renderE04Stab3D(root: HTMLElement): void { async function analyze(force: boolean): Promise { const values = settings.read(); - const { start_m, end_m, method, ...search } = values; + const { start_m, end_m, condition, ...search } = values; try { const body = await api<{ status: string }>(`/projects/${projectId}/stab/analyze3d`, { method: "POST", - body: JSON.stringify({ start_m, end_m, method, search, force }), + body: JSON.stringify({ start_m, end_m, condition, search, force }), }); if (body.status === "cached") { showToast(t("E04_Cached"), "info"); @@ -415,7 +423,7 @@ export function renderE04Stab3D(root: HTMLElement): void { ...ov.defaults, start_m: first?.start_m ?? 0, end_m: first?.end_m ?? 100, - method: "bishop", + condition: "dry", }, ); map.setRoute(ov.line, ov.stations); diff --git a/E04_Stab3D/E04_Stab3D_UI_Settings.ts b/E04_Stab3D/E04_Stab3D_UI_Settings.ts index 69377dded..215201303 100644 --- a/E04_Stab3D/E04_Stab3D_UI_Settings.ts +++ b/E04_Stab3D/E04_Stab3D_UI_Settings.ts @@ -1,6 +1,6 @@ /* ============================================================================= * E04_Stab3D_UI_Settings.ts - * E04 ์ขŒ์ธก ํƒ์ƒ‰ ์„ค์ • ์นธ(PLAN 533-3) โ€” ๊ตฌ๊ฐ„ ์‹œ์ž‘ ยท ๋ ยท ๊ธฐ๋ณธ(๋ชจ์–‘ ยท ํƒ์ƒ‰ ํญ ยท ๋ฐฉ๋ฒ•) ยท ๊ณ ๊ธ‰(์ ‘ํž˜ ยท + * E04 ์ขŒ์ธก ํƒ์ƒ‰ ์„ค์ • ์นธ(PLAN 533-3) โ€” ๊ตฌ๊ฐ„ ์‹œ์ž‘ ยท ๋ ยท ๊ธฐ๋ณธ(์กฐ๊ฑด ยท ๋ชจ์–‘ ยท ํƒ์ƒ‰ ํญ) ยท ๊ณ ๊ธ‰(์ ‘ํž˜ ยท * ๊ธฐ๋‘ฅ ยท ๊ตฌ ์ค‘์‹ฌ ๊ฒฉ์ž ยท ๋ฐ˜์ง€๋ฆ„ ์ฆ๋ถ„ ยท ๊นŠ์ด ยท ๋ถ€ํ”ผ ์ œ์•ฝ) ยท [๊ธฐ๋ณธ๊ฐ’]. ๊ธฐ๋ณธ๊ฐ’ = ์„œ๋ฒ„ SearchSettings. * ========================================================================== */ @@ -17,7 +17,7 @@ import { t, type LocaleKey } from "@ui/ui_template_locale"; export interface Settings { start_m: number; end_m: number; - method: string; + condition: string; shape: string; ellipsoid_ratio: number; box_half_width_m: number; @@ -32,7 +32,7 @@ export interface Settings { v_max_m3: number; } -type NumberKey = Exclude; +type NumberKey = Exclude; const BASIC: [NumberKey, LocaleKey][] = [ ["ellipsoid_ratio", "E04_Ratio"], @@ -69,23 +69,23 @@ export function buildSettingsCards() { const shape = createSelectField({ label: t("E04_Shape"), compact: true, - options: (["both", "sphere", "ellipsoid"] as const).map((v) => ({ + options: (["both", "sphere"] as const).map((v) => ({ value: v, text: t(`E04_Shape_${v}` as LocaleKey), })), onChange: changed, }); - const method = createSelectField({ - label: t("E04_Method"), + const condition = createSelectField({ + label: t("E04_Condition"), compact: true, - options: (["bishop", "hovland"] as const).map((v) => ({ + options: (["dry", "wet"] as const).map((v) => ({ value: v, - text: t(`E04_Method_${v}` as LocaleKey), + text: t(`E04_Condition_${v}` as LocaleKey), })), onChange: changed, }); const basicCard = createPanelCard(t("E04_Card_Search"), false); - basicCard.append(shape.root, ...BASIC.map(([k, l]) => number(k, l)), method.root); + basicCard.append(condition.root, shape.root, ...BASIC.map(([k, l]) => number(k, l))); let defaults: Partial = {}; const advancedCard = createPanelCard(t("E04_Card_Advanced"), true); @@ -99,14 +99,14 @@ export function buildSettingsCards() { size: "sm", onClick: () => { const range = { start_m: read().start_m, end_m: read().end_m }; - write({ ...read(), ...defaults, ...range, method: "bishop" }); + write({ ...read(), ...defaults, ...range, condition: "dry" }); changed(); }, }), ); function read(): Settings { - const out = { shape: shape.select.value, method: method.select.value } as Settings; + const out = { shape: shape.select.value, condition: condition.select.value } as Settings; for (const [key, field] of fields) out[key] = Number(field.input.value); return out; } @@ -117,7 +117,7 @@ export function buildSettingsCards() { if (typeof value === "number" && Number.isFinite(value)) field.input.value = String(value); } if (values.shape) shape.select.value = values.shape; - if (values.method) method.select.value = values.method; + if (values.condition) condition.select.value = values.condition; } return { diff --git a/E04_Stab3D/E04_Stab3D_UI_Style.css b/E04_Stab3D/E04_Stab3D_UI_Style.css index 74fc63e89..f215ec07a 100644 --- a/E04_Stab3D/E04_Stab3D_UI_Style.css +++ b/E04_Stab3D/E04_Stab3D_UI_Style.css @@ -86,7 +86,7 @@ .e04-map__empty { position: absolute; - inset: auto 0 45% 0; + inset: var(--spacing-16) 0 auto 0; margin: 0; text-align: center; color: var(--color-text-muted); @@ -266,6 +266,14 @@ justify-content: flex-end; } +.e04-result__list dd.is-ok { + color: var(--color-success); +} + +.e04-result__list dd.is-ng { + color: var(--color-danger); +} + .e04-warn { margin: var(--spacing-12) 0 0; color: var(--color-danger); diff --git a/E04_Stab3D/logic/E04_Stab3D_Mesh.ts b/E04_Stab3D/logic/E04_Stab3D_Mesh.ts index 7ff8d3c66..66361007a 100644 --- a/E04_Stab3D/logic/E04_Stab3D_Mesh.ts +++ b/E04_Stab3D/logic/E04_Stab3D_Mesh.ts @@ -70,23 +70,42 @@ export function slipHeight(best: SlipBest, x: number, y: number): number | null return rest < 0 ? null : best.z - Math.sqrt(rest); } -/** ์ž˜๋ฆฐ ์…€ โ†’ ํ™œ๋™๋ฉด ๋†’์ด(ํ™œ๋™๋ฉด์ด ์ง€ํ‘œ๋ณด๋‹ค ๋‚ฎ์€ ์…€๋งŒ) */ +/** ์ž˜๋ฆฐ ์…€ โ†’ ํ™œ๋™๋ฉด ๋†’์ด(ํ™œ๋™๋ฉด์ด ์ง€ํ‘œ๋ณด๋‹ค ๋‚ฎ์€ ์…€๋งŒ ยท ๋ชป ๋šซ๋Š” ์ธต ์œ—๋ฉด ์•„๋ž˜๋กœ๋Š” ์•ˆ ๋‚ด๋ ค๊ฐ) */ export function cutCells( grid: Grid, z: readonly (number | null)[], best: SlipBest, ): Map { + const floor = new Map(best.floor ?? []); const out = new Map(); z.forEach((top, index) => { if (top === null) return; const x = grid.x0 + (index % grid.nx) * grid.dx; const y = grid.y0 + Math.floor(index / grid.nx) * grid.dx; const slip = slipHeight(best, x, y); - if (slip !== null && slip < top) out.set(index, slip); + if (slip === null) return; + const base = Math.max(slip, floor.get(index) ?? -Infinity); + if (base < top) out.set(index, base); }); return out; } +/** ์ž„๊ณ„ ํ™œ๋™์ฒด ํ‰๋ฉด ์ค‘์‹ฌ = ์ž˜๋ฆฐ ์…€ ์ค‘์‹ฌ์˜ ํ‰๊ท (์—†์œผ๋ฉด ๊ตฌ ์ค‘์‹ฌ) โ€” ์ง€๋„ ์ตœ์†Œ ํ‘œ์‹œ ์ž๋ฆฌ */ +export function footprintCenter( + grid: Grid, + cut: ReadonlyMap, + best: SlipBest, +): { x: number; y: number } { + if (!cut.size) return { x: best.x, y: best.y }; + let sx = 0; + let sy = 0; + for (const index of cut.keys()) { + sx += grid.x0 + (index % grid.nx) * grid.dx; + sy += grid.y0 + Math.floor(index / grid.nx) * grid.dx; + } + return { x: sx / cut.size, y: sy / cut.size }; +} + /** ์ž˜๋ฆฐ ์…€์˜ 8 ์ด์›ƒ ์ค‘ ์ž˜๋ฆฌ์ง€ ์•Š์€ ์…€(๊ฐ’ ์žˆ์Œ) โ€” ํ™œ๋™๋ฉด ํ…Œ๋‘๋ฆฌ */ export function rimCells( grid: Grid, diff --git a/E04_Stab3D/logic/E04_Stab3D_Raster.ts b/E04_Stab3D/logic/E04_Stab3D_Raster.ts index dd73bdaac..a98e95df8 100644 --- a/E04_Stab3D/logic/E04_Stab3D_Raster.ts +++ b/E04_Stab3D/logic/E04_Stab3D_Raster.ts @@ -26,6 +26,8 @@ export interface SlipBest { method: number; ix: number; iy: number; + /** ๋ชป ๋šซ๋Š” ์ธต์— ์ž˜๋ฆฐ ์…€ โ€” [์…€ ๋ฒˆํ˜ธ, ์ธต ์œ—๋ฉด] ยท ํ™œ๋™๋ฉด์„ ๊ทธ ๋†’์ด์—์„œ ์ž๋ฆ„ */ + floor?: [number, number][]; } export interface Summary3d { @@ -37,12 +39,16 @@ export interface Summary3d { chainage_m: number; cells: number; trials: number; + condition: string; + reference_fs: number; + verdict: "ok" | "ng"; + /** ๊ฐ™์€ ์ž„๊ณ„๋ฉด์˜ ๋ฐฉ๋ฒ•๋ณ„ ์•ˆ์ „์œจ */ + by_method: { hovland: number; bishop: number; janbu_corrected: number }; } export interface Result3d { key: string; stale: boolean; - preview: boolean; computed_at: string; elapsed_s: number; settings: Record; diff --git a/E05_StabReport/E05_StabReport_Area.py b/E05_StabReport/E05_StabReport_Area.py new file mode 100644 index 000000000..87633ad7a --- /dev/null +++ b/E05_StabReport/E05_StabReport_Area.py @@ -0,0 +1,130 @@ +"""E05 ๋ณด๊ณ ์„œ s7 ๊ด‘์—ญ์š”์•ฝ โ€” E07 ๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ ์ €์žฅ๋ณธ โ†’ ์‹œํŠธ (PLAN 536-2). + +์นธ ์ž๋ฆฌ = `resources/master_template/report/stab_report_slots.md` s7. +E07 ์ €์žฅ๋ณธ์ด ์—†์œผ๋ฉด ใ€Œ๊ด‘์—ญ ํ•ด์„ ์ „ใ€. +""" + +from __future__ import annotations + +from pathlib import Path +from typing import Any + +from E02_StabGround.E02_StabGround_Model import load_defaults +from E05_StabReport.E05_StabReport_Cells import UNCONFIRMED +from E07_StabArea.E07_StabArea_Engine import CLASS_KEYS, LEVEL_KEYS +from E07_StabArea.E07_StabArea_Router import RESULT, AreaRequest, load + +LEVEL_NAMES = {"low": "๋‚ฎ์Œ", "mid": "๋ณดํ†ต", "high": "๋†’์Œ"} + + +def scenario_text(s: dict[str, Any]) -> str: + """์‹œ๋‚˜๋ฆฌ์˜ค ๊ธ€ โ€” ํ™”๋ฉด ์‹œ๋‚˜๋ฆฌ์˜ค ์ด๋ฆ„๊ณผ ๊ฐ™์€ ๋ง.""" + if s.get("scenario") == "saturated": + return "์ตœ์•… โ€” ์™„์ „ ํฌํ™”(m = 1 ยท ๊ฐ•์šฐ ๊ทนํ•œ)" + if s.get("scenario") == "sinmap": + return ( + f"ํ‘œ์ค€ โ€” SINMAP ์Šต์œค์ง€์ˆ˜ W = min(Rยทa / (Tยทsinฮธ), 1) ยท " + f"T/R {s['tr_lo']:g}~{s['tr_hi']:g} m" + ) + return f"์˜› ์กฐ๊ฑด({s.get('scenario')}) โ€” E07 ์—์„œ [๋‹ค์‹œ ํ•ด์„]" + + +def stamp(project_root: Path) -> list[int] | None: + """ํ•ด์„ ์ง€๋ฌธ์šฉ โ€” ์ €์žฅ๋ณธ ํฌ๊ธฐ ยท ๊ณ ์นœ ๋•Œ.""" + path = Path(project_root) / RESULT + return [path.stat().st_size, path.stat().st_mtime_ns] if path.is_file() else None + + +def read(project_root: Path) -> dict[str, Any] | None: + """์ €์žฅ๋ณธ์—์„œ ๋ณด๊ณ ์„œ์— ์“ฐ๋Š” ๊ฒƒ๋งŒ(๊ฒฉ์ž ๋ฐฐ์—ด ๋บŒ).""" + doc = load(project_root) + if not doc: + return None + return {k: doc[k] for k in ("settings", "computed_at", "stations", "summary")} + + +def _set(sheet: dict[str, Any], a1: str, value: Any, style: int | None = None) -> None: + cell = dict(sheet["์นธ"].get(a1) or {}) + cell["๊ฐ’"] = round(value, 6) if isinstance(value, float) else value + if style is not None: + cell["์„œ์‹"] = style + sheet["์นธ"][a1] = cell + + +UNCONFIRMED_KEYS = ( # (์„ค์ • ์นธ, ๊ธ€) โ€” ๊ธฐ๋ณธ๊ฐ’ ๊ทธ๋Œ€๋กœ๋ฉด ์‚ฌ์šฉ์ž ํ™•์ • ์ „ + ("c_root", "๋ฟŒ๋ฆฌ ์ ์ฐฉ {c_root:g} kPa"), + ("soil_depth_m", "ํ† ์ธต {soil_depth_m:g} m"), + ("tr_lo", "T/R {tr_lo:g}~{tr_hi:g} m"), + ("level_lo_pct", "์ธก์  ๋ฌธํ„ฑ {level_lo_pct:g} % ยท {level_hi_pct:g} %"), +) +PAIRS = {"tr_lo": "tr_hi", "level_lo_pct": "level_hi_pct"} + + +def _unconfirmed(s: dict[str, Any]) -> str: + """๊ธฐ๋ณธ๊ฐ’ ๊ทธ๋Œ€๋กœ์ธ ์„ค์ • ยท ๋ฌผ์„ฑ(E02 ๋ถ•์ ํ†  ๋ฌธํ—Œ ์ถ”์ •์น˜) โ€” ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ํ•œ ์ค„.""" + fields = AreaRequest.model_fields + same = [ + text.format(**s) + for key, text in UNCONFIRMED_KEYS + if all( + k in s and float(s[k]) == float(fields[k].default) for k in (key, PAIRS.get(key, key)) + ) + ] + base = {m["id"]: m for m in load_defaults()["materials"]}.get( + s.get("material_id") or "colluvium" + ) + if base and all( + float(s.get(k, -1)) == float(base[k]) for k in ("gamma_t", "gamma_sat", "c", "phi") + ): + same.insert(0, f"ํ† ์ธต ๋ฌผ์„ฑ = {base['name']} ๋ฌธํ—Œ ์ถ”์ •์น˜") + return f"{' ยท '.join(same)} โ€” {UNCONFIRMED}" if same else "์—†์Œ(๋ชจ๋‘ ์‚ฌ์šฉ์ž ์ž…๋ ฅ)" + + +def fill_area(sheet: dict[str, Any], area: dict[str, Any] | None, labels: dict[float, str]) -> None: + """s7 โ€” ์กฐ๊ฑด ๋„ท ยท ๋“ฑ๊ธ‰๋ณ„ ๋ฉด์  ์—ฌ์„ฏ ยท ์ธก์  ๋“ฑ๊ธ‰๋ณ„ ์ˆ˜ ยท ๋ณดํ†ต ยท ๋†’์Œ ์ธก์ (22 ํ–‰๋ถ€ํ„ฐ ํ•œ ์ค„์”ฉ).""" + if not area: + _set(sheet, "B5", "๊ด‘์—ญ ํ•ด์„ ์ „ โ€” E07 ๊ด‘์—ญ์—์„œ [ํ•ด์„] ๋’ค [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]") + return + if "station_levels" not in area["summary"]: + _set(sheet, "B5", "์˜› ํŒ ์ €์žฅ๋ณธ โ€” E07 ๊ด‘์—ญ์—์„œ [๋‹ค์‹œ ํ•ด์„] ๋’ค [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]") + return + s, summary = area["settings"], area["summary"] + _set(sheet, "B5", f"์ค‘์‹ฌ์„  ยฑ {s['band_m']:g} m ยท ๊ฒฉ์ž {s['cell_m']:g} m") + _set( + sheet, + "B6", + f"ฮณt {s['gamma_t']:g} ยท ฮณsat {s['gamma_sat']:g} kN/mยณ ยท c {s['c']:g}~{s['c_hi']:g} kPa ยท " + f"ฯ† {s['phi']:g}~{s['phi_hi']:g}ยฐ ยท ๋ฟŒ๋ฆฌ ์ ์ฐฉ {s['c_root']:g} kPa ยท " + f"ํ† ์ธต {s['soil_depth_m']:g} m", + ) + _set(sheet, "B7", scenario_text(s)) + _set(sheet, "B8", str(area["computed_at"]).replace("T", " ")) + _set(sheet, "B9", _unconfirmed(s)) + for k in range(1, 7): + _set(sheet, f"C{11 + k}", float(summary["areas_m2"][CLASS_KEYS[k]])) + lo, hi = s["level_lo_pct"], s["level_hi_pct"] + bounds = {"low": f"< {lo:g} %", "mid": f"{lo:g} ~ {hi:g} %", "high": f"โ‰ฅ {hi:g} %"} + for col, text in zip("EFG", ("์ธก์  ๋“ฑ๊ธ‰", "SI<1 ์…€ ๋น„์œจ", "์ธก์  ์ˆ˜")): + _set(sheet, f"{col}11", text, 6) + for i, key in enumerate(LEVEL_KEYS): + _set(sheet, f"E{12 + i}", LEVEL_NAMES[key], 7) + _set(sheet, f"F{12 + i}", bounds[key], 7) + _set(sheet, f"G{12 + i}", int(summary["station_levels"][key]), 14) + _set(sheet, "A20", f"3. ์œ„ํ—˜ ์ธก์  โ€” ๋ณดํ†ต ยท ๋†’์Œ (์˜ํ–ฅ ๊ตฌ์—ญ ์•ˆ SI < 1 ์…€ ๋น„์œจ {lo:g} % ์ด์ƒ)") + _set(sheet, "E21", "์ธก์  ๋“ฑ๊ธ‰ (SI<1 ๋น„์œจ)") + rows = [r for r in area["stations"] if r.get("level") in ("mid", "high")] + template = {col: dict(sheet["์นธ"].get(f"{col}22") or {}) for col in "ABCDEFG"} + if not rows: + _set(sheet, "A22", "์œ„ํ—˜ ์ธก์  ์—†์Œ") + for i, row in enumerate(rows): + r = 22 + i + for col, cell in template.items(): + sheet["์นธ"][f"{col}{r}"] = dict(cell) + ch = float(row["chainage_m"]) + _set(sheet, f"A{r}", row.get("label") or labels.get(round(ch, 3)) or f"{ch:.2f}") + _set(sheet, f"B{r}", ch) + _set(sheet, f"C{r}", float(row["fs_min"])) + _set(sheet, f"D{r}", float(row["si_min"])) + _set(sheet, f"E{r}", f"{LEVEL_NAMES[row['level']]} ({row['risk_ratio'] * 100:.1f} %)") + _set(sheet, f"F{r}", float(row["risk_area_m2"])) + _set(sheet, f"G{r}", float(row["slope_max_deg"])) diff --git a/E05_StabReport/E05_StabReport_Build.py b/E05_StabReport/E05_StabReport_Build.py index 6606c2a6c..241c49336 100644 --- a/E05_StabReport/E05_StabReport_Build.py +++ b/E05_StabReport/E05_StabReport_Build.py @@ -1,13 +1,16 @@ -"""E05 ์•ˆ์ •์„ฑํ‰๊ฐ€ ๋ณด๊ณ ์„œ ๋นŒ๋”(์š”๊ตฌ 347-5 ยท 351 ยท ์š”๊ตฌ์„œ 4-3 ยท PLAN 534-3). +"""E05 ์•ˆ์ •์„ฑํ‰๊ฐ€ ๋ณด๊ณ ์„œ ๋นŒ๋”(์š”๊ตฌ 347-5 ยท 351 ยท ์š”๊ตฌ์„œ 4-3 ยท PLAN 534-3 ยท 534-5). -์ž…๋ ฅ = E02 ์ง€๋ฐ˜ ์กฐ๊ฑด(`ground.json`) ยท E03 2D ๊ฒฐ๊ณผ ์š”์•ฝ(`_derived/result2d.json`) ยท ์ธก์ ๋ณ„ ์ €์žฅ๋ณธ ยท -๋Œ€ํ‘œ ๋‹จ๋ฉด(`representative.json`) ยท E04 3D ์ €์žฅ๋ณธ ์š”์•ฝ(์žˆ์œผ๋ฉด) ยท E01 ๋ณต์‚ฌ๋ณธ(์ธก์  ยท ํšก๋‹จ ยท ๋…ธ์„ ). +์ž…๋ ฅ = E02 ์ง€๋ฐ˜ ์กฐ๊ฑด(`ground.json`) ยท E03 2D ํŒŒ์ƒ ์ €์žฅ๋ณธ(`stab2d_summary` ๊ฐ€์žฅ ์ตœ๊ทผ ยท ์ธก์ ๋ณ„ +`stab2d_station`) ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด(E03 ์ž๋™ + `representative.json` ์ผฌ/๋”) ยท E04 3D ์ €์žฅ๋ณธ ์š”์•ฝ +(์žˆ์œผ๋ฉด) ยท E07 ๊ด‘์—ญ ์ €์žฅ๋ณธ(์žˆ์œผ๋ฉด) ยท E03 ๋ณด๊ฐ• ๊ฒ€ํ†  ์ €์žฅ๋ณธ(์žˆ์œผ๋ฉด ยท ์—†์œผ๋ฉด s8 ๋บŒ) ยท +E01 ๋ณต์‚ฌ๋ณธ(์ธก์  ยท ํšก๋‹จ ยท ๋…ธ์„ ). ์–‘์‹ = `resources/master_template/report/stab_report.json` ์นธ ์ž๋ฆฌ = ๊ฐ™์€ ํด๋” `stab_report_slots.md`. ๋ชซ: ์‚ฌ์šฉ์ž(ํ‘œ์ง€ s1 ยท ๊ฐœ์š” s2) = ์ฒ˜์Œ ๋งŒ๋“ค ๋•Œ๋งŒ ์ฑ„์›€ ยท -ํ”„๋กœ๊ทธ๋žจ(์ž…๋ ฅ์กฐ๊ฑด s3 ยท ์ธก์ ํ‘œ s4 ยท ๋Œ€ํ‘œ๋‹จ๋ฉด s5 ยท 3D์š”์•ฝ s6) -= [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ๋•Œ ์–‘์‹์—์„œ ํ†ต์งธ๋กœ ๋‹ค์‹œ ์”€. +ํ”„๋กœ๊ทธ๋žจ(๋ชฉ์ฐจ s9 ยท ์ž…๋ ฅ์กฐ๊ฑด s3 ยท ์ธก์ ํ‘œ s4 ยท ๋Œ€ํ‘œ๋‹จ๋ฉด s5 ยท 3D์š”์•ฝ s6 ยท ๊ด‘์—ญ์š”์•ฝ s7 ยท ๋ณด๊ฐ•๊ฒ€ํ†  s8 ยท +๋ถ€๋ก_์ ˆํŽธ s10) = [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ๋•Œ ์–‘์‹์—์„œ ํ†ต์งธ๋กœ ๋‹ค์‹œ ์”€. ๋Œ€ํ‘œ ๋‹จ๋ฉด ยท ๋ถ€๋ก = `_Section` ยท +๋ชฉ์ฐจ ์ชฝ ๋ฒˆํ˜ธ ยท ๋จธ๋ฆฌ๋ง/๊ผฌ๋ฆฌ๋ง ยท ๊ธด ํ‘œ ์ชฝ ๋‚˜๋ˆ” = `_Print` ยท ์นธ ๊ณต์šฉ = `_Cells`. ํŒ์ • ๊ธ€ ์นธ = ์—”์ง„ ํŒ์ • ๊ฐ’ + N.G ์„œ์‹ 10 ยท O.K ์„œ์‹ 11 ์„ ์ง์ ‘ ๋ถ™์ž„ (์ธ์‡„ HTML ยท xlsx ๋Š” ์กฐ๊ฑด๋ถ€ ์„œ์‹์„ ์•ˆ ๋จน์Œ). @@ -18,7 +21,6 @@ storage(์ž‘์—…๋ณธ ยท ์ดˆ๊ธฐ๊ฐ’ ยท ๊ทธ๋ฆผ ยท ์˜› ํŒ) = `E05_StabReport_Store.py` from __future__ import annotations -import copy import hashlib import json import re @@ -26,43 +28,41 @@ from datetime import date from pathlib import Path from typing import Any +from common_util.common_util_derived_store import read_derived from E01_StabRegister import E01_StabRegister_Source as source from E02_StabGround import E02_StabGround_Model as ground_model -from E03_StabSection.E03_StabSection_Engine_Model import final_surface +from E03_StabSection import E03_StabSection_View as view2d +from E03_StabSection.E03_StabSection_Router import KIND_STATION, _latest_summary, _overrides from E04_Stab3D import E04_Stab3D_Run as run3d +from E05_StabReport import E05_StabReport_Area as area from E05_StabReport import E05_StabReport_Figure as figure +from E05_StabReport import E05_StabReport_Print as printer +from E05_StabReport import E05_StabReport_Reinforce as reinforce +from E05_StabReport import E05_StabReport_Section as section +from E05_StabReport.E05_StabReport_Cells import ( # noqa: F401 โ€” ์‹œํ—˜ ยท ๋‹ค๋ฅธ ๋ชจ๋“ˆ์ด build.* ๋กœ ์”€ + A1, + SIDE_NAMES, + ST_NG, + ST_OK, + _copy_row, + _criterion, + _label_at, + _labels, + _seismic, + _set, + _verdict, + fig_key, + mark, +) ROOT = Path(__file__).resolve().parents[1] FORM_PATH = ROOT / "resources" / "master_template" / "report" / "stab_report.json" STAGE = "E05_StabReport" REPORT, META = "report.json", "report_meta.json" USER_SHEETS = ("s1", "s2") -PROGRAM_SHEETS = ("s3", "s4", "s5", "s6") -BLOCK = 28 # ๋Œ€ํ‘œ๋‹จ๋ฉด ํ•œ ์ชฝ = 28 ํ–‰ -E03 = Path("E03_StabSection") -RESULT2D = E03 / "_derived" / "result2d.json" -STATION_RESULTS = E03 / "_derived" / "results" -REPRESENTATIVE = E03 / "representative.json" +PROGRAM_SHEETS = ("s9", "s3", "s4", "s5", "s6", "s7", "s8", "s10") +REPRESENTATIVE = Path("E03_StabSection") / "representative.json" BAND_KEY = "fig/3d_band.png" -LOWEST_N = 5 # representative.json ์ด ์—†์„ ๋•Œ โ€” ์ตœ์†Œ ์•ˆ์ „์œจ ํ•˜์œ„ N -CASES = ("dry", "wet", "seismic") -SIDE_NAMES = {"cut": "๊นŽ๊ธฐ", "fill": "์Œ“๊ธฐ"} -METHOD_NAMES = { - "bishop": "Bishop ๊ฐ„ํŽธ๋ฒ•", - "janbu": "Janbu ๋ณด์ •", - "janbu_corrected": "Janbu ๋ณด์ •", - "ordinary": "Fellenius", - "spencer": "Spencer", -} -REASONS = { - "fill_10m": "์Œ“๊ธฐ ๋†’์ด 10 m ์ด์ƒ", - "cut_15m": "๊นŽ๊ธฐ ๋†’์ด 15 m ์ด์ƒ", - "lowest_fs": "์ตœ์†Œ ์•ˆ์ „์œจ ํ•˜์œ„", - "user": "์‚ฌ์šฉ์ž ์ง€์ •", -} -GROUND_NAMES = {"soil": "ํ† ์‚ฌ", "ripping_rock": "๋ฆฌํ•‘์•”", "blasting_rock": "๋ฐœํŒŒ์•”", "fill": "์„ฑํ† "} -ST_TEXT, ST_BODY, ST_FS, ST_NG, ST_OK, ST_NUM1, ST_PROP, ST_CHAIN = 5, 7, 8, 10, 11, 13, 16, 17 -A1 = re.compile(r"^([A-Z]+)(\d+)$") class MissingInput(Exception): @@ -82,10 +82,6 @@ def initial_dir(project_root: Path) -> Path: return Path(project_root) / "initial_snapshot" / STAGE -def fig_key(station_key: str) -> str: - return f"fig/{station_key}.png" - - def fig_path(folder: Path, key: str) -> Path: """๊ทธ๋ฆผ ์—ด์‡  โ†’ ํŒŒ์ผ(์œˆ๋„์šฐ ๋ชป ์“ฐ๋Š” ๊ธ€์ž `:` ๋“ฑ์€ `_`).""" name = key.removeprefix("fig/") @@ -104,23 +100,64 @@ def _ground(project_root: Path) -> dict[str, Any]: ) -def _station_result(project_root: Path, key: str) -> dict[str, Any] | None: - folder = Path(project_root) / STATION_RESULTS - for name in (key, re.sub(r"[^0-9A-Za-z._+-]", "_", key)): - found = _read(folder / f"{name}.json") - if found: - return found - return None +def _station_result(project_root: Path, fp: str) -> tuple[dict[str, Any] | None, list | None]: + """์ธก์  ์ €์žฅ๋ณธ โ€” (๊ฒฐ๊ณผ, ํ•ด์„์— ์“ด ๋„“ํžŒ ์ง€๋ฐ˜์„  ยท ์—†์œผ๋ฉด None).""" + value = (read_derived(Path(project_root), KIND_STATION, fp) or {}).get("value") or {} + return value.get("out"), (value.get("inputs") or {}).get("ground") + + +def _row2d(row: dict[str, Any], method: str) -> dict[str, Any]: + """E03 ์š”์•ฝ ์ค„ โ†’ ๋ณด๊ณ ์„œ ์ค„ โ€” ๋น„ํƒˆ = ๊ธฐ์ค€ ๋Œ€๋น„ ๊ฐ€์žฅ ๋‚ฎ์€ ์ชฝ(fs / ๊ธฐ์ค€) ยท ํŒ์ • = ์กฐ๊ฑด ํ•˜๋‚˜๋ผ๋„ + N.G ๋ฉด ng.""" + roles = {s["side"]: s["role"] for s in row.get("sides") or []} + cases = row.get("cases") or {} + worst = min( + ( + (c["fs"] / c["required"], side) + for sides in cases.values() + for side, c in sides.items() + if c.get("fs") is not None and c.get("required") + ), + default=None, + ) + side = worst[1] if worst else None + verdicts = set((row.get("verdict") or {}).values()) + values = [v for v in (row.get("min") or {}).values() if v is not None] + return { + "station_key": row["key"], + "chainage_m": row["chainage_m"], + "fp": row.get("fp"), + "side": roles.get(side), + "fs_by_condition": { + cond: sides[side]["fs"] for cond, sides in cases.items() if side in sides + }, + "fs_min": min(values) if values else None, + "min_by_condition": row.get("min") or {}, + "method": method, + "verdict": "ng" if "ng" in verdicts else "ok" if verdicts == {"ok"} else None, + "representative": row.get("representative") or {}, + } + + +def _result2d(project_root: Path) -> dict[str, Any] | None: + """E03 ๊ฐ€์žฅ ์ตœ๊ทผ ์š”์•ฝ(์ง€๊ธˆ ์ž…๋ ฅ๊ณผ ๋‹ฌ๋ผ๋„ ยท E03 ํ™”๋ฉด๊ณผ ๊ฐ™์€ ํŒ) + ๋Œ€ํ‘œ ๋‹จ๋ฉด ํ‘œ์‹œ.""" + summary, _ = _latest_summary(project_root, "") + if not summary: + return None + rows = [dict(r) for r in summary.get("stations") or []] + view2d.mark_representative(rows, _overrides(project_root)) + method = summary.get("method") or view2d.PRIMARY + return {**summary, "stations": [_row2d(r, method) for r in rows]} def analysis_fingerprint(project_root: Path) -> str: """ํ•ด์„ ์ง€๋ฌธ โ€” 2D ์š”์•ฝ ์ง€๋ฌธ ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด ยท ์ง€๋ฐ˜ ์กฐ๊ฑด ยท 3D ์ €์žฅ๋ณธ ยท ์–‘์‹์ด ๊ฐ™์œผ๋ฉด ๊ฐ™์Œ (๊ฐ€๋ฒผ์šด ์ฝ๊ธฐ๋งŒ).""" project_root = Path(project_root) - r2 = _read(project_root / RESULT2D) + r2, _ = _latest_summary(project_root, "") folder = run3d.results_dir(project_root) body = { - "2d": [r2.get("input_fingerprint"), r2.get("computed_at")] if r2 else None, + "2d": [r2.get("fingerprint"), r2.get("computed_at")] if r2 else None, "rep": _read(project_root / REPRESENTATIVE), "ground": _ground(project_root), "3d": [ @@ -128,42 +165,39 @@ def analysis_fingerprint(project_root: Path) -> str: ] if folder.is_dir() else [], + "area": area.stamp(project_root), + "reinforce": reinforce.stamp(project_root), "form": hashlib.sha1(FORM_PATH.read_bytes()).hexdigest(), } text = json.dumps(body, ensure_ascii=False, sort_keys=True, default=str) return hashlib.sha1(text.encode("utf-8")).hexdigest() -def _representatives(r2: dict[str, Any], rep: dict[str, Any] | None) -> list[dict[str, Any]]: - by_key = {s["station_key"]: s for s in r2.get("stations") or []} - if rep and isinstance(rep.get("stations"), list): - picked = [s for s in rep["stations"] if s.get("enabled", True)] - else: - ranked = sorted( - (s for s in by_key.values() if s.get("fs_min") is not None), key=lambda s: s["fs_min"] - ) - picked = [{"station_key": s["station_key"], "reason": "lowest_fs"} for s in ranked][ - :LOWEST_N - ] - return [ - {**p, "summary": by_key[p["station_key"]]} for p in picked if p["station_key"] in by_key - ] +def _representatives(r2: dict[str, Any]) -> list[dict[str, Any]]: + """E03 ๋Œ€ํ‘œ ๋‹จ๋ฉด ์ผฌ โ€” ๊นŒ๋‹ญ = ์‚ฌ์šฉ์ž ์ผฌ์ด๋ฉด user ยท ์•„๋‹ˆ๋ฉด ์ž๋™ ์ฒซ ๊นŒ๋‹ญ.""" + out = [] + for s in r2.get("stations") or []: + mark = s["representative"] + if mark.get("on"): + reason = "user" if mark.get("user") else (mark.get("auto") or ["user"])[0] + out.append({"station_key": s["station_key"], "reason": reason, "summary": s}) + return out def gather(project_root: Path) -> dict[str, Any]: """๋นŒ๋” ์ž…๋ ฅ ํ•œ ๋ฒŒ โ€” 2D ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด `MissingInput`.""" project_root = Path(project_root) - r2 = _read(project_root / RESULT2D) + r2 = _result2d(project_root) if not r2 or not r2.get("stations"): raise MissingInput("2D ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค โ€” E03 ์—์„œ [ํ•ด์„] ๋’ค ๋งŒ๋“œ์„ธ์š”.") root = source.source_dir(project_root) db = source.db_snapshot(root) ground = _ground(project_root) resolved = {round(float(s["chainage_m"]), 3): s for s in ground_model.resolve_stations(ground)} - reps = _representatives(r2, _read(project_root / REPRESENTATIVE)) + reps = _representatives(r2) for item in reps: chainage = float(item["summary"]["chainage_m"]) - item["result"] = _station_result(project_root, item["station_key"]) + item["result"], item["ground"] = _station_result(project_root, item["summary"]["fp"]) try: item["cross"] = source.cross_section(root, chainage) except KeyError: @@ -181,68 +215,13 @@ def gather(project_root: Path) -> dict[str, Any]: "representatives": reps, "ground": ground, "sections3d": sorted(sections3d, key=lambda s: s["start_m"]), + "area": area.read(project_root), + "reinforce": reinforce.read(project_root), "fingerprint": analysis_fingerprint(project_root), } # โ”€โ”€ ์นธ ์“ฐ๊ธฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ -def _set(sheet: dict[str, Any], a1: str, value: Any, style: int | None = None) -> None: - """๊ฐ’(None = ์ง€์›€) ยท ์„œ์‹(None = ์–‘์‹ ๊ทธ๋Œ€๋กœ).""" - cell = dict(sheet["์นธ"].get(a1) or {}) - cell.pop("์‹", None) - if value is None or value == "": - cell.pop("๊ฐ’", None) - else: - cell["๊ฐ’"] = round(value, 6) if isinstance(value, float) else value - if style is not None: - cell["์„œ์‹"] = style - if cell: - sheet["์นธ"][a1] = cell - else: - sheet["์นธ"].pop(a1, None) - - -def _copy_row(sheet: dict[str, Any], src: int, dst: int, cols: str) -> None: - for col in cols: - cell = sheet["์นธ"].get(f"{col}{src}") - if cell and dst != src: - sheet["์นธ"][f"{col}{dst}"] = {"์„œ์‹": cell["์„œ์‹"]} if "์„œ์‹" in cell else {} - - -def _shift(a1: str, dr: int) -> str: - m = A1.match(a1) - return f"{m.group(1)}{int(m.group(2)) + dr}" - - -def _verdict(sheet: dict[str, Any], a1: str, ok: bool | None) -> None: - if ok is None: - _set(sheet, a1, None) - else: - _set(sheet, a1, "O.K" if ok else "N.G", ST_OK if ok else ST_NG) - - -def _criterion(ground: dict[str, Any], case: str, side: str | None = None) -> float: - sf = (ground.get("safety_factors") or {}).get(case) or {} - if side in ("cut", "fill"): - return float(sf[side]) - return max(float(sf["cut"]), float(sf["fill"])) # ํ‘œ ํ•˜๋‚˜ = ์—„ํ•œ ์ชฝ - - -def _seismic(ground: dict[str, Any]) -> bool: - return bool((ground.get("seismic") or {}).get("enabled")) - - -def _labels(stations: list[dict[str, Any]]) -> dict[float, str]: - return {round(float(s["chainage_m"]), 3): s.get("label") or "" for s in stations} - - -def _label_at(labels: dict[float, str], chainage: float) -> str: - if not labels: - return f"{chainage:.2f}" - near = min(labels, key=lambda c: abs(c - chainage)) - return labels[near] or f"{chainage:.2f}" - - # โ”€โ”€ ์‚ฌ์šฉ์ž ๋ชซ(์ฒ˜์Œ ํ•œ ๋ฒˆ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ def fill_cover(sheet: dict[str, Any], inputs: dict[str, Any]) -> None: """ํ‘œ์ง€ โ€” ์‚ฌ์—…๋ช… ยท ๋…ธ์„ ๋ช…(์ง€์—ญ ยท ์ž„๋„ ์ข…๋ฅ˜) ยท ์ž‘์„ฑ์ผ. ๋ฐœ์ฃผ์ฒ˜ ยท ์ž‘์„ฑ์ž = ์‚ฌ์šฉ์ž๊ฐ€ ์”€.""" @@ -264,9 +243,30 @@ WATER_TEXT = { } +def _wet_text(water: dict[str, Any], base: dict[str, Any]) -> str: + """์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜ ๊ฐˆ๋ž˜ ๊ธ€(E02 `wet_mode`) ยท ๊ธฐ๋ณธ๊ฐ’์ด๋ฉด ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ํ‘œ๊ธฐ.""" + mode = water.get("wet_mode") or "kds" + if mode == "depth_ratio": + ratio = float(water["depth_ratio"]) + return mark(f"์ง€ํ‘œ ์•„๋ž˜ ๊นŠ์ด = ๋น„ํƒˆ ๋†’์ด ร— {ratio:g}", ratio == float(base["depth_ratio"])) + if mode == "ru": + ru = float(water["ru"]) + return mark(f"๊ฐ„๊ทน์ˆ˜์••๋น„ r_u = {ru:g}", ru == float(base["ru"])) + kds = ( + f"ํ† ์‚ฌ ยท ํ’ํ™”์•”: {WATER_TEXT.get(water.get('wet_soil'), '-')} ยท " + f"์•”๋ฐ˜: {WATER_TEXT.get(water.get('wet_rock'), '-')}" + ) + if mode == "line": + return f"์‚ฌ์šฉ์ž ์ˆ˜์œ„์„  {len(water.get('lines') or [])} ๊ตฌ๊ฐ„ ยท ๊ตฌ๊ฐ„ ๋ฐ– = {kds} (์‚ฌ์šฉ์ž ์ž…๋ ฅ)" + same = all(water.get(k) == base.get(k) for k in ("wet_soil", "wet_rock")) + return mark(f"KDS ๊ธฐ๋ณธ โ€” {kds}", same) + + def fill_conditions(sheet: dict[str, Any], inputs: dict[str, Any]) -> None: """s3 ์ž…๋ ฅ์กฐ๊ฑด.""" ground = inputs["ground"] + base = ground_model.load_defaults() + base_sf = base["safety_factors"] _set(sheet, "B5", "ํ•œ๊ณ„ํ‰ํ˜•ํ•ด์„ โ€” Bishop ๊ฐ„ํŽธ๋ฒ•(๊ธฐ๋ณธ) ยท Janbu ๋ณด์ • ยท Fellenius ๋น„๊ต") _set(sheet, "B6", "์›ํ˜ธ(๊ฒฉ์ž ยท ์ง„์ž…-ํƒˆ์ถœ) + ๋น„์›ํ˜ธ(๊บพ์€์„ ) ยท ์ธก์ ๋งˆ๋‹ค ์ขŒ ยท ์šฐ ๋น„ํƒˆ ๋”ฐ๋กœ") _set( @@ -283,16 +283,14 @@ def fill_conditions(sheet: dict[str, Any], inputs: dict[str, Any]) -> None: _set(sheet, f"C{r}", "์ง€์ง„ ๊ฒ€ํ†  ์•ˆ ํ•จ") continue sf = ground["safety_factors"][case] + sf_base = base_sf.get(case) or {} + same = all(float(sf_base.get(k, -1)) == float(sf[k]) for k in ("cut", "fill")) _set(sheet, f"B{r}", _criterion(ground, case)) - _set(sheet, f"C{r}", f"๊นŽ๊ธฐ {sf['cut']:.2f} ยท ์Œ“๊ธฐ {sf['fill']:.2f} โ€” ํ‘œ ๊ฐ’ = ์—„ํ•œ ์ชฝ") + text = f"๊นŽ๊ธฐ {sf['cut']:.2f} ยท ์Œ“๊ธฐ {sf['fill']:.2f} โ€” ํ‘œ ๊ฐ’ = ์—„ํ•œ ์ชฝ ยท KDS 11 70 05" + _set(sheet, f"C{r}", mark(text, same)) water = ground.get("groundwater") or {} _set(sheet, "B17", WATER_TEXT.get(water.get("dry"), "-")) - _set( - sheet, - "B18", - f"ํ† ์‚ฌ ยท ํ’ํ™”์•”: {WATER_TEXT.get(water.get('wet_soil'), '-')} ยท " - f"์•”๋ฐ˜: {WATER_TEXT.get(water.get('wet_rock'), '-')}", - ) + _set(sheet, "B18", _wet_text(water, base["groundwater"])) seismic = ground.get("seismic") or {} _set( sheet, @@ -301,17 +299,25 @@ def fill_conditions(sheet: dict[str, Any], inputs: dict[str, Any]) -> None: if _seismic(ground) else "์ ์šฉ ์•ˆ ํ•จ", ) - _set(sheet, "B22", float(ground.get("road_surcharge_kpa") or 0)) - defaults = {m["id"]: m for m in ground_model.load_defaults()["materials"]} + load = float(ground.get("road_surcharge_kpa") or 0) + _set(sheet, "B22", load) + default_load = float(base["road_surcharge_kpa"]) + _set(sheet, "D22", mark("์˜น๋ฒฝ ๋ฐฐ๋ฉด ์ƒ์žฌํ•˜์ค‘ ์ค€์šฉ", load == default_load)) + defaults = {m["id"]: m for m in base["materials"]} names = {m["id"]: m["name"] for m in ground.get("materials") or []} - rows: list[tuple[dict[str, Any], str]] = [(m, "์ „์ฒด") for m in ground.get("materials") or []] + mapped = set((ground.get("label_map") or {}).values()) # ์ง€๋ฐ˜ ๋ผ๋ฒจ์— ์ด์€ ์ง€์ธต๋งŒ ใ€Œ์ „์ฒดใ€ + rows: list[tuple[dict[str, Any], str]] = [ + (m, "์ „์ฒด" if m["id"] in mapped else "-") for m in ground.get("materials") or [] + ] for zone in ground.get("zones") or []: span = f"{float(zone['from_m']):g} ~ {float(zone['to_m']):g} m" for layer, where in (("soil", "์œ„"), ("rock", "์•„๋ž˜")): mid = zone.get(layer) match = next((m for m in ground["materials"] if m["id"] == mid), None) if match: - rows.append((match, f"{span} {where} ์ง€์ธต")) + depth = zone.get("soil_thickness_m") if layer == "soil" else None + more = f" ยท ๋‘๊ป˜ {float(depth):g} m" if depth is not None else "" + rows.append((match, f"{span} {where} ์ง€์ธต{more}")) if zone.get("soil_thickness_m") is not None and not (zone.get("soil") or zone.get("rock")): rows.append(({"name": "ํ† ์ธต ๋‘๊ป˜"}, f"{span} ยท {zone['soil_thickness_m']:g} m")) for i, (m, span) in enumerate(rows): @@ -326,7 +332,9 @@ def fill_conditions(sheet: dict[str, Any], inputs: dict[str, Any]) -> None: float(base[f]) == float(m.get(f, -1)) for f in ground_model.MATERIAL_FIELDS ) _set( - sheet, f"G{r}", ("๋ฌธํ—Œ ์ถ”์ •์น˜(๊ธฐ๋ณธ๊ฐ’)" if same else "์‚ฌ์šฉ์ž ์ž…๋ ฅ") if "c" in m else None + sheet, + f"G{r}", + ("๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ์‚ฌ์šฉ์ž ํ™•์ • ์ „" if same else "์‚ฌ์šฉ์ž ์ž…๋ ฅ") if "c" in m else None, ) @@ -339,7 +347,7 @@ def fill_stations(sheet: dict[str, Any], inputs: dict[str, Any]) -> int: ng = 0 for i, st in enumerate(rows): r = 6 + i - _copy_row(sheet, 6, r, "ABCDEFGHIJ") + _copy_row(sheet, 6, r, "ABCDEFGHIJK") side = st.get("side") fs = st.get("fs_by_condition") or {} _set(sheet, f"A{r}", _label_at(labels, float(st["chainage_m"]))) @@ -354,178 +362,32 @@ def fill_stations(sheet: dict[str, Any], inputs: dict[str, Any]) -> int: ok = None if verdict not in ("ok", "ng") else verdict == "ok" ng += ok is False _verdict(sheet, f"J{r}", ok) + _set(sheet, f"K{r}", None if ok is None else "-" if ok else "๋Œ€์ฑ… ๊ฒ€ํ† ") return ng -def _case_slip(result: dict[str, Any] | None, condition: str, side: str, method: str): - """์กฐ๊ฑด ํ•˜๋‚˜ โ€” ๊ทธ ๋น„ํƒˆ ์—ญํ• (๊นŽ๊ธฐ ยท ์Œ“๊ธฐ) ์ค‘ ๊ทธ ๋ฐฉ๋ฒ• ์ตœ์†Œ ๋ฉด ยท ๋†’์ด.""" - best = None - for case in (result or {}).get("cases") or []: - if case.get("condition") != condition: - continue - if side in ("cut", "fill") and case.get("role") not in (side, None): - continue - methods = (case.get("result") or {}).get("methods") or {} - found = (methods.get(method) or {}).get("min") or next( - (v["min"] for v in methods.values() if v.get("min")), None - ) - if found and found.get("F") is not None and (best is None or found["F"] < best[0]["F"]): - best = (found, case) - return best - - -def _block_copy(sheet: dict[str, Any], template: dict[str, Any], k: int) -> None: - """์–‘์‹ 1~28 ํ–‰ ๋ฉ์ด๋ฅผ k ๋ฒˆ์งธ ์ž๋ฆฌ๋กœ(์นธ ยท ๋ณ‘ํ•ฉ ยท ํ–‰ ๋†’์ด ยท ์กฐ๊ฑด๋ถ€์„œ์‹).""" - dr = BLOCK * k - for a1, cell in template["์นธ"].items(): - if int(A1.match(a1).group(2)) <= BLOCK: - sheet["์นธ"][_shift(a1, dr)] = copy.deepcopy(cell) - for m in template.get("๋ณ‘ํ•ฉ", []): - sheet["๋ณ‘ํ•ฉ"].append(":".join(_shift(p, dr) for p in m.split(":"))) - for r, info in (template.get("ํ–‰") or {}).items(): - if int(r) <= BLOCK: - sheet["ํ–‰"][str(int(r) + dr)] = dict(info) - for rule in template.get("์กฐ๊ฑด๋ถ€์„œ์‹", []): - moved = copy.deepcopy(rule) - moved["id"] = f"{rule['id']}_{k}" - moved["๋ฒ”์œ„"] = [{**g, "r0": g["r0"] + dr, "r1": g["r1"] + dr} for g in rule["๋ฒ”์œ„"]] - sheet["์กฐ๊ฑด๋ถ€์„œ์‹"].append(moved) - - -CIRCLED = "โ‘ โ‘กโ‘ขโ‘ฃโ‘คโ‘ฅโ‘ฆโ‘งโ‘จโ‘ฉโ‘ชโ‘ซโ‘ฌโ‘ญโ‘ฎโ‘ฏโ‘ฐโ‘ฑโ‘ฒโ‘ณ" - - -def fill_sections(sheet: dict[str, Any], inputs: dict[str, Any]) -> list[tuple[str, str]]: - """s5 ๋Œ€ํ‘œ๋‹จ๋ฉด โ€” ๋‹จ๋ฉด ํ•˜๋‚˜ = 28 ํ–‰ ๋ฉ์ด = ํ•œ ์ชฝ ยท ๊ทธ๋ฆผ ์ผ๊ฐ [(์—ด์‡ , SVG)].""" - template = copy.deepcopy(sheet) - reps = inputs["representatives"] - ground = inputs["ground"] - labels = _labels(inputs["stations"]) - sheet["์นธ"] = {} - sheet["๋ณ‘ํ•ฉ"], sheet["ํ–‰"], sheet["์กฐ๊ฑด๋ถ€์„œ์‹"], sheet["๊ทธ๋ฆผ"] = [], {}, [], [] - if not reps: - _block_copy(sheet, template, 0) - _set(sheet, "A1", "๋Œ€ํ‘œ ๋‹จ๋ฉด โ€” ํ•ด๋‹น ์—†์Œ") - sheet["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] = [] - return [] - jobs: list[tuple[str, str]] = [] - for k, rep in enumerate(reps): - dr = BLOCK * k - _block_copy(sheet, template, k) - st = rep["summary"] - side = st.get("side") - method = st.get("method") or "bishop" - label = _label_at(labels, float(st["chainage_m"])) - picks = {c: _case_slip(rep.get("result"), c, side, method) for c in CASES} - dry = picks.get("dry") - height = float(dry[1].get("height_m") or 0) if dry else 0.0 - mark = CIRCLED[k] if k < len(CIRCLED) else str(k + 1) - _set( - sheet, - f"A{1 + dr}", - f"๋Œ€ํ‘œ ๋‹จ๋ฉด {mark} โ€” {label} ยท {SIDE_NAMES.get(side, side or '-')} ยท H {height:.1f} m", - ) - reason = REASONS.get(rep.get("reason"), rep.get("reason") or "") - if rep.get("reason") == "lowest_fs": - reason = f"{reason} {LOWEST_N}" - _set(sheet, f"B{2 + dr}", reason) - key = fig_key(rep["station_key"]) - sheet["๊ทธ๋ฆผ"].append( - {"์—ด์‡ ": key, "๋‹ป": f"A{3 + dr}:H{22 + dr}", "์ด๋ฆ„": f"๋Œ€ํ‘œ ๋‹จ๋ฉด {label}"} - ) - fs = st.get("fs_by_condition") or {} - slips = [] - for r, case in zip((24, 25, 26), CASES): - row = r + dr - if case == "seismic" and not _seismic(ground): - continue - value = fs.get(case) - crit = _criterion(ground, case, side) - pick = picks.get(case) - _set(sheet, f"B{row}", METHOD_NAMES.get(method, method)) - _set(sheet, f"C{row}", float(value) if value is not None else None) - _set(sheet, f"D{row}", crit) - _verdict(sheet, f"E{row}", None if value is None else float(value) >= crit - 1e-9) - slip = pick[0] if pick else None - center = (slip or {}).get("center") - _set(sheet, f"F{row}", float(slip["radius"]) if slip and slip.get("radius") else None) - _set(sheet, f"G{row}", float(center[0]) if center else None) - _set(sheet, f"H{row}", float(center[1]) if center else None) - if slip and slip.get("points"): - slips.append({"condition": case, "F": slip.get("F"), "points": slip["points"]}) - design = (rep.get("cross") or {}).get("design") or {} - kind = (dry[0].get("kind") if dry else None) or "" - layers = rep.get("layers") or {} - note = [ - {"circle": "ํ™œ๋™๋ฉด ์›ํ˜ธ", "polyline": "ํ™œ๋™๋ฉด ๋น„์›ํ˜ธ"}.get(kind, ""), - f"์ง€๋ฐ˜ {GROUND_NAMES.get(design.get('ground_type'), design.get('ground_type') or '-')}", - f"ํ† ์ธต ๋‘๊ป˜ {layers['soil_thickness_m']:g} m" if layers.get("soil_thickness_m") else "", - ] - _set(sheet, f"B{27 + dr}", " ยท ".join(n for n in note if n)) - jobs.append((key, _section_figure(rep, slips, f"{label} โ€” {reason}"))) - sheet["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] = [BLOCK * (k + 1) for k in range(len(reps))] - return jobs - - -def _section_figure(rep: dict[str, Any], slips: list[dict[str, Any]], title: str) -> str: - cross = rep.get("cross") or {} - design = cross.get("design") or {} - ground = sorted( - (float(s["offset_m"]), float(s["z"])) - for s in cross.get("samples") or [] - if s.get("valid", True) and s.get("z") is not None - ) - line = sorted( - (float(p["offset_m"]), float(p["elevation_m"])) for p in design.get("design_line") or [] - ) - surface = [tuple(p) for p in final_surface(ground, line)] if ground and line else ground - thickness = (rep.get("layers") or {}).get("soil_thickness_m") - rock = [(x, z - float(thickness)) for x, z in ground] if thickness else None - edges = design.get("road_edges") or {} - road = None - if edges.get("left") and edges.get("right"): - road = (float(edges["right"]["offset_m"]), float(edges["left"]["offset_m"])) - heights = [] - if road and line: - for case in (rep.get("result") or {}).get("cases") or []: - if case.get("condition") != "dry" or not case.get("height_m"): - continue - left = case.get("side") == "left" - edge = road[1] if left else road[0] - day = line[-1][0] if left else line[0][0] - z0, z1 = figure._interp(surface, edge), figure._interp(surface, day) - if z0 is not None and z1 is not None: - heights.append({"x": day, "z0": z0, "z1": z1}) - return figure.section_svg( - title=title, - ground=ground, - surface=surface, - rock=rock, - slips=slips, - road=road, - heights=heights, - wet=any(s["condition"] == "wet" for s in slips), - ) - - def fill_3d(sheet: dict[str, Any], inputs: dict[str, Any]) -> tuple[str, str]: """s6 3D์š”์•ฝ โ€” ๊ตฌ๊ฐ„ ํ•˜๋‚˜ = ํ•œ ์ค„(20 ํ–‰๋ถ€ํ„ฐ) ยท ํ‰๋ฉด ์œ„ํ—˜ ๋  ๊ทธ๋ฆผ ์ผ๊ฐ.""" ground = inputs["ground"] labels = _labels(inputs["stations"]) rows2d = inputs["result2d"]["stations"] - crit = _criterion(ground, "dry") sections = [] for i, sec in enumerate(inputs["sections3d"]): r = 20 + i _copy_row(sheet, 20, r, "ABCDEGH") start, end = float(sec["start_m"]), float(sec["end_m"]) + cond = sec.get("condition") or "dry" # 2D ๋Š” 3D ์™€ ๊ฐ™์€ ์กฐ๊ฑด๋ผ๋ฆฌ ๊ฒฌ์คŒ inside = [ - float(s["fs_min"]) + float(s["min_by_condition"][cond]) for s in rows2d - if s.get("fs_min") is not None and start - 1e-6 <= float(s["chainage_m"]) <= end + 1e-6 + if s["min_by_condition"].get(cond) is not None + and start - 1e-6 <= float(s["chainage_m"]) <= end + 1e-6 ] f3 = float(sec["f_min"]) - ok = f3 >= crit - 1e-9 + if sec.get("verdict") in ("ok", "ng"): # ํŒ์ • = E04 ์—”์ง„ ๊ฐ’(๊ธฐ์ค€์•ˆ์ „์œจ๋„ E04) + ok = sec["verdict"] == "ok" + else: + ok = f3 >= _criterion(ground, cond) - 1e-9 _set(sheet, f"A{r}", f"{i + 1}๊ตฌ๊ฐ„" + (" (๋ฏธ๋ฆฌ๋ณด๊ธฐ)" if sec.get("preview") else "")) _set(sheet, f"B{r}", _label_at(labels, start)) _set(sheet, f"C{r}", _label_at(labels, end)) @@ -568,9 +430,22 @@ def build_program(book: dict[str, Any], inputs: dict[str, Any]) -> list[tuple[st """์–‘์‹ ์‚ฌ๋ณธ `book` ์˜ ํ”„๋กœ๊ทธ๋žจ ๋ชซ ์‹œํŠธ๋ฅผ ์ฑ„์›€ ยท ๊ทธ๋ฆผ ์ผ๊ฐ.""" by = {s["id"]: s for s in book["์‹œํŠธ"]} fill_conditions(by["s3"], inputs) + n = len(inputs["result2d"]["stations"]) fill_stations(by["s4"], inputs) - jobs = fill_sections(by["s5"], inputs) + printer.balance_breaks(by["s4"], book, 6, 5 + n) + jobs = section.fill_sections(by["s5"], inputs) + section.fill_slices(by["s10"], inputs) jobs.append(fill_3d(by["s6"], inputs)) + area.fill_area(by["s7"], inputs["area"], _labels(inputs["stations"])) + risky = [ + r for r in (inputs["area"] or {}).get("stations") or [] if r.get("level") in ("mid", "high") + ] + printer.balance_breaks(by["s7"], book, 22, 21 + len(risky)) + if inputs.get("reinforce"): + reinforce.fill_reinforce(by["s8"], inputs["reinforce"], _labels(inputs["stations"])) + else: # ๋ณด๊ฐ• ๊ฒ€ํ† ๊ฐ€ ์—†์œผ๋ฉด ์‹œํŠธ๋ฅผ ๋บŒ + book["์‹œํŠธ"] = [s for s in book["์‹œํŠธ"] if s["id"] != "s8"] + printer.fill_toc(by["s9"], book, None) # ์ชฝ ๋ฒˆํ˜ธ = ์ €์žฅ ์ „ `printer.number_toc` return jobs @@ -589,5 +464,16 @@ def sheet_sign(sheet: dict[str, Any]) -> str: "ํ–‰": {k: v for k, v in (sheet.get("ํ–‰") or {}).items() if v}, "์—ด": {k: v for k, v in (sheet.get("์—ด") or {}).items() if v}, } - text = json.dumps(body, ensure_ascii=False, sort_keys=True) + text = json.dumps(_numbers(body), ensure_ascii=False, sort_keys=True) return hashlib.sha1(text.encode("utf-8")).hexdigest() + + +def _numbers(value: Any) -> Any: + """์ˆ˜๋Š” float ๋กœ โ€” ํ™”๋ฉด(JS)์„ ๊ฑฐ์น˜๋ฉด 10.0 ์ด 10 ์œผ๋กœ ์˜ด(๊ฐ™์€ ๊ฐ’์„ ๋‹ค๋ฅด๋‹ค๊ณ  ๋ณด์ง€ ์•Š๊ฒŒ).""" + if isinstance(value, dict): + return {k: _numbers(v) for k, v in value.items()} + if isinstance(value, list): + return [_numbers(v) for v in value] + if isinstance(value, int | float) and not isinstance(value, bool): + return round(float(value), 9) + return value diff --git a/E05_StabReport/E05_StabReport_Cells.py b/E05_StabReport/E05_StabReport_Cells.py new file mode 100644 index 000000000..fedf4c4ec --- /dev/null +++ b/E05_StabReport/E05_StabReport_Cells.py @@ -0,0 +1,98 @@ +"""E05 ๋ณด๊ณ ์„œ ์นธ ์“ฐ๊ธฐ ๊ณต์šฉ โ€” ์ด๋ฆ„ํ‘œ ยท ์„œ์‹ ๋ฒˆํ˜ธ ยท ์นธ ๊ฐ’ ยท ํŒ์ • ยท ๊ธฐ์ค€ ์•ˆ์ „์œจ ยท ์ธก์  ์ด๋ฆ„. + +(PLAN 534-3 ยท 534-5) ๋นŒ๋” ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด(`_Section`) ยท ์ธ์‡„(`_Print`)๊ฐ€ ๊ฐ™์ด ์”€. +""" + +from __future__ import annotations + +import re +from typing import Any + +CASES = ("dry", "wet", "seismic") +SIDE_NAMES = {"cut": "๊นŽ๊ธฐ", "fill": "์Œ“๊ธฐ"} +METHOD_NAMES = { + "bishop": "Bishop", + "janbu": "Janbu ๋ณด์ •", + "janbu_corrected": "Janbu ๋ณด์ •", + "ordinary": "Fellenius", + "spencer": "Spencer", +} +REASONS = { + "fill_height": "์Œ“๊ธฐ ๋†’์ด 10 m ์ด์ƒ", + "cut_height": "๊นŽ๊ธฐ ๋†’์ด 15 m ์ด์ƒ", + "lowest": "์ตœ์†Œ ์•ˆ์ „์œจ ํ•˜์œ„", + "user": "์‚ฌ์šฉ์ž ์ง€์ •", +} +GROUND_NAMES = {"soil": "ํ† ์‚ฌ", "ripping_rock": "๋ฆฌํ•‘์•”", "blasting_rock": "๋ฐœํŒŒ์•”", "fill": "์„ฑํ† "} +ST_TEXT, ST_BODY, ST_FS, ST_NG, ST_OK, ST_NUM1, ST_PROP, ST_CHAIN = 5, 7, 8, 10, 11, 13, 16, 17 +A1 = re.compile(r"^([A-Z]+)(\d+)$") + + +def fig_key(station_key: str) -> str: + return f"fig/{station_key}.png" + + +UNCONFIRMED = "๊ธฐ๋ณธ๊ฐ’ ยท ์‚ฌ์šฉ์ž ํ™•์ • ์ „" + + +def mark(text: str, default: bool) -> str: + """์‚ฌ์šฉ์ž ํ™•์ • ์ „ ํ‘œ๊ธฐ ํ•œ ๊ทœ์น™ โ€” ๊ธฐ๋ณธ๊ฐ’ ๊ทธ๋Œ€๋กœ = ใ€Œ(๊ธฐ๋ณธ๊ฐ’ ยท ์‚ฌ์šฉ์ž ํ™•์ • ์ „)ใ€ ยท ์•„๋‹ˆ๋ฉด + ใ€Œ(์‚ฌ์šฉ์ž ์ž…๋ ฅ)ใ€.""" + return f"{text} ({UNCONFIRMED if default else '์‚ฌ์šฉ์ž ์ž…๋ ฅ'})" + + +def _set(sheet: dict[str, Any], a1: str, value: Any, style: int | None = None) -> None: + """๊ฐ’(None = ์ง€์›€) ยท ์„œ์‹(None = ์–‘์‹ ๊ทธ๋Œ€๋กœ).""" + cell = dict(sheet["์นธ"].get(a1) or {}) + cell.pop("์‹", None) + if value is None or value == "": + cell.pop("๊ฐ’", None) + else: + cell["๊ฐ’"] = round(value, 6) if isinstance(value, float) else value + if style is not None: + cell["์„œ์‹"] = style + if cell: + sheet["์นธ"][a1] = cell + else: + sheet["์นธ"].pop(a1, None) + + +def _copy_row(sheet: dict[str, Any], src: int, dst: int, cols: str) -> None: + for col in cols: + cell = sheet["์นธ"].get(f"{col}{src}") + if cell and dst != src: + sheet["์นธ"][f"{col}{dst}"] = {"์„œ์‹": cell["์„œ์‹"]} if "์„œ์‹" in cell else {} + + +def _shift(a1: str, dr: int) -> str: + m = A1.match(a1) + return f"{m.group(1)}{int(m.group(2)) + dr}" + + +def _verdict(sheet: dict[str, Any], a1: str, ok: bool | None) -> None: + if ok is None: + _set(sheet, a1, None) + else: + _set(sheet, a1, "O.K" if ok else "N.G", ST_OK if ok else ST_NG) + + +def _criterion(ground: dict[str, Any], case: str, side: str | None = None) -> float: + sf = (ground.get("safety_factors") or {}).get(case) or {} + if side in ("cut", "fill"): + return float(sf[side]) + return max(float(sf["cut"]), float(sf["fill"])) # ํ‘œ ํ•˜๋‚˜ = ์—„ํ•œ ์ชฝ + + +def _seismic(ground: dict[str, Any]) -> bool: + return bool((ground.get("seismic") or {}).get("enabled")) + + +def _labels(stations: list[dict[str, Any]]) -> dict[float, str]: + return {round(float(s["chainage_m"]), 3): s.get("label") or "" for s in stations} + + +def _label_at(labels: dict[float, str], chainage: float) -> str: + if not labels: + return f"{chainage:.2f}" + near = min(labels, key=lambda c: abs(c - chainage)) + return labels[near] or f"{chainage:.2f}" diff --git a/E05_StabReport/E05_StabReport_Figure.py b/E05_StabReport/E05_StabReport_Figure.py index 9f39a4a91..36816d8d6 100644 --- a/E05_StabReport/E05_StabReport_Figure.py +++ b/E05_StabReport/E05_StabReport_Figure.py @@ -1,8 +1,8 @@ """E05 ๋ณด๊ณ ์„œ ๊ทธ๋ฆผ โ€” ์„œ๋ฒ„ SVG(์š”๊ตฌ 347-5 ยท PLAN 534-3). PNG ๋Š” `common_util_svg_png` ๊ฐ€ ๋งŒ๋“ฆ. - section_svg(โ€ฆ) โ†’ ๋Œ€ํ‘œ ๋‹จ๋ฉด(712 ร— 533 ยท 4 : 3) โ€” ์›์ง€๋ฐ˜ ยท ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ ยท ์•” ๊ฒฝ๊ณ„ ยท - ์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜์œ„ ยท - ์กฐ๊ฑด๋ณ„ ์ž„๊ณ„ ํ™œ๋™๋ฉด ยท ๋…ธํญ ยท ๋น„ํƒˆ ๋†’์ด ์น˜์ˆ˜ ยท ํ‘œ๊ณ  ยท ๊ฑฐ๋ฆฌ ๋ˆˆ๊ธˆ + section_svg(โ€ฆ) โ†’ ๋Œ€ํ‘œ ๋‹จ๋ฉด(712 ร— 640) โ€” ์›์ง€๋ฐ˜ ยท ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ ยท ์ง€์ธต(๋ฌด๋Šฌ) ยท ์„ฑํ† (์ ) ยท + ์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜์œ„ ยท ์กฐ๊ฑด๋ณ„ ์ž„๊ณ„ ํ™œ๋™๋ฉด(์„  ๋ชจ์–‘) ยท ๋…ธํญ ยท ๋น„ํƒˆ ๋†’์ด ์น˜์ˆ˜ ยท + ํ‘œ๊ณ  ยท ๊ฑฐ๋ฆฌ ๋ˆˆ๊ธˆ ยท ์ถ•์ฒ™ ๋ง‰๋Œ€ ยท ์•ˆ์ „์œจ ์ƒ์ž ยท ์ง€์ธต ๋ฒ”๋ก€ band_svg(โ€ฆ) โ†’ ํ‰๋ฉด ์œ„ํ—˜ ๋ (684 ร— 320) โ€” ๋…ธ์„  ยท ์ธก์  2D ํŒ์ • ์ƒ‰ ยท 3D ๊ตฌ๊ฐ„ ๋  ์ขŒํ‘œ = B06 ํšก๋‹จ offset(์ขŒ +) ยท ํ™”๋ฉด ์™ผ์ชฝ = +offset @@ -16,11 +16,12 @@ import math from html import escape from typing import Any -SECTION_W, SECTION_H = 712, 533 +SECTION_W, SECTION_H = 712, 640 BAND_W, BAND_H = 684, 320 OK, NG, MUTED, INK = "#2E7D32", "#C00000", "#9E9E9E", "#222222" CONDITION_COLORS = {"dry": "#D32F2F", "wet": "#1565C0", "seismic": "#7B1FA2"} CONDITION_NAMES = {"dry": "๊ฑด๊ธฐ", "wet": "์šฐ๊ธฐ", "seismic": "์ง€์ง„"} +SLIP_DASH = {"wet": "7 4", "seismic": "2 3"} # ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ์ž„๊ณ„๋ฉด์ด ๊ฒน์ณ๋„ ๋‘˜ ๋‹ค ๋ณด์ด๊ฒŒ FONT = "font-family='Malgun Gothic, sans-serif'" @@ -53,6 +54,38 @@ def _interp(line: list[tuple[float, float]], x: float) -> float | None: return None +def _legend_rows(items: list[tuple[str, str]], width: float, size: int) -> list[list[int]]: + """๋ฒ”๋ก€ ํ•ญ๋ชฉ [(์ข…๋ฅ˜, ๊ธ€)] โ†’ ์ค„๋งˆ๋‹ค ํ•ญ๋ชฉ ๋ฒˆํ˜ธ(ํญ์„ ๋„˜์œผ๋ฉด ๋‹ค์Œ ์ค„).""" + rows: list[list[int]] = [[]] + used = 0.0 + for i, (_, label) in enumerate(items): + w = 30 + _text_w(label, size) + if rows[-1] and used + w > width: + rows.append([]) + used = 0.0 + rows[-1].append(i) + used += w + return rows + + +def _text_w(text: str, size: int) -> float: + """๊ธ€ ํญ ๊ทผ์‚ฌ(px) โ€” ํ•œ๊ธ€ ยท ๊ธฐํ˜ธ = ๊ธ€์ž ํฌ๊ธฐ ยท ๊ทธ ๋ฐ– = 0.6 ๋ฐฐ.""" + return sum(size if ord(ch) > 0x2000 else 0.6 * size for ch in text) + + +PATTERNS = ( + "" + "" + "" + "" + "" + "" +) +STRATA_FILL = {"soil": "#F3E9D8", "rock": "url(#rockHatch)", "fill": "url(#fillDots)"} + + def section_svg( *, title: str, @@ -63,16 +96,20 @@ def section_svg( road: tuple[float, float] | None, heights: list[dict[str, Any]], wet: bool, + strata: list[dict[str, str]] | None = None, + fs_box: list[dict[str, Any]] | None = None, ) -> str: - """๋Œ€ํ‘œ ๋‹จ๋ฉด SVG. + """๋Œ€ํ‘œ ๋‹จ๋ฉด SVG(ํ‘๋ฐฑ ์ธ์‡„์—๋„ ์ฝํž˜ โ€” ์ž„๊ณ„๋ฉด = ์„  ๋ชจ์–‘ ยท ์ง€์ธต = ๋ฌด๋Šฌ). ground ยท surface ยท rock = [(offset, z)] ยท slips = [{condition, F, points}] ยท - road = (์˜ค๋ฅธ ๋ offset, ์™ผ ๋ offset) ยท heights = [{x, z0, z1}] ๋น„ํƒˆ ๋†’์ด ์น˜์ˆ˜. + road = (์˜ค๋ฅธ ๋ offset, ์™ผ ๋ offset) ยท heights = [{x, z0, z1, label?}] ๋น„ํƒˆ ๋†’์ด ์น˜์ˆ˜ ยท + strata = [{kind: soil | rock | fill, text}] ์ง€์ธต ๋ฒ”๋ก€ ยท fs_box = [{condition, F, required, ok}] + ์•ˆ์ „์œจ ์ƒ์ž ยท title = ๋นˆ ๊ธ€์ด๋ฉด ์•ˆ ์”€(๋ณด๊ณ ์„œ ์ชฝ ์ œ๋ชฉ์ด ์žˆ์Œ). """ - pad_l, pad_r, pad_t, pad_b = 56, 16, 34, 64 + pad_l, pad_r, pad_t, pad_b = 58, 18, 30 if title else 22, 88 pts = [*ground, *surface, *(p for s in slips for p in s["points"])] if not pts: - return _empty(SECTION_W, SECTION_H, title) + return _empty(SECTION_W, SECTION_H, title or "๋Œ€ํ‘œ ๋‹จ๋ฉด") xs = [p[0] for p in pts] zs = [p[1] for p in pts] x_lo, x_hi = min(xs), max(xs) @@ -93,12 +130,24 @@ def section_svg( def screen(line: list[tuple[float, float]]) -> list[tuple[float, float]]: return [(sx(x), sz(z)) for x, z in line] + def area(line: list[tuple[float, float]], y: float) -> str: + """์„ ๊ณผ y ์‚ฌ์ด๋ฅผ ๋‹ซ์€ ๋‹ค๊ฐํ˜• ์  ๊ธ€.""" + body = screen(line) + return " ".join(f"{a:.1f},{b:.1f}" for a, b in [*body, (body[-1][0], y), (body[0][0], y)]) + + bottom, top = sz(z_lo), pad_t out = [ f"", - f"", - _text(SECTION_W / 2, 20, title, 13, font_weight="bold", fill=INK), + f"{PATTERNS}", ] + if surface: + out.append(f"") + if ground: + out.append(f"") + out += ["", f""] + if title: + out.append(_text(SECTION_W / 2, 20, title, 13, font_weight="bold", fill=INK)) # ๋ˆˆ๊ธˆ(ํ‘œ๊ณ  ยท ๊ฑฐ๋ฆฌ) step_z = _nice_step(z_hi - z_lo, 6) z = math.ceil(z_lo / step_z) * step_z @@ -106,56 +155,57 @@ def section_svg( y = sz(z) out.append( f"" + "stroke='#E6E6E6' stroke-width='1'/>" ) - out.append(_text(pad_l - 6, y + 4, f"{z:g}", 10, "end", fill="#666666")) + out.append(_text(pad_l - 6, y + 4, f"{z:g}", 11, "end", fill="#444444")) z += step_z step_x = _nice_step(x_hi - x_lo, 8) x = math.ceil(x_lo / step_x) * step_x - base_y = SECTION_H - pad_b + 14 + base_y = SECTION_H - pad_b + 15 while x <= x_hi: - out.append(_text(sx(x), base_y, f"{x:g}", 10, fill="#666666")) + out.append(_text(sx(x), base_y, f"{x:g}", 11, fill="#444444")) x += step_x - out.append(_text(pad_l - 6, pad_t - 8, "EL. (m)", 10, "end", fill="#666666")) + out.append(_text(pad_l - 6, pad_t - 8, "EL.(m)", 10, "end", fill="#444444")) out.append( - _text(SECTION_W - pad_r, base_y + 14, "offset (m) ยท ์ขŒ +", 10, "end", fill="#666666") + _text(SECTION_W - pad_r, base_y + 15, "offset (m) ยท ์ขŒ +", 10, "end", fill="#444444") ) - # ์ง€๋ฐ˜ ยท ์ง€์ธต - if ground: - bottom = sz(z_lo) - body = screen(ground) - out.append( - "" + f"" + ) + out.append(_text(bx + 2 * half + 6, by, f"{bar:g} m ยท ๊ฐ€๋กœ = ์„ธ๋กœ ์ถ•์ฒ™", 10, "start", fill=INK)) + # ์ง€๋ฐ˜ ยท ์ง€์ธต โ€” ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ ์•„๋ž˜ = ๋ชธ์ฒด ยท ์›์ง€๋ฐ˜ ์œ„ = ์„ฑํ† (์ ) ยท ์•”(๋น—๊ธˆ) + if surface: + out.append(f"") + if ground: + out.append( + f"" ) - + "' fill='#F3E9D8' stroke='none'/>" - ) if rock: - rb = screen(rock) - out.append( - "" - ) - out.append(_poly(rb, stroke="#795548", stroke_width="1.2", stroke_dasharray="6 3")) - out.append(_poly(screen(ground), stroke="#8D6E63", stroke_width="1.4")) - out.append(_poly(screen(surface), stroke=INK, stroke_width="2")) + clip = " clip-path='url(#below)'" if surface else "" + out.append(f"") + out.append(_poly(screen(rock), stroke="#6D4C41", stroke_width="1", stroke_dasharray="6 3")) + out.append(_poly(screen(ground), stroke="#6D4C41", stroke_width="1.3", stroke_dasharray="3 2")) + out.append(_poly(screen(surface), stroke=INK, stroke_width="2.2")) if wet and surface: # ์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜์œ„ = ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ(KDS 11 70 05 ํ‘œ 4.3-3) โ€” โ–ฝ ํ‘œ์‹œ for frac in (0.2, 0.8): px, pz = surface[int(frac * (len(surface) - 1))] a, b = sx(px), sz(pz) - 4 out.append( - f"" + f"" ) # ์ž„๊ณ„ ํ™œ๋™๋ฉด for slip in slips: color = CONDITION_COLORS.get(slip["condition"], NG) - out.append(_poly(screen(slip["points"]), stroke=color, stroke_width="2.2")) + dash = SLIP_DASH.get(slip["condition"]) + extra = {"stroke_dasharray": dash} if dash else {} + out.append(_poly(screen(slip["points"]), stroke=color, stroke_width="2.6", **extra)) # ์น˜์ˆ˜ โ€” ๋…ธํญ ยท ๋น„ํƒˆ ๋†’์ด if road: r_off, l_off = road @@ -165,9 +215,9 @@ def section_svg( a, b = sx(l_off), sx(r_off) out.append( f"" + "stroke-width='0.9' marker-start='url(#t)' marker-end='url(#t)'/>" ) - out.append(_text((a + b) / 2, y - 4, f"๋…ธํญ {abs(l_off - r_off):.2f} m", 10, fill=INK)) + out.append(_text((a + b) / 2, y - 4, f"๋…ธํญ {abs(l_off - r_off):.2f} m", 11, fill=INK)) for dim in heights: if abs(dim["z1"] - dim["z0"]) < 0.05: continue @@ -175,55 +225,92 @@ def section_svg( y0, y1 = sz(dim["z0"]), sz(dim["z1"]) out.append( f"" + "stroke-width='0.9' marker-start='url(#t)' marker-end='url(#t)'/>" ) - left = a < SECTION_W / 2 # ํ™”๋ฉด ์™ผ์ชฝ ์น˜์ˆ˜ = ๊ธ€์€ ๋” ์™ผ์ชฝ(๋น„ํƒˆ๊ณผ ์•ˆ ๊ฒน์นจ) - label = f"H {abs(dim['z1'] - dim['z0']):.2f} m" - out.append( - _text( - a - 4 if left else a + 4, - (y0 + y1) / 2, - label, - 10, - "end" if left else "start", - fill=INK, - ) - ) - out.insert( - 2, - "" - "", - ) - # ๋ฒ”๋ก€ - legend = [("#8D6E63", "์›์ง€๋ฐ˜", ""), (INK, "๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ", "")] + label = dim.get("label") or f"H {abs(dim['z1'] - dim['z0']):.2f} m" + # ํ™”๋ฉด ์™ผ์ชฝ ์น˜์ˆ˜ = ๊ธ€์€ ๋” ์™ผ์ชฝ(๋น„ํƒˆ๊ณผ ์•ˆ ๊ฒน์นจ) ยท ๊ทธ๋ฆผ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€๋ฉด ๋ฐ˜๋Œ€์ชฝ + left = a < SECTION_W / 2 + if not left and a + 4 + _text_w(label, 11) > SECTION_W - 2: + left = True + if left and a - 4 - _text_w(label, 11) < 2: + left = False + anchor = "end" if left else "start" + out.append(_text(a - 4 if left else a + 4, (y0 + y1) / 2, label, 11, anchor, fill=INK)) + if fs_box: + out.append(_fs_box(fs_box, surface, sx, sz, pad_l, pad_t, SECTION_W - pad_r)) + # ๋ฒ”๋ก€ โ€” ์„  ยท ํ‘œ์‹œ ยท ์ง€์ธต ๋ฌด๋Šฌ(ํญ์„ ๋„˜์œผ๋ฉด ๋‹ค์Œ ์ค„) + items: list[tuple[str, str]] = [("#6D4C41|3 2", "์›์ง€๋ฐ˜"), (f"{INK}|", "๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ")] if rock: - legend.append(("#795548", "์•” ๊ฒฝ๊ณ„", "6 3")) + items.append(("#6D4C41|6 3", "์ง€์ธต ๊ฒฝ๊ณ„")) for slip in slips: name = CONDITION_NAMES.get(slip["condition"], slip["condition"]) f = slip.get("F") - legend.append( - ( - CONDITION_COLORS.get(slip["condition"], NG), - f"{name} ์ž„๊ณ„๋ฉด Fs {f:.2f}" if f else name, - "", - ) - ) + color = CONDITION_COLORS.get(slip["condition"], NG) + label = f"{name} ์ž„๊ณ„๋ฉด Fs {f:.2f}" if f else name + items.append((f"{color}|{SLIP_DASH.get(slip['condition'], '')}", label)) if wet: - legend.append(("#1565C0", "โ–ฝ ์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜์œ„ = ์ง€ํ‘œ", "")) - lx, ly = pad_l, SECTION_H - 22 - for color, label, dash in legend: - dash_attr = f" stroke-dasharray='{dash}'" if dash else "" - out.append( - f"" - ) - out.append(_text(lx + 22, ly + 4, label, 10, "start", fill=INK)) - lx += 34 + sum(10 if ord(ch) > 0x2000 else 6 for ch in label) # ํ•œ๊ธ€ ยท ๊ธฐํ˜ธ ํญ + items.append(("โ–ฝ", "์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜์œ„ = ์ง€ํ‘œ")) + for layer in strata or []: + items.append((f"@{layer['kind']}", layer["text"])) + ly = SECTION_H - 46 + for row in _legend_rows(items, SECTION_W - pad_l - pad_r, 11): + lx = float(pad_l) + for i in row: + kind, label = items[i] + if kind.startswith("@"): + out.append( + f"" + ) + elif kind == "โ–ฝ": + out.append( + f"" + ) + else: + color, dash = kind.split("|") + dash_attr = f" stroke-dasharray='{dash}'" if dash else "" + out.append( + f"" + ) + out.append(_text(lx + 27, ly + 1, label, 11, "start", fill=INK)) + lx += 30 + _text_w(label, 11) + ly += 17 out.append("") return "".join(out) +def _fs_box(rows, surface, sx, sz, left: float, top: float, right: float) -> str: + """์•ˆ์ „์œจ ์ƒ์ž โ€” ํ•˜๋Š˜ ์ชฝ(์ง€ํ‘œ๊ฐ€ ๋‚ฎ์€ ๋) ์œ„ ๋ชจ์„œ๋ฆฌ ยท ์กฐ๊ฑด ยท Fs ยท ๊ธฐ์ค€ ยท ํŒ์ •.""" + width, line = 236, 19 + height = 24 + line * len(rows) + # surface = offset ์˜ค๋ฆ„์ฐจ์ˆœ ยท ํ™”๋ฉด ์™ผ๋ = ํฐ offset = ๋ ์  + low_left = bool(surface) and sz(surface[-1][1]) > sz(surface[0][1]) + x = left + 6 if low_left else right - width - 6 + y = top + 6 + out = [ + f"", + f"", + ] + for cx, head, anchor in ((8, "์กฐ๊ฑด", "start"), (78, "Fs", "end"), (132, "๊ธฐ์ค€", "end")): + out.append(_text(x + cx, y + 16, head, 11, anchor, font_weight="bold", fill=INK)) + out.append(_text(x + 196, y + 16, "ํŒ์ •", 11, font_weight="bold", fill=INK)) + for k, r in enumerate(rows): + yy = y + 22 + line * (k + 1) - 5 + name = CONDITION_NAMES.get(r["condition"], r["condition"]) + f = "-" if r.get("F") is None else f"{r['F']:.2f}" + verdict = "-" if r.get("ok") is None else "O.K" if r["ok"] else "N.G" + color = INK if r.get("ok") is None else OK if r["ok"] else NG + out.append(_text(x + 8, yy, name, 11, "start", fill=INK)) + out.append(_text(x + 78, yy, f, 11, "end", font_weight="bold", fill=INK)) + out.append(_text(x + 132, yy, f"โ‰ฅ {r['required']:.2f}", 11, "end", fill=INK)) + out.append(_text(x + 196, yy, verdict, 11, font_weight="bold", fill=color)) + return "".join(out) + + def band_svg( *, title: str, diff --git a/E05_StabReport/E05_StabReport_Print.py b/E05_StabReport/E05_StabReport_Print.py new file mode 100644 index 000000000..2da6e4f76 --- /dev/null +++ b/E05_StabReport/E05_StabReport_Print.py @@ -0,0 +1,229 @@ +"""E05 ๋ณด๊ณ ์„œ ์ธ์‡„ โ€” ๋ชฉ์ฐจ ยท ์ชฝ ๋ฒˆํ˜ธ ยท ๋จธ๋ฆฌ๋ง/๊ผฌ๋ฆฌ๋ง ยท ๊ธด ํ‘œ ์ชฝ ๋‚˜๋ˆ”(์š”๊ตฌ 347-5 ยท 351 ยท PLAN 534-5). + + fill_toc(sheet, book, pages) ๋ชฉ์ฐจ ์ค„(์žฅ ยท ๋‚ด์šฉ ยท ์ชฝ) โ€” ์ชฝ ์—†์œผ๋ฉด ใ€Œ-ใ€ + number_toc(book) ์‹œํŠธ๋ณ„ ์ชฝ ์ˆ˜๋ฅผ ์žฌ์„œ ๋ชฉ์ฐจ ์ชฝ ๋ฒˆํ˜ธ๋ฅผ ์ฑ„์›€(๋ธŒ๋ผ์šฐ์ € ํ•œ ๋ฒˆ) + report_pdf(book, figures) ํ‘œ์ง€(์ชฝ ๋ฒˆํ˜ธ ์—†์Œ) + ๋ณธ๋ฌธ(๋จธ๋ฆฌ๋ง ์‚ฌ์—…๋ช… ยท ๊ผฌ๋ฆฌ๋ง n / N) ํ•œ ๋ฒŒ + balance_breaks(sheet, book, first, last) + ๊ธด ํ‘œ ์ชฝ ๋‚˜๋ˆ” โ€” ์ชฝ๋งˆ๋‹ค ๊ณ ๋ฅด๊ฒŒ(๋งˆ์ง€๋ง‰ ์ชฝ์— ํ•œ๋‘ ์ค„๋งŒ ๋‚จ์ง€ ์•Š๊ฒŒ) + +์ชฝ ๋ฒˆํ˜ธ = ํ‘œ์ง€๋ฅผ ๋บ€ ๋ณธ๋ฌธ ์ฐจ๋ก€(๋ชฉ์ฐจ = 1 ์ชฝ). ์ชฝ ์ˆ˜ = ์‹œํŠธ ํ•˜๋‚˜์”ฉ ์ธ์‡„ HTML โ†’ PDF ์ชฝ ์ˆ˜ +(์‹œํŠธ ๊ฒฝ๊ณ„ = ๋Š˜ ์ƒˆ ์ชฝ ยท `spreadsheet_print_html.ts`). ์ชฝ ์žฌ๊ธฐ ยท PDF = ๊ณต์šฉ PDF ์—”์ง„(ํ—ค๋“œ๋ฆฌ์Šค). +""" + +from __future__ import annotations + +import io +import math +from html import escape +from typing import Any + +from common_util.common_util_print_pdf import merge_pdfs, render_htmls_to_pdfs +from common_util.common_util_spreadsheet import print_htmls +from E05_StabReport.E05_StabReport_Cells import A1, _set + +COVER, TOC = "s1", "s9" +TOC_FIRST = 4 # ๋ชฉ์ฐจ ์ค„ = 4 ํ–‰๋ถ€ํ„ฐ(4 ํ–‰ ์„œ์‹) +TITLES = { + "s2": "๊ฐœ์š” ๋ฐ ํŒ์ • ์š”์•ฝ", + "s3": "์ž…๋ ฅ ์กฐ๊ฑด โ€” ํ•ด์„ ๋ฐฉ๋ฒ• ยท ๊ธฐ์ค€ ์•ˆ์ „์œจ ยท ์ง€ํ•˜์ˆ˜ ยท ํ•˜์ค‘ ยท ๋ฌผ์„ฑ์น˜", + "s4": "์ธก์ ๋ณ„ ์•ˆ์ •์„ฑ ํŒ์ • ์ผ๋žŒํ‘œ", + "s5": "๋Œ€ํ‘œ ๋‹จ๋ฉด ํ•ด์„ ๊ฒฐ๊ณผ โ€” ๋‹จ๋ฉด๋„ ยท ์ž„๊ณ„ ํ™œ๋™๋ฉด ยท ์•ˆ์ „์œจ", + "s6": "3D ํ•ด์„ ์š”์•ฝ", + "s7": "๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ ์š”์•ฝ", + "s8": "๋ณด๊ฐ• ๊ฒ€ํ†  โ€” ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ยท ๋ฒฝ ์™ธ์  ์•ˆ์ •", + "s10": "๋Œ€ํ‘œ ๋‹จ๋ฉด ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ", +} +APPENDIX = {"s10"} +REPORT_NAME = "๋น„ํƒˆ๋ฉด ์•ˆ์ •์„ฑ ๊ฒ€ํ†  ๋ณด๊ณ ์„œ" +FONT = "font-family:'Malgun Gothic','๋ง‘์€ ๊ณ ๋”•',sans-serif" +PAPER_MM = {"A4": (210, 297), "A3": (297, 420)} +PX_MM = 96 / 25.4 + + +def _row(a1: str) -> int: + return int(A1.match(a1.split(":")[0]).group(2)) + + +def _visible(book: dict[str, Any]) -> list[dict[str, Any]]: + return [s for s in book["์‹œํŠธ"] if not s.get("์ˆจ๊น€")] + + +def _start_pages(book: dict[str, Any], pages: dict[str, int]) -> dict[str, int]: + """์‹œํŠธ id โ†’ ๋ณธ๋ฌธ ์‹œ์ž‘ ์ชฝ(ํ‘œ์ง€ ๋บŒ ยท ์ฒซ ๋ณธ๋ฌธ ์‹œํŠธ = 1).""" + out, at = {}, 1 + for s in _visible(book): + if s["id"] == COVER: + continue + out[s["id"]] = at + at += pages.get(s["id"], 0) + return out + + +def fill_toc(sheet: dict[str, Any], book: dict[str, Any], pages: dict[str, int] | None) -> None: + """๋ชฉ์ฐจ โ€” ๋ณด์ด๋Š” ๋ณธ๋ฌธ ์‹œํŠธ(ํ‘œ์ง€ ยท ๋ชฉ์ฐจ ๋บŒ)๋งˆ๋‹ค ํ•œ ์ค„ ยท ์žฅ ๋ฒˆํ˜ธ ยท ๋ถ€๋ก ยท ์‹œ์ž‘ ์ชฝ.""" + tpl = {c: dict(sheet["์นธ"].get(f"{c}{TOC_FIRST}") or {}) for c in "ABC"} + for a1 in [a for a in sheet["์นธ"] if _row(a) >= TOC_FIRST]: + cell = sheet["์นธ"][a1] + if _row(a1) == TOC_FIRST: # ์–‘์‹ ์ค„ = ์„œ์‹๋งŒ ๋‚จ๊น€ + cell.pop("๊ฐ’", None) + else: + del sheet["์นธ"][a1] + height = (sheet.get("ํ–‰") or {}).get(str(TOC_FIRST)) + start = _start_pages(book, pages) if pages else {} + chapter = 0 + for i, s in enumerate(x for x in _visible(book) if x["id"] not in (COVER, TOC)): + r = TOC_FIRST + i + for c, cell in tpl.items(): + sheet["์นธ"][f"{c}{r}"] = {k: v for k, v in cell.items() if k != "๊ฐ’"} + if height: + sheet["ํ–‰"][str(r)] = dict(height) + if s["id"] in APPENDIX: + mark = "๋ถ€๋ก" + else: + chapter += 1 + mark = f"{chapter}." + _set(sheet, f"A{r}", mark) + _set(sheet, f"B{r}", TITLES.get(s["id"], s["์ด๋ฆ„"])) + _set(sheet, f"C{r}", start.get(s["id"], "-")) + + +def _only(book: dict[str, Any], keep: set[str], hide: bool = False) -> dict[str, Any]: + """`keep` ์‹œํŠธ๋งŒ ๋ณด์ด๋Š”(hide = ์ฐธ์ด๋ฉด `keep` ๋งŒ ์ˆจ๊ธด) ์–•์€ ์‚ฌ๋ณธ ยท ๋ธŒ๋ผ์šฐ์ € ๊ณ„์‚ฐ๊ฐ’ ๋บŒ.""" + sheets = [{**s, "์ˆจ๊น€": True} if (s["id"] in keep) == hide else s for s in book["์‹œํŠธ"]] + return {**{k: v for k, v in book.items() if k != "๊ณ„์‚ฐ๊ฐ’"}, "์‹œํŠธ": sheets} + + +def _figures(book: dict[str, Any], ids: set[str], figures: dict[str, bytes]) -> dict[str, bytes]: + keys = {f["์—ด์‡ "] for s in book["์‹œํŠธ"] if s["id"] in ids for f in s.get("๊ทธ๋ฆผ") or []} + return {k: v for k, v in figures.items() if k in keys} + + +def sheet_pages(book: dict[str, Any]) -> dict[str, int]: + """๋ณด์ด๋Š” ์‹œํŠธ๋งˆ๋‹ค ์ธ์‡„ ์ชฝ ์ˆ˜(๊ทธ๋ฆผ ์—†์ด โ€” ๊ทธ๋ฆผ ์นธ ํฌ๊ธฐ๋Š” ํ–‰์—ด์ด ์ •ํ•จ).""" + from pypdf import PdfReader + + ids = [s["id"] for s in _visible(book)] + htmls = print_htmls([{"book": _only(book, {sid})} for sid in ids]) + pdfs = render_htmls_to_pdfs([{"html": h, "page_size": None} for h in htmls]) + return {sid: len(PdfReader(io.BytesIO(pdf)).pages) for sid, pdf in zip(ids, pdfs)} + + +def number_toc(book: dict[str, Any]) -> dict[str, int]: + """๋ชฉ์ฐจ ์‹œํŠธ๊ฐ€ ์žˆ์œผ๋ฉด ์ชฝ ์ˆ˜๋ฅผ ์žฌ์„œ ์ชฝ ๋ฒˆํ˜ธ๋ฅผ ์ฑ„์›€(์ฑ…์„ ๊ณ ์นจ) ยท ์‹œํŠธ๋ณ„ ์ชฝ ์ˆ˜.""" + pages = sheet_pages(book) + toc = next((s for s in book["์‹œํŠธ"] if s["id"] == TOC and not s.get("์ˆจ๊น€")), None) + if toc is not None: + fill_toc(toc, book, pages) + return pages + + +def _header(project: str) -> str: + return ( + f'
{escape(project)}{REPORT_NAME}
' + ) + + +FOOTER = ( + f'
' + '- / -
' +) + + +def report_pdf(book: dict[str, Any], figures: dict[str, bytes]) -> bytes: + """๋ณด๊ณ ์„œ PDF โ€” ๋ชฉ์ฐจ ์ชฝ ๋ฒˆํ˜ธ = ์‹ค์ œ ์ชฝ ยท ํ‘œ์ง€ = ์ชฝ ๋ฒˆํ˜ธ ยท ๋จธ๋ฆฌ๋ง ์—†์Œ.""" + clean = _only(book, set(), hide=True) + clean["์‹œํŠธ"] = [ + {**s, "์นธ": {k: dict(v) for k, v in s["์นธ"].items()}, "ํ–‰": dict(s.get("ํ–‰") or {})} + if s["id"] == TOC + else s + for s in clean["์‹œํŠธ"] + ] # ๋ชฉ์ฐจ๋งŒ ๊ณ ์น  ์‚ฌ๋ณธ + number_toc(clean) + ids = {s["id"] for s in _visible(clean)} + cover = next((s for s in clean["์‹œํŠธ"] if s["id"] == COVER), None) + project = str(((cover or {}).get("์นธ", {}).get("C8") or {}).get("๊ฐ’") or "") + asks, jobs = [], [] + if COVER in ids: + asks.append({"book": _only(clean, {COVER}), "figures": _figures(clean, {COVER}, figures)}) + jobs.append({"page_size": None}) + if ids - {COVER}: + asks.append({"book": _only(clean, {COVER}, hide=True), "figures": figures}) + jobs.append( + {"page_size": None, "header_template": _header(project), "footer_template": FOOTER} + ) + htmls = print_htmls(asks) + pdfs = render_htmls_to_pdfs([{**job, "html": h} for job, h in zip(jobs, htmls)]) + return merge_pdfs(pdfs) if len(pdfs) > 1 else pdfs[0] + + +# โ”€โ”€ ๊ธด ํ‘œ ์ชฝ ๋‚˜๋ˆ” โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def _col_px(chars: float) -> int: + """`spreadsheet_headers.colCharsToPx` ์™€ ๊ฐ™์€ ๊ผด.""" + return round(min(chars, 255) * 7 + 5) + + +def _page_fit(sheet: dict[str, Any], book: dict[str, Any]) -> tuple[float, Any]: + """(์ชฝ ํ•˜๋‚˜ ๋†’์ด px ยท ๋ฐฐ์œจ ๋ฐ˜์˜) ยท ํ–‰ ๋†’์ด px ํ•จ์ˆ˜ โ€” ์ธ์‡„ HTML ๊ณผ ๊ฐ™์€ ์…ˆ.""" + pr = sheet.get("์ธ์‡„") or {} + w, h = PAPER_MM[pr.get("์šฉ์ง€") or "A4"] + if pr.get("๋ฐฉํ–ฅ") == "landscape": + w, h = h, w + m = {"์œ„": 10, "์•„๋ž˜": 10, "์™ผ": 10, "์˜ค๋ฅธ": 10, **(pr.get("์—ฌ๋ฐฑ") or {})} + last_col = max( + (sum((ord(ch) - 64) * 26**i for i, ch in enumerate(reversed(A1.match(a).group(1)))) + for a in sheet["์นธ"]), + default=1, + ) # fmt: skip + cols = [_col_name(c) for c in range(1, last_col + 1)] + default_w = ( + (sheet.get("๊ธฐ๋ณธ") or {}).get("์—ดํญ") or (book.get("๊ธฐ๋ณธ") or {}).get("์—ดํญ") or 8.43 + ) + table = sum(_col_px((sheet.get("์—ด") or {}).get(c, {}).get("ํญ") or default_w) for c in cols) + if (pr.get("๋งž์ถค") or {}).get("ํญ"): + zoom = min(1.0, (w - m["์™ผ"] - m["์˜ค๋ฅธ"]) * PX_MM / max(table, 1)) + else: + zoom = (pr.get("๋ฐฐ์œจ") or 100) / 100 + default_pt = (book.get("๊ธฐ๋ณธ") or {}).get("ํ–‰๋†’์ด") or 15 + + def row_px(r: int) -> float: + return ((sheet.get("ํ–‰") or {}).get(str(r), {}).get("๋†’์ด") or default_pt) * 4 / 3 + + return (h - m["์œ„"] - m["์•„๋ž˜"]) * PX_MM / zoom, row_px + + +def _col_name(n: int) -> str: + out = "" + while n: + n, k = divmod(n - 1, 26) + out = chr(65 + k) + out + return out + + +def balance_breaks(sheet: dict[str, Any], book: dict[str, Any], first: int, last: int) -> None: + """`first`~`last` ํ–‰ ํ‘œ๋ฅผ ์ชฝ๋งˆ๋‹ค ๊ณ ๋ฅด๊ฒŒ ๋‚˜๋ˆ”(์ชฝ๋‚˜๋ˆ” = ๋ฉ์ด ๋ ํ–‰) โ€” ํ•œ ์ชฝ์ด๋ฉด ๊ทธ๋Œ€๋กœ. + + ponytail: ํ•œ ์ค„ ๊ธ€ ํ–‰๋งŒ ๋งž์Œ(์ค„๋ฐ”๊ฟˆ์œผ๋กœ ํ‚ค ํฐ ํ–‰์€ ์…ˆ ๋ฐ–) ยท ์–ด๊ธ‹๋‚จ ๋Œ€๋น„ ํ•œ ์ค„ ๋œ ๋‹ด์Œ. + """ + if last < first: + return + page, row_px = _page_fit(sheet, book) + head = (sheet.get("์ธ์‡„") or {}).get("๋จธ๋ฆฌํ–‰") + head_px = sum(row_px(r) for r in range(head[0], head[1] + 1)) if head else 0.0 + pre_px = sum(row_px(r) for r in range(1, head[0] if head else first)) + rows = list(range(first, last + 1)) + cap_first = max(1, math.floor((page - pre_px - head_px) / row_px(first)) - 1) + cap_next = max(1, math.floor((page - head_px) / row_px(first)) - 1) + caps = [cap_first] + while sum(caps) < len(rows): # ์ชฝ ์ˆ˜ + caps.append(cap_next) + if len(caps) == 1: + return + breaks, at, left = [], 0, len(rows) + for i, cap in enumerate(caps[:-1]): + take = min(cap, math.ceil(left / (len(caps) - i))) + at += take + left -= take + breaks.append(rows[at - 1]) + sheet["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] = breaks diff --git a/E05_StabReport/E05_StabReport_Reinforce.py b/E05_StabReport/E05_StabReport_Reinforce.py new file mode 100644 index 000000000..bd41246a7 --- /dev/null +++ b/E05_StabReport/E05_StabReport_Reinforce.py @@ -0,0 +1,140 @@ +"""E05 ๋ณด๊ณ ์„œ s8 ๋ณด๊ฐ•๊ฒ€ํ†  โ€” E03 ๋ณด๊ฐ• ๊ฒ€ํ†  ์ €์žฅ๋ณธ(๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ • ยท ๋ฒฝ ์™ธ์  ์•ˆ์ •) โ†’ ์‹œํŠธ (PLAN 536-3). + +์นธ ์ž๋ฆฌ = `resources/master_template/report/stab_report_slots.md` s8. +์ €์žฅ๋ณธ์ด ์—†์œผ๋ฉด ์‹œํŠธ๋ฅผ ๋บŒ(์žˆ์„ ๋•Œ๋งŒ) โ€” `build_program` ์ด ์ฑ…์—์„œ ์ง€์›€. +""" + +from __future__ import annotations + +import copy +import hashlib +import json +from pathlib import Path +from typing import Any + +from E03_StabSection.E03_StabSection_Router_WhatIf import records + +CONDITION_NAMES = {"dry": "๊ฑด๊ธฐ", "wet": "์šฐ๊ธฐ", "seismic": "์ง€์ง„"} +SIDE_NAMES = {"left": "์ขŒ", "right": "์šฐ"} +ROLE_NAMES = {"cut": "๊นŽ๊ธฐ", "fill": "์Œ“๊ธฐ"} +CASE_NAMES = {"normal": "ํ‰์ƒ์‹œ", "seismic": "์ง€์ง„ ์‹œ"} +CHECKS = ("sliding", "overturning", "bearing") +ST_NG, ST_OK = 10, 11 + + +def read(project_root: Path) -> list[dict[str, Any]]: + return records(Path(project_root)) + + +def stamp(project_root: Path) -> str: + """ํ•ด์„ ์ง€๋ฌธ์šฉ โ€” ์ €์žฅ๋ณธ ๋ชฉ๋ก ๋‚ด์šฉ.""" + body = json.dumps(read(project_root), ensure_ascii=False, sort_keys=True, default=str) + return hashlib.sha1(body.encode("utf-8")).hexdigest() + + +def _set(sheet: dict[str, Any], a1: str, value: Any, style: int | None = None) -> None: + cell = dict(sheet["์นธ"].get(a1) or {}) + cell.pop("์‹", None) + if value is None: + cell.pop("๊ฐ’", None) + else: + cell["๊ฐ’"] = round(value, 6) if isinstance(value, float) else value + if style is not None: + cell["์„œ์‹"] = style + sheet["์นธ"][a1] = cell + + +def _verdict(sheet: dict[str, Any], a1: str, ok: bool | None) -> None: + if ok is None: + _set(sheet, a1, "-") + else: + _set(sheet, a1, "O.K" if ok else "N.G", ST_OK if ok else ST_NG) + + +def _assume_text(rec: dict[str, Any]) -> str: + a = rec["assume"] + text = f"1:{a['ratio_before']:g} โ†’ 1:{a['ratio']:g}" + if a.get("bench_w") and a.get("bench_h"): + text += f" ยท ์†Œ๋‹จ ํญ {a['bench_w']:g} m / ๋†’์ด {a['bench_h']:g} m ๋งˆ๋‹ค" + search = rec.get("search") + if search: + goal = CONDITION_NAMES.get(search.get("condition"), "์ „ ์กฐ๊ฑด") + text += ( + f" ยท ์ตœ์†Œ ์™„ํ™”({goal} ๋ชฉํ‘œ)" + if search.get("found") + else f" ยท ์ตœ์†Œ ์™„ํ™” ๋ชป ์ฐพ์Œ({goal} ยท ๋๊ฐ’)" + ) + return text + + +def fill_reinforce(sheet: dict[str, Any], recs: list[dict[str, Any]], labels: dict) -> None: + """s8 โ€” 1. ๊ฐ€์ • ์ค„(์กฐ๊ฑด๋งˆ๋‹ค ํ•œ ์ค„ ยท 6 ํ–‰ ์„œ์‹) ยท 2. ๋ฒฝ ์ค„(๊ฒฝ์šฐ๋งˆ๋‹ค ํ•œ ์ค„ ยท 10 ํ–‰ ์„œ์‹).""" + tpl = { + r: {c: copy.deepcopy(sheet["์นธ"].get(f"{c}{r}") or {}) for c in "ABCDEFGHI"} + for r in (8, 9, 10) + } + row6 = {c: copy.deepcopy(sheet["์นธ"].get(f"{c}6") or {}) for c in "ABCDEFGHI"} + for a1 in [k for k in sheet["์นธ"] if int("".join(ch for ch in k if ch.isdigit())) >= 6]: + del sheet["์นธ"][a1] + sheet["๋ณ‘ํ•ฉ"] = [ + m for m in sheet["๋ณ‘ํ•ฉ"] if int("".join(ch for ch in m.split(":")[0] if ch.isdigit())) < 6 + ] + sheet["ํ–‰"] = {k: v for k, v in sheet["ํ–‰"].items() if int(k) < 6} + + def put_row(r: int, styles: dict[str, dict]) -> None: + for c, cell in styles.items(): + sheet["์นธ"][f"{c}{r}"] = copy.deepcopy(cell) + + def name(rec: dict[str, Any]) -> str: + ch = float(rec["chainage_m"]) + return rec.get("label") or labels.get(round(ch, 3)) or f"{ch:.2f}" + + r = 6 + slopes = [x for x in recs if x["type"] == "slope"] + for rec in slopes: + a = rec["assume"] + for cond, after in rec["after"].items(): + before = rec["before"].get(cond) or {} + put_row(r, row6) + _set(sheet, f"A{r}", name(rec)) + _set(sheet, f"B{r}", f"{SIDE_NAMES[a['side']]} ยท {ROLE_NAMES[a['role']]}") + _set(sheet, f"C{r}", _assume_text(rec)) + _set(sheet, f"D{r}", CONDITION_NAMES.get(cond, cond)) + _set(sheet, f"E{r}", before.get("fs")) + _set(sheet, f"F{r}", after.get("fs")) + _set(sheet, f"G{r}", after.get("required")) + _verdict(sheet, f"H{r}", before.get("ok")) + _verdict(sheet, f"I{r}", after.get("ok")) + r += 1 + if not slopes: + put_row(r, row6) + _set(sheet, f"A{r}", "๊ฐ€์ • ์—†์Œ") + r += 1 + r += 1 + put_row(r, tpl[8]) + sheet["๋ณ‘ํ•ฉ"].append(f"A{r}:I{r}") + sheet["ํ–‰"][str(r)] = sheet["ํ–‰"][str(r + 1)] = {"๋†’์ด": 22} + put_row(r + 1, tpl[9]) + r += 2 + walls = [x for x in recs if x["type"] == "wall"] + for rec in walls: + p = rec["params"] + for wall in rec["walls"]: + for case in wall["cases"]: + put_row(r, tpl[10]) + _set(sheet, f"A{r}", name(rec)) + _set( + sheet, + f"B{r}", + f"{wall.get('name') or '๋ฒฝ'} ยท {CASE_NAMES[case['case']]} ยท " + f"{'Coulomb' if p['method'] == 'coulomb' else 'Rankine'} ยท ์ˆ˜์œ„ {p['water_ratio']:g}H", + ) + for c, need, check in zip("CEG", "DFH", CHECKS): + fs = case["fs"][check] + _set(sheet, f"{c}{r}", fs if fs == fs and abs(fs) < 1e6 else None) + _set(sheet, f"{need}{r}", case["required"][check]) + _verdict(sheet, f"I{r}", all(case["ok"].values())) + r += 1 + if not walls: + put_row(r, tpl[10]) + _set(sheet, f"A{r}", "๋ฒฝ ๊ฒ€ํ†  ์—†์Œ") diff --git a/E05_StabReport/E05_StabReport_Router.py b/E05_StabReport/E05_StabReport_Router.py index 9132c1d7a..28961e1ec 100644 --- a/E05_StabReport/E05_StabReport_Router.py +++ b/E05_StabReport/E05_StabReport_Router.py @@ -29,11 +29,12 @@ from pydantic import BaseModel from common_util.common_util_auth import verify_session from common_util.common_util_print_pdf import PrintError -from common_util.common_util_spreadsheet import RecalcError, with_server_values, workbook_pdf +from common_util.common_util_spreadsheet import RecalcError, with_server_values from common_util.common_util_storage import resolve_stored_project_path from common_util.common_util_workbook_xlsx import workbook_to_xlsx from config.config_db import get_db_pool from E05_StabReport import E05_StabReport_Build as build +from E05_StabReport import E05_StabReport_Print as printer from E05_StabReport import E05_StabReport_Store as store router = APIRouter(prefix="/api/projects", tags=["E05 Stab Report"]) @@ -137,7 +138,7 @@ async def put_report(project_id: UUID, payload: SaveRequest) -> dict[str, Any]: status_code=409, detail="๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ณด๊ณ ์„œ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค โ€” ๋‹ค์‹œ ์—ฌ์„ธ์š”." ) try: - await asyncio.to_thread(build.save, root, payload.book) + await asyncio.to_thread(store.save, root, payload.book) except RecalcError as error: raise _recalc_errors(error) from error return {"status": "success", **await asyncio.to_thread(_state, root)} @@ -153,7 +154,7 @@ async def refresh_report(project_id: UUID, payload: RevRequest) -> dict[str, Any status_code=409, detail="๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ณด๊ณ ์„œ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค โ€” ๋‹ค์‹œ ์—ฌ์„ธ์š”." ) try: - result = await asyncio.to_thread(build.refresh, root) + result = await asyncio.to_thread(store.refresh, root) except build.MissingInput as error: raise HTTPException(status_code=409, detail=str(error)) from error except (RecalcError, PrintError) as error: @@ -169,7 +170,7 @@ async def refresh_report(project_id: UUID, payload: RevRequest) -> dict[str, Any async def reset_report(project_id: UUID) -> dict[str, Any]: root, _ = await _project(project_id) async with _lock(project_id): - if not await asyncio.to_thread(build.reset, root): + if not await asyncio.to_thread(store.reset, root): raise HTTPException(status_code=404, detail="์ดˆ๊ธฐ๊ฐ’์ด ์—†์Šต๋‹ˆ๋‹ค.") return {"status": "success", **await asyncio.to_thread(_state, root)} @@ -192,9 +193,7 @@ def _export(root: Path, book: dict[str, Any] | None, kind: str) -> bytes: figures = store.figures_of(root, book) if kind == "xlsx": return workbook_to_xlsx(with_server_values(book), figures) - clean = dict(book) - clean.pop("๊ณ„์‚ฐ๊ฐ’", None) # ์ธ์‡„ HTML ์ด ๋‹ค์‹œ ํ’‚(๋ธŒ๋ผ์šฐ์ € ๊ฐ’ ์•ˆ ์”€) - return workbook_pdf(clean, figures) + return printer.report_pdf(book, figures) # ์ธ์‡„ HTML ์ด ๋‹ค์‹œ ํ’‚(๋ธŒ๋ผ์šฐ์ € ๊ฐ’ ์•ˆ ์”€) async def _export_response(project_id: UUID, book: dict[str, Any] | None, kind: str) -> Response: diff --git a/E05_StabReport/E05_StabReport_Section.py b/E05_StabReport/E05_StabReport_Section.py new file mode 100644 index 000000000..37ef40bf6 --- /dev/null +++ b/E05_StabReport/E05_StabReport_Section.py @@ -0,0 +1,318 @@ +"""E05 ๋ณด๊ณ ์„œ s5 ๋Œ€ํ‘œ๋‹จ๋ฉด ยท s10 ๋ถ€๋ก ์ ˆํŽธ โ€” ๋‹จ๋ฉด ํ•˜๋‚˜ = ํ•œ ์ชฝ(PLAN 534-3 ยท 534-5). + +์นธ ์ž๋ฆฌ = `resources/master_template/report/stab_report_slots.md` s5 ยท s10. +๊ทธ๋ฆผ = `E05_StabReport_Figure.section_svg`(๊ฐ’ ์…ˆ ์—†์Œ ยท ํ•ด์„ ์ €์žฅ๋ณธ ์˜ฎ๊ฒจ ๊ทธ๋ฆผ). +""" + +from __future__ import annotations + +import copy +import math +from typing import Any + +from E03_StabSection import E03_StabSection_View as view2d +from E03_StabSection.E03_StabSection_Engine_Model import final_surface +from E05_StabReport import E05_StabReport_Figure as figure +from E05_StabReport.E05_StabReport_Cells import ( + A1, + CASES, + GROUND_NAMES, + METHOD_NAMES, + REASONS, + SIDE_NAMES, + _criterion, + _label_at, + _labels, + _seismic, + _set, + _shift, + _verdict, + fig_key, +) + +BLOCK = 28 # ๋Œ€ํ‘œ๋‹จ๋ฉด ํ•œ ์ชฝ = 28 ํ–‰ +LOWEST_N = view2d.REPRESENTATIVE_LOWEST +CIRCLED = "โ‘ โ‘กโ‘ขโ‘ฃโ‘คโ‘ฅโ‘ฆโ‘งโ‘จโ‘ฉโ‘ชโ‘ซโ‘ฌโ‘ญโ‘ฎโ‘ฏโ‘ฐโ‘ฑโ‘ฒโ‘ณ" +CONDITION_NAMES = {"dry": "๊ฑด๊ธฐ", "wet": "์šฐ๊ธฐ", "seismic": "์ง€์ง„"} +SLICE_COLS = "ABCDEFGHIJKLM" +SLICE_TPL = 5 # ๋ถ€๋ก ์ ˆํŽธ ๋ฉ์ด ๊ผด = ์–‘์‹ 1~5 ํ–‰(์ œ๋ชฉ ยท ๋‹จ์œ„ ยท ๋จธ๋ฆฌ ยท ์ค„ ยท ํ•ฉ๊ณ„) + + +def _case_slip(result: dict[str, Any] | None, condition: str, side: str, method: str): + """์กฐ๊ฑด ํ•˜๋‚˜ โ€” ๊ทธ ๋น„ํƒˆ ์—ญํ• (๊นŽ๊ธฐ ยท ์Œ“๊ธฐ) ์ค‘ ๊ทธ ๋ฐฉ๋ฒ• ์ตœ์†Œ ๋ฉด ยท ๋†’์ด.""" + best = None + for case in (result or {}).get("cases") or []: + if case.get("condition") != condition: + continue + if side in ("cut", "fill") and case.get("role") not in (side, None): + continue + methods = (case.get("result") or {}).get("methods") or {} + found = (methods.get(method) or {}).get("min") or next( + (v["min"] for v in methods.values() if v.get("min")), None + ) + if found and found.get("F") is not None and (best is None or found["F"] < best[0]["F"]): + best = (found, case) + return best + + +def _block_copy( + sheet: dict[str, Any], template: dict[str, Any], dr: int, rows: int, tag: str +) -> None: + """์–‘์‹ 1~rows ํ–‰ ๋ฉ์ด๋ฅผ dr ํ–‰ ์•„๋ž˜๋กœ(์นธ ยท ๋ณ‘ํ•ฉ ยท ํ–‰ ๋†’์ด ยท ์กฐ๊ฑด๋ถ€์„œ์‹).""" + for a1, cell in template["์นธ"].items(): + if int(A1.match(a1).group(2)) <= rows: + sheet["์นธ"][_shift(a1, dr)] = copy.deepcopy(cell) + for m in template.get("๋ณ‘ํ•ฉ", []): + if int(A1.match(m.split(":")[0]).group(2)) <= rows: + sheet["๋ณ‘ํ•ฉ"].append(":".join(_shift(p, dr) for p in m.split(":"))) + for r, info in (template.get("ํ–‰") or {}).items(): + if int(r) <= rows: + sheet["ํ–‰"][str(int(r) + dr)] = dict(info) + for rule in template.get("์กฐ๊ฑด๋ถ€์„œ์‹", []): + moved = copy.deepcopy(rule) + moved["id"] = f"{rule['id']}_{tag}" + moved["๋ฒ”์œ„"] = [{**g, "r0": g["r0"] + dr, "r1": g["r1"] + dr} for g in rule["๋ฒ”์œ„"]] + sheet["์กฐ๊ฑด๋ถ€์„œ์‹"].append(moved) + + +def _clear(sheet: dict[str, Any]) -> dict[str, Any]: + """์‹œํŠธ๋ฅผ ๋น„์šฐ๊ณ  ์–‘์‹ ์‚ฌ๋ณธ์„ ๋Œ๋ ค์คŒ.""" + template = copy.deepcopy(sheet) + sheet["์นธ"] = {} + sheet["๋ณ‘ํ•ฉ"], sheet["ํ–‰"], sheet["์กฐ๊ฑด๋ถ€์„œ์‹"] = [], {}, [] + if "๊ทธ๋ฆผ" in sheet: + sheet["๊ทธ๋ฆผ"] = [] + return template + + +def _mark(k: int) -> str: + return CIRCLED[k] if k < len(CIRCLED) else str(k + 1) + + +def _picks(rep: dict[str, Any], ground: dict[str, Any]) -> dict[str, Any]: + """๋Œ€ํ‘œ ๋‹จ๋ฉด ํ•˜๋‚˜ โ€” ์กฐ๊ฑด๋ณ„ (๋ฉด, ๊ฒฝ์šฐ) ยท ์“ฐ๋Š” ์กฐ๊ฑด ยท ์ง€๋ฐฐ ์กฐ๊ฑด(Fs / ๊ธฐ์ค€ ๊ฐ€์žฅ ๋‚ฎ์Œ).""" + st = rep["summary"] + side, method = st.get("side"), st.get("method") or "bishop" + used = [c for c in CASES if c != "seismic" or _seismic(ground)] + picks = {c: _case_slip(rep.get("result"), c, side, method) for c in used} + fs = st.get("fs_by_condition") or {} + ratios = [ + (float(fs[c]) / _criterion(ground, c, side), c) + for c in used + if fs.get(c) is not None and picks.get(c) + ] + governing = min(ratios)[1] if ratios else None + return {"side": side, "method": method, "used": used, "picks": picks, "governing": governing} + + +def fill_sections(sheet: dict[str, Any], inputs: dict[str, Any]) -> list[tuple[str, str]]: + """s5 ๋Œ€ํ‘œ๋‹จ๋ฉด โ€” ๋‹จ๋ฉด ํ•˜๋‚˜ = 28 ํ–‰ ๋ฉ์ด = ํ•œ ์ชฝ ยท ๊ทธ๋ฆผ ์ผ๊ฐ [(์—ด์‡ , SVG)].""" + reps = inputs["representatives"] + ground = inputs["ground"] + labels = _labels(inputs["stations"]) + template = _clear(sheet) + if not reps: + _block_copy(sheet, template, 0, BLOCK, "0") + _set(sheet, "A1", "๋Œ€ํ‘œ ๋‹จ๋ฉด โ€” ํ•ด๋‹น ์—†์Œ") + sheet["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] = [] + return [] + jobs: list[tuple[str, str]] = [] + for k, rep in enumerate(reps): + dr = BLOCK * k + _block_copy(sheet, template, dr, BLOCK, str(k)) + st = rep["summary"] + got = _picks(rep, ground) + side, method, picks = got["side"], got["method"], got["picks"] + label = _label_at(labels, float(st["chainage_m"])) + dry = picks.get("dry") + height = float(dry[1].get("height_m") or 0) if dry else 0.0 + _set( + sheet, + f"A{1 + dr}", + f"๋Œ€ํ‘œ ๋‹จ๋ฉด {_mark(k)} โ€” {label} ยท {SIDE_NAMES.get(side, side or '-')} ยท " + f"H {height:.1f} m", + ) + reason = REASONS.get(rep.get("reason"), rep.get("reason") or "") + if rep.get("reason") == "lowest": + reason = f"{reason} {LOWEST_N}" + _set(sheet, f"B{2 + dr}", reason) + key = fig_key(rep["station_key"]) + sheet["๊ทธ๋ฆผ"].append( + {"์—ด์‡ ": key, "๋‹ป": f"A{3 + dr}:H{22 + dr}", "์ด๋ฆ„": f"๋Œ€ํ‘œ ๋‹จ๋ฉด {label}"} + ) + fs = st.get("fs_by_condition") or {} + slips, box = [], [] + for r, case in zip((24, 25, 26), CASES): + row = r + dr + if case not in got["used"]: + _set(sheet, f"B{row}", "๊ฒ€ํ†  ์•ˆ ํ•จ") + continue + value = fs.get(case) + crit = _criterion(ground, case, side) + ok = None if value is None else float(value) >= crit - 1e-9 + pick = picks.get(case) + _set(sheet, f"B{row}", METHOD_NAMES.get(method, method)) + _set(sheet, f"C{row}", float(value) if value is not None else None) + _set(sheet, f"D{row}", crit) + _verdict(sheet, f"E{row}", ok) + slip = pick[0] if pick else None + center = (slip or {}).get("center") + circle = bool(slip and slip.get("radius") and center) + _set(sheet, f"F{row}", float(slip["radius"]) if circle else "-" if slip else None) + _set(sheet, f"G{row}", float(center[0]) if circle else "-" if slip else None) + _set(sheet, f"H{row}", float(center[1]) if circle else "-" if slip else None) + if slip and slip.get("points"): + slips.append({"condition": case, "F": slip.get("F"), "points": slip["points"]}) + box.append({"condition": case, "F": value, "required": crit, "ok": ok}) + design = (rep.get("cross") or {}).get("design") or {} + kind = (dry[0].get("kind") if dry else None) or "" + layers = rep.get("layers") or {} + note = [ + {"circle": "ํ™œ๋™๋ฉด ์›ํ˜ธ", "polyline": "ํ™œ๋™๋ฉด ๋น„์›ํ˜ธ"}.get(kind, ""), + f"์ง€๋ฐ˜ {GROUND_NAMES.get(design.get('ground_type'), design.get('ground_type') or '-')}", + f"ํ† ์ธต ๋‘๊ป˜ {layers['soil_thickness_m']:g} m" if layers.get("soil_thickness_m") else "", + f"๋ถ€๋ก {_mark(k)} ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ" if got["governing"] else "", + ] + _set(sheet, f"B{27 + dr}", " ยท ".join(n for n in note if n)) + jobs.append((key, _section_figure(rep, slips, box, ground))) + sheet["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] = [BLOCK * (k + 1) for k in range(len(reps))] + return jobs + + +def _strata(rep: dict[str, Any], ground: dict[str, Any], fill: bool) -> list[dict[str, str]]: + """์ง€์ธต ๋ฒ”๋ก€ โ€” ์„ฑํ† (๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ๊ฐ€ ์›์ง€๋ฐ˜ ์œ„์ธ ๊ณณ์ด ์žˆ์œผ๋ฉด) ยท ์œ„ ์ง€์ธต ยท ์•„๋ž˜ ์ง€์ธต.""" + names = {m["id"]: m for m in ground.get("materials") or []} + layers = rep.get("layers") or {} + out = [] + picks = [ + ("fill", (ground.get("label_map") or {}).get("fill") or "fill", fill), + ("soil", layers.get("upper"), True), + ("rock", layers.get("lower"), bool(layers.get("soil_thickness_m"))), + ] + for kind, mid, show in picks: + m = names.get(mid) + if show and m: + text = f"{m['name']} ฮณt {m['gamma_t']:g} ยท c {m['c']:g} ยท ฯ† {m['phi']:g}" + out.append({"kind": kind, "text": text}) + return out + + +def _section_figure( + rep: dict[str, Any], slips: list[dict[str, Any]], box: list[dict[str, Any]], soil: dict +) -> str: + cross = rep.get("cross") or {} + design = cross.get("design") or {} + ground = sorted( # ํ•ด์„์— ์“ด ๋„“ํžŒ ์ง€๋ฐ˜์„ (์ž„๊ณ„๋ฉด์ด ํšก๋‹จ ํ‘œ๋ณธ ๋ฐ–๊นŒ์ง€ ๊ฐ) ยท ์—†์œผ๋ฉด ํšก๋‹จ ํ‘œ๋ณธ + (float(x), float(z)) for x, z in rep.get("ground") or [] + ) or sorted( + (float(s["offset_m"]), float(s["z"])) + for s in cross.get("samples") or [] + if s.get("valid", True) and s.get("z") is not None + ) + line = sorted( + (float(p["offset_m"]), float(p["elevation_m"])) for p in design.get("design_line") or [] + ) + surface = [tuple(p) for p in final_surface(ground, line)] if ground and line else ground + thickness = (rep.get("layers") or {}).get("soil_thickness_m") + rock = [(x, z - float(thickness)) for x, z in ground] if thickness else None + edges = design.get("road_edges") or {} + road = None + if edges.get("left") and edges.get("right"): + road = (float(edges["right"]["offset_m"]), float(edges["left"]["offset_m"])) + heights = [] + if road and line: + for case in (rep.get("result") or {}).get("cases") or []: + if case.get("condition") != "dry" or not case.get("height_m"): + continue + left = case.get("side") == "left" + edge = road[1] if left else road[0] + day = line[-1][0] if left else line[0][0] + z_edge, z1 = figure._interp(surface, edge), figure._interp(surface, day) + if z_edge is not None and z1 is not None: # ๋†’์ด = ์—”์ง„ ๊ฐ’(๋น„ํƒˆ ๋์—์„œ ๊ธธ ์ชฝ์œผ๋กœ) + h = float(case["height_m"]) + z0 = z1 + h if z_edge > z1 else z1 - h + heights.append({"x": day, "z0": z0, "z1": z1, "label": f"H {h:.2f} m"}) + filled = any( + (z_g := figure._interp(ground, x)) is not None and z - z_g > 0.05 for x, z in surface + ) + return figure.section_svg( + title="", + ground=ground, + surface=surface, + rock=rock, + slips=slips, + road=road, + heights=heights, + wet=any(s["condition"] == "wet" for s in slips), + strata=_strata(rep, soil, filled), + fs_box=box, + ) + + +# โ”€โ”€ s10 ๋ถ€๋ก ์ ˆํŽธ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def fill_slices(sheet: dict[str, Any], inputs: dict[str, Any]) -> None: + """s10 ๋ถ€๋ก ์ ˆํŽธ โ€” ๋Œ€ํ‘œ ๋‹จ๋ฉด๋งˆ๋‹ค ์ง€๋ฐฐ ์กฐ๊ฑด(Fs / ๊ธฐ์ค€ ๊ฐ€์žฅ ๋‚ฎ์Œ) ์ž„๊ณ„๋ฉด ์ ˆํŽธํ‘œ ํ•œ ์ชฝ.""" + ground = inputs["ground"] + labels = _labels(inputs["stations"]) + template = _clear(sheet) + data_tpl = {c: dict(template["์นธ"].get(f"{c}4") or {}) for c in SLICE_COLS} + sum_tpl = {c: dict(template["์นธ"].get(f"{c}5") or {}) for c in SLICE_COLS} + data_h = (template.get("ํ–‰") or {}).get("4") + sum_h = (template.get("ํ–‰") or {}).get("5") + dr, breaks = 0, [] + for k, rep in enumerate(inputs["representatives"]): + got = _picks(rep, ground) + cond = got["governing"] + slip = got["picks"][cond][0] if cond else None + rows = (slip or {}).get("slices") or [] + if not rows: + continue + _block_copy(sheet, template, dr, 3, str(k)) + label = _label_at(labels, float(rep["summary"]["chainage_m"])) + F = float(slip["F"]) + _set( + sheet, + f"A{1 + dr}", + f"๋ถ€๋ก {_mark(k)} ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ โ€” {label} ยท {SIDE_NAMES.get(got['side'], '-')} ยท " + f"{CONDITION_NAMES[cond]} ยท {METHOD_NAMES.get(got['method'], got['method'])} ยท " + f"Fs {F:.3f}", + ) + kind = {"circle": "์›ํ˜ธ", "polyline": "๋น„์›ํ˜ธ"}.get(slip.get("kind"), "") + _set( + sheet, + f"A{2 + dr}", + f"๋Œ€ํ‘œ ๋‹จ๋ฉด {_mark(k)} ์˜ {CONDITION_NAMES[cond]} ์ž„๊ณ„ ํ™œ๋™๋ฉด({kind}) ยท " + f"{template['์นธ']['A2']['๊ฐ’']}", + ) + sums = [0.0, 0.0, 0.0, 0.0] + for i, s in enumerate(rows): + r = 4 + dr + i + for c, cell in data_tpl.items(): + sheet["์นธ"][f"{c}{r}"] = dict(cell) + if data_h: + sheet["ํ–‰"][str(r)] = dict(data_h) + drive = (s["weight"] + s["surcharge"]) * math.sin(math.radians(s["alpha_deg"])) + resist = s["shear_mobilized"] * F + values = ( + i + 1, s["width"], s["height"], s["alpha_deg"], s["weight"], s["surcharge"], + s["pore_pressure"], s["base_length"], s["c"], s["phi_deg"], s["normal_eff"], + drive, resist, + ) # fmt: skip + for c, v in zip(SLICE_COLS, values): + _set(sheet, f"{c}{r}", float(v) if c != "A" else v) + for j, v in enumerate((s["weight"], s["surcharge"], drive, resist)): + sums[j] += v + r = 4 + dr + len(rows) + for c, cell in sum_tpl.items(): + sheet["์นธ"][f"{c}{r}"] = dict(cell) + if sum_h: + sheet["ํ–‰"][str(r)] = dict(sum_h) + _set(sheet, f"A{r}", "ํ•ฉ๊ณ„") + for c, v in zip("EFLM", sums): + _set(sheet, f"{c}{r}", v) + dr = r + breaks.append(r) + if not breaks: + _block_copy(sheet, template, 0, 2, "0") + _set(sheet, "A1", "๋ถ€๋ก โ€” ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ ์—†์Œ(๋Œ€ํ‘œ ๋‹จ๋ฉด ์—†์Œ)") + sheet["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] = breaks diff --git a/E05_StabReport/E05_StabReport_Store.py b/E05_StabReport/E05_StabReport_Store.py index ac96b8429..d234d8959 100644 --- a/E05_StabReport/E05_StabReport_Store.py +++ b/E05_StabReport/E05_StabReport_Store.py @@ -19,8 +19,10 @@ from pathlib import Path from typing import Any from common_util.common_util_json import atomic_write_json +from common_util.common_util_print_pdf import PrintError from common_util.common_util_spreadsheet import with_server_values from common_util.common_util_svg_png import svgs_to_pngs +from E05_StabReport import E05_StabReport_Print as printer from E05_StabReport.E05_StabReport_Build import ( META, PROGRAM_SHEETS, @@ -64,6 +66,15 @@ def _write(project_root: Path, book: dict[str, Any], meta: dict[str, Any]) -> No atomic_write_json(folder / META, meta) +def _numbered(book: dict[str, Any]) -> dict[str, Any]: + """๋ชฉ์ฐจ ์ชฝ ๋ฒˆํ˜ธ๋ฅผ ์‹ค์ œ ์ชฝ ์ˆ˜๋กœ(๋ธŒ๋ผ์šฐ์ € ์—†์œผ๋ฉด ใ€Œ-ใ€ ๊ทธ๋Œ€๋กœ ยท PDF ๋‚ด๋ณด๋‚ด๊ธฐ ๋•Œ ๋‹ค์‹œ ์…ˆ).""" + try: + printer.number_toc(book) + except PrintError: + pass + return book + + def _meta(book: dict[str, Any], inputs: dict[str, Any], figures: list[str]) -> dict[str, Any]: return { "fingerprint": inputs["fingerprint"], @@ -79,7 +90,7 @@ def create(project_root: Path) -> dict[str, Any]: book = form() fill_cover(next(s for s in book["์‹œํŠธ"] if s["id"] == "s1"), inputs) jobs = build_program(book, inputs) - book = with_server_values(book) + book = _numbered(with_server_values(book)) folder = stage_dir(project_root) figures = _write_figures(folder, jobs) meta = _meta(book, inputs, figures) @@ -94,6 +105,22 @@ def create(project_root: Path) -> dict[str, Any]: return meta +def _merge_styles(have: list[dict], form_styles: list[dict], sheets: Any) -> list[dict]: + """์ƒˆ ํŒ ์‹œํŠธ์˜ ์„œ์‹ ๋ฒˆํ˜ธ(์–‘์‹ ๊ธฐ์ค€)๋ฅผ ์ž‘์—…๋ณธ ์„œ์‹ ํ‘œ ๋ฒˆํ˜ธ๋กœ โ€” ์—†๋Š” ์„œ์‹์€ ๋์— ๋ถ™์ž„ + (์˜› ์ž‘์—…๋ณธ ยท ์‚ฌ์šฉ์ž๊ฐ€ ๋”ํ•œ ์„œ์‹๊ณผ ๋ฒˆํ˜ธ๊ฐ€ ์—‡๊ฐˆ๋ฆฌ์ง€ ์•Š๊ฒŒ).""" + styles = list(have) + remap: dict[int, int] = {} + for i, style in enumerate(form_styles): + if style not in styles: + styles.append(style) + remap[i] = styles.index(style) + for sheet in sheets: + for cell in sheet["์นธ"].values(): + if "์„œ์‹" in cell: + cell["์„œ์‹"] = remap.get(cell["์„œ์‹"], cell["์„œ์‹"]) + return styles + + def refresh(project_root: Path) -> dict[str, Any]: """[๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] โ€” ํ”„๋กœ๊ทธ๋žจ ๋ชซ ์‹œํŠธ ยท ๊ทธ๋ฆผ๋งŒ ๋‹ค์‹œ. ์‚ฌ์šฉ์ž ๋ชซ ยท ์‚ฌ์šฉ์ž๊ฐ€ ๋”ํ•œ ์‹œํŠธ๋Š” ๊ทธ๋Œ€๋กœ. ๋ฎ์ธ ์‹œํŠธ์— ์‚ฌ์šฉ์ž ๊ณ ์นจ์ด ์žˆ์—ˆ์œผ๋ฉด `overwritten`(์‹œํŠธ ์ด๋ฆ„) ยท ์˜› ํŒ `_์ง€๋‚œ/`.""" @@ -104,6 +131,7 @@ def refresh(project_root: Path) -> dict[str, Any]: fresh = form() jobs = build_program(fresh, inputs) built = {s["id"]: s for s in fresh["์‹œํŠธ"] if s["id"] in PROGRAM_SHEETS} + book = {**book, "์„œ์‹": _merge_styles(book.get("์„œ์‹") or [{}], fresh["์„œ์‹"], built.values())} signs = (meta or {}).get("signs") or {} overwritten = [ s["์ด๋ฆ„"] @@ -117,9 +145,19 @@ def refresh(project_root: Path) -> dict[str, Any]: stamp = datetime.now().strftime("%Y%m%d_%H%M%S") shutil.copy2(folder / REPORT, old / f"{stamp}.json") have = {s["id"] for s in book["์‹œํŠธ"]} - sheets = [built.get(s["id"], s) for s in book["์‹œํŠธ"]] - sheets += [built[sid] for sid in PROGRAM_SHEETS if sid not in have] # ์ง€์šด ์‹œํŠธ๋Š” ๋์— ๋‹ค์‹œ - book = with_server_values({**book, "์‹œํŠธ": sheets}) + # ์ƒˆ ํŒ์— ์—†๋Š” ํ”„๋กœ๊ทธ๋žจ ๋ชซ(๋ณด๊ฐ•๊ฒ€ํ†  โ€” ์ €์žฅ๋ณธ ์—†์Œ)์€ ๋บŒ + sheets = [ + built.get(s["id"], s) + for s in book["์‹œํŠธ"] + if s["id"] in built or s["id"] not in PROGRAM_SHEETS + ] + order = [s["id"] for s in fresh["์‹œํŠธ"]] + for sid in order: # ์—†๋Š” ํ”„๋กœ๊ทธ๋žจ ๋ชซ(์ง€์›€ ยท ์ƒˆ๋กœ ์ƒ๊ธด ๋ชฉ์ฐจ ยท ๋ถ€๋ก) = ์–‘์‹ ์ฐจ๋ก€ ์ž๋ฆฌ์— + if sid in built and sid not in have: + before = {x for x in order[: order.index(sid)]} + at = max((i + 1 for i, x in enumerate(sheets) if x["id"] in before), default=0) + sheets.insert(at, built[sid]) + book = _numbered(with_server_values({**book, "์‹œํŠธ": sheets})) figures = _write_figures(folder, jobs) new_meta = _meta(book, inputs, figures) _write(project_root, book, new_meta) diff --git a/E05_StabReport/E05_StabReport_UI_Style.css b/E05_StabReport/E05_StabReport_UI_Style.css index f958c575c..fda06c9da 100644 --- a/E05_StabReport/E05_StabReport_UI_Style.css +++ b/E05_StabReport/E05_StabReport_UI_Style.css @@ -6,7 +6,7 @@ .e05-page { display: flex; flex-direction: column; - height: 100%; + height: calc(100dvh - var(--app-header-h)); /* ๋ณธ๋ฌธ ์นธ์€ min-height ๋ฟ โ€” 100% ๊ฐ€ ์•ˆ ํ’€๋ฆผ */ min-height: 0; box-sizing: border-box; } diff --git a/E06_StabRock/E06_StabRock_Engine.py b/E06_StabRock/E06_StabRock_Engine.py new file mode 100644 index 000000000..a69b468b3 --- /dev/null +++ b/E06_StabRock/E06_StabRock_Engine.py @@ -0,0 +1,444 @@ +"""E06 ์•”๋ฐ˜ ๋น„ํƒˆ๋ฉด โ€” Markland ํŒ์ • ยท Hoek-Bray ํ‰๋ฉด ยท ์๊ธฐ ์•ˆ์ „์œจ (์š”๊ตฌ 345 ยท 351 ยท PLAN 536-1). + +๊ทผ๊ฑฐ = ์ง€์‹DB `original/์•ˆ์ •์„ฑํ‰๊ฐ€/03`(Markland ๋ถ€๋“ฑ์‹ ยท Hoek-Bray ํ‰๋ฉด ยท ์๊ธฐ) ยท 06(์ƒ์šฉ SW) ยท +Rocscience RocPlane ยท SWedge ์ด๋ก ์„œ(ํž˜ ๋ฒกํ„ฐ ยท ๋ฏธ๋„๋Ÿผ ๋ฐฉํ–ฅ 8 ๊ฒฝ์šฐ ยท ์ˆ˜์ง๋ ฅ ยท ์ง€์ง€๋ ฅ์€ ์ˆ˜๋™๋ ฅ). +์ฝ”๋“œ๋Š” ์ง์ ‘ ์งฌ(Dips ยท RocPlane ยท SWedge ์ฝ”๋“œ ์•ˆ ์”€) ยท NumPy ยท SciPy ๋งŒ. + +์ขŒํ‘œ = ๋™(+x) ยท ๋ถ(+y) ยท ์œ„(+z). ๋ฉด = (๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ฮฑ ยท ๊ฒฝ์‚ฌ ฯˆ) ยท ์„  = (์ถ”ํ–ฅ ยท ์„ ์ฃผ๊ฐ). +๋‹จ์œ„ = ๊ธธ์ด m ยท ๋‹จ์œ„์ค‘๋Ÿ‰ kN/mยณ ยท ์ ์ฐฉ๋ ฅ kPa ยท ๊ฐ ยฐ ยท ํž˜ kN(ํ‰๋ฉด = ๋‹จ์œ„ ํญ๋‹น kN/m). + +โš  ์ง€์‹DB 03 ์˜ ํ‰๋ฉด ๋ณผํŠธ ํ•ญ(์ˆ˜์ง T cos(ฮธ+ฯˆp) ยท ํ™œ๋™ โˆ’T sin(ฮธ+ฯˆp))์€ ฮธ = ๋ณผํŠธ์™€ ๋ฉด ๋ฒ•์„  ์‚ฌ์ด ๊ฐ์ผ +๋•Œ ๋งž์Œ โ€” ์—ฌ๊ธฐ๋Š” ฮธ = ๋ณผํŠธ ๊ฒฝ์‚ฌ(์ˆ˜ํ‰ ์•„๋ž˜)๋กœ ๋ฐ›์•„ ํž˜ ๋ฒกํ„ฐ๋กœ ํ’‚(์ˆ˜์ง T sin(ฮธ+ฯˆp) ยท ํ™œ๋™ โˆ’T cos(ฮธ+ฯˆp)). +""" + +from __future__ import annotations + +import math +from typing import Any + +import numpy as np +from scipy.optimize import minimize_scalar + +GAMMA_W = 9.81 # ๋ฌผ ๋‹จ์œ„์ค‘๋Ÿ‰ kN/mยณ (E03 Types ์™€ ๊ฐ™์Œ) +LATERAL_PLANAR_DEG = 20.0 # ํ‰๋ฉด ์ธก๋ฐฉ ํ•œ๊ณ„(Dips ๊ธฐ๋ณธ ยท Wyllie & Mah) +LATERAL_TOPPLING_DEG = 20.0 # ์ „๋„ ์ธก๋ฐฉ ํ•œ๊ณ„(Dips ๊ธฐ๋ณธ ยท ์ง€์‹DB 03 ์€ 30 โ€” ํ™”๋ฉด์—์„œ ๋ฐ”๊ฟˆ) + + +# ---- ๋ฐฉํ–ฅ ---------------------------------------------------------------------- +def ang_diff(a: float, b: float) -> float: + """๋‘ ๋ฐฉ์œ„๊ฐ ์‚ฌ์ด ์ตœ์†Œ ๊ฐ๊ฑฐ๋ฆฌ 0~180.""" + d = abs((a - b) % 360.0) + return min(d, 360.0 - d) + + +def plane_normal(dip_dir: float, dip: float) -> np.ndarray: + """๋ฉด์˜ ์œ„์ชฝ ๋‹จ์œ„ ๋ฒ•์„ .""" + a, p = math.radians(dip_dir), math.radians(dip) + return np.array([math.sin(a) * math.sin(p), math.cos(a) * math.sin(p), math.cos(p)]) + + +def line_vector(trend: float, plunge: float) -> np.ndarray: + """์„ (์ถ”ํ–ฅ ยท ์„ ์ฃผ๊ฐ) โ†’ ์•„๋ž˜๋กœ ํ–ฅํ•œ ๋‹จ์œ„ ๋ฒกํ„ฐ.""" + t, p = math.radians(trend), math.radians(plunge) + return np.array([math.sin(t) * math.cos(p), math.cos(t) * math.cos(p), -math.sin(p)]) + + +def vector_line(v: np.ndarray) -> tuple[float, float]: + """๋ฒกํ„ฐ โ†’ (์ถ”ํ–ฅ ยท ์„ ์ฃผ๊ฐ) ํ•˜๋ฐ˜๊ตฌ(์œ„๋กœ ํ–ฅํ•˜๋ฉด ๋’ค์ง‘์Œ).""" + v = np.asarray(v, float) + if v[2] > 0: + v = -v + v = v / np.linalg.norm(v) + trend = math.degrees(math.atan2(v[0], v[1])) % 360.0 + return trend, math.degrees(math.asin(max(-1.0, min(1.0, -v[2])))) + + +def pole(dip_dir: float, dip: float) -> tuple[float, float]: + """๋ฉด์˜ ๊ทน์ (ํ•˜๋ฐ˜๊ตฌ) = (ฮฑ + 180 ยท 90 โˆ’ ฯˆ).""" + return (dip_dir + 180.0) % 360.0, 90.0 - dip + + +def intersection(a: dict, b: dict) -> tuple[float, float] | None: + """๋‘ ๋ฉด ๊ต์„ (์ถ”ํ–ฅ ยท ์„ ์ฃผ๊ฐ) โ€” ๋‚˜๋ž€ํ•˜๋ฉด None.""" + v = np.cross( + plane_normal(a["dip_direction"], a["dip"]), plane_normal(b["dip_direction"], b["dip"]) + ) + if np.linalg.norm(v) < 1e-9: + return None + return vector_line(v) + + +def apparent_dip(dip: float, dip_dir: float, trend: float) -> float: + """๋ฉด (ฮฑ ยท ฯˆ) ์˜ trend ๋ฐฉํ–ฅ ๊ฒ‰๋ณด๊ธฐ ๊ฒฝ์‚ฌ โ€” ๋ฐ˜๋Œ€์ชฝ(90ยฐ ๋„˜์Œ)์ด๋ฉด ์Œ์ˆ˜.""" + return math.degrees( + math.atan(math.tan(math.radians(dip)) * math.cos(math.radians(trend - dip_dir))) + ) + + +def strike_to_dip_direction(strike: float) -> float: + """์˜ค๋ฅธ์† ๋ฒ•์น™ ์ฃผํ–ฅ โ†’ ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ.""" + return (strike + 90.0) % 360.0 + + +# ---- Markland ํŒ์ • ------------------------------------------------------------- +def kinematic( + slope: dict, + joints: list[dict], + lateral_planar: float = LATERAL_PLANAR_DEG, + lateral_toppling: float = LATERAL_TOPPLING_DEG, +) -> dict[str, list]: + """slope = {dip_direction, dip} ยท joints = [{id, dip_direction, dip, phi}, โ€ฆ]. + + ํ‰๋ฉด = ฮ”(ฮฑf, ฮฑp) โ‰ค ์ธก๋ฐฉ ยท ฯ† < ฯˆp < ๊ฒ‰๋ณด๊ธฐ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ / ์๊ธฐ = ๊ต์„  ฮ” < 90 ยท ฯ† < ์„ ์ฃผ๊ฐ < + ๊ฒ‰๋ณด๊ธฐ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ(ฯ† = ๋‘ ๋ฉด ์ค‘ ์ž‘์€ ๊ฐ’) / + ์ „๋„ = ฮ”(ฮฑf + 180, ฮฑp) โ‰ค ์ธก๋ฐฉ ยท ฯˆp > 90 โˆ’ ฯˆf + ฯ† (Goodman). + """ + af, pf = float(slope["dip_direction"]), float(slope["dip"]) + planar, toppling, wedge = [], [], [] + for j in joints: + aj, pj, phi = float(j["dip_direction"]), float(j["dip"]), float(j["phi"]) + if ang_diff(af, aj) <= lateral_planar and phi < pj < apparent_dip(pf, af, aj): + planar.append(j["id"]) + if ang_diff(af + 180.0, aj) <= lateral_toppling and pj > 90.0 - pf + phi: + toppling.append(j["id"]) + for i, a in enumerate(joints): + for b in joints[i + 1 :]: + line = intersection(a, b) + if line is None: + continue + trend, plunge = line + phi = min(float(a["phi"]), float(b["phi"])) + if ang_diff(af, trend) < 90.0 and phi < plunge < apparent_dip(pf, af, trend): + wedge.append( + { + "ids": [a["id"], b["id"]], + "trend": round(trend, 2), + "plunge": round(plunge, 2), + } + ) + return {"planar": planar, "wedge": wedge, "toppling": toppling} + + +# ---- ํ‰๋ฉด ํŒŒ๊ดด (Hoek-Bray ยท 2D ๋‹จ์œ„ ํญ) ----------------------------------------- +def _plane_block(H: float, pf: float, pp: float, ps: float, crack_x: float | None): + """์‚ฌ๋ฉด ๋(0,0) ยท x = ์‚ฐ ์ชฝ ์ˆ˜ํ‰ ยท z = ์œ„. ๋ฉ์ด ๋‹ค๊ฐํ˜• ยท ํ™œ๋™๋ฉด ๋ x ยท ๊ท ์—ด ๊นŠ์ด. + + ์œ—๋ฉด = ๋งˆ๋ฃจ(H cotฯˆf, H)์—์„œ ฯˆs ๋กœ ์˜ค๋ฆ„. ๊ท ์—ด ์—†์Œ = ํ™œ๋™๋ฉด์ด ์œ—๋ฉด๊ณผ ๋งŒ๋‚˜๋Š” ์ ๊นŒ์ง€.""" + tf, tp, ts = (math.tan(math.radians(v)) for v in (pf, pp, ps)) + xc = H / tf # ๋งˆ๋ฃจ + + def top(x: float) -> float: + return x * tf if x <= xc else H + (x - xc) * ts + + if crack_x is None: + if ts >= tp - 1e-12: + return None # ํ™œ๋™๋ฉด์ด ์œ—๋ฉด๊ณผ ์•ˆ ๋งŒ๋‚จ = ๋ฉ์ด ์•ˆ ์ƒ๊น€ + xe = (H - xc * ts) / (tp - ts) + poly = [(0.0, 0.0), (xc, H), (xe, xe * tp)] + return poly, xe, 0.0 + if crack_x < xc or top(crack_x) - crack_x * tp <= 1e-9: + return None + poly = [(0.0, 0.0), (xc, H), (crack_x, top(crack_x)), (crack_x, crack_x * tp)] + return poly, crack_x, top(crack_x) - crack_x * tp + + +def _area(poly) -> float: + xs, zs = np.array(poly).T + return 0.5 * abs(float(np.dot(xs, np.roll(zs, -1)) - np.dot(zs, np.roll(xs, -1)))) + + +def plane_fs( + *, + H: float, + slope_dip: float, + plane_dip: float, + c: float, + phi: float, + gamma: float, + upper_dip: float = 0.0, + crack: dict | None = None, + water_fill: float = 0.0, + kh: float = 0.0, + bolt_t: float = 0.0, + bolt_plunge: float = 0.0, + gamma_w: float = GAMMA_W, +) -> dict[str, Any] | None: + """ํ‰๋ฉด ํŒŒ๊ดด ์•ˆ์ „์œจ โ€” ๋ฉ์ด๊ฐ€ ์•ˆ ์ƒ๊ธฐ๋ฉด None. + + crack = None | {"mode": "depth", "z": m} | {"mode": "critical"}(์•ˆ์ „์œจ ์ตœ์†Œ ์ž๋ฆฌ ์ฐพ์Œ) ยท + ๋ฌผ = ๊ท ์—ด ์žˆ์Œ: zw = fillยทz ยท U = ยฝฮณw zw A ยท V = ยฝฮณw zwยฒ / ๊ท ์—ด ์—†์Œ: ๊ฐ€์šด๋ฐ ์ตœ๋Œ€ ์ˆ˜๋‘ + fillยทh/2(h = ํ™œ๋™๋ฉด ์˜ค๋ฆ„) ยท U = ยฝฮณw (fillยทh/2) A (Hoek ยท RocPlane) ยท + kh = ๋ฐ”๊นฅ ์ˆ˜ํ‰ ยท ๋ณผํŠธ = ์ˆ˜๋™๋ ฅ(์‚ฐ ์ชฝ ยท ์ˆ˜ํ‰ ์•„๋ž˜ bolt_plungeยฐ).""" + if not 0 < plane_dip < slope_dip < 90 or H <= 0 or upper_dip >= slope_dip: + return None + mode = (crack or {}).get("mode", "none") + if mode == "critical": + return _critical_crack(locals()) + tp = math.tan(math.radians(plane_dip)) + xc = H / math.tan(math.radians(slope_dip)) + crack_x = None + if mode == "depth": + z = float(crack["z"]) + crack_x = _crack_x_for_depth(H, slope_dip, plane_dip, upper_dip, z) + if crack_x is None: + return None + elif mode == "x": + crack_x = xc + float(crack["b"]) + block = _plane_block(H, slope_dip, plane_dip, upper_dip, crack_x) + if block is None: + return None + poly, x_end, z = block + psi = math.radians(plane_dip) + A = x_end / math.cos(psi) + W = gamma * _area(poly) + if crack_x is not None: + zw = water_fill * z + U, V = 0.5 * gamma_w * zw * A, 0.5 * gamma_w * zw * zw + else: + zw = water_fill * (x_end * tp) / 2.0 + U, V = 0.5 * gamma_w * zw * A, 0.0 + d = np.array([-math.cos(psi), -math.sin(psi)]) # ์•„๋ž˜๋กœ ๋ฏธ๋„๋Ÿฌ์ง(๋ฐ”๊นฅ = โˆ’x) + n = np.array([-math.sin(psi), math.cos(psi)]) # ๋ฉด์—์„œ ๋ฉ์ด ์ชฝ + active = np.array([-kh * W - V, -W]) + U * n + drive = float(active @ d) + if drive <= 0: + return None + bolt = bolt_t * np.array( + [math.cos(math.radians(bolt_plunge)), -math.sin(math.radians(bolt_plunge))] + ) + N = max(0.0, -float((active + bolt) @ n)) + resist = c * A + N * math.tan(math.radians(phi)) - float(bolt @ d) + return { + "F": resist / drive, + "W": W, + "A": A, + "U": U, + "V": V, + "z": z, + "zw": zw, + "crack_b": None if crack_x is None else crack_x - xc, + "N": N, + "drive": drive, + "resist": resist, + "polygon": [[round(x, 3), round(y, 3)] for x, y in poly], + } + + +def _crack_x_for_depth(H, pf, pp, ps, z) -> float | None: + """์œ—๋ฉด ์•„๋ž˜ ๊ท ์—ด ๊นŠ์ด z ๊ฐ€ ๋˜๋Š” ์ž๋ฆฌ(๋งˆ๋ฃจ ๋’ค) โ€” ๊นŠ์ด๊ฐ€ ๋งˆ๋ฃจ ํ‹ˆ๋ณด๋‹ค ํฌ๋ฉด None.""" + tf, tp, ts = (math.tan(math.radians(v)) for v in (pf, pp, ps)) + xc = H / tf + gap = H - xc * tp # ๋งˆ๋ฃจ์—์„œ ์œ—๋ฉด โˆ’ ํ™œ๋™๋ฉด + if z <= 0 or z >= gap: + return None + if ts >= tp: # ํ‹ˆ์ด ์ปค์ง€๊ธฐ๋งŒ ํ•จ = ๋’ค๋กœ ๊ฐˆ์ˆ˜๋ก ๊นŠ์–ด์ง + return xc + (z - gap) / (ts - tp) if ts > tp else None + return xc + (gap - z) / (tp - ts) + + +def plane_critical_depth_hoek(H: float, slope_dip: float, plane_dip: float) -> float: + """Hoek-Bray ์ž„๊ณ„ ์ธ์žฅ๊ท ์—ด ๊นŠ์ด(์œ—๋ฉด ์ˆ˜ํ‰ ยท ๊ฑด์กฐ) z = H(1 โˆ’ โˆš(cotฯˆf tanฯˆp)).""" + return H * ( + 1.0 - math.sqrt(math.tan(math.radians(plane_dip)) / math.tan(math.radians(slope_dip))) + ) + + +def _critical_crack(kw: dict) -> dict | None: + """๊ท ์—ด ์ž๋ฆฌ๋ฅผ ๋งˆ๋ฃจ ๋’ค๋กœ ์˜ฎ๊ฒจ ์•ˆ์ „์œจ ์ตœ์†Œ โ€” ๊ท ์—ด ์—†์Œ๋ณด๋‹ค ํฌ๋ฉด ๊ท ์—ด ์—†์Œ.""" + args = {k: kw[k] for k in kw if k not in ("crack", "mode")} + tf, tp, ts = (math.tan(math.radians(kw[k])) for k in ("slope_dip", "plane_dip", "upper_dip")) + xc = kw["H"] / tf + x_far = (kw["H"] - xc * ts) / (tp - ts) if ts < tp else xc + 10 * kw["H"] + span = x_far - xc + + def fs_at(b: float) -> float: + r = plane_fs(**args, crack={"mode": "x", "b": b}) + return r["F"] if r else math.inf + + best = minimize_scalar(fs_at, bounds=(span * 1e-3, span * (1 - 1e-3)), method="bounded") + found = plane_fs(**args, crack={"mode": "x", "b": float(best.x)}) + plain = plane_fs(**args, crack=None) + if found is None or (plain is not None and plain["F"] <= found["F"]): + return plain + return found + + +# ---- ์๊ธฐ ํŒŒ๊ดด (Hoek-Bray ยท SWedge ๊ผด ์‚ฌ๋ฉด์ฒด) ------------------------------------ +def _meet(planes: list[tuple[np.ndarray, float]]) -> np.ndarray | None: + """์„ธ ๋ฉด nยทx = d ์˜ ๊ต์ .""" + m = np.array([p[0] for p in planes]) + if abs(np.linalg.det(m)) < 1e-10: + return None + return np.linalg.solve(m, np.array([p[1] for p in planes])) + + +def _tri_area(a, b, c) -> float: + return 0.5 * float(np.linalg.norm(np.cross(b - a, c - a))) + + +def wedge_geometry(j1: dict, j2: dict, slope: dict, upper: dict, H: float) -> dict | None: + """์‚ฌ๋ฉด์ฒด ์๊ธฐ โ€” ๊ผญ์ง“์  O(๋‘ ์ ˆ๋ฆฌ ยท ์‚ฌ๋ฉด = ์‚ฌ๋ฉด ๋) ยท A(1 ยท ์‚ฌ๋ฉด ยท ์œ—๋ฉด) ยท B(2 ยท ์‚ฌ๋ฉด ยท ์œ—๋ฉด) ยท + C(1 ยท 2 ยท ์œ—๋ฉด). ์‚ฌ๋ฉด ๋†’์ด H = ์‚ฌ๋ฉด ์œ„ ๊ผญ์ง“์  A ยท B ์ค‘ ๋†’์€ ์ชฝ์˜ O ์œ„ ๋†’์ด(SWedge ์™€ ๊ฐ™์Œ โ€” + Priest ์˜ˆ์ œ ๋ถ€ํ”ผ 45.201 mยณ ์žฌํ˜„). ๊ต์„ ์ด ์‚ฌ๋ฉด ยท ์œ—๋ฉด์œผ๋กœ ์•ˆ ๋‚˜์˜ค๋ฉด None(์๊ธฐ ์•ˆ ์ƒ๊น€).""" + n1 = plane_normal(j1["dip_direction"], j1["dip"]) + n2 = plane_normal(j2["dip_direction"], j2["dip"]) + nf = plane_normal(slope["dip_direction"], slope["dip"]) + nu = plane_normal(upper["dip_direction"], upper["dip"]) + # ์œ—๋ฉด = ์‚ฌ๋ฉด ์œ„๋กœ H ์˜ค๋ฅธ ์ ์„ ์ง€๋‚จ(์‚ฌ๋ฉด ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ๋ฐ˜๋Œ€๋กœ H cotฯˆf ๋“ค์–ด๊ฐ„ ์ ) + back = -line_vector(slope["dip_direction"], 0.0) * (H / math.tan(math.radians(slope["dip"]))) + crest = back + np.array([0.0, 0.0, H]) + du = float(nu @ crest) + O = np.zeros(3) # noqa: E741 โ€” ๊ผญ์ง“์  ์ด๋ฆ„(SWedge ๊ทธ๋ฆผ๊ณผ ๊ฐ™์Œ) + A = _meet([(n1, 0.0), (nf, 0.0), (nu, du)]) + B = _meet([(n2, 0.0), (nf, 0.0), (nu, du)]) + C = _meet([(n1, 0.0), (n2, 0.0), (nu, du)]) + if A is None or B is None or C is None: + return None + # C ๋Š” ์‚ฌ๋ฉด ๋’ค(์‚ฐ ์ชฝ) ยท ์œ„ โ€” ๊ต์„ ์ด ์‚ฌ๋ฉด์œผ๋กœ ๋‚˜์™€ ์œ—๋ฉด์— ๋‹ฟ์Œ + if C[2] <= 1e-9 or float(nf @ C) >= -1e-9 or A[2] <= 1e-9 or B[2] <= 1e-9: + return None + scale = H / max(A[2], B[2]) # ๋ฉด์ด ๋ชจ๋‘ O ๋ฅผ ์ง€๋‚˜ ๋‹ฎ์€๊ผด โ€” ๋†’์ด๋งŒ ๋งž์ถค + A, B, C = A * scale, B * scale, C * scale + volume = abs(float(np.linalg.det(np.array([A - O, B - O, C - O])))) / 6.0 + if volume <= 1e-9: + return None + centroid = (O + A + B + C) / 4.0 + inward = [n if float(n @ centroid) > 0 else -n for n in (n1, n2)] + return { + "vertices": {"O": O, "A": A, "B": B, "C": C}, + "volume": volume, + "areas": [_tri_area(O, A, C), _tri_area(O, B, C)], + "face_area": _tri_area(O, A, B), + "upper_area": _tri_area(A, B, C), + "normals": inward, + "Hw": float(C[2]), + "line_length": float(np.linalg.norm(C)), + } + + +def _unit(v: np.ndarray) -> np.ndarray: + n = float(np.linalg.norm(v)) + return v / n if n > 1e-12 else v * 0.0 + + +def _sliding(A: np.ndarray, n1: np.ndarray, n2: np.ndarray, W: np.ndarray): + """SWedge ๋ฏธ๋„๋Ÿผ ๋ฐฉํ–ฅ โ€” (๊ฐˆ๋ž˜, ๋ฐฉํ–ฅ). ๊ฐˆ๋ž˜ = lift ยท 1 ยท 2 ยท 12 ยท stable.""" + s1 = _unit(np.cross(np.cross(n1, A), n1)) + s2 = _unit(np.cross(np.cross(n2, A), n2)) + c12 = np.cross(n1, n2) + s12 = _unit(c12) * np.sign(float(c12 @ A)) + if A @ n1 > 0 and A @ n2 > 0: + return "lift", _unit(A) + if A @ n1 <= 0 and s1 @ n2 > 0: + return "1", s1 + if A @ n2 <= 0 and s2 @ n1 > 0: + return "2", s2 + if s1 @ n2 <= 0 and s2 @ n1 <= 0: + return "12", s12 + return "stable", None + + +def _normals(mode: str, F: np.ndarray, n1: np.ndarray, n2: np.ndarray) -> tuple[float, float]: + if mode == "1": + return -float(F @ n1), 0.0 + if mode == "2": + return 0.0, -float(F @ n2) + if mode == "12": + c12, c21 = np.cross(n1, n2), np.cross(n2, n1) + return ( + -float(np.cross(F, n2) @ c12) / float(c12 @ c12), + -float(np.cross(F, n1) @ c21) / float(c21 @ c21), + ) + return 0.0, 0.0 + + +def wedge_fs( + *, + j1: dict, + j2: dict, + slope: dict, + upper: dict, + H: float, + gamma: float, + water: dict | None = None, + kh: float = 0.0, + seismic_trend: float | None = None, + external: list[dict] | None = None, + bolt: dict | None = None, + gamma_w: float = GAMMA_W, +) -> dict[str, Any] | None: + """์๊ธฐ ์•ˆ์ „์œจ(SWedge ์ด๋ก ์„œ ๊ผด) โ€” ์๊ธฐ๊ฐ€ ์•ˆ ์ƒ๊ธฐ๋ฉด None. + + j = {dip_direction, dip, c, phi} ยท water = {"fill": p}(์ค‘๋ ฅ์‹ U = pยณฮณw Hw a/6) | + {"pressure": [u1, u2]}(ํ‰๊ท  ์ˆ˜์•• kPa) ยท kh = ์ˆ˜ํ‰(๊ธฐ๋ณธ ๊ต์„  ์ถ”ํ–ฅ) ยท external = [{force, trend, + plunge}] ยท bolt = {force, trend, plunge}(์ˆ˜๋™๋ ฅ). FS = max(์ง€์ง€ ์ „ ยท ์ง€์ง€ ๋’ค ยท ๋“ค๋ฆผ).""" + geo = wedge_geometry(j1, j2, slope, upper, H) + if geo is None: + return None + n1, n2 = geo["normals"] + a1, a2 = geo["areas"] + weight = gamma * geo["volume"] + Wv = np.array([0.0, 0.0, -weight]) + active = Wv.copy() + water = water or {} + if "pressure" in water: + u1, u2 = (float(u) for u in water["pressure"]) + else: + p = float(water.get("fill", 0.0)) + u1 = u2 = p**3 * gamma_w * geo["Hw"] / 6.0 + Uf = [u1 * a1 * n1, u2 * a2 * n2] + active += Uf[0] + Uf[1] + line = vector_line(np.cross(n1, n2)) + if kh: + trend = line[0] if seismic_trend is None else seismic_trend + active += kh * weight * line_vector(trend, 0.0) + for item in external or []: + active += float(item["force"]) * line_vector(item["trend"], item["plunge"]) + passive = np.zeros(3) + if bolt and bolt.get("force"): + passive = float(bolt["force"]) * line_vector(bolt["trend"], bolt["plunge"]) + mode, s = _sliding(active, n1, n2, Wv) + out = { + "volume": geo["volume"], + "W": weight, + "areas": [a1, a2], + "face_area": geo["face_area"], + "upper_area": geo["upper_area"], + "Hw": geo["Hw"], + "line_length": geo["line_length"], + "line": [round(line[0], 2), round(line[1], 2)], + "water_force": [float(np.linalg.norm(Uf[0])), float(np.linalg.norm(Uf[1]))], + "mode": mode, + "vertices": {k: [round(float(x), 3) for x in v] for k, v in geo["vertices"].items()}, + } + if mode == "stable": + return {**out, "F": math.inf, "N": [0.0, 0.0], "drive": 0.0, "resist": 0.0} + drive = float(active @ s) + if mode == "lift": + fs = -float(passive @ s) / drive if drive > 0 else math.inf + return {**out, "F": fs, "N": [0.0, 0.0], "drive": drive, "resist": -float(passive @ s)} + joints = (j1, j2) + + def resisting(F: np.ndarray) -> tuple[float, list[float]]: + N = _normals(mode, F, n1, n2) + total = 0.0 + for k, (n_k, a_k) in enumerate(zip(N, (a1, a2))): + if str(k + 1) not in mode or n_k <= 0: + continue # ๋–จ์–ด์ง„ ๋ฉด = ์ €ํ•ญ ์—†์Œ(SWedge ์™€ ๊ฐ™์Œ) + tau = float(joints[k]["c"]) + n_k / a_k * math.tan( + math.radians(float(joints[k]["phi"])) + ) + total += tau * a_k + return total, [max(0.0, n) for n in N] + + j_u, n_u = resisting(active) + fs_u = j_u / drive if drive > 0 else math.inf + fs, n_used, resist = fs_u, n_u, j_u + if np.any(passive): + j_s, n_s = resisting(active + passive) + fs_s = (-float(passive @ s) + j_s) / drive if drive > 0 else math.inf + fs_lift = -float(passive @ _unit(active)) / float(np.linalg.norm(active)) + if fs_s > fs: + fs, n_used, resist = fs_s, n_s, -float(passive @ s) + j_s + fs = max(fs, fs_lift) + return {**out, "F": fs, "N": n_used, "drive": drive, "resist": resist} diff --git a/E06_StabRock/E06_StabRock_Model.py b/E06_StabRock/E06_StabRock_Model.py new file mode 100644 index 000000000..2cfe7d1ae --- /dev/null +++ b/E06_StabRock/E06_StabRock_Model.py @@ -0,0 +1,446 @@ +"""E06 ์ ˆ๋ฆฌ ์ •๋ณธ ๊ผด ยท ๊ฒ€์‚ฌ ยท ์ธก์  ํ•ด์„ ๋ฌถ์Œ (์š”๊ตฌ 345 ยท 351 ยท PLAN 536-1). + +์ •๋ณธ = `E06_StabRock/joints.json` ยท ์ดˆ๊ธฐ๊ฐ’ = `initial_snapshot/E06_StabRock/joints.json` +(์ ˆ๋ฆฌ ์—†์Œ + ๊ธฐ๋ณธ ์„ค์ • ยท ์ฒ˜์Œ GET ๋•Œ ํ•œ ๋ฒˆ). ์ ˆ๋ฆฌ๊ตฐ ํ•œ ์ค„ = ๊ตฌ๊ฐ„(from_m ~ to_m) ยท ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ยท ๊ฒฝ์‚ฌ ยท +ฯ†j ยท cj ยท ์ง€์†์„ฑ(์„ ํƒ). ์ธก์  = ๋ณต์‚ฌ๋ณธ ์•” ์ธก์ (๋ฆฌํ•‘์•” ยท ๋ฐœํŒŒ์•”)์˜ ๊นŽ๊ธฐ ์ชฝ๋งŒ. + +์‚ฌ๋ฉด = ์ธก์  frame ๋ฐฉํ–ฅ + ๊นŽ๊ธฐ ์ชฝ(๊ฒฝ์‚ฌ๋ฐฉํ–ฅ = ๋…ธ๋ฉด ์ชฝ) ยท ๊ฒฝ์‚ฌ = ๊นŽ๊ธฐ(์•”) ๊ฒฝ์‚ฌ๋น„ ยท ๋†’์ด = E03 ๊ณผ ๊ฐ™์€ +`side_cases` ๋†’์ด ยท ์œ—๋ฉด = ๋น„ํƒˆ ๋ ๋ฐ”๊นฅ ์›์ง€๋ฐ˜ ๊ฒฝ์‚ฌ. ๋ฌผ์„ฑ ยท ์ง€์ง„ ยท ๊ธฐ์ค€์•ˆ์ „์œจ = E02 ์ง€๋ฐ˜ ์กฐ๊ฑด. +""" + +from __future__ import annotations + +import copy +import hashlib +import math +from functools import lru_cache +from pathlib import Path +from typing import Any + +import numpy as np + +from common_util.common_util_station_key import chainage_key +from E01_StabRegister import E01_StabRegister_Source as src +from E02_StabGround import E02_StabGround_Model as ground_model +from E03_StabSection.E03_StabSection_Engine_Model import ground_from_cross, side_cases +from E06_StabRock import E06_StabRock_Engine as engine +from E06_StabRock.E06_StabRock_Stereonet import PROJECTIONS, stereonet + +STAGE_DIR = "E06_StabRock" +JOINTS_NAME = "joints.json" +VERSION = 1 +ENGINE_FILES = ("E06_StabRock_Engine.py", "E06_StabRock_Stereonet.py", "E06_StabRock_Model.py") +DEFAULT_GAMMA_R = 25.0 # ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ โ€” E02 ์•” ๋ฌผ์„ฑ์ด ์—†์„ ๋•Œ(์ง€์‹DB 07 Hoek & Bray 25~27) +MIN_HEIGHT_M = 1.0 # ์ด๋ณด๋‹ค ๋‚ฎ์€ ๊นŽ๊ธฐ๋Š” ์•”๋ฐ˜ ๋ธ”๋ก ๊ฒ€ํ†  ์•ˆ ํ•จ +CRACK_MODES = ("none", "critical", "depth") +CONDITIONS = ("dry", "wet", "seismic") + +DEFAULT_SETTINGS: dict[str, Any] = { + "projection": "equal_angle", + "lateral_planar_deg": engine.LATERAL_PLANAR_DEG, + "lateral_toppling_deg": engine.LATERAL_TOPPLING_DEG, + "gamma_r": None, # None = E02 ์ธก์  ์•” ๋ฌผ์„ฑ ฮณt + "water_fill_wet": 0.5, # ์šฐ๊ธฐ ์ˆ˜์œ„ = ๋น„ํƒˆ ๋†’์ด ยฝ(KDS 11 70 05 ํ‘œ 4.3-3 ์•”๋ฐ˜) + "crack": {"mode": "none", "z": None}, + "bolt": {"force": 0.0, "plunge": 15.0}, +} + +# ์นธ ๋ฒ”์œ„ โ€” ํ™”๋ฉด ์ž…๋ ฅ ยท ์„œ๋ฒ„ ๊ฒ€์‚ฌ ๊ฐ™์€ ๊ฐ’ +LIMITS: dict[str, list[float]] = { + "chainage_m": [0.0, 100000.0], + "dip_direction": [0.0, 360.0], + "dip": [0.0, 90.0], + "phi": [0.0, 60.0], + "c": [0.0, 1000.0], + "persistence_m": [0.0, 1000.0], + "lateral_deg": [5.0, 45.0], + "gamma_r": [15.0, 35.0], + "water_fill_wet": [0.0, 1.0], + "crack_z": [0.1, 100.0], + "bolt_force": [0.0, 5000.0], + "bolt_plunge": [-30.0, 60.0], +} + +# ๊ธฐ๋ณธ ์ ˆ๋ฆฌ๋ฉด ๊ฐ’(์ƒˆ ์ค„) โ€” ์ง€์‹DB 07 ํ‘œ 7: cj 0~30 ยท ฯ†j 25~35(Barton ๊ธฐ๋ณธ๋งˆ์ฐฐ๊ฐ) ๋ณด์ˆ˜ ์ชฝ +NEW_SET = {"phi": 30.0, "c": 0.0} + + +def joints_path(project_root: Path) -> Path: + return Path(project_root) / STAGE_DIR / JOINTS_NAME + + +def initial_path(project_root: Path) -> Path: + return Path(project_root) / "initial_snapshot" / STAGE_DIR / JOINTS_NAME + + +def build_initial() -> dict[str, Any]: + return {"version": VERSION, "sets": [], "settings": copy.deepcopy(DEFAULT_SETTINGS)} + + +def upgrade(doc: dict[str, Any]) -> dict[str, Any]: + """๋น ์ง„ ์„ค์ • ์นธ๋งŒ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ฑ„์šด ์‚ฌ๋ณธ.""" + out = copy.deepcopy(doc) + settings = out.setdefault("settings", {}) + for key, value in DEFAULT_SETTINGS.items(): + settings.setdefault(key, copy.deepcopy(value)) + out.setdefault("sets", []) + return out + + +# ---- ๊ฒ€์‚ฌ ---------------------------------------------------------------------- +def _num(value: Any) -> bool: + return isinstance(value, int | float) and not isinstance(value, bool) and math.isfinite(value) + + +def _in(value: Any, key: str) -> bool: + lo, hi = LIMITS[key] + return _num(value) and lo <= value <= hi + + +def check_joints(doc: dict[str, Any]) -> list[dict[str, str]]: + """ํ‹€๋ฆฐ ์นธ [{path, code}] โ€” code = range ยท order ยท name ยท id_dup ยท enum ยท type.""" + errors: list[dict[str, str]] = [] + + def bad(path: str, code: str = "range") -> None: + errors.append({"path": path, "code": code}) + + sets = doc.get("sets") + if not isinstance(sets, list): + return [{"path": "sets", "code": "type"}] + seen: set[str] = set() + for k, row in enumerate(sets): + base = f"sets.{k}" + if not isinstance(row, dict): + bad(base, "type") + continue + ident = str(row.get("id") or "") + if not ident or ident in seen: + bad(f"{base}.id", "id_dup") + seen.add(ident) + if not str(row.get("name") or "").strip(): + bad(f"{base}.name", "name") + for key, limit in ( + ("from_m", "chainage_m"), + ("to_m", "chainage_m"), + ("dip_direction", "dip_direction"), + ("dip", "dip"), + ("phi", "phi"), + ("c", "c"), + ): + if not _in(row.get(key), limit): + bad(f"{base}.{key}") + if _num(row.get("from_m")) and _num(row.get("to_m")) and row["to_m"] < row["from_m"]: + bad(f"{base}.to_m", "order") + if row.get("persistence_m") is not None and not _in(row["persistence_m"], "persistence_m"): + bad(f"{base}.persistence_m") + settings = doc.get("settings") + if not isinstance(settings, dict): + return errors + [{"path": "settings", "code": "type"}] + if settings.get("projection") not in PROJECTIONS: + bad("settings.projection", "enum") + for key in ("lateral_planar_deg", "lateral_toppling_deg"): + if not _in(settings.get(key), "lateral_deg"): + bad(f"settings.{key}") + if settings.get("gamma_r") is not None and not _in(settings["gamma_r"], "gamma_r"): + bad("settings.gamma_r") + if not _in(settings.get("water_fill_wet"), "water_fill_wet"): + bad("settings.water_fill_wet") + crack = settings.get("crack") if isinstance(settings.get("crack"), dict) else {} + if crack.get("mode") not in CRACK_MODES: + bad("settings.crack.mode", "enum") + elif crack["mode"] == "depth" and not _in(crack.get("z"), "crack_z"): + bad("settings.crack.z") + bolt = settings.get("bolt") if isinstance(settings.get("bolt"), dict) else {} + if not _in(bolt.get("force"), "bolt_force"): + bad("settings.bolt.force") + if not _in(bolt.get("plunge"), "bolt_plunge"): + bad("settings.bolt.plunge") + return errors + + +def clean(doc: dict[str, Any]) -> dict[str, Any]: + """์ •๋ณธ ์นธ๋งŒ ยท ์ˆซ์ž๋Š” float.""" + sets = [] + for row in doc["sets"]: + item = { + "id": str(row["id"]), + "name": str(row["name"]).strip(), + **{ + key: float(row[key]) + for key in ("from_m", "to_m", "dip_direction", "dip", "phi", "c") + }, + "persistence_m": None + if row.get("persistence_m") is None + else float(row["persistence_m"]), + } + item["dip_direction"] %= 360.0 + sets.append(item) + s = doc["settings"] + crack = s["crack"] + return { + "version": VERSION, + "sets": sets, + "settings": { + "projection": s["projection"], + "lateral_planar_deg": float(s["lateral_planar_deg"]), + "lateral_toppling_deg": float(s["lateral_toppling_deg"]), + "gamma_r": None if s.get("gamma_r") is None else float(s["gamma_r"]), + "water_fill_wet": float(s["water_fill_wet"]), + "crack": { + "mode": crack["mode"], + "z": float(crack["z"]) if crack["mode"] == "depth" else None, + }, + "bolt": {"force": float(s["bolt"]["force"]), "plunge": float(s["bolt"]["plunge"])}, + }, + } + + +# ---- ์ธก์  ์‚ฌ๋ฉด ------------------------------------------------------------------ +def rock_stations(source: Path) -> list[dict[str, Any]]: + """์•” ์ธก์ (๋ฆฌํ•‘์•” ยท ๋ฐœํŒŒ์•”).""" + return [s for s in src.stations(source) if s.get("ground_type") in ground_model.ROCK_LABELS] + + +def _upper_dip(ground: list, start: float, sign: float, H: float, face_dip: float) -> float: + """๋น„ํƒˆ ๋ ๋ฐ”๊นฅ max(H, 5 m) ์›์ง€๋ฐ˜ ํ‰๊ท  ๊ฒฝ์‚ฌ(์˜ค๋ฅด๋ง‰๋งŒ ยท ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ โˆ’ 1ยฐ ์•„๋ž˜).""" + gx = np.array([p[0] for p in ground]) + gz = np.array([p[1] for p in ground]) + end = start + sign * max(H, 5.0) + end = min(max(end, gx[0]), gx[-1]) + run = abs(end - start) + if run < 0.5: + return 0.0 + rise = float(np.interp(end, gx, gz) - np.interp(start, gx, gz)) + return float(min(max(0.0, math.degrees(math.atan2(rise, run))), face_dip - 1.0)) + + +def station_slopes(cross: dict, design: dict) -> list[dict[str, Any]]: + """์ธก์ ์˜ ๊นŽ๊ธฐ ์ชฝ๋งˆ๋‹ค ์‚ฌ๋ฉด โ€” {side, dip_direction, dip, height_m, upper_dip}.""" + ground = ground_from_cross(cross) + line = sorted( + (float(p["offset_m"]), float(p["elevation_m"])) for p in design.get("design_line") or [] + ) + ratio = design.get("cut_slope_ratio") + if len(ground) < 2 or len(line) < 2 or not ratio: + return [] + face_dip = math.degrees(math.atan2(1.0, float(ratio))) + left = np.array([float(v) for v in cross["frame"]["left_xy"]]) + out = [] + for case in side_cases(design, ground, line): + if case["role"] != "cut" or case["height_m"] < MIN_HEIGHT_M: + continue + side = case["side"] + sign = 1.0 if side == "left" else -1.0 + # ๋น„ํƒˆ ๋ = ํƒ์ƒ‰ ๋์—์„œ ๋˜์งš์Œ(side_cases: reach = ๋น„ํƒˆ ๋ + sign ยท max(5, 2H)) + day = case["limits"][1 if sign > 0 else 0] - sign * max(5.0, 2.0 * case["height_m"]) + face = -sign * left # ๋…ธ๋ฉด ์ชฝ์œผ๋กœ ๊ธฐ์›€ + dip_dir = math.degrees(math.atan2(face[0], face[1])) % 360.0 + out.append( + { + "side": side, + "dip_direction": round(dip_dir, 2), + "dip": round(face_dip, 2), + "height_m": round(case["height_m"], 3), + "upper_dip": round(_upper_dip(ground, day, sign, case["height_m"], face_dip), 2), + } + ) + return out + + +# ---- ํ•ด์„ ---------------------------------------------------------------------- +@lru_cache(maxsize=1) +def engine_hash() -> str: + digest = hashlib.sha1() + for name in ENGINE_FILES: + digest.update((Path(__file__).parent / name).read_bytes()) + return digest.hexdigest()[:12] + + +def _ground_inputs(ground: dict[str, Any]) -> dict[str, Any]: + """E02 ์—์„œ E06 ์ด ์“ฐ๋Š” ์นธ โ€” ์ง€๋ฌธ์—๋„ ์ด๊ฒƒ๋งŒ.""" + return { + k: ground.get(k) + for k in ("materials", "label_map", "stations", "zones", "safety_factors", "seismic") + } + + +def _gamma_for(resolved: dict | None, materials: dict, override: float | None) -> float: + if override: + return float(override) + mat = materials.get((resolved or {}).get("lower") or (resolved or {}).get("upper")) + return float(mat["gamma_t"]) if mat else DEFAULT_GAMMA_R + + +def _fmt(value: float | None) -> float | None: + if value is None or not math.isfinite(value): + return None + return round(value, 4) + + +def _cases_for(slope: dict, joints: list[dict], kin: dict, settings: dict, gamma: float, conds): + """์œ„ํ—˜ ๋ชจ๋“œ๋งŒ ์•ˆ์ „์œจ โ€” ์กฐ๊ฑด = [(์ด๋ฆ„, ์ˆ˜์œ„ ๋น„, kh)].""" + upper = {"dip_direction": slope["dip_direction"], "dip": slope["upper_dip"]} + by_id = {j["id"]: j for j in joints} + bolt = settings["bolt"] + crack = settings["crack"] if settings["crack"]["mode"] != "none" else None + out = [] + for jid in kin["planar"]: + j = by_id[jid] + for name, fill, kh in conds: + r = engine.plane_fs( + H=slope["height_m"], + slope_dip=slope["dip"], + plane_dip=j["dip"], + c=j["c"], + phi=j["phi"], + gamma=gamma, + upper_dip=slope["upper_dip"], + crack=crack, + water_fill=fill, + kh=kh, + bolt_t=bolt["force"], + bolt_plunge=bolt["plunge"], + ) + short = ( + r is not None and j.get("persistence_m") is not None and j["persistence_m"] < r["A"] + ) + out.append( + { + "mode": "planar", + "ids": [jid], + "condition": name, + "F": _fmt(r["F"]) if r else None, + "formed": r is not None, + "persistence_short": short, + "W": _fmt(r["W"]) if r else None, + "A": _fmt(r["A"]) if r else None, + "z": _fmt(r["z"]) if r else None, + } + ) + for item in kin["wedge"]: + a, b = (by_id[k] for k in item["ids"]) + for name, fill, kh in conds: + r = engine.wedge_fs( + j1=a, + j2=b, + slope=slope, + upper=upper, + H=slope["height_m"], + gamma=gamma, + water={"fill": fill}, + kh=kh, + bolt={ + "force": bolt["force"], + "trend": (slope["dip_direction"] + 180.0) % 360.0, + "plunge": bolt["plunge"], + }, + ) + length = r["line_length"] if r else None + short = any( + j.get("persistence_m") is not None + and length is not None + and j["persistence_m"] < length + for j in (a, b) + ) + out.append( + { + "mode": "wedge", + "ids": list(item["ids"]), + "condition": name, + "F": _fmt(r["F"]) if r else None, + "formed": r is not None, + "persistence_short": short, + "sliding": r["mode"] if r else None, + "W": _fmt(r["W"]) if r else None, + "volume": _fmt(r["volume"]) if r else None, + } + ) + return out + + +def analyze(source: Path, joints_doc: dict, ground: dict) -> list[dict[str, Any]]: + """์•” ์ธก์  ร— ๊นŽ๊ธฐ ์ชฝ โ€” ์‚ฌ๋ฉด ยท ์ ˆ๋ฆฌ ยท Markland ยท ์•ˆ์ „์œจ ยท ํŒ์ •.""" + settings = joints_doc["settings"] + sets = joints_doc["sets"] + materials = {m["id"]: m for m in ground.get("materials") or []} + resolved = {chainage_key(r["chainage_m"]): r for r in ground_model.resolve_stations(ground)} + seismic = ground.get("seismic") or {} + factors = ground.get("safety_factors") or {} + conds = [("dry", 0.0, 0.0), ("wet", float(settings["water_fill_wet"]), 0.0)] + if seismic.get("enabled") and seismic.get("kh"): + conds.append(("seismic", 0.0, float(seismic["kh"]))) + out = [] + for station in rock_stations(source): + ch = float(station["chainage_m"]) + key = chainage_key(ch) + joints = [j for j in sets if j["from_m"] <= ch <= j["to_m"]] + cross = src.cross_section(source, ch) + gamma = _gamma_for(resolved.get(key), materials, settings.get("gamma_r")) + for slope in station_slopes(cross, cross["design"]): + kin = engine.kinematic( + slope, joints, settings["lateral_planar_deg"], settings["lateral_toppling_deg"] + ) + cases = _cases_for(slope, joints, kin, settings, gamma, conds) + for case in cases: + need = (factors.get(case["condition"]) or {}).get("cut") + case["required"] = need + case["ok"] = None if case["F"] is None or need is None else case["F"] >= need + out.append( + { + "key": key, + "chainage_m": ch, + "label": station.get("label"), + "ground_type": station.get("ground_type"), + "slope": slope, + "gamma_r": gamma, + "joints": [ + {k: j[k] for k in ("id", "name", "dip_direction", "dip", "phi", "c")} + for j in joints + ], + "kinematic": kin, + "cases": cases, + **_verdict(kin, cases), + } + ) + return out + + +def _verdict(kin: dict, cases: list[dict]) -> dict[str, Any]: + """ํŒ์ • โ€” NG(๊ธฐ์ค€ ๋ฏธ๋‹ฌ ํ•˜๋‚˜๋ผ๋„) ยท check(์ „๋„๋งŒ ยท ๋ธ”๋ก ์•ˆ ์ƒ๊น€) ยท OK ยท none(์œ„ํ—˜ ๋ชจ๋“œ ์—†์Œ).""" + values = [c["F"] for c in cases if c["F"] is not None] + min_f = min(values) if values else None + if any(c["ok"] is False for c in cases): + verdict = "NG" + elif kin["toppling"] or (cases and not values): + verdict = "check" + elif cases: + verdict = "OK" + else: + verdict = "none" + return { + "counts": { + "planar": len(kin["planar"]), + "wedge": len(kin["wedge"]), + "toppling": len(kin["toppling"]), + }, + "min_F": min_f, + "verdict": verdict, + } + + +def station_net(entry: dict, settings: dict) -> dict[str, Any]: + """ํ•œ ์ธก์  ์ชฝ ์Šคํ…Œ๋ ˆ์˜ค๋„ท(ํ•ด์„ ๋•Œ ์ ˆ๋ฆฌ ๊ทธ๋Œ€๋กœ) โ€” ๋‘ ํˆฌ์˜ ๋ชจ๋‘(ํ™”๋ฉด์ด ๊ณ ๋ฆ„).""" + joints = entry["joints"] + phi = min((j["phi"] for j in joints), default=NEW_SET["phi"]) + nets = { + kind: stereonet( + entry["slope"], + joints, + phi, + kind, + settings["lateral_planar_deg"], + settings["lateral_toppling_deg"], + ) + for kind in PROJECTIONS + } + return {**nets, "phi": phi} diff --git a/E06_StabRock/E06_StabRock_Router.py b/E06_StabRock/E06_StabRock_Router.py new file mode 100644 index 000000000..559e41d2d --- /dev/null +++ b/E06_StabRock/E06_StabRock_Router.py @@ -0,0 +1,204 @@ +"""E06 ์•”๋ฐ˜ ํ‰์‚ฌํˆฌ์˜ ํ•ด์„ API (์š”๊ตฌ 345 ยท 351 ยท ์š”๊ตฌ์„œ 4-2 2์ฐจ ยท PLAN 536-1). + +- `GET /api/projects/{id}/stab/rock` โ€” ์ ˆ๋ฆฌ ์ž‘์—…๋ณธ(์—†์œผ๋ฉด ์ดˆ๊ธฐ๊ฐ’) ยท ๋ฒ”์œ„ ยท ์•” ์ธก์  ยท ๊ณ ์น  ์ˆ˜ ์žˆ๋Š”์ง€ +- `PUT โ€ฆ/stab/rock` โ€” [์ €์žฅ] ๊ฒ€์‚ฌ ๋’ค ์ž‘์—…๋ณธ `E06_StabRock/joints.json` +- `POST โ€ฆ/stab/rock/reset` โ€” [์ดˆ๊ธฐํ™”] ์ดˆ๊ธฐ๊ฐ’์„ ์ž‘์—…๋ณธ์— ๋ฎ์–ด์”€(์žฌ๊ณ„์‚ฐ ์•„๋‹˜) +- `POST โ€ฆ/stab/rock/analyze` โ€” [ํ•ด์„] ์•” ์ธก์  ร— ๊นŽ๊ธฐ ์ชฝ ยท ์ง€๋ฌธ์ด ๊ฐ™์œผ๋ฉด ์•ˆ ์…ˆ(`fresh`) +- `GET โ€ฆ/stab/rock/result` โ€” ์ธก์ ๋ณ„ ๋ชจ๋“œ ยท ์•ˆ์ „์œจ ยท ํŒ์ •(+ ์ง€๋ฌธ์ด ์ง€๊ธˆ๊ณผ ๋‹ค๋ฅธ์ง€) +- `GET โ€ฆ/stab/rock/result?station=&side=` โ€” ๊ทธ ์ธก์  ์ชฝ + ์Šคํ…Œ๋ ˆ์˜ค๋„ท(๋“ฑ๊ฐ ยท ๋“ฑ๋ฉด์ ) + +์ž…๋ ฅ = E01 ๋ณต์‚ฌ๋ณธ + E02 ์ง€๋ฐ˜ ์กฐ๊ฑด(๋ฌผ์„ฑ ยท ์ง€์ง„ ยท ๊ธฐ์ค€์•ˆ์ „์œจ) + ์ ˆ๋ฆฌ ์ž‘์—…๋ณธ(์ €์žฅ ์•ˆ ํ•œ ์บ์‹œ๋Š” ์•ˆ ์”€). +๊ณ„์‚ฐ ์ž๋ฆฌ = โ‘ข ์„œ๋ฒ„ ๋‹จ๋…. ๊ฒฐ๊ณผ = ํŒŒ์ƒ ์ €์žฅ๋ณธ `_derived/stab_rock/<์ง€๋ฌธ>`. +๊ณต์œ  ํšŒ์‚ฌ(๋ณด๊ธฐ๋งŒ)๋Š” `require_project_access` ๊ฐ€ PUT ยท POST ๋ฅผ ๋ง‰์Œ. +""" + +from __future__ import annotations + +import asyncio +import json +from pathlib import Path +from typing import Any +from uuid import UUID + +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel + +from common_util.common_util_auth import verify_session +from common_util.common_util_derived_store import ( + derived_dir, + derived_key, + read_derived, + write_derived, +) +from common_util.common_util_json import atomic_write_json +from E01_StabRegister import E01_StabRegister_Source as src +from E02_StabGround import E02_StabGround_Router as e02 +from E06_StabRock import E06_StabRock_Model as model + +router = APIRouter(prefix="/api/projects", tags=["E06 Stab Rock"]) + +KIND = "stab_rock" + + +class JointsRequest(BaseModel): + joints: dict[str, Any] + + +def ensure_initial(project_root: Path) -> dict[str, Any]: + """์ดˆ๊ธฐ๊ฐ’์ด ์—†์„ ๋•Œ๋งŒ ํ•œ ๋ฒˆ โ€” ์ ˆ๋ฆฌ ์—†์Œ + ๊ธฐ๋ณธ ์„ค์ •.""" + path = model.initial_path(project_root) + if path.is_file(): + return json.loads(path.read_text(encoding="utf-8")) + doc = model.build_initial() + atomic_write_json(path, doc) + return doc + + +def _working(project_root: Path) -> dict[str, Any] | None: + path = model.joints_path(project_root) + return json.loads(path.read_text(encoding="utf-8")) if path.is_file() else None + + +def _joints(project_root: Path) -> dict[str, Any]: + return model.upgrade(_working(project_root) or ensure_initial(project_root)) + + +def _source(project_root: Path) -> Path: + root = src.source_dir(project_root) + if not (root / src.MANIFEST_NAME).is_file(): + raise HTTPException(status_code=409, detail="์›๋ณธ ๋ณต์‚ฌ๋ณธ์ด ์—†์Šต๋‹ˆ๋‹ค.") + return root + + +def _fingerprint(project_root: Path, source: Path) -> tuple[str, dict, dict]: + joints = _joints(project_root) + ground = e02._read_working(project_root) or e02.ensure_initial(project_root) + fp = derived_key( + model.engine_hash(), + src.manifest(source)["source_fingerprint"], + { + "sets": joints["sets"], + "settings": {k: v for k, v in joints["settings"].items() if k != "projection"}, + }, + model._ground_inputs(ground), + ) + return fp, joints, ground + + +def _latest(project_root: Path) -> dict[str, Any] | None: + """๊ฐ€์žฅ ์ตœ๊ทผ ์ €์žฅ๋ณธ(์ง€๋ฌธ์ด ๋‹ฌ๋ผ๋„ โ€” ํ™”๋ฉด์ด ใ€Œ๋‹ค์‹œ ํ•ด์„ใ€ ๋ ๋ฅผ ๋„์›€).""" + folder = derived_dir(project_root, KIND) + files = sorted(folder.glob("*.json"), key=lambda p: p.stat().st_mtime_ns, reverse=True) + for path in files: + saved = read_derived(project_root, KIND, path.stem) + if saved: + return saved + return None + + +@router.get("/{project_id}/stab/rock") +async def get_rock( + project_id: UUID, session: dict[str, Any] = Depends(verify_session) +) -> dict[str, Any]: + root, project = await e02._project(project_id) + initial = ensure_initial(root) + working = _working(root) + try: + stations = [ + {"chainage_m": s["chainage_m"], "label": s["label"], "ground_type": s["ground_type"]} + for s in model.rock_stations(_source(root)) + ] + except HTTPException: + stations = [] + return { + "joints": model.upgrade(working or initial), + "saved": working is not None, + "defaults": { + "settings": model.DEFAULT_SETTINGS, + "new_set": model.NEW_SET, + "limits": model.LIMITS, + }, + "stations": stations, + "editable": e02._editable(session, project), + } + + +@router.put("/{project_id}/stab/rock") +async def put_rock(project_id: UUID, payload: JointsRequest) -> dict[str, Any]: + root, _ = await e02._project(project_id) + errors = model.check_joints(payload.joints) + if errors: + raise HTTPException(status_code=422, detail={"errors": errors}) + doc = model.clean(payload.joints) + atomic_write_json(model.joints_path(root), doc) + return {"joints": doc} + + +@router.post("/{project_id}/stab/rock/reset") +async def reset_rock(project_id: UUID) -> dict[str, Any]: + root, _ = await e02._project(project_id) + initial = ensure_initial(root) + atomic_write_json(model.joints_path(root), initial) + return {"joints": model.upgrade(initial)} + + +def _summary(stations: list[dict[str, Any]]) -> dict[str, Any]: + verdicts: dict[str, int] = {} + for item in stations: + verdicts[item["verdict"]] = verdicts.get(item["verdict"], 0) + 1 + return { + "slopes": len(stations), + "verdicts": verdicts, + "modes": { + mode: sum(item["counts"][mode] for item in stations) + for mode in ("planar", "wedge", "toppling") + }, + } + + +def _run(root: Path) -> dict[str, Any]: + source = _source(root) + fp, joints, ground = _fingerprint(root, source) + if read_derived(root, KIND, fp): + return {"status": "fresh", "fingerprint": fp} + stations = model.analyze(source, joints, ground) + value = { + "fingerprint": fp, + "settings": joints["settings"], + "summary": _summary(stations), + "stations": stations, + } + write_derived(root, KIND, fp, value) + return {"status": "done", "fingerprint": fp, "summary": value["summary"]} + + +@router.post("/{project_id}/stab/rock/analyze") +async def analyze_rock(project_id: UUID) -> dict[str, Any]: + """[ํ•ด์„] โ€” ์•” ์ธก์  ์ˆ˜์‹ญ ๊ฐœ ยท 1 ์ดˆ ์•ˆ์ชฝ์ด๋ผ ๋ฐ”๋กœ ์…ˆ(์ง„ํ–‰๋ฅ  ์—†์Œ).""" + root, _ = await e02._project(project_id) + return await asyncio.to_thread(_run, root) + + +def _result(root: Path, station: str | None, side: str | None) -> dict[str, Any]: + source = _source(root) + fp, _, _ = _fingerprint(root, source) + saved = read_derived(root, KIND, fp) or _latest(root) + if not saved: + return {"status": "empty", "stale": False, "result": None} + value = saved["value"] + stale = value["fingerprint"] != fp + if station is None: + return {"status": "success", "stale": stale, "result": value} + for entry in value["stations"]: + if entry["key"] == station and (side is None or entry["slope"]["side"] == side): + net = model.station_net(entry, value["settings"]) + return {"status": "success", "stale": stale, "entry": entry, "stereonet": net} + raise HTTPException(status_code=404, detail="๊ทธ ์ธก์ ์˜ ์•”๋ฐ˜ ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.") + + +@router.get("/{project_id}/stab/rock/result") +async def result_rock( + project_id: UUID, station: str | None = None, side: str | None = None +) -> dict[str, Any]: + root, _ = await e02._project(project_id) + return await asyncio.to_thread(_result, root, station, side) diff --git a/E06_StabRock/E06_StabRock_Stereonet.py b/E06_StabRock/E06_StabRock_Stereonet.py new file mode 100644 index 000000000..fb0b3cd3e --- /dev/null +++ b/E06_StabRock/E06_StabRock_Stereonet.py @@ -0,0 +1,137 @@ +"""E06 ์Šคํ…Œ๋ ˆ์˜ค๋„ท ํˆฌ์˜ ์ขŒํ‘œ โ€” ํ•˜๋ฐ˜๊ตฌ ยท ๋“ฑ๊ฐ(Wulff) ยท ๋“ฑ๋ฉด์ (Schmidt) (PLAN 536-1). + +ํ™”๋ฉด(SVG)์€ ์ด ์ขŒํ‘œ๋ฅผ ๊ทธ๋ฆฌ๊ธฐ๋งŒ ํ•จ โ€” ๋ฐ˜์ง€๋ฆ„ 1 ์› ยท x = ๋™ ยท y = ๋ถ(ํ™”๋ฉด์€ y ๋ฅผ ๋’ค์ง‘์Œ). +๊ทน์  ยท ๋Œ€์› ยท ๊ต์„  ยท ๋งˆ์ฐฐ์› ยท ์ผ๊ด‘ ์—”๋ฒจ๋กœํ”„ ยท ์œ„ํ—˜ ์˜์—ญ(Markland ๋ถ€๋“ฑ์‹๊ณผ ๊ฐ™์€ ๊ฒฝ๊ณ„ โ€” +`E06_StabRock_Engine.kinematic`)์„ ๋ƒ„. ํˆฌ์˜์‹ = ํ‘œ์ค€ ๊ตฌ๋ฉด ํˆฌ์˜(mplstereonet ๋“ฑ์€ ๋Œ€์กฐ๋งŒ). +""" + +from __future__ import annotations + +import math +from typing import Any + +from E06_StabRock.E06_StabRock_Engine import ( + LATERAL_PLANAR_DEG, + LATERAL_TOPPLING_DEG, + apparent_dip, + intersection, + pole, +) + +PROJECTIONS = ("equal_angle", "equal_area") +STEP_DEG = 2.0 # ๊ณก์„  ํ‘œ๋ณธ ๊ฐ„๊ฒฉ + + +def radius(plunge: float, kind: str) -> float: + """์„ ์ฃผ๊ฐ โ†’ ์ค‘์‹ฌ์—์„œ ๊ฑฐ๋ฆฌ(0 = ์—ฐ์ง ยท 1 = ์ˆ˜ํ‰).""" + half = math.radians(90.0 - plunge) / 2.0 + return math.tan(half) if kind == "equal_angle" else math.sqrt(2.0) * math.sin(half) + + +def project(trend: float, plunge: float, kind: str) -> list[float]: + r = radius(plunge, kind) + t = math.radians(trend) + return [round(r * math.sin(t), 5), round(r * math.cos(t), 5)] + + +def inverse(x: float, y: float, kind: str) -> tuple[float, float]: + """ํˆฌ์˜ ์ขŒํ‘œ โ†’ (์ถ”ํ–ฅ ยท ์„ ์ฃผ๊ฐ) โ€” ์™•๋ณต ์‹œํ—˜ ยท ํ™”๋ฉด ๋งˆ์šฐ์Šค ์ž๋ฆฌ ์ฝ๊ธฐ.""" + r = math.hypot(x, y) + half = math.atan(r) if kind == "equal_angle" else math.asin(min(1.0, r / math.sqrt(2.0))) + return math.degrees(math.atan2(x, y)) % 360.0, 90.0 - math.degrees(2.0 * half) + + +def _samples(lo: float, hi: float) -> list[float]: + n = max(2, int(math.ceil((hi - lo) / STEP_DEG)) + 1) + return [lo + (hi - lo) * k / (n - 1) for k in range(n)] + + +def great_circle(dip_dir: float, dip: float, kind: str) -> list[list[float]]: + """๋ฉด์˜ ๋Œ€์› โ€” ์ฃผํ–ฅ ํ•œ๋ โ†’ ๊ฒฝ์‚ฌ ๋ฐฉํ–ฅ โ†’ ๋‹ค๋ฅธ ๋.""" + return [ + project(dip_dir + b, max(0.0, apparent_dip(dip, dip_dir, dip_dir + b)), kind) + for b in _samples(-90.0, 90.0) + ] + + +def _zone(trend_lo, trend_hi, plunge_lo, plunge_hi, kind) -> list[list[float]] | None: + """(์ถ”ํ–ฅ ยท ์„ ์ฃผ๊ฐ) ๋  โ†’ ๋‹ซํžŒ ๋‹ค๊ฐํ˜• โ€” plunge_lo/hi ๋Š” ์ถ”ํ–ฅ์˜ ํ•จ์ˆ˜ ยท ๋น„๋ฉด None.""" + ts = _samples(trend_lo, trend_hi) + lows = [plunge_lo(t) for t in ts] + highs = [plunge_hi(t) for t in ts] + keep = [k for k in range(len(ts)) if highs[k] > lows[k]] + if len(keep) < 2: + return None + outer = [project(ts[k], lows[k], kind) for k in keep] + inner = [project(ts[k], highs[k], kind) for k in reversed(keep)] + return outer + inner + + +def stereonet( + slope: dict, + joints: list[dict], + phi: float, + kind: str = "equal_angle", + lateral_planar: float = LATERAL_PLANAR_DEG, + lateral_toppling: float = LATERAL_TOPPLING_DEG, +) -> dict[str, Any]: + """slope = {dip_direction, dip} ยท joints = [{id, dip_direction, dip}] ยท phi = ์˜์—ญ ๋งˆ์ฐฐ๊ฐ.""" + af, pf = float(slope["dip_direction"]), float(slope["dip"]) + + def face_app(t: float) -> float: + return apparent_dip(pf, af, t) + + planes = [] + for j in joints: + tr, pl = pole(j["dip_direction"], j["dip"]) + planes.append( + { + "id": j["id"], + "pole": project(tr, pl, kind), + "great_circle": great_circle(j["dip_direction"], j["dip"], kind), + } + ) + lines = [] + for i, a in enumerate(joints): + for b in joints[i + 1 :]: + line = intersection(a, b) + if line is not None: + lines.append({"ids": [a["id"], b["id"]], "point": project(*line, kind)}) + # ์ผ๊ด‘ ์—”๋ฒจ๋กœํ”„(๊ทน์  ๊ผด) = ์‚ฌ๋ฉด ๊ฒ‰๋ณด๊ธฐ ๊ฒฝ์‚ฌ๋กœ ๊ธฐ์šด ๋ฉด๋“ค์˜ ๊ทน์  + envelope = [ + project(af + b + 180.0, 90.0 - face_app(af + b), kind) for b in _samples(-90.0, 90.0) + ] + zones = { + # ํ‰๋ฉด(๊ทน์ ) โ€” ๊ทน์  ์ถ”ํ–ฅ ฮฑf + 180 ยฑ ์ธก๋ฐฉ ยท ์„ ์ฃผ๊ฐ 90 โˆ’ ฯˆapp ~ 90 โˆ’ ฯ† + "planar": _zone( + af + 180.0 - lateral_planar, + af + 180.0 + lateral_planar, + lambda t: 90.0 - face_app(t - 180.0), + lambda t: 90.0 - phi, + kind, + ), + # ์๊ธฐ(๊ต์„ ) โ€” ์ถ”ํ–ฅ ฮฑf ยฑ 90 ยท ์„ ์ฃผ๊ฐ ฯ† ~ ฯˆapp + "wedge": _zone(af - 90.0, af + 90.0, lambda t: phi, face_app, kind), + # ์ „๋„(๊ทน์ ) โ€” ๊ทน์  ์ถ”ํ–ฅ ฮฑf ยฑ ์ธก๋ฐฉ ยท ์„ ์ฃผ๊ฐ 0 ~ ฯˆf โˆ’ ฯ† + "toppling": _zone( + af - lateral_toppling, + af + lateral_toppling, + lambda t: 0.0, + lambda t: pf - phi, + kind, + ), + } + return { + "projection": kind, + "slope": { + "pole": project(*pole(af, pf), kind), + "great_circle": great_circle(af, pf, kind), + }, + "planes": planes, + "intersections": lines, + # ๋งˆ์ฐฐ์› โ€” ๊ทน์  ๊ผด = ์ค‘์‹ฌ์—์„œ ฯ†(์„ ์ฃผ๊ฐ 90 โˆ’ ฯ†) ยท ๊ต์„  ๊ผด = ์„ ์ฃผ๊ฐ ฯ† + "friction_pole_r": round(radius(90.0 - phi, kind), 5), + "friction_line_r": round(radius(phi, kind), 5), + "daylight_envelope": envelope, + "zones": zones, + } diff --git a/E06_StabRock/E06_StabRock_UI_Joints.ts b/E06_StabRock/E06_StabRock_UI_Joints.ts new file mode 100644 index 000000000..6350ae45f --- /dev/null +++ b/E06_StabRock/E06_StabRock_UI_Joints.ts @@ -0,0 +1,304 @@ +/* ============================================================================= + * E06_StabRock_UI_Joints.ts + * E06 ์ขŒ์ธก โ€” ์ ˆ๋ฆฌ๊ตฐ ํ‘œ(๊ตฌ๊ฐ„ ยท ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ยท ๊ฒฝ์‚ฌ ยท ฯ†j ยท cj ยท ์ง€์†์„ฑ ยท [์ถ”๊ฐ€] [์ง€์›€]) + ํ•ด์„ ์„ค์ • + * (์ธก๋ฐฉ ํ•œ๊ณ„ ยท ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ ยท ์šฐ๊ธฐ ์ˆ˜์œ„ ยท ์ธ์žฅ๊ท ์—ด ยท ๋ณผํŠธ). ์นธ์„ ๋ฐ”๊พธ๋ฉด doc ์„ ๊ณ ์น˜๊ณ  onChange โ€” + * ์บ์‹œ ยท ์ €์žฅ์€ ํŽ˜์ด์ง€ ๋ชซ. ๋ฒ”์œ„ = ์„œ๋ฒ„ LIMITS(๋ฒ—์–ด๋‚˜๋ฉด ์นธ ๋นจ๊ฐ• ยท ๋งˆ์šฐ์Šค ๊ธ€). + * ========================================================================== */ + +import { createPanelCard } from "@ui/ui_template_collapsible"; +import { createButton, el } from "@ui/ui_template_elements"; +import { t, type LocaleKey } from "@ui/ui_template_locale"; + +export interface JointSet { + id: string; + name: string; + from_m: number; + to_m: number; + dip_direction: number; + dip: number; + phi: number; + c: number; + persistence_m: number | null; +} + +export type Projection = "equal_angle" | "equal_area"; + +export interface RockSettings { + projection: Projection; + lateral_planar_deg: number; + lateral_toppling_deg: number; + gamma_r: number | null; + water_fill_wet: number; + crack: { mode: "none" | "critical" | "depth"; z: number | null }; + bolt: { force: number; plunge: number }; +} + +export interface JointsDoc { + version: number; + sets: JointSet[]; + settings: RockSettings; +} + +export interface RockDefaults { + settings: RockSettings; + new_set: { phi: number; c: number }; + limits: Record; +} + +export interface JointsView { + doc: JointsDoc; + defaults: RockDefaults; + editable: boolean; + endM: number; // ๋งˆ์ง€๋ง‰ ์•” ์ธก์ (์ƒˆ ์ค„ ๊ตฌ๊ฐ„ ๋) + change: () => void; // ๊ฐ’๋งŒ ๋ฐ”๋€œ + rebuild: () => void; // ์ค„ ์ˆ˜๊ฐ€ ๋ฐ”๋€œ +} + +/** ์ˆซ์ž ์นธ โ€” ๋ฒ”์œ„ ๋ฐ– ยท ๋นˆ ์นธ(ํ•„์ˆ˜)์ด๋ฉด is-invalid ยท ๋ฐ”๊พธ๋ฉด set ๋ถ€๋ฅด๊ณ  change */ +function numberInput( + view: JointsView, + value: number | null, + limit: string, + set: (v: number | null) => void, + opts: { optional?: boolean; step?: string; path: string }, +): HTMLInputElement { + const [lo, hi] = view.defaults.limits[limit] ?? [-Infinity, Infinity]; + const input = el("input", { + className: "e06-input", + attrs: { type: "number", step: opts.step ?? "any", "data-path": opts.path }, + }) as HTMLInputElement; + input.value = value === null ? "" : String(value); + input.disabled = !view.editable; + const check = (): boolean => { + const raw = input.value.trim(); + const num = Number(raw); + const ok = raw === "" ? Boolean(opts.optional) : Number.isFinite(num) && num >= lo && num <= hi; + input.classList.toggle("is-invalid", !ok); + input.title = ok ? "" : `${t("E06_Err_Range")} (${lo} ~ ${hi})`; + return ok; + }; + input.addEventListener("input", () => { + const raw = input.value.trim(); + set(raw === "" ? null : Number(raw)); + check(); + view.change(); + }); + check(); + return input; +} + +function nextId(sets: JointSet[]): string { + let k = sets.length + 1; + while (sets.some((s) => s.id === `J${k}`)) k += 1; + return `J${k}`; +} + +const COLUMNS: LocaleKey[] = [ + "E06_Col_Name", + "E06_Col_From", + "E06_Col_To", + "E06_Col_DipDir", + "E06_Col_Dip", + "E06_Col_Phi", + "E06_Col_C", + "E06_Col_Persist", +]; + +function jointTable(view: JointsView): HTMLElement { + const wrap = el("div", { className: "e06-table-wrap" }); + const table = el("table", { className: "e06-table" }); + const head = el("tr"); + for (const key of COLUMNS) head.append(el("th", { text: t(key) })); + head.append(el("th")); + table.append(el("thead"), el("tbody")); + table.tHead!.append(head); + view.doc.sets.forEach((row, k) => { + const tr = el("tr", { attrs: { "data-id": row.id } }); + const name = el("input", { + className: "e06-input e06-input--name", + attrs: { type: "text", "data-path": `sets.${k}.name` }, + }) as HTMLInputElement; + name.value = row.name; + name.disabled = !view.editable; + name.addEventListener("input", () => { + row.name = name.value; + name.classList.toggle("is-invalid", !name.value.trim()); + view.change(); + }); + tr.append(el("td", { children: [name] })); + const fields: [keyof JointSet, string, boolean][] = [ + ["from_m", "chainage_m", false], + ["to_m", "chainage_m", false], + ["dip_direction", "dip_direction", false], + ["dip", "dip", false], + ["phi", "phi", false], + ["c", "c", false], + ["persistence_m", "persistence_m", true], + ]; + for (const [key, limit, optional] of fields) { + const input = numberInput( + view, + row[key] as number | null, + limit, + (v) => ((row as unknown as Record)[key] = v), + { optional, path: `sets.${k}.${key}` }, + ); + tr.append(el("td", { children: [input] })); + } + const remove = createButton({ + label: "ร—", + variant: "danger-ghost", + size: "sm", + onClick: () => { + view.doc.sets.splice(k, 1); + view.rebuild(); + }, + }); + remove.title = t("E06_Remove"); + remove.disabled = !view.editable; + tr.append(el("td", { children: [remove] })); + table.tBodies[0].append(tr); + }); + wrap.append(table); + if (!view.doc.sets.length) wrap.append(el("p", { className: "e06-hint", text: t("E06_NoSets") })); + return wrap; +} + +function settingRow(label: LocaleKey, control: HTMLElement): HTMLElement { + const row = el("label", { className: "e06-setting" }); + row.append(el("span", { text: t(label) }), control); + return row; +} + +function select( + view: JointsView, + value: T, + options: [T, LocaleKey][], + set: (v: T) => void, + path: string, +): HTMLSelectElement { + const box = el("select", { + className: "e06-input", + attrs: { "data-path": path }, + }) as HTMLSelectElement; + for (const [v, key] of options) box.append(el("option", { text: t(key), attrs: { value: v } })); + box.value = value; + box.disabled = !view.editable; + box.addEventListener("change", () => { + set(box.value as T); + view.rebuild(); + }); + return box; +} + +export function buildJointCards(view: JointsView): HTMLElement[] { + const s = view.doc.settings; + const setsCard = createPanelCard(t("E06_Card_Joints"), false); + const add = createButton({ + label: t("E06_Add"), + variant: "ghost", + size: "sm", + onClick: () => { + const id = nextId(view.doc.sets); + view.doc.sets.push({ + id, + name: id, + from_m: 0, + to_m: Math.ceil(view.endM), + dip_direction: 0, + dip: 45, + phi: view.defaults.new_set.phi, + c: view.defaults.new_set.c, + persistence_m: null, + }); + view.rebuild(); + }, + }); + add.disabled = !view.editable; + setsCard.append( + el("p", { className: "e06-hint", text: t("E06_JointsHint") }), + jointTable(view), + el("div", { className: "e06-row-actions", children: [add] }), + ); + + const setCard = createPanelCard(t("E06_Card_Settings"), true); + const crack = s.crack; + setCard.append( + settingRow( + "E06_Set_LateralPlanar", + numberInput( + view, + s.lateral_planar_deg, + "lateral_deg", + (v) => (s.lateral_planar_deg = v ?? NaN), + { + path: "settings.lateral_planar_deg", + }, + ), + ), + settingRow( + "E06_Set_LateralTopple", + numberInput( + view, + s.lateral_toppling_deg, + "lateral_deg", + (v) => (s.lateral_toppling_deg = v ?? NaN), + { path: "settings.lateral_toppling_deg" }, + ), + ), + settingRow( + "E06_Set_Gamma", + numberInput(view, s.gamma_r, "gamma_r", (v) => (s.gamma_r = v), { + optional: true, + path: "settings.gamma_r", + }), + ), + settingRow( + "E06_Set_WaterWet", + numberInput(view, s.water_fill_wet, "water_fill_wet", (v) => (s.water_fill_wet = v ?? NaN), { + step: "0.05", + path: "settings.water_fill_wet", + }), + ), + settingRow( + "E06_Set_Crack", + select( + view, + crack.mode, + [ + ["none", "E06_Crack_None"], + ["critical", "E06_Crack_Critical"], + ["depth", "E06_Crack_Depth"], + ], + (v) => { + crack.mode = v; + crack.z = v === "depth" ? (crack.z ?? 1) : null; + }, + "settings.crack.mode", + ), + ), + ); + if (crack.mode === "depth") + setCard.append( + settingRow( + "E06_Set_CrackZ", + numberInput(view, crack.z, "crack_z", (v) => (crack.z = v), { path: "settings.crack.z" }), + ), + ); + setCard.append( + settingRow( + "E06_Set_BoltForce", + numberInput(view, s.bolt.force, "bolt_force", (v) => (s.bolt.force = v ?? NaN), { + path: "settings.bolt.force", + }), + ), + settingRow( + "E06_Set_BoltPlunge", + numberInput(view, s.bolt.plunge, "bolt_plunge", (v) => (s.bolt.plunge = v ?? NaN), { + path: "settings.bolt.plunge", + }), + ), + el("p", { className: "e06-hint", text: t("E06_SettingsHint") }), + ); + return [setsCard, setCard]; +} diff --git a/E06_StabRock/E06_StabRock_UI_Page.ts b/E06_StabRock/E06_StabRock_UI_Page.ts new file mode 100644 index 000000000..f96156bd7 --- /dev/null +++ b/E06_StabRock/E06_StabRock_UI_Page.ts @@ -0,0 +1,397 @@ +/* ============================================================================= + * E06_StabRock_UI_Page.ts + * E06 ์•”๋ฐ˜ ํ‰์‚ฌํˆฌ์˜ ํ•ด์„(์š”๊ตฌ 345 ยท 351 ยท ์š”๊ตฌ์„œ 4-2 2์ฐจ ยท PLAN 536-1) โ€” B05~B09 ํ‹€(group_workflow). + * ์ขŒ = ์ ˆ๋ฆฌ๊ตฐ ํ‘œ ยท ํ•ด์„ ์„ค์ • ยท [์ดˆ๊ธฐํ™”] [์ €์žฅ] [ํ•ด์„] + * ๊ฐ€์šด๋ฐ = ์œ„ํ—˜ ๋  ยท ์Šคํ…Œ๋ ˆ์˜ค๋„ท(๋“ฑ๊ฐ ยท ๋“ฑ๋ฉด์  ยท ๋ณด๊ธฐ = ์ „์ฒด ยท ํ‰๋ฉด ยท ์๊ธฐ ยท ์ „๋„) + * ์šฐ = ์ธก์ ๋ณ„ ๋ชจ๋“œ ์ˆ˜ ยท ์ตœ์†Œ ์•ˆ์ „์œจ ยท ํŒ์ • ยท ๊ณ ๋ฅธ ์ธก์  ์•ˆ์ „์œจ ํ‘œ + * ์กฐ์ž‘ = sessionStorage ์บ์‹œ๋งŒ(์ž๋™์ €์žฅ ์—†์Œ) ยท [์ €์žฅ] = PUT ยท [์ดˆ๊ธฐํ™”] = POST โ€ฆ/reset ยท + * [ํ•ด์„] = ์ €์žฅ ์•ˆ ํ•œ ๊ณ ์นจ์ด ์žˆ์œผ๋ฉด ๋จผ์ € ์ €์žฅ โ†’ POST โ€ฆ/analyze(์„œ๋ฒ„ ๋‹จ๋…) โ†’ ๊ฒฐ๊ณผ GET. + * ========================================================================== */ + +import "./E06_StabRock_UI_Style.css"; +import { API_BASE_URL, CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend"; +import { attachCollapsible } from "@ui/ui_template_collapsible"; +import { + createButton, + el, + hideLoadingOverlay, + showConfirmDialog, + showLoadingOverlay, + showToast, +} from "@ui/ui_template_elements"; +import { t, type LocaleKey } from "@ui/ui_template_locale"; +import { createSidePanels } from "@ui/ui_template_side_panels"; +import { mountGroupStep } from "../A00_Common/group_workflow"; +import { + buildJointCards, + type JointsDoc, + type JointsView, + type Projection, + type RockDefaults, +} from "./E06_StabRock_UI_Joints"; +import { + buildBand, + buildDetail, + buildList, + entryId, + type RockResult, + type StationEntry, +} from "./E06_StabRock_UI_Result"; +import { drawStereonet, netLegend, type NetData, type NetMode } from "./E06_StabRock_UI_Stereonet"; + +interface RockResponse { + joints: JointsDoc; + saved?: boolean; + defaults?: RockDefaults; + stations?: { chainage_m: number; label: string; ground_type: string }[]; + editable?: boolean; +} + +interface ResultResponse { + status: string; + stale: boolean; + result: RockResult | null; +} + +interface StationResponse { + entry: StationEntry; + stereonet: Record & { phi: number }; +} + +const cacheKey = (projectId: string): string => `e06_rock_cache:${projectId}`; + +async function api(path: string, init?: RequestInit): Promise { + const response = await fetch(`${API_BASE_URL}${path}`, { + credentials: "include", + headers: { "Content-Type": "application/json" }, + ...init, + }); + const body = (await response.json().catch(() => ({}))) as T & { detail?: unknown }; + if (!response.ok) { + const detail = body.detail; + if (typeof detail === "string") throw new Error(detail); + const errors = (detail as { errors?: unknown[] } | undefined)?.errors; + throw new Error(errors ? `${t("E06_Invalid")} ${errors.length}` : `HTTP ${response.status}`); + } + return body; +} + +function toggle(options: [LocaleKey, T][], value: T, onPick: (v: T) => void) { + const group = el("div", { className: "e06-toggle" }); + const buttons = options.map(([key, v]) => { + const button = createButton({ label: t(key), variant: "ghost", size: "sm" }); + button.dataset.value = v; + button.classList.toggle("is-on", v === value); + button.addEventListener("click", () => { + buttons.forEach((b) => b.classList.toggle("is-on", b === button)); + onPick(v); + }); + return button; + }); + group.append(...buttons); + return group; +} + +export function renderE06StabRock(root: HTMLElement): void { + const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY); + const panel = el("div", { className: "ui-sidebar-panel e06-panel" }); + const main = el("div", { className: "e06-page" }); + mountGroupStep(root, ROUTES.E06_STAB_ROCK, { mainContent: main, leftPanel: panel }); + if (!projectId) + return void main.append(el("p", { className: "e06-empty", text: t("E06_NoProject") })); + + // ---- ์ขŒ ---- + const cards = el("div", { className: "e06-cards" }); + const status = el("p", { className: "e06-status" }); + const saveButton = createButton({ + label: t("Common_Btn_Save"), + variant: "ghost", + onClick: () => void save(), + }); + const resetButton = createButton({ + label: t("Common_Btn_Reset"), + variant: "danger-ghost", + onClick: () => void reset(), + }); + const analyzeButton = createButton({ + label: t("E06_Analyze"), + variant: "filled", + onClick: () => void analyze(), + }); + saveButton.disabled = resetButton.disabled = analyzeButton.disabled = true; + const actions = el("div", { className: "ui-sidebar-actions" }); + actions.append(resetButton, saveButton, analyzeButton); + panel.append(el("p", { className: "e06-hint", text: t("E06_Note") }), cards, status, actions); + attachCollapsible(cards); + + // ---- ๊ฐ€์šด๋ฐ ยท ์šฐ ---- + const staleBar = el("div", { className: "e06-stale", text: t("E06_Stale") }); + staleBar.hidden = true; + const bandSlot = el("div"); + const title = el("strong", { className: "e06-title" }); + const netSlot = el("div", { className: "e06-net-slot" }); + const legendSlot = el("div"); + let projection: Projection = "equal_angle"; + let netMode: NetMode = "all"; + const toolbar = el("div", { className: "e06-toolbar" }); + const projectionToggle = el("div"); + toolbar.append( + title, + projectionToggle, + toggle( + [ + ["E06_View_All", "all"], + ["E06_Mode_planar", "planar"], + ["E06_Mode_wedge", "wedge"], + ["E06_Mode_toppling", "toppling"], + ], + netMode, + (v) => { + netMode = v; + paintNet(); + }, + ), + ); + const stage = el("div", { className: "e06-stage" }); + stage.append(netSlot, legendSlot); + const center = el("div", { className: "e06-center" }); + center.append(staleBar, bandSlot, toolbar, stage); + const listSlot = el("div", { className: "e06-list-slot" }); + const detailSlot = el("div"); + const sideBody = el("div", { className: "e06-side" }); + sideBody.append(listSlot, detailSlot); + const side = createSidePanels({ + prefKey: "e06-stab-rock-result", + panels: [{ id: "result", title: t("E06_Result_Title"), body: sideBody }], + defaultOpen: "result", + defaultWidth: 380, + follow: false, + }); + const row = el("div", { className: "ui-side-panels-row e06-row" }); + row.append(center, side.root); + main.append(row); + + // ---- ์ƒํƒœ ---- + let view: JointsView | null = null; + let dirty = false; + let result: RockResult | null = null; + let active: string | null = null; + let shown: StationResponse | null = null; + + const invalidCount = (): number => cards.querySelectorAll(".is-invalid").length; + const paintStatus = (): void => { + const lines: string[] = []; + if (view && !view.editable) lines.push(t("E06_ReadOnly")); + if (dirty) lines.push(t("E06_Unsaved")); + const bad = invalidCount(); + if (bad) lines.push(`${t("E06_Invalid")} ${bad}`); + status.textContent = lines.join("\n"); + status.classList.toggle("is-error", bad > 0); + }; + + const renderCards = (): void => { + if (!view) return; + // ๋‹ค์‹œ ๊ทธ๋ ค๋„ ์ ‘ํž˜์€ ๊ทธ๋Œ€๋กœ + const folded = [...cards.children].map((c) => c.classList.contains("is-collapsed")); + const built = buildJointCards(view); + built.forEach( + (c, i) => folded[i] !== undefined && c.classList.toggle("is-collapsed", folded[i]), + ); + cards.replaceChildren(...built); + paintStatus(); + }; + + const remember = (): void => { + if (!view) return; + dirty = true; + sessionStorage.setItem(cacheKey(projectId), JSON.stringify(view.doc)); + }; + + function paintProjection(): void { + projectionToggle.replaceChildren( + toggle( + [ + ["E06_Proj_Angle", "equal_angle"], + ["E06_Proj_Area", "equal_area"], + ], + projection, + (v) => { + projection = v; // ๋ณด๊ธฐ ๊ณ ๋ฆ„ โ€” ์ •๋ณธ ์•„๋‹˜(ํ•ด์„ ์ง€๋ฌธ์—๋„ ์•ˆ ๋“ค์–ด๊ฐ) + paintNet(); + }, + ), + ); + } + + function paintNet(): void { + legendSlot.replaceChildren(netLegend(netMode)); + if (!shown) { + title.textContent = ""; + netSlot.replaceChildren(el("p", { className: "e06-empty", text: t("E06_PickStation") })); + return; + } + const e = shown.entry; + title.textContent = `${e.label ?? e.key} ยท ${t(e.slope.side === "left" ? "E06_Left" : "E06_Right")} ยท ฯ† ${shown.stereonet.phi}ยฐ`; + netSlot.replaceChildren( + drawStereonet(shown.stereonet[projection], netMode, { + planar: e.kinematic.planar, + toppling: e.kinematic.toppling, + wedge: e.kinematic.wedge.map((w) => w.ids.join("|")), + }), + ); + } + + function paintResult(): void { + const stations = result?.stations ?? []; + bandSlot.replaceChildren(buildBand(stations, active, (id) => void select(id))); + listSlot.replaceChildren( + stations.length + ? buildList(stations, active, (id) => void select(id)) + : el("p", { className: "e06-hint", text: t(result ? "E06_NoRock" : "E06_NoResult") }), + ); + detailSlot.replaceChildren(shown ? buildDetail(shown.entry) : el("span")); + paintNet(); + } + + async function select(id: string): Promise { + active = id; + const [key, sideName] = id.split("|"); + try { + shown = await api( + `/projects/${projectId}/stab/rock/result?station=${encodeURIComponent(key)}&side=${sideName}`, + ); + } catch (error) { + shown = null; + showToast(error instanceof Error ? error.message : t("Common_Status_Error"), "error"); + } + paintResult(); + } + + async function loadResult(): Promise { + const body = await api(`/projects/${projectId}/stab/rock/result`); + result = body.result; + staleBar.hidden = !body.stale; + const stations = result?.stations ?? []; + const keep = stations.find((e) => entryId(e) === active); + const first = keep ?? stations.find((e) => e.verdict === "NG") ?? stations[0]; + if (first) await select(entryId(first)); + else { + active = null; + shown = null; + paintResult(); + } + } + + const adopt = (body: RockResponse, keepCache: boolean): void => { + if (!view) return; + const cached = keepCache ? sessionStorage.getItem(cacheKey(projectId)) : null; + if (!cached) sessionStorage.removeItem(cacheKey(projectId)); + view.doc = cached ? (JSON.parse(cached) as JointsDoc) : body.joints; + dirty = Boolean(cached); + saveButton.disabled = resetButton.disabled = analyzeButton.disabled = !view.editable; + renderCards(); + }; + + async function putJoints(): Promise { + if (!view?.editable) return false; + if (invalidCount()) { + showToast(`${t("E06_Invalid")} ${invalidCount()}`, "error"); + return false; + } + const body = await api(`/projects/${projectId}/stab/rock`, { + method: "PUT", + body: JSON.stringify({ joints: view.doc }), + }); + adopt(body, false); + return true; + } + + async function save(): Promise { + showLoadingOverlay(); + try { + if (await putJoints()) { + showToast(t("E06_Saved"), "success"); + await loadResult(); + } + } catch (error) { + showToast(error instanceof Error ? error.message : t("Common_Status_Error"), "error"); + } finally { + hideLoadingOverlay(); + } + } + + async function reset(): Promise { + if (!view?.editable) return; + const ok = await showConfirmDialog( + t("E06_ResetConfirm"), + t("Common_Btn_Reset"), + t("Common_Btn_Cancel"), + ); + if (!ok) return; + showLoadingOverlay(); + try { + const body = await api(`/projects/${projectId}/stab/rock/reset`, { + method: "POST", + }); + adopt(body, false); + showToast(t("E06_ResetDone"), "success"); + await loadResult(); + } catch (error) { + showToast(error instanceof Error ? error.message : t("Common_Status_Error"), "error"); + } finally { + hideLoadingOverlay(); + } + } + + async function analyze(): Promise { + if (!view?.editable) return; + analyzeButton.disabled = true; + status.textContent = t("E06_Running"); + try { + if (dirty && !(await putJoints())) return; + const body = await api<{ status: string; summary?: RockResult["summary"] }>( + `/projects/${projectId}/stab/rock/analyze`, + { method: "POST" }, + ); + await loadResult(); + showToast(t(body.status === "fresh" ? "E06_Fresh" : "E06_Done"), "success"); + } catch (error) { + showToast(error instanceof Error ? error.message : t("Common_Status_Error"), "error"); + } finally { + analyzeButton.disabled = !view.editable; + paintStatus(); + } + } + + void (async () => { + try { + const body = await api(`/projects/${projectId}/stab/rock`); + const stations = body.stations ?? []; + projection = body.joints.settings.projection; + paintProjection(); + view = { + doc: body.joints, + defaults: body.defaults as RockDefaults, + editable: body.editable ?? false, + endM: stations.length ? stations[stations.length - 1].chainage_m : 0, + change: () => { + remember(); + paintStatus(); + }, + rebuild: () => { + remember(); + renderCards(); + }, + }; + adopt(body, true); + await loadResult(); + } catch (error) { + main.replaceChildren( + el("p", { + className: "e06-empty", + text: error instanceof Error ? error.message : t("Common_Status_Error"), + }), + ); + } + })(); +} diff --git a/E06_StabRock/E06_StabRock_UI_Result.ts b/E06_StabRock/E06_StabRock_UI_Result.ts new file mode 100644 index 000000000..a2d3352f8 --- /dev/null +++ b/E06_StabRock/E06_StabRock_UI_Result.ts @@ -0,0 +1,219 @@ +/* ============================================================================= + * E06_StabRock_UI_Result.ts + * E06 ๊ฒฐ๊ณผ โ€” ์œ„ํ—˜ ๋ (๋ˆ„๊ฐ€ ์ถ• ยท ์ธก์  ํŒ์ • ์ƒ‰ ยท ๋ˆ„๋ฅด๋ฉด ๊ทธ ์ธก์ ) ยท ์šฐ์ธก ์ธก์  ๋ชฉ๋ก(์ชฝ ยท ๋ชจ๋“œ ์ˆ˜ ยท + * ์ตœ์†Œ ์•ˆ์ „์œจ ยท ํŒ์ •) ยท ๊ณ ๋ฅธ ์ธก์  ์‚ฌ๋ฉด ยท ๋ชจ๋“œ๋ณ„ ์•ˆ์ „์œจ ํ‘œ. ๊ฐ’์€ ์„œ๋ฒ„ ์ €์žฅ๋ณธ ๊ทธ๋Œ€๋กœ(๊ณ„์‚ฐ ์—†์Œ). + * ========================================================================== */ + +import { el } from "@ui/ui_template_elements"; +import { t, type LocaleKey } from "@ui/ui_template_locale"; + +export type Verdict = "NG" | "check" | "OK" | "none"; + +export interface RockCase { + mode: "planar" | "wedge"; + ids: string[]; + condition: "dry" | "wet" | "seismic"; + F: number | null; + formed: boolean; + persistence_short: boolean; + required: number | null; + ok: boolean | null; + sliding?: string | null; +} + +export interface StationEntry { + key: string; + chainage_m: number; + label: string | null; + ground_type: string; + slope: { side: string; dip_direction: number; dip: number; height_m: number; upper_dip: number }; + gamma_r: number; + joints: { + id: string; + name: string; + dip_direction: number; + dip: number; + phi: number; + c: number; + }[]; + kinematic: { + planar: string[]; + toppling: string[]; + wedge: { ids: [string, string]; trend: number; plunge: number }[]; + }; + cases: RockCase[]; + counts: { planar: number; wedge: number; toppling: number }; + min_F: number | null; + verdict: Verdict; +} + +export interface RockResult { + fingerprint: string; + summary: { + slopes: number; + verdicts: Partial>; + modes: Record; + }; + stations: StationEntry[]; +} + +const ORDER: Verdict[] = ["NG", "check", "OK", "none"]; +export const entryId = (e: StationEntry): string => `${e.key}|${e.slope.side}`; +const fmt = (v: number | null | undefined, d = 2): string => + v === null || v === undefined ? "โ€“" : v.toFixed(d); + +function badge(verdict: Verdict): HTMLElement { + return el("span", { + className: `e06-verdict e06-verdict--${verdict.toLowerCase()}`, + text: t(`E06_V_${verdict}` as LocaleKey), + }); +} + +/** ์œ„ํ—˜ ๋  โ€” ์ธก์ ๋งˆ๋‹ค ๊ฐ€์žฅ ๋‚˜์œ ํŒ์ • ยท ์ด์›ƒ ์ธก์  ๊ฐ€์šด๋ฐ์„œ ๋Š์Œ */ +export function buildBand( + stations: StationEntry[], + active: string | null, + onSelect: (id: string) => void, +): HTMLElement { + const box = el("div", { className: "e06-band", attrs: { "data-role": "band" } }); + const worst = new Map(); + for (const e of stations) { + const prev = worst.get(e.chainage_m); + if (!prev || ORDER.indexOf(e.verdict) < ORDER.indexOf(prev.verdict)) worst.set(e.chainage_m, e); + } + const list = [...worst.values()].sort((a, b) => a.chainage_m - b.chainage_m); + const legend = el("div", { className: "e06-band__legend" }); + legend.append(el("strong", { text: t("E06_Band") })); + for (const v of ORDER) { + const n = list.filter((e) => e.verdict === v).length; + if (n) legend.append(badge(v), el("span", { className: "e06-band__count", text: String(n) })); + } + const strip = el("div", { className: "e06-band__strip" }); + if (list.length) { + const lo = list[0].chainage_m; + const hi = Math.max(list[list.length - 1].chainage_m, lo + 1); + list.forEach((e, i) => { + const a = i ? (list[i - 1].chainage_m + e.chainage_m) / 2 : lo; + const b = i < list.length - 1 ? (e.chainage_m + list[i + 1].chainage_m) / 2 : hi; + const seg = el("button", { + className: `e06-band__seg e06-band__seg--${e.verdict.toLowerCase()}${ + active?.startsWith(`${e.key}|`) ? " is-active" : "" + }`, + attrs: { + type: "button", + "data-key": e.key, + title: `${e.label ?? e.key} ยท ${t(`E06_V_${e.verdict}` as LocaleKey)}`, + }, + }); + seg.style.left = `${((a - lo) / (hi - lo)) * 100}%`; + seg.style.width = `${Math.max(0.4, ((b - a) / (hi - lo)) * 100)}%`; + seg.addEventListener("click", () => onSelect(entryId(e))); + strip.append(seg); + }); + const ticks = el("div", { className: "e06-band__ticks" }); + ticks.append( + el("span", { text: `${lo.toFixed(0)} m` }), + el("span", { text: `${hi.toFixed(0)} m` }), + ); + box.append(legend, strip, ticks); + } else box.append(legend, el("p", { className: "e06-hint", text: t("E06_NoRock") })); + return box; +} + +export function buildList( + stations: StationEntry[], + active: string | null, + onSelect: (id: string) => void, +): HTMLElement { + const table = el("table", { className: "e06-list", attrs: { "data-role": "station-list" } }); + const head = el("tr"); + for (const key of [ + "E06_L_Station", + "E06_L_Side", + "E06_L_Modes", + "E06_L_MinF", + "E06_L_Verdict", + ] as LocaleKey[]) + head.append(el("th", { text: t(key) })); + table.append(el("thead", { children: [head] }), el("tbody")); + for (const e of stations) { + const id = entryId(e); + const tr = el("tr", { + className: id === active ? "is-active" : "", + attrs: { "data-id": id, "data-verdict": e.verdict }, + }); + tr.append( + el("td", { text: e.label ?? e.key }), + el("td", { text: t(e.slope.side === "left" ? "E06_Left" : "E06_Right") }), + el("td", { text: `${e.counts.planar} ยท ${e.counts.wedge} ยท ${e.counts.toppling}` }), + el("td", { text: fmt(e.min_F) }), + el("td", { children: [badge(e.verdict)] }), + ); + tr.addEventListener("click", () => onSelect(id)); + table.tBodies[0].append(tr); + } + return table; +} + +export function buildDetail(e: StationEntry): HTMLElement { + const box = el("div", { className: "e06-detail", attrs: { "data-role": "detail" } }); + const s = e.slope; + box.append( + el("h4", { text: `${e.label ?? e.key} ยท ${t(s.side === "left" ? "E06_Left" : "E06_Right")}` }), + el("p", { + className: "e06-hint", + text: t("E06_SlopeLine") + .replace("{dd}", s.dip_direction.toFixed(0)) + .replace("{dip}", s.dip.toFixed(1)) + .replace("{h}", s.height_m.toFixed(2)) + .replace("{up}", s.upper_dip.toFixed(1)) + .replace("{g}", e.gamma_r.toFixed(1)), + }), + ); + const kin = e.kinematic; + const kinText = [ + `${t("E06_Mode_planar")} ${kin.planar.join(", ") || "โ€“"}`, + `${t("E06_Mode_wedge")} ${kin.wedge.map((w) => `${w.ids.join("ยท")}(${w.trend.toFixed(0)}/${w.plunge.toFixed(0)})`).join(", ") || "โ€“"}`, + `${t("E06_Mode_toppling")} ${kin.toppling.join(", ") || "โ€“"}`, + ]; + box.append(el("p", { className: "e06-kin", text: kinText.join("\n") })); + if (!e.cases.length) { + box.append( + el("p", { + className: "e06-hint", + text: t(e.joints.length ? "E06_NoRisk" : "E06_NoJointsHere"), + }), + ); + return box; + } + const table = el("table", { className: "e06-cases" }); + const head = el("tr"); + for (const key of [ + "E06_C_Mode", + "E06_C_Joints", + "E06_C_Cond", + "E06_C_F", + "E06_C_Req", + "E06_L_Verdict", + ] as LocaleKey[]) + head.append(el("th", { text: t(key) })); + table.append(el("thead", { children: [head] }), el("tbody")); + for (const c of e.cases) { + const verdict: Verdict = c.F === null ? "check" : c.ok === false ? "NG" : "OK"; + const note = !c.formed ? t("E06_NotFormed") : c.persistence_short ? t("E06_Persist") : ""; + const tr = el("tr", { attrs: { "data-mode": c.mode, "data-cond": c.condition } }); + tr.append( + el("td", { text: t(`E06_Mode_${c.mode}` as LocaleKey) }), + el("td", { text: c.ids.join("ยท") }), + el("td", { text: t(`E06_Cond_${c.condition}` as LocaleKey) }), + el("td", { text: `${fmt(c.F, 3)}${note ? ` ${note}` : ""}` }), + el("td", { text: fmt(c.required) }), + el("td", { children: [badge(verdict)] }), + ); + table.tBodies[0].append(tr); + } + box.append(table); + if (kin.toppling.length) + box.append(el("p", { className: "e06-hint", text: t("E06_ToppleNote") })); + return box; +} diff --git a/E06_StabRock/E06_StabRock_UI_Stereonet.ts b/E06_StabRock/E06_StabRock_UI_Stereonet.ts new file mode 100644 index 000000000..73e2b9e21 --- /dev/null +++ b/E06_StabRock/E06_StabRock_UI_Stereonet.ts @@ -0,0 +1,193 @@ +/* ============================================================================= + * E06_StabRock_UI_Stereonet.ts + * E06 ๊ฐ€์šด๋ฐ ์Šคํ…Œ๋ ˆ์˜ค๋„ท(SVG ยท ํ•˜๋ฐ˜๊ตฌ) โ€” ์„œ๋ฒ„๊ฐ€ ๋‚ด๋ ค ์ค€ ํˆฌ์˜ ์ขŒํ‘œ(๋ฐ˜์ง€๋ฆ„ 1 ยท x = ๋™ ยท y = ๋ถ)๋ฅผ + * ๊ทธ๋ฆฌ๊ธฐ๋งŒ ํ•จ(๊ณ„์‚ฐ = E06_StabRock_Stereonet.py). ๋ณด๊ธฐ = ์ „์ฒด ยท ํ‰๋ฉด ยท ์๊ธฐ ยท ์ „๋„. + * ํ‰๋ฉด ยท ์ „๋„ = ๊ทน์  + ๊ทน์  ๋งˆ์ฐฐ์›(ฯ†) ยท ์๊ธฐ = ๊ต์„  + ๊ต์„  ๋งˆ์ฐฐ์› ยท ์œ„ํ—˜ ์˜์—ญ = ๋ชจ๋“œ ์ƒ‰ ยท + * ์ผ๊ด‘ ์—”๋ฒจ๋กœํ”„(ํ‰๋ฉด) ยท ์‚ฌ๋ฉด ๋Œ€์› = ๊ตต์€ ์„ . ์œ„ํ—˜ ํŒ์ •๋œ ์ ˆ๋ฆฌ ยท ๊ต์„  = ํ…Œ๋‘๋ฆฌ ๊ฐ•์กฐ. + * ========================================================================== */ + +import { el } from "@ui/ui_template_elements"; +import { t, type LocaleKey } from "@ui/ui_template_locale"; + +const SVG_NS = "http://www.w3.org/2000/svg"; +const SIZE = 440; +const R = 190; +const C = SIZE / 2; + +export type NetMode = "all" | "planar" | "wedge" | "toppling"; +type XY = [number, number]; + +export interface NetData { + projection: string; + slope: { pole: XY; great_circle: XY[] }; + planes: { id: string; pole: XY; great_circle: XY[] }[]; + intersections: { ids: [string, string]; point: XY }[]; + friction_pole_r: number; + friction_line_r: number; + daylight_envelope: XY[]; + zones: Record<"planar" | "wedge" | "toppling", XY[] | null>; +} + +export interface NetMarks { + planar: string[]; + toppling: string[]; + wedge: string[]; // "J1|J2" +} + +function svg( + tag: K, + attrs: Record = {}, + text?: string, +): SVGElementTagNameMap[K] { + const node = document.createElementNS(SVG_NS, tag); + for (const [k, v] of Object.entries(attrs)) node.setAttribute(k, String(v)); + if (text !== undefined) node.textContent = text; + return node; +} + +const sx = (p: XY): number => C + R * p[0]; +const sy = (p: XY): number => C - R * p[1]; +const path = (pts: XY[], close = false): string => + pts.map((p, i) => `${i ? "L" : "M"}${sx(p).toFixed(1)},${sy(p).toFixed(1)}`).join("") + + (close ? "Z" : ""); + +/** ์ ˆ๋ฆฌ ์ƒ‰ ๋ฒˆํ˜ธ(0~5) โ€” ํ‘œ ยท ๊ทธ๋ฆผ ๊ฐ™์€ ์ฐจ๋ก€ */ +export const jointTone = (index: number): number => index % 6; + +export function drawStereonet(net: NetData, mode: NetMode, marks: NetMarks): SVGSVGElement { + const root = svg("svg", { + viewBox: `0 0 ${SIZE} ${SIZE}`, + class: "e06-net", + "data-role": "stereonet", + "data-projection": net.projection, + "data-mode": mode, + }); + const clip = svg("clipPath", { id: "e06-net-clip" }); + clip.append(svg("circle", { cx: C, cy: C, r: R })); + const defs = svg("defs"); + defs.append(clip); + root.append(defs, svg("circle", { cx: C, cy: C, r: R, class: "e06-net__rim" })); + const body = svg("g", { "clip-path": "url(#e06-net-clip)" }); + root.append(body); + + const show = (m: Exclude): boolean => mode === "all" || mode === m; + for (const m of ["planar", "wedge", "toppling"] as const) { + const zone = net.zones[m]; + if (zone && show(m)) + body.append( + svg("path", { + d: path(zone, true), + class: `e06-net__zone e06-net__zone--${m}`, + "data-zone": m, + }), + ); + } + // ๋งˆ์ฐฐ์› + if (show("planar") || show("toppling")) + body.append( + svg("circle", { + cx: C, + cy: C, + r: R * net.friction_pole_r, + class: "e06-net__friction", + "data-role": "friction-pole", + }), + ); + if (show("wedge")) + body.append( + svg("circle", { + cx: C, + cy: C, + r: R * net.friction_line_r, + class: "e06-net__friction e06-net__friction--line", + "data-role": "friction-line", + }), + ); + if (show("planar")) + body.append( + svg("path", { + d: path(net.daylight_envelope), + class: "e06-net__envelope", + "data-role": "daylight", + }), + ); + // ๋Œ€์› + net.planes.forEach((p, i) => + body.append( + svg("path", { + d: path(p.great_circle), + class: `e06-net__gc e06-tone-${jointTone(i)}`, + "data-id": p.id, + }), + ), + ); + body.append( + svg("path", { d: path(net.slope.great_circle), class: "e06-net__slope", "data-role": "slope" }), + ); + + // ๊ทน์ (ํ‰๋ฉด ยท ์ „๋„ ๋ณด๊ธฐ) ยท ๊ต์„ (์๊ธฐ ๋ณด๊ธฐ) + if (mode !== "wedge") + net.planes.forEach((p, i) => { + const risky = marks.planar.includes(p.id) || marks.toppling.includes(p.id); + const g = svg("g", { + class: `e06-net__pole e06-tone-${jointTone(i)}${risky ? " is-risk" : ""}`, + "data-pole": p.id, + }); + g.append(svg("circle", { cx: sx(p.pole), cy: sy(p.pole), r: 5 })); + g.append(svg("text", { x: sx(p.pole) + 7, y: sy(p.pole) - 6 }, p.id)); + root.append(g); + }); + if (mode === "all" || mode === "wedge") + for (const line of net.intersections) { + const key = line.ids.join("|"); + const risky = marks.wedge.includes(key); + const [x, y] = [sx(line.point), sy(line.point)]; + const g = svg("g", { class: `e06-net__line${risky ? " is-risk" : ""}`, "data-line": key }); + g.append(svg("path", { d: `M${x},${y - 6}L${x + 5.5},${y + 4}L${x - 5.5},${y + 4}Z` })); + g.append(svg("text", { x: x + 7, y: y + 12 }, line.ids.join("ยท"))); + root.append(g); + } + // ์‚ฌ๋ฉด ๊ทน์  ยท ๋ฐฉ์œ„ + root.append( + svg("rect", { + x: sx(net.slope.pole) - 4, + y: sy(net.slope.pole) - 4, + width: 8, + height: 8, + class: "e06-net__slope-pole", + }), + ); + for (const [label, x, y] of [ + ["N", C, C - R - 8], + ["E", C + R + 10, C + 4], + ["S", C, C + R + 18], + ["W", C - R - 10, C + 4], + ] as const) + root.append(svg("text", { x, y, class: "e06-net__dir", "text-anchor": "middle" }, label)); + root.append(svg("line", { x1: C - 6, y1: C, x2: C + 6, y2: C, class: "e06-net__tick" })); + root.append(svg("line", { x1: C, y1: C - 6, x2: C, y2: C + 6, class: "e06-net__tick" })); + return root; +} + +export function netLegend(mode: NetMode): HTMLElement { + const box = el("div", { className: "e06-legend" }); + const items: [string, LocaleKey][] = [ + ["slope", "E06_Lg_Slope"], + ["friction", "E06_Lg_Friction"], + ]; + if (mode === "all" || mode === "planar") + items.push(["planar", "E06_Lg_Planar"], ["daylight", "E06_Lg_Daylight"]); + if (mode === "all" || mode === "wedge") + items.push(["wedge", "E06_Lg_Wedge"], ["line", "E06_Lg_Line"]); + if (mode === "all" || mode === "toppling") items.push(["toppling", "E06_Lg_Toppling"]); + if (mode !== "wedge") items.push(["pole", "E06_Lg_Pole"]); + for (const [cls, key] of items) { + const row = el("span", { className: "e06-legend__row" }); + row.append( + el("i", { className: `e06-legend__mark e06-legend__mark--${cls}` }), + el("span", { text: t(key) }), + ); + box.append(row); + } + return box; +} diff --git a/E06_StabRock/E06_StabRock_UI_Style.css b/E06_StabRock/E06_StabRock_UI_Style.css new file mode 100644 index 000000000..47b4a8b49 --- /dev/null +++ b/E06_StabRock/E06_StabRock_UI_Style.css @@ -0,0 +1,485 @@ +/* ============================================================================= + * E06_StabRock_UI_Style.css + * E06 ์•”๋ฐ˜ ํ•ด์„ โ€” ์ขŒ์ธก ์ ˆ๋ฆฌ ํ‘œ ยท ์„ค์ • / ๊ฐ€์šด๋ฐ ์œ„ํ—˜ ๋  ยท ์Šคํ…Œ๋ ˆ์˜ค๋„ท / ์šฐ์ธก ์ธก์  ๊ฒฐ๊ณผ + * ์ƒ‰์€ ํ…Œ๋งˆ ํ† ํฐ(๋ฐ์Œ ยท ์–ด๋‘์›€ ๊ฐ™์ด) ยท ํŒ์ • ยท ๋ชจ๋“œ ยท ์ ˆ๋ฆฌ ์ƒ‰๋งŒ ๊ณ ์ •์ƒ‰(E03 ํŒ์ •์ƒ‰๊ณผ ๊ฐ™์Œ) + * ========================================================================== */ + +.e06-page { + --e06-ng: #d03b3b; + --e06-ok: #0ca30c; + --e06-check: #e0a106; + --e06-none: #9a9a95; + --e06-planar: #d03b3b; + --e06-wedge: #f1963b; + --e06-toppling: #9b6dde; + height: 100dvh; + box-sizing: border-box; +} + +.e06-row { + height: 100%; +} + +.e06-center { + display: flex; + flex-direction: column; + gap: var(--spacing-8); + height: 100%; + min-height: 0; + padding: var(--spacing-12) 0 var(--spacing-12) var(--spacing-12); + box-sizing: border-box; + overflow: auto; +} + +.e06-empty, +.e06-hint, +.e06-status, +.e06-kin { + margin: 0; + white-space: pre-line; + color: var(--color-text-muted); + font-size: 12px; +} + +.e06-empty { + padding: var(--spacing-16); +} + +.e06-status.is-error { + color: var(--color-danger); +} + +.e06-kin { + color: var(--color-text); +} + +.e06-panel .ui-sidebar-actions { + flex-wrap: wrap; +} + +.e06-stale { + padding: var(--spacing-8) var(--spacing-12); + border-radius: var(--radius-sm); + background: color-mix(in srgb, var(--color-warning) 18%, var(--color-surface)); + color: var(--color-text); +} + +.e06-stale[hidden] { + display: none; +} + +/* ---- ์ขŒ์ธก ํ‘œ ยท ์„ค์ • ---- */ +.e06-table-wrap { + overflow-x: auto; +} + +.e06-table { + border-collapse: collapse; + font-size: 12px; +} + +.e06-table th { + padding: 2px 3px; + font-weight: var(--font-weight-medium); + color: var(--color-text-secondary); + white-space: nowrap; +} + +.e06-table td { + padding: 1px 2px; +} + +.e06-input { + width: 4.2em; + padding: 2px 4px; + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + background: var(--color-surface); + color: var(--color-text); + font: inherit; + font-size: 12px; + box-sizing: border-box; +} + +.e06-input--name { + width: 3.6em; +} + +select.e06-input { + width: auto; +} + +.e06-input.is-invalid { + border-color: var(--color-danger); + background: color-mix(in srgb, var(--color-danger) 12%, var(--color-surface)); +} + +.e06-row-actions { + display: flex; + justify-content: flex-end; + margin-top: var(--spacing-4); +} + +.e06-setting { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--spacing-8); + margin: 2px 0; + font-size: 12px; + color: var(--color-text); +} + +.e06-setting .e06-input { + width: 5.5em; +} + +/* ---- ๊ฐ€์šด๋ฐ ---- */ +.e06-toolbar { + display: flex; + align-items: center; + gap: var(--spacing-12); + flex-wrap: wrap; +} + +.e06-title { + color: var(--color-text); +} + +.e06-toggle { + display: inline-flex; + gap: var(--spacing-4); +} + +.e06-toggle .ui-btn.is-on { + border-color: var(--color-primary); + background: var(--color-primary); + color: var(--color-primary-text); +} + +.e06-stage { + display: flex; + gap: var(--spacing-12); + align-items: flex-start; + flex-wrap: wrap; + padding: var(--spacing-8); + border-radius: var(--radius-sm); + background: var(--color-surface-raised); +} + +.e06-net-slot { + flex: 1 1 360px; + max-width: 560px; +} + +.e06-net { + display: block; + width: 100%; + height: auto; + color: var(--color-text); + font-size: 11px; +} + +.e06-net__rim { + fill: var(--color-surface); + stroke: var(--color-text); + stroke-width: 1.4; +} + +.e06-net__zone { + stroke: none; + opacity: 0.28; +} + +.e06-net__zone--planar { + fill: var(--e06-planar); +} + +.e06-net__zone--wedge { + fill: var(--e06-wedge); +} + +.e06-net__zone--toppling { + fill: var(--e06-toppling); +} + +.e06-net__friction { + fill: none; + stroke: var(--color-text-secondary); + stroke-width: 1; + stroke-dasharray: 5 3; +} + +.e06-net__friction--line { + stroke-dasharray: 2 3; +} + +.e06-net__envelope { + fill: none; + stroke: var(--e06-planar); + stroke-width: 1; + stroke-dasharray: 6 3; +} + +.e06-net__gc { + fill: none; + stroke: currentColor; + stroke-width: 1.2; +} + +.e06-net__slope { + fill: none; + stroke: var(--color-text); + stroke-width: 2.6; +} + +.e06-net__slope-pole { + fill: var(--color-text); +} + +.e06-net__pole circle { + fill: currentColor; + stroke: var(--color-surface); + stroke-width: 1; +} + +.e06-net__pole.is-risk circle, +.e06-net__line.is-risk path { + stroke: var(--e06-ng); + stroke-width: 2.4; +} + +.e06-net__pole text, +.e06-net__line text, +.e06-net__dir { + fill: var(--color-text); +} + +.e06-net__line path { + fill: var(--e06-wedge); + stroke: var(--color-surface); +} + +.e06-net__tick { + stroke: var(--color-text-secondary); +} + +.e06-tone-0 { + color: #2f6fd6; +} +.e06-tone-1 { + color: #1f9d6a; +} +.e06-tone-2 { + color: #c2410c; +} +.e06-tone-3 { + color: #7c4dcc; +} +.e06-tone-4 { + color: #b8860b; +} +.e06-tone-5 { + color: #0e7490; +} + +.e06-legend { + display: flex; + flex-direction: column; + gap: 4px; + font-size: 12px; + color: var(--color-text); +} + +.e06-legend__row { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.e06-legend__mark { + display: inline-block; + width: 16px; + height: 10px; + border-radius: 2px; +} + +.e06-legend__mark--slope { + height: 3px; + background: var(--color-text); +} +.e06-legend__mark--friction { + height: 0; + border-top: 1px dashed var(--color-text-secondary); +} +.e06-legend__mark--daylight { + height: 0; + border-top: 1px dashed var(--e06-planar); +} +.e06-legend__mark--planar { + background: color-mix(in srgb, var(--e06-planar) 35%, transparent); +} +.e06-legend__mark--wedge { + background: color-mix(in srgb, var(--e06-wedge) 35%, transparent); +} +.e06-legend__mark--toppling { + background: color-mix(in srgb, var(--e06-toppling) 35%, transparent); +} +.e06-legend__mark--line { + width: 10px; + background: var(--e06-wedge); + clip-path: polygon(50% 0, 100% 100%, 0 100%); +} +.e06-legend__mark--pole { + width: 10px; + border-radius: 50%; + background: #2f6fd6; +} + +/* ---- ์œ„ํ—˜ ๋  ---- */ +.e06-band { + display: flex; + flex-direction: column; + gap: 4px; +} + +.e06-band__legend { + display: flex; + align-items: center; + gap: 6px; + flex-wrap: wrap; + font-size: 12px; + color: var(--color-text); +} + +.e06-band__count { + margin-right: 6px; + color: var(--color-text-secondary); +} + +.e06-band__strip { + position: relative; + height: 18px; + border-radius: var(--radius-sm); + background: var(--color-surface-raised); + overflow: hidden; +} + +.e06-band__seg { + position: absolute; + top: 0; + bottom: 0; + padding: 0; + border: none; + cursor: pointer; +} + +.e06-band__seg--ng { + background: var(--e06-ng); +} +.e06-band__seg--ok { + background: var(--e06-ok); +} +.e06-band__seg--check { + background: var(--e06-check); +} +.e06-band__seg--none { + background: var(--e06-none); +} + +.e06-band__seg.is-active { + outline: 2px solid var(--color-text); + outline-offset: -2px; + z-index: 1; +} + +.e06-band__ticks { + display: flex; + justify-content: space-between; + font-size: 11px; + color: var(--color-text-secondary); +} + +/* ---- ์šฐ์ธก ---- */ +.e06-side { + display: flex; + flex-direction: column; + gap: var(--spacing-12); +} + +.e06-list-slot { + max-height: 46vh; + overflow: auto; +} + +.e06-list, +.e06-cases { + width: 100%; + border-collapse: collapse; + font-size: 12px; + color: var(--color-text); +} + +.e06-list th, +.e06-cases th { + position: sticky; + top: 0; + padding: 3px 4px; + background: var(--color-surface); + color: var(--color-text-secondary); + font-weight: var(--font-weight-medium); + text-align: left; +} + +.e06-list td, +.e06-cases td { + padding: 3px 4px; + border-top: 1px solid var(--color-border); +} + +.e06-list tbody tr { + cursor: pointer; +} + +.e06-list tbody tr:hover { + background: var(--color-surface-raised); +} + +.e06-list tr.is-active { + background: color-mix(in srgb, var(--color-primary) 16%, transparent); +} + +.e06-detail h4 { + margin: 0 0 4px; + color: var(--color-text); +} + +.e06-verdict { + display: inline-flex; + align-items: center; + gap: 4px; + font-weight: var(--font-weight-medium); + white-space: nowrap; +} + +.e06-verdict::before { + content: ""; + width: 9px; + height: 9px; + border-radius: 50%; + background: var(--e06-none); +} + +.e06-verdict--ng::before { + background: var(--e06-ng); + border-radius: 2px; + transform: rotate(45deg); +} +.e06-verdict--ok::before { + background: var(--e06-ok); +} +.e06-verdict--check::before { + background: var(--e06-check); + border-radius: 0; + clip-path: polygon(50% 0, 100% 100%, 0 100%); +} diff --git a/E06_StabRock/draft/E06_StabRock_Route.ts b/E06_StabRock/draft/E06_StabRock_Route.ts new file mode 100644 index 000000000..627bed897 --- /dev/null +++ b/E06_StabRock/draft/E06_StabRock_Route.ts @@ -0,0 +1,16 @@ +/* ============================================================================= + * E06_StabRock_Route.ts + * ๊ธธ ์„ ์–ธ(PLAN 536-1) โ€” ๋ผ์šฐํ„ฐ๊ฐ€ ํŽ˜์ด์ง€ ํด๋”๋งˆ๋‹ค draft/*_Route.ts ๋ฅผ glob ํ•ด ๋“ฑ๋ก. + * ๋กœ๊ทธ์ธ ยท ํšŒ์‚ฌ ยท ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ํ•„์š” ยท ๊ผฌ๋ฆฌ๋ง ์—†์Œ ยท ๋ณธ๋ฌธ = E06 ์•”๋ฐ˜ ํ‰์‚ฌํˆฌ์˜ ํŽ˜์ด์ง€ + * ========================================================================== */ + +import { ROUTES } from "@config/config_frontend"; +import type { DraftRoute } from "../../A00_Common/router"; + +export const ROUTE: DraftRoute = { + path: ROUTES.E06_STAB_ROCK, + workflow: true, + project: true, + footerless: true, + load: async () => (await import("../E06_StabRock_UI_Page")).renderE06StabRock, +}; diff --git a/E07_StabArea/E07_StabArea_Engine.py b/E07_StabArea/E07_StabArea_Engine.py new file mode 100644 index 000000000..1ff42172b --- /dev/null +++ b/E07_StabArea/E07_StabArea_Engine.py @@ -0,0 +1,328 @@ +"""E07 ๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ โ€” ๋ฌดํ•œ์‚ฌ๋ฉด ์•ˆ์ „์œจ ยท SINMAP ์•ˆ์ • ์ง€์ˆ˜ (์š”๊ตฌ 345 ยท 351 ยท PLAN 536-2). + +์ˆœ์ˆ˜ ๊ณ„์‚ฐ(storage ๋ชจ๋ฆ„) โ€” D ์˜ˆ์ •๋…ธ์„  ์žฌํ•ด ์•ˆ์ „์„ฑ๋„ ๊ฐ™์€ ์—”์ง„์„ ์”€(ํ•œ ๋ฒŒ). +๊ทผ๊ฑฐ = ์ง€์‹DB `original/์˜ˆ์ •๋…ธ์„ /02` ยง1.1 ยท ยง2 โ‘  ยท `original/์•ˆ์ •์„ฑํ‰๊ฐ€/04`(๋ถ•์ ํ†  ๋ฒ”์œ„). + + ์ง€ํ˜• DEM โ†’ ๊ณ ๋ฅธ ๊ฒฉ์ž(cell_m) ยท ๊ฒฝ์‚ฌ ฮฒ(Horn 3ร—3) ยท D8 ํ๋ฆ„(์›…๋ฉ์ด ์ฑ„์›€ ยท ์ˆ˜์‹  ์…€ = B04 ๋ฐฐ์ˆ˜์œ ์—ญ + ํ•จ์ˆ˜ ์žฌ์‚ฌ์šฉ) ยท ๋‹จ์œ„ ์ง‘์ˆ˜๋ฉด์  a = ์ƒ๋ฅ˜ ์…€ ์ˆ˜ ร— cellยฒ/cell(๋“ฑ๊ณ ์„  ํญ = cell) + ํฌํ™”๋น„ m ์‹œ๋‚˜๋ฆฌ์˜ค ๋‘˜ + sinmap(๊ธฐ๋ณธ ยท SINMAP ํ‘œ์ค€) = ์Šต์œค์ง€์ˆ˜ W = min(xยทa / sinฮฒ, 1) ยท x = R/T โˆˆ [1/(T/R)_hi, 1/(T/R)_lo] + saturated(์ตœ์•… ยท ๊ฐ•์šฐ ๊ทนํ•œ) = ์ง€ํ‘œ ํฌํ™” m = 1 + ์•ˆ์ „์œจ Fs = (c + c_r + [ฮณt z(1โˆ’m) + (ฮณsatโˆ’ฮณw) m z] cosยฒฮฒ tanฯ†) + / ([ฮณt z(1โˆ’m) + ฮณsat m z] sinฮฒ cosฮฒ) + ์•ˆ์ • ์ง€์ˆ˜ SI(SINMAP ยท Pack 1998) โ€” c โˆˆ [c, c_hi] ยท tanฯ† โˆˆ [tanฯ†, tanฯ†_hi] ยท x โˆˆ [x_lo, x_hi] ๊ณ ๋ฅธ ๋ถ„ํฌ + ํ•˜ํ•œ Fs_min = (c, tanฯ†, x_hi ๊ฐ€์žฅ ์ –์Œ) โ‰ฅ 1 โ†’ SI = Fs_min ยท ์ƒํ•œ Fs_max = (c_hi, tanฯ†_hi, x_lo) < 1 + โ†’ SI = 0 ยท ๊ทธ ์‚ฌ์ด โ†’ SI = P(Fs > 1)(c ยท tanฯ† ๋‹ซํžŒ ๊ผด ยท x ๋Š” ์ค‘์  ์ ๋ถ„ X_STEPS ๋งˆ๋””) + ๋“ฑ๊ธ‰ 1 ์•ˆ์ •(SI โ‰ฅ 1.5) ยท 2 ๋ณดํ†ต ์•ˆ์ •(โ‰ฅ 1.25) ยท 3 ์ค€์•ˆ์ •(โ‰ฅ 1.0) ยท 4 ์œ„ํ—˜ ํ•˜ํ•œ(โ‰ฅ 0.5) + ยท 5 ์œ„ํ—˜ ์ƒํ•œ(> 0) ยท 6 ๋ถˆ์•ˆ์ •(SI = 0) ยท 0 = ์ž๋ฃŒ ์—†์Œ + +๊ฒฉ์ž ๊ผด = ํ–‰ 0 ์ด ๋‚จ์ชฝ(y0) ยท ์…€ (iy, ix) ์ค‘์‹ฌ = (x0 + ixยทdx, y0 + iyยทdx) โ€” E04 ๋ž˜์Šคํ„ฐ์™€ ๊ฐ™์Œ. +""" + +from __future__ import annotations + +import math +from dataclasses import dataclass +from types import SimpleNamespace +from typing import Any + +import numpy as np +from scipy.interpolate import RegularGridInterpolator +from scipy.spatial import cKDTree + +from B04_PreProcess.B04_PreProcess_Engine_Watershed_Grid import ( + compute_receivers, + condition_surface, +) + +GAMMA_W = 9.81 # kN/mยณ +SI_EDGES = (1.5, 1.25, 1.0, 0.5) # ๋“ฑ๊ธ‰ 1~4 ์•„๋ž˜ ๊ฒฝ๊ณ„ ยท 5 = (0, 0.5) ยท 6 = 0 +CLASS_KEYS = ("", "stable", "moderate", "quasi", "lower", "upper", "defended") +RISK_CLASS = 4 # ์ด ๋“ฑ๊ธ‰๋ถ€ํ„ฐ ์œ„ํ—˜(SI < 1) +LEVEL_KEYS = ("low", "mid", "high") # ์ธก์  ๋“ฑ๊ธ‰ โ€” ์˜ํ–ฅ ๊ตฌ์—ญ ์•ˆ ์œ„ํ—˜ ์…€ ๋น„์œจ ๊ธฐ์ค€ +LEVEL_LO_PCT = 5.0 # ๋‚ฎ์Œ < ์ด % โ‰ค ๋ณดํ†ต +LEVEL_HI_PCT = 20.0 # ๋ณดํ†ต < ์ด % โ‰ค ๋†’์Œ(์ด % ์ด์ƒ = ๋†’์Œ) +FLAT_SLOPE_RAD = 1e-4 # ์ด๋ณด๋‹ค ํ‰ํ‰ํ•˜๋ฉด ํ™œ๋™ ์—†์Œ(Fs = โˆž) +X_STEPS = 20 # SI ํ™•๋ฅ ์˜ x = R/T ์ค‘์  ์ ๋ถ„ ๋งˆ๋”” + + +@dataclass(frozen=True) +class AreaParams: + """ํ•œ ๋ฒˆ ํ•ด์„ ์กฐ๊ฑด โ€” ๋‹จ์œ„ kN/mยณ ยท kPa ยท ยฐ ยท m ยท mm/h ยท m/s.""" + + gamma_t: float + gamma_sat: float + c: float # ์ ์ฐฉ๋ ฅ ์•„๋ž˜(๊ฒฐ์ • Fs ยท SI ๋ฒ”์œ„ ์•„๋ž˜) + c_hi: float # SI ๋ฒ”์œ„ ์œ„ + phi: float + phi_hi: float + c_root: float = 0.0 # ๋ฟŒ๋ฆฌ ์ ์ฐฉ(๋‚˜์ง€ = 0) + soil_depth_m: float = 1.5 + scenario: str = "sinmap" # sinmap = ์Šต์œค์ง€์ˆ˜(T/R ๋ฒ”์œ„) ยท saturated = ์ง€ํ‘œ ํฌํ™” m = 1(์ตœ์•…) + tr_lo: float = 2000.0 # T/R ์•„๋ž˜(m ยท ์ –์€ ์ชฝ) + tr_hi: float = 3000.0 # T/R ์œ„(m ยท ๋งˆ๋ฅธ ์ชฝ) + + +# --------------------------------------------------------------------------- +# ์ง€ํ˜• +# --------------------------------------------------------------------------- + + +def resample_dem(dem: dict[str, Any], cell_m: float) -> tuple[np.ndarray, float, float]: + """DTM npz(x ยท y ์˜ค๋ฆ„ ์ถ• ยท z (ny, nx) ยท valid_mask) โ†’ ๊ณ ๋ฅธ ๊ฒฉ์ž (z, x0, y0) ยท ๋ฐ– = NaN.""" + x = np.asarray(dem["x"], float) + y = np.asarray(dem["y"], float) + z = np.where(np.asarray(dem["valid_mask"], bool), np.asarray(dem["z"], float), np.nan) + nx = int(math.floor((x[-1] - x[0]) / cell_m)) + 1 + ny = int(math.floor((y[-1] - y[0]) / cell_m)) + 1 + gx = x[0] + np.arange(nx) * cell_m + gy = y[0] + np.arange(ny) * cell_m + yy, xx = np.meshgrid(gy, gx, indexing="ij") + f = RegularGridInterpolator((y, x), z, bounds_error=False, fill_value=np.nan) + return f(np.column_stack([yy.ravel(), xx.ravel()])).reshape(ny, nx), float(x[0]), float(y[0]) + + +def slope_rad(z: np.ndarray, cell_m: float) -> np.ndarray: + """Horn 3ร—3 ๊ฒฝ์‚ฌ(rad) โ€” ์ด์›ƒ์— NaN ์ด ์žˆ๊ฑฐ๋‚˜ ๊ฒฉ์ž ๋์ด๋ฉด NaN.""" + p = np.pad(z, 1, constant_values=np.nan) + + def at(dy: int, dx: int) -> np.ndarray: + return p[1 + dy : 1 + dy + z.shape[0], 1 + dx : 1 + dx + z.shape[1]] + + gx = ((at(-1, 1) + 2 * at(0, 1) + at(1, 1)) - (at(-1, -1) + 2 * at(0, -1) + at(1, -1))) / ( + 8 * cell_m + ) + gy = ((at(1, -1) + 2 * at(1, 0) + at(1, 1)) - (at(-1, -1) + 2 * at(-1, 0) + at(-1, 1))) / ( + 8 * cell_m + ) + return np.arctan(np.hypot(gx, gy)) + + +def catchment_area(z: np.ndarray, cell_m: float) -> np.ndarray: + """D8 ๋‹จ์œ„ ์ง‘์ˆ˜๋ฉด์  a(m) = ์ž๊ธฐ ํฌํ•จ ์ƒ๋ฅ˜ ์…€ ์ˆ˜ ร— cell. ๋ฐ– = NaN. + + ponytail: ์ง€ํ˜• ๊ฒฉ์ž ๋ ๋ฐ– ์ƒ๋ฅ˜๋Š” ๋ชจ๋ฆ„(๋ณต์‚ฌ DTM = ๋…ธ์„  ๋ ) โ€” ๋  ๋ ์…€์€ a ๊ฐ€ ์ž‘๊ฒŒ ๋‚˜์˜ด ยท + ๋„“์€ DEM(D ์˜ˆ์ •๋…ธ์„  ๋“ฑ)์ด๋ฉด ๊ทธ๋Œ€๋กœ ๋งž์Œ. + """ + filled, valid = condition_surface(z.astype(np.float32)) + spec = SimpleNamespace(n_rows=z.shape[0], n_cols=z.shape[1], cell_m=cell_m) + receiver, _ = compute_receivers(spec, filled, valid) + flat = filled.ravel() + order = np.flatnonzero(valid.ravel()) + order = order[np.argsort(-flat[order], kind="stable")].tolist() # ๋†’์€ ๋ฐ์„œ ๋‚ฎ์€ ๋ฐ๋กœ + acc = valid.ravel().astype(float).tolist() + recv = receiver.tolist() + for i in order: + r = recv[i] + if r != i: + acc[r] += acc[i] + out = np.asarray(acc).reshape(z.shape) * cell_m + return np.where(valid, out, np.nan) + + +# --------------------------------------------------------------------------- +# ๋ฌดํ•œ์‚ฌ๋ฉด ยท SINMAP +# --------------------------------------------------------------------------- + + +def wetness(beta: np.ndarray, a: np.ndarray, x: float) -> np.ndarray: + """์Šต์œค์ง€์ˆ˜ W = min(xยทa / sinฮฒ, 1) ยท x = R/T(1/m).""" + with np.errstate(divide="ignore", invalid="ignore"): + m = x * a / np.sin(beta) + return np.clip(np.nan_to_num(m, nan=1.0, posinf=1.0), 0.0, 1.0) + + +def x_range(p: AreaParams) -> tuple[float, float]: + """x = R/T ๋ฒ”์œ„(๋งˆ๋ฅธ ์ชฝ, ์ –์€ ์ชฝ) โ€” T/R ์œ„ ยท ์•„๋ž˜์˜ ์—ญ์ˆ˜.""" + return 1.0 / max(p.tr_hi, p.tr_lo), 1.0 / p.tr_lo + + +def wet_m(beta: np.ndarray, a: np.ndarray, p: AreaParams, x: float | None = None) -> np.ndarray: + """์‹œ๋‚˜๋ฆฌ์˜ค ํฌํ™”๋น„ โ€” saturated = 1 ยท sinmap = W(x ยท ์—†์œผ๋ฉด ๊ฐ€์žฅ ์ –์€ x_hi).""" + if p.scenario == "saturated": + return np.ones_like(beta) + return wetness(beta, a, x_range(p)[1] if x is None else x) + + +def _terms(beta: np.ndarray, m: np.ndarray, p: AreaParams) -> tuple[np.ndarray, np.ndarray]: + """Fs = (c + c_r)ยทA + tanฯ†ยทB ์˜ A ยท B(ํ‰ํ‰ํ•œ ์…€ = โˆž).""" + z = p.soil_depth_m + cos, sin = np.cos(beta), np.sin(beta) + normal = (p.gamma_t * z * (1 - m) + (p.gamma_sat - GAMMA_W) * m * z) * cos**2 + shear = (p.gamma_t * z * (1 - m) + p.gamma_sat * m * z) * sin * cos + flat = beta < FLAT_SLOPE_RAD + with np.errstate(divide="ignore", invalid="ignore"): + a = np.where(flat, np.inf, 1.0 / shear) + b = np.where(flat, np.inf, normal / shear) + return a, b + + +def infinite_slope_fs(beta: np.ndarray, m: np.ndarray, p: AreaParams) -> np.ndarray: + """๊ฒฐ์ • ์•ˆ์ „์œจ(c ยท ฯ† = ๋ฒ”์œ„ ์•„๋ž˜ = ๋ณด์ˆ˜) โ€” ์ง€์‹DB 02 ยง1.1 ์‹.""" + a, b = _terms(beta, m, p) + with np.errstate(invalid="ignore"): + return np.where( + np.isinf(a), np.inf, (p.c + p.c_root) * a + math.tan(math.radians(p.phi)) * b + ) + + +def _cdf_sum_uniform( + s: np.ndarray, a1: np.ndarray, a2: np.ndarray, b1: np.ndarray, b2: np.ndarray +) -> np.ndarray: + """P(X + Y โ‰ค s) ยท X ~ U[a1, a2] ยท Y ~ U[b1, b2] ๋…๋ฆฝ โ€” ํญ 0 ์ด๋ฉด ๊ทธ ๋ณ€์ˆ˜๋Š” ์ƒ์ˆ˜.""" + wa, wb = a2 - a1, b2 - b1 + eps = 1e-12 + + def g(u: np.ndarray) -> np.ndarray: + return np.maximum(u, 0.0) ** 2 / 2 + + with np.errstate(divide="ignore", invalid="ignore"): + both = (g(s - a1 - b1) - g(s - a2 - b1) - g(s - a1 - b2) + g(s - a2 - b2)) / (wa * wb) + only_a = (s - a1 - b1) / wa + only_b = (s - a1 - b1) / wb + step = (a1 + b1 <= s).astype(float) + out = np.where( + (wa > eps) & (wb > eps), both, np.where(wa > eps, only_a, np.where(wb > eps, only_b, step)) + ) + return np.clip(np.nan_to_num(out, nan=0.0), 0.0, 1.0) + + +def stability_index(beta: np.ndarray, area: np.ndarray, p: AreaParams) -> np.ndarray: + """SINMAP ์•ˆ์ • ์ง€์ˆ˜ โ€” ํ•˜ํ•œ Fs_min โ‰ฅ 1 โ†’ Fs_min ยท ์ƒํ•œ Fs_max < 1 โ†’ 0 ยท ๊ทธ ์‚ฌ์ด โ†’ P(Fs > 1).""" + t1, t2 = math.tan(math.radians(p.phi)), math.tan(math.radians(max(p.phi_hi, p.phi))) + c1, c2 = p.c + p.c_root, max(p.c_hi, p.c) + p.c_root + x_lo, x_hi = x_range(p) + + def ab(m: np.ndarray) -> tuple[np.ndarray, np.ndarray]: + a, b = _terms(beta, m, p) + return np.where(np.isinf(a), 0.0, a), np.where(np.isinf(b), 0.0, b) + + a, b = ab(wet_m(beta, area, p, x_hi)) + lo = c1 * a + t1 * b + a, b = ab(wet_m(beta, area, p, x_lo)) + hi = c2 * a + t2 * b + xs = ( + [x_hi] + if p.scenario == "saturated" + else x_lo + (x_hi - x_lo) * (np.arange(X_STEPS) + 0.5) / X_STEPS + ) + prob = np.zeros_like(beta, dtype=float) + one = np.ones_like(prob) + for x in xs: + a, b = ab(wet_m(beta, area, p, x)) + prob += 1.0 - _cdf_sum_uniform(one, c1 * a, c2 * a, t1 * b, t2 * b) + prob /= len(xs) + si = np.where(lo >= 1.0, lo, np.where(hi < 1.0, 0.0, prob)) + return np.where(beta < FLAT_SLOPE_RAD, np.inf, si) + + +def classify(si: np.ndarray) -> np.ndarray: + """SI โ†’ ๋“ฑ๊ธ‰ 1~6(0 = ์ž๋ฃŒ ์—†์Œ ยท NaN).""" + cls = np.full(si.shape, 6, dtype=np.uint8) + cls[si > 0] = 5 + for k, edge in enumerate(reversed(SI_EDGES)): # 0.5 โ†’ 4 ยท 1.0 โ†’ 3 ยท 1.25 โ†’ 2 ยท 1.5 โ†’ 1 + cls[si >= edge] = 4 - k + cls[np.isnan(si)] = 0 + return cls + + +def analyze_grid(z: np.ndarray, cell_m: float, p: AreaParams) -> dict[str, np.ndarray]: + """๊ณ ๋ฅธ ๊ฒฉ์ž ์ง€ํ˜• โ†’ {slope(rad) ยท a ยท m ยท fs ยท si ยท cls} ยท ๋ฐ– = NaN / 0.""" + beta = slope_rad(z, cell_m) + a = catchment_area(z, cell_m) if p.scenario != "saturated" else np.full(z.shape, np.nan) + m = wet_m(beta, a, p) # ๊ฐ€์žฅ ์ –์€ ์ชฝ(Fs = ํ•˜ํ•œ๊ณผ ๊ฐ™์€ ์กฐ๊ฑด) + fs = infinite_slope_fs(beta, m, p) + si = stability_index(beta, a, p) + nodata = np.isnan(beta) + fs[nodata] = np.nan + si[nodata] = np.nan + m = np.where(nodata, np.nan, m) + return {"slope": beta, "a": a, "m": m, "fs": fs, "si": si, "cls": classify(si)} + + +# --------------------------------------------------------------------------- +# ๋…ธ์„  ๋  ยท ์ธก์  +# --------------------------------------------------------------------------- + + +def dense_line(line: np.ndarray, step_m: float = 0.5) -> np.ndarray: + """์ค‘์‹ฌ์„  [x, y, ๋ˆ„๊ฐ€] ๋ฅผ step ๊ฐ„๊ฒฉ์œผ๋กœ ์ด˜์ด˜ํžˆ(๊บพ์ธ ์  ์‚ฌ์ด ์…€๋„ ๋ฐ”๋ฅธ ๋ˆ„๊ฐ€).""" + out = [line[:1]] + for a, b in zip(line[:-1], line[1:]): + n = max(1, int(math.ceil(math.hypot(*(b[:2] - a[:2])) / step_m))) + out.append(a + (b - a) * np.linspace(0, 1, n + 1)[1:, None]) + return np.vstack(out) + + +def band_cells( + shape: tuple[int, int], x0: float, y0: float, cell_m: float, line: np.ndarray, half_m: float +) -> tuple[np.ndarray, np.ndarray]: + """๋…ธ์„  ๋ (์ค‘์‹ฌ์„  ยฑ half_m) ์…€ โ†’ (๋  ์•ˆ, ์…€์˜ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ค‘์‹ฌ์„  ๋ˆ„๊ฐ€).""" + dense = dense_line(np.asarray(line, float)) + yy, xx = np.meshgrid( + y0 + np.arange(shape[0]) * cell_m, x0 + np.arange(shape[1]) * cell_m, indexing="ij" + ) + dist, at = cKDTree(dense[:, :2]).query(np.column_stack([xx.ravel(), yy.ravel()])) + return (dist <= half_m).reshape(shape), dense[at, 2].reshape(shape) + + +def station_level( + risk_cells: int, cells: int, lo_pct: float = LEVEL_LO_PCT, hi_pct: float = LEVEL_HI_PCT +) -> str: + """์ธก์  ๋“ฑ๊ธ‰ โ€” ์œ„ํ—˜(SI < 1) ์…€ ๋น„์œจ < lo_pct ๋‚ฎ์Œ ยท lo_pct ์ด์ƒ hi_pct ๋ฏธ๋งŒ ๋ณดํ†ต ยท + hi_pct ์ด์ƒ ๋†’์Œ. + (์ •์ˆ˜ ๊ณฑ ๋น„๊ต โ€” ๋ฌธํ„ฑ ๊ฒฝ๊ณ„์—์„œ ์†Œ์ˆ˜ ์˜ค์ฐจ ์—†์Œ)""" + if risk_cells * 100 >= hi_pct * cells: + return "high" + return "mid" if risk_cells * 100 >= lo_pct * cells else "low" + + +def station_table( + chainages: list[float], + cell_chain: np.ndarray, + inside: np.ndarray, + out: dict[str, np.ndarray], + cell_m: float, + lo_pct: float = LEVEL_LO_PCT, + hi_pct: float = LEVEL_HI_PCT, +) -> list[dict[str, Any]]: + """์ธก์ ๋งˆ๋‹ค ๋งก์€ ๋  ์…€(์ด์›ƒ ์ธก์ ๊ณผ ์ค‘๊ฐ„ ๋ˆ„๊ฐ€๋กœ ๋‚˜๋ˆ” = ์•ž๋’ค ยฝ ๊ฐ„๊ฒฉ ร— ๋  ํญ) โ€” + ์ตœ์†Œ Fs ยท SI ยท ๊ฐ€์žฅ ๋‚˜์œ ๋“ฑ๊ธ‰ ยท ์œ„ํ—˜ ๋ฉด์  ยท ์œ„ํ—˜ ์…€ ๋น„์œจ ยท ์ธก์  ๋“ฑ๊ธ‰.""" + chs = np.asarray(sorted(chainages), float) + if not len(chs): + return [] + mids = (chs[:-1] + chs[1:]) / 2 + owner = np.searchsorted(mids, cell_chain) + have = inside & (out["cls"] > 0) + rows = [] + for k, ch in enumerate(chs): + sel = have & (owner == k) + n = int(sel.sum()) + if not n: + rows.append({"chainage_m": float(ch), "cells": 0}) + continue + cls = out["cls"][sel] + fs = out["fs"][sel] + si = out["si"][sel] + risk = int((cls >= RISK_CLASS).sum()) + rows.append( + { + "chainage_m": float(ch), + "cells": n, + "fs_min": round(float(np.min(fs)), 3), + "si_min": round(float(np.min(si)), 3), + "worst_class": int(cls.max()), + "risk_area_m2": round(risk * cell_m**2, 1), + "risk_ratio": round(risk / n, 4), + "level": station_level(risk, n, lo_pct, hi_pct), + "slope_max_deg": round(float(np.degrees(np.nanmax(out["slope"][sel]))), 1), + } + ) + return rows + + +def class_areas(cls: np.ndarray, inside: np.ndarray, cell_m: float) -> dict[str, float]: + """๋  ์•ˆ ๋“ฑ๊ธ‰๋ณ„ ๋ฉด์  mยฒ.""" + return { + CLASS_KEYS[k]: round(float(((cls == k) & inside).sum()) * cell_m**2, 1) for k in range(1, 7) + } diff --git a/E07_StabArea/E07_StabArea_Router.py b/E07_StabArea/E07_StabArea_Router.py new file mode 100644 index 000000000..159cf7d51 --- /dev/null +++ b/E07_StabArea/E07_StabArea_Router.py @@ -0,0 +1,350 @@ +"""E07 ๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ ์ง€๋„ API (์š”๊ตฌ 345 ยท 351 ยท ์š”๊ตฌ์„œ 4-2 2์ฐจ ยท PLAN 536-2). + +- `POST /api/projects/{id}/stab/area/analyze` โ€” ํ•ด์„(๋’ค์—์„œ) ยท ์ž…๋ ฅ ์ง€๋ฌธ์ด ์ €์žฅ๋ณธ๊ณผ ๊ฐ™์œผ๋ฉด + `cached` ยท force = ๋‹ค์‹œ +- `GET โ€ฆ/stab/area/progress` โ€” ์ง„ํ–‰๋ฅ  +- `GET โ€ฆ/stab/area` โ€” ๊ธฐ๋ณธ๊ฐ’(E02 ๋ถ•์ ํ† ) ยท ๋ฌผ์„ฑ ๋ชฉ๋ก ยท ๋…ธ์„  ยท ์ธก์  ยท ์ €์žฅ๋ณธ(๊ฒฉ์ž ยท ์ธก์ ํ‘œ ยท ์š”์•ฝ) + +๊ณ„์‚ฐ ์ž๋ฆฌ = โ‘ข ์„œ๋ฒ„ ๋‹จ๋…(์—”์ง„ `E07_StabArea_Engine` ยท ์ˆœ์ˆ˜). ์ง€ํ˜• = ๋ณต์‚ฌ๋ณธ ์›์ง€ํ˜• DTM +(๋…ธ์„ ๋ฉด ํ•ฉ์„ฑ ์•ˆ ํ•จ โ€” ๋…ธ์„  ์œ„ ยท ์•„๋ž˜ ์ž์—ฐ์‚ฌ๋ฉด). +ํŒŒ์ƒ ์ €์žฅ๋ณธ = `E07_StabArea/_derived/area.json`(์ง€๋ฌธ์ด ๊ฐ™์œผ๋ฉด ์•ˆ ์…ˆ). +๊ถŒํ•œ = main.py `protected_with_company`(์ง€์ •๋ฐ›์€ ํšŒ์‚ฌ = GET ๋งŒ). + +๊ฒฉ์ž ๊ผด(ํ™”๋ฉด `logic/E07_StabArea_Raster.ts` ์™€ ๊ฐ™์€ ์—ด์‡ ) โ€” grid = {x0, y0, dx, nx, ny} +(ํ–‰ 0 = ๋‚จ์ชฝ) ยท fs ยท si = uint16 ร—1000(65535 = ์ž๋ฃŒ ์—†์Œ ยท 65.534 ์œ„๋กœ ์ž๋ฆ„) ยท +cls = uint8 ๋“ฑ๊ธ‰(0 ์—†์Œ ยท 1~6) ยท slope = uint16 ร—10 ยฐ ยท m = uint8 ร—100(255 ์—†์Œ) +โ€” ๋ชจ๋‘ ์ค„ ์šฐ์„  ยท ์ž‘์€ ๋ base64 ยท ๋  ๋ฐ– ์…€ = ์ž๋ฃŒ ์—†์Œ. +""" + +from __future__ import annotations + +import asyncio +import base64 +import hashlib +import json +import logging +import time +from collections.abc import Callable +from datetime import datetime +from pathlib import Path +from typing import Any, Literal +from uuid import UUID + +import aiomysql +import numpy as np +from fastapi import APIRouter, Depends, HTTPException +from pydantic import BaseModel, Field, model_validator + +from common_util.common_util_auth import require_company +from common_util.common_util_json import atomic_write_json +from common_util.common_util_storage import resolve_stored_project_path +from config.config_db import get_db_pool +from E01_StabRegister import E01_StabRegister_Source as source +from E02_StabGround import E02_StabGround_Model as ground_model +from E07_StabArea import E07_StabArea_Engine as engine + +logger = logging.getLogger(__name__) +router = APIRouter(prefix="/api/projects", tags=["E07 Stab Area"]) + +ENGINE_VERSION = "e07-3" # ์—”์ง„ ํŒ โ€” ๋ฐ”๋€Œ๋ฉด ์ง€๋ฌธ์ด ๋‹ฌ๋ผ์ ธ ๋‹ค์‹œ ์…ˆ +RESULT = Path("E07_StabArea") / "_derived" / "area.json" +DEFAULT_MATERIAL = "colluvium" # ํ‘œ์ธต ๋ถ•๊ดด์— ๊ฐ€์žฅ ์ทจ์•ฝ(์ง€์‹DB ์•ˆ์ •์„ฑํ‰๊ฐ€/04 ๋ถ•์ ํ† ) +RANGE_C_KPA, RANGE_PHI_DEG = ( + 5.0, + 3.0, +) # SI ๋ฒ”์œ„ ์œ„ = ์•„๋ž˜ + ์ด ํญ(04 ๋ถ•์ ํ†  ๋ฒ”์œ„ c 0~10 ยท ฯ† 25~30 ํญ์˜ ๋ฐ˜) + +_JOBS: dict[str, dict[str, Any]] = {} +_TASKS: set[asyncio.Task] = set() +_IDLE = {"running": False, "percent": 0, "message": None, "error": None} + + +class AreaRequest(BaseModel): + band_m: float = Field(100.0, ge=10.0, le=500.0) # ๋…ธ์„  ๋  = ์ค‘์‹ฌ์„  ยฑ ์ด ํญ + cell_m: float = Field(2.0, ge=1.0, le=10.0) + material_id: str | None = None # ์ด๋ฆ„ํ‘œ๋งŒ(๊ฐ’์€ ์•„๋ž˜ ์นธ) + gamma_t: float = Field(..., gt=0.0, le=30.0) + gamma_sat: float = Field(..., gt=engine.GAMMA_W, le=30.0) + c: float = Field(..., ge=0.0, le=500.0) + c_hi: float = Field(..., ge=0.0, le=500.0) + phi: float = Field(..., ge=0.0, le=60.0) + phi_hi: float = Field(..., ge=0.0, le=60.0) + # ๊ธฐ๋ณธ๊ฐ’ = ๊ทผ๊ฑฐ ํ›„๋ณด(๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ ยท ํ™•์ •์€ ์‚ฌ์šฉ์ž) โ€” ์ถœ์ฒ˜ = ์ง€์‹DB ์˜ˆ์ •๋…ธ์„ /02 ยง1.1 ยท + # SINMAP 2 ์„ค๋ช…์„œ ๊ธฐ๋ณธ ๋ณด์ • ยท ๊ตญ๋‚ด ์ ์šฉ ์—ฐ๊ตฌ(๊น€๋™์—ฝ 2026 ๋ด‰ํ™” TRIGRS Cr 0~5 kPa ยท ์šฐ๋ฉด์‚ฐ 1 kPa) + c_root: float = Field(1.0, ge=0.0, le=50.0) # ์‚ฐ๋ฆผ ๋ฟŒ๋ฆฌ ์ ์ฐฉ ยท ๋‚˜์ง€ ยท ์ ˆ์„ฑํ† ๋ฉด = 0 + soil_depth_m: float = Field(1.5, ge=0.1, le=10.0) + scenario: Literal["sinmap", "saturated"] = "sinmap" # ํ‘œ์ค€(์Šต์œค์ง€์ˆ˜) ยท ์ตœ์•…(์™„์ „ ํฌํ™”) + tr_lo: float = Field(2000.0, ge=1.0, le=1e5) # T/R(m) โ€” SINMAP ๊ธฐ๋ณธ ๋ณด์ • 2000~3000 + tr_hi: float = Field(3000.0, ge=1.0, le=1e5) + # ์ธก์  ๋“ฑ๊ธ‰ ๋ฌธํ„ฑ(%) โ€” ์˜ํ–ฅ ๊ตฌ์—ญ ์•ˆ SI < 1 ์…€ ๋น„์œจ ยท ๋ธŒ๋ ˆ์ธ ์ •ํ•จ(์‚ฌ์šฉ์ž ํ™•์ • ์ „) + level_lo_pct: float = Field(engine.LEVEL_LO_PCT, gt=0.0, le=100.0) + level_hi_pct: float = Field(engine.LEVEL_HI_PCT, gt=0.0, le=100.0) + force: bool = False + + @model_validator(mode="after") + def _ranges(self) -> AreaRequest: + if self.level_hi_pct < self.level_lo_pct: + raise ValueError("์ธก์  ๋“ฑ๊ธ‰ ๋ฌธํ„ฑ: ๋†’์Œ์ด ๋ณดํ†ต๋ณด๋‹ค ์ž‘์Šต๋‹ˆ๋‹ค.") + if self.c_hi < self.c or self.phi_hi < self.phi or self.tr_hi < self.tr_lo: + raise ValueError("๋ฒ”์œ„ ์œ„๊ฐ€ ์•„๋ž˜๋ณด๋‹ค ์ž‘์Šต๋‹ˆ๋‹ค(c ยท ฯ† ยท T/R).") + return self + + +# --------------------------------------------------------------------------- +# ์ž…๋ ฅ ยท ์ง€๋ฌธ ยท ์ €์žฅ๋ณธ +# --------------------------------------------------------------------------- + + +def _ground(project_root: Path) -> dict[str, Any]: + for path in (ground_model.ground_path(project_root), ground_model.initial_path(project_root)): + if path.is_file(): + return json.loads(path.read_text(encoding="utf-8")) + return ground_model.pick(ground_model.load_defaults()) + + +def defaults(project_root: Path) -> dict[str, Any]: + """๊ธฐ๋ณธ ์กฐ๊ฑด โ€” E02 ๋ถ•์ ํ† (์—†์œผ๋ฉด ์ฒซ ๋ฌผ์„ฑ) ยท ๋ฒ”์œ„ ์œ„ = + 5 kPa ยท + 3ยฐ.""" + materials = _ground(project_root).get("materials") or [] + mat = next((m for m in materials if m.get("id") == DEFAULT_MATERIAL), None) or materials[0] + return { + **{ + k: f.default + for k, f in AreaRequest.model_fields.items() + if not f.is_required() and k != "force" + }, + "material_id": mat.get("id"), + "gamma_t": float(mat["gamma_t"]), + "gamma_sat": float(mat["gamma_sat"]), + "c": float(mat["c"]), + "c_hi": float(mat["c"]) + RANGE_C_KPA, + "phi": float(mat["phi"]), + "phi_hi": float(mat["phi"]) + RANGE_PHI_DEG, + } + + +def fingerprint(source_fingerprint: str, settings: dict[str, Any]) -> str: + body = {"source": source_fingerprint, "settings": settings, "engine": ENGINE_VERSION} + text = json.dumps(body, ensure_ascii=False, sort_keys=True) + return hashlib.sha1(text.encode("utf-8")).hexdigest() + + +def load(project_root: Path) -> dict[str, Any] | None: + path = Path(project_root) / RESULT + return json.loads(path.read_text(encoding="utf-8")) if path.is_file() else None + + +def _route_line(root: Path) -> np.ndarray: + points = sorted( + source.db_snapshot(root).get("route_points") or [], key=lambda p: p["sequence_num"] + ) + return np.array([[p["model_x"], p["model_y"], p["chainage_m"]] for p in points], float) + + +def _b64(values: np.ndarray, dtype: str) -> str: + return base64.b64encode(np.ascontiguousarray(values, dtype=dtype).tobytes()).decode("ascii") + + +def _u16(values: np.ndarray, scale: float, keep: np.ndarray) -> str: + v = np.round(np.clip(np.nan_to_num(values * scale, nan=-1.0, posinf=65534.0), -1, 65534)) + return _b64(np.where(keep & (v >= 0), v, 65535), " dict[str, Any]: + """ํ•ด์„ ํ•œ ๋ฒˆ โ†’ ์ €์žฅ๋ณธ ์“ฐ๊ณ  ์š”์•ฝ ๋Œ๋ ค์คŒ.""" + started = time.perf_counter() + cell = float(settings["cell_m"]) + report(5, "์ง€ํ˜• ๊ฒฉ์ž") + with np.load(source.terrain_grid_path(root)) as npz: + dem = {k: npz[k] for k in ("x", "y", "z", "valid_mask")} + z, x0, y0 = engine.resample_dem(dem, cell) + report(25, "๊ฒฝ์‚ฌ ยท ์ง‘์ˆ˜๋ฉด์  ยท ์•ˆ์ „์œจ") + keys = ( + "gamma_t", + "gamma_sat", + "c", + "c_hi", + "phi", + "phi_hi", + "c_root", + "soil_depth_m", + "scenario", + "tr_lo", + "tr_hi", + ) + out = engine.analyze_grid(z, cell, engine.AreaParams(**{k: settings[k] for k in keys})) + report(75, "๋…ธ์„  ๋  ยท ์ธก์ ") + stations = sorted(source.stations(root), key=lambda s: float(s["chainage_m"])) + inside, chain = engine.band_cells( + z.shape, x0, y0, cell, _route_line(root), float(settings["band_m"]) + ) + rows = engine.station_table( + [s["chainage_m"] for s in stations], + chain, + inside, + out, + cell, + float(settings["level_lo_pct"]), + float(settings["level_hi_pct"]), + ) + labels = {round(float(s["chainage_m"]), 3): s.get("label") for s in stations} + for row in rows: + row["label"] = labels.get(round(row["chainage_m"], 3)) + # ๋  ์ƒ์ž๋กœ ์ž˜๋ผ ์ €์žฅ + iy, ix = np.nonzero(inside) + if not len(iy): + raise ValueError("๋…ธ์„  ๋ ๊ฐ€ ์ง€ํ˜• ๊ฒฉ์ž์™€ ๊ฒน์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.") + win = (slice(iy.min(), iy.max() + 1), slice(ix.min(), ix.max() + 1)) + keep = inside[win] & (out["cls"][win] > 0) + areas = engine.class_areas(out["cls"], inside, cell) + have = inside & (out["cls"] > 0) + summary = { + "areas_m2": areas, + "band_area_m2": round(float(inside.sum()) * cell**2, 1), + "covered_area_m2": round(float(have.sum()) * cell**2, 1), + "risk_area_m2": round( + sum(v for k, v in areas.items() if engine.CLASS_KEYS.index(k) >= engine.RISK_CLASS), 1 + ), + "fs_min": round(float(np.min(out["fs"][have])), 3) if have.any() else None, + "station_levels": {k: sum(r.get("level") == k for r in rows) for k in engine.LEVEL_KEYS}, + "stations": len(rows), + } + doc = { + "fingerprint": finger, + "engine": ENGINE_VERSION, + "settings": settings, + "computed_at": datetime.now().isoformat(timespec="seconds"), + "elapsed_s": round(time.perf_counter() - started, 2), + "grid": { + "x0": x0 + int(ix.min()) * cell, + "y0": y0 + int(iy.min()) * cell, + "dx": cell, + "nx": int(keep.shape[1]), + "ny": int(keep.shape[0]), + }, + "fs": _u16(out["fs"][win], 1000, keep), + "si": _u16(out["si"][win], 1000, keep), + "cls": _b64(np.where(keep, out["cls"][win], 0), "u1"), + "slope": _u16(np.degrees(out["slope"][win]), 10, keep), + "m": _b64(np.where(keep, np.round(np.nan_to_num(out["m"][win], nan=0) * 100), 255), "u1"), + "stations": rows, + "summary": summary, + } + report(95, "์ €์žฅ") + atomic_write_json(Path(project_root) / RESULT, doc) + return summary + + +# --------------------------------------------------------------------------- +# ๊ธธ +# --------------------------------------------------------------------------- + + +async def _project_root(project_id: UUID) -> Path: + async with ( + get_db_pool().acquire() as connection, + connection.cursor(aiomysql.DictCursor) as cursor, + ): + await cursor.execute( + "SELECT storage_path FROM projects" + " WHERE id = %s AND project_kind = 'STABILITY' AND deleted_at IS NULL", + (str(project_id),), + ) + row = await cursor.fetchone() + if not row or not row["storage_path"]: + raise HTTPException(status_code=404, detail="์•ˆ์ •์„ฑํ‰๊ฐ€ ํ”„๋กœ์ ํŠธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.") + return Path(resolve_stored_project_path(row["storage_path"])) + + +def _source_root(project_root: Path) -> Path: + root = source.source_dir(project_root) + if not (root / source.MANIFEST_NAME).is_file(): + raise HTTPException(status_code=409, detail="์›๋ณธ ๋ณต์‚ฌ๋ณธ์ด ์—†์Šต๋‹ˆ๋‹ค.") + return root + + +@router.post("/{project_id}/stab/area/analyze", response_model=None) +async def analyze(project_id: UUID, payload: AreaRequest) -> dict[str, Any]: + """[ํ•ด์„] โ€” ์ง€๋ฌธ์ด ๊ฐ™์œผ๋ฉด `cached`(์•ˆ ์…ˆ) ยท ๋‹ค๋ฅด๊ฑฐ๋‚˜ force ๋ฉด ๋’ค์—์„œ ์…ˆ.""" + pid = str(project_id) + project_root = await _project_root(project_id) + root = _source_root(project_root) + settings = payload.model_dump(exclude={"force"}) + finger = fingerprint(source.manifest(root)["source_fingerprint"], settings) + saved = await asyncio.to_thread(load, project_root) + if not payload.force and saved and saved["fingerprint"] == finger: + return {"status": "cached", "summary": saved["summary"]} + if (_JOBS.get(pid) or {}).get("running"): + raise HTTPException(status_code=409, detail="์ด๋ฏธ ๊ด‘์—ญ ํ•ด์„์ด ๋Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.") + job = {**_IDLE, "running": True, "message": "์‹œ์ž‘"} + _JOBS[pid] = job + + def report(percent: int, message: str) -> None: + job.update(percent=max(0, min(99, int(percent))), message=message) + + async def work() -> None: + try: + await asyncio.to_thread(run, project_root, root, settings, finger, report) + job.update(percent=100, message="๋") + except Exception as error: # noqa: BLE001 โ€” ์ง„ํ–‰๋ฅ ์— ๊นŒ๋‹ญ์„ ๊ทธ๋Œ€๋กœ ์˜ฌ๋ฆผ + logger.exception("E07 ๊ด‘์—ญ ํ•ด์„ ์‹คํŒจ: project_id=%s", pid) + job["error"] = str(error) + finally: + job["running"] = False + + task = asyncio.create_task(work(), name=f"E07 area {pid}") + _TASKS.add(task) + task.add_done_callback(_TASKS.discard) + return {"status": "accepted"} + + +@router.get("/{project_id}/stab/area/progress", response_model=None) +async def progress(project_id: UUID) -> dict[str, Any]: + return {"status": "success", **dict(_JOBS.get(str(project_id)) or _IDLE)} + + +def _overview(project_root: Path, root: Path) -> dict[str, Any]: + saved = load(project_root) + if saved: + current = fingerprint(source.manifest(root)["source_fingerprint"], saved["settings"]) + saved["stale"] = saved["fingerprint"] != current + return { + "defaults": defaults(project_root), + "materials": _ground(project_root).get("materials") or [], + "line": _route_line(root).round(3).tolist(), + "stations": [ + { + "chainage_m": s["chainage_m"], + "label": s["label"], + "x": s["center_x"], + "y": s["center_y"], + } + for s in sorted(source.stations(root), key=lambda s: float(s["chainage_m"])) + ], + "result": saved, + } + + +async def _editable(project_id: UUID, session: dict[str, Any]) -> bool: + """[ํ•ด์„] ๊ฐ€๋Šฅ = ์‹œ์Šคํ…œ ๊ด€๋ฆฌ์ž ยท ๊ฐ™์€ ํšŒ์‚ฌ(์ง€์ •๋ฐ›์€ ํšŒ์‚ฌ๋Š” ๋ณด๊ธฐ๋งŒ).""" + if session.get("role") == "SYSTEM_ADMIN": + return True + async with get_db_pool().acquire() as connection, connection.cursor() as cursor: + await cursor.execute("SELECT company_id FROM projects WHERE id = %s", (str(project_id),)) + row = await cursor.fetchone() + return bool(row) and row[0] == session.get("company_id") + + +@router.get("/{project_id}/stab/area", response_model=None) +async def area( + project_id: UUID, session: dict[str, Any] = Depends(require_company) +) -> dict[str, Any]: + project_root = await _project_root(project_id) + root = _source_root(project_root) + overview = await asyncio.to_thread(_overview, project_root, root) + return {"status": "success", "editable": await _editable(project_id, session), **overview} diff --git a/E07_StabArea/E07_StabArea_UI_Map.ts b/E07_StabArea/E07_StabArea_UI_Map.ts new file mode 100644 index 000000000..b2a77b581 --- /dev/null +++ b/E07_StabArea/E07_StabArea_UI_Map.ts @@ -0,0 +1,296 @@ +/* ============================================================================= + * E07_StabArea_UI_Map.ts + * E07 ๊ฐ€์šด๋ฐ ํ‰๋ฉด ์ง€๋„(PLAN 536-2) โ€” ๋“ฑ๊ธ‰(SI) ยท ์•ˆ์ „์œจ ์ƒ‰ ๊ฒฉ์ž ยท ๋…ธ์„  ๋  ยท ๋…ธ์„  ยท ์ธก์  ยท ๊ณ ๋ฅธ ์ธก์  ยท + * ๋ฐฉ์œ„ ยท ์ถ•์ฒ™ ยท ์…€ ํˆดํŒ. ํœ  = ํ™•๋Œ€ ยท ๋Œ๊ธฐ = ์˜ฎ๊น€ ยท ๋‘ ๋ฒˆ ๋ˆ„๋ฆ„ = ๋งž์ถค. ์ƒ‰ ยท ํ’€๊ธฐ = logic/E07_StabArea_Raster. + * ========================================================================== */ + +import { el } from "@ui/ui_template_elements"; +import { t, type LocaleKey } from "@ui/ui_template_locale"; +import { CLASS_KEYS, cellIndex, rasterRgba, type Decoded } from "./logic/E07_StabArea_Raster"; + +export type MapLayer = "cls" | "fs"; + +export interface StationPoint { + chainage_m: number; + label: string | null; + x: number; + y: number; +} + +export interface AreaMap { + root: HTMLElement; + setRoute: (line: number[][], stations: StationPoint[]) => void; + setBand: (halfWidthM: number) => void; + setResult: (data: Decoded | null) => void; + setLayer: (layer: MapLayer) => void; + focus: (chainage: number) => void; + dispose: () => void; +} + +interface View { + cx: number; + cy: number; + scale: number; // px / m +} + +const shortLabel = (label: string): string => label.replace(/^STA\./, "").replace(/\.0+$/, ""); + +const css = (name: string, fallback: string): string => + getComputedStyle(document.documentElement).getPropertyValue(name).trim() || fallback; + +export function createAreaMap(): AreaMap { + const root = el("div", { className: "e07-map" }); + const canvas = el("canvas", { className: "e07-map__canvas" }); + const tip = el("div", { className: "e07-map__tip" }); + const empty = el("p", { className: "e07-map__empty", text: t("E07_Map_Empty") }); + tip.hidden = true; + root.append(canvas, tip, empty); + const ctx = canvas.getContext("2d")!; + + let line: number[][] = []; + let stations: StationPoint[] = []; + let band = 100; + let data: Decoded | null = null; + let layer: MapLayer = "cls"; + let image: HTMLCanvasElement | null = null; + let picked: number | null = null; + let view: View = { cx: 0, cy: 0, scale: 1 }; + let width = 0; + let height = 0; + + const toScreen = (x: number, y: number): [number, number] => [ + width / 2 + (x - view.cx) * view.scale, + height / 2 - (y - view.cy) * view.scale, + ]; + const toModel = (px: number, py: number): [number, number] => [ + view.cx + (px - width / 2) / view.scale, + view.cy - (py - height / 2) / view.scale, + ]; + + function buildImage(): void { + image = null; + if (!data) return; + const { nx, ny } = data.grid; + const off = document.createElement("canvas"); + off.width = nx; + off.height = ny; + off.getContext("2d")!.putImageData(new ImageData(rasterRgba(data, layer), nx, ny), 0, 0); + image = off; + } + + function fit(): void { + const xs = line.map((p) => p[0]); + const ys = line.map((p) => p[1]); + if (!xs.length || !width || !height) return; + const pad = band; + const [x0, x1] = [Math.min(...xs) - pad, Math.max(...xs) + pad]; + const [y0, y1] = [Math.min(...ys) - pad, Math.max(...ys) + pad]; + const scale = Math.min(width / Math.max(x1 - x0, 1), height / Math.max(y1 - y0, 1)) * 0.92; + view = { cx: (x0 + x1) / 2, cy: (y0 + y1) / 2, scale }; + } + + function path(): void { + ctx.beginPath(); + line.forEach((p, i) => + i ? ctx.lineTo(...toScreen(p[0], p[1])) : ctx.moveTo(...toScreen(p[0], p[1])), + ); + } + + function drawRoute(text: string, accent: string): void { + if (line.length < 2) return; + ctx.lineJoin = ctx.lineCap = "round"; + // ๋…ธ์„  ๋  โ€” ์ค‘์‹ฌ์„  ยฑ band(์˜…์€ ๋ ) + ctx.globalAlpha = 0.12; + ctx.strokeStyle = accent; + ctx.lineWidth = Math.max(2 * band * view.scale, 1); + path(); + ctx.stroke(); + ctx.globalAlpha = 1; + ctx.strokeStyle = text; + ctx.lineWidth = 2; + path(); + ctx.stroke(); + const gap = stations.length > 1 ? 20 * view.scale : 999; + const every = [1, 2, 5, 10, 25, 50, 100].find((n) => n * gap >= 70) ?? 100; + ctx.font = "11px sans-serif"; + ctx.textBaseline = "middle"; + stations.forEach((s, i) => { + const [px, py] = toScreen(s.x, s.y); + if (px < -50 || py < -50 || px > width + 50 || py > height + 50) return; + const on = picked !== null && Math.abs(s.chainage_m - picked) < 1e-6; + ctx.fillStyle = on ? accent : text; + ctx.beginPath(); + ctx.arc(px, py, on ? 6 : i % every === 0 ? 3 : 1.5, 0, Math.PI * 2); + ctx.fill(); + if ((on || i % every === 0) && s.label) ctx.fillText(shortLabel(s.label), px + 7, py - 9); + }); + } + + function drawFurniture(text: string): void { + ctx.fillStyle = text; + ctx.strokeStyle = text; + ctx.beginPath(); + ctx.moveTo(width - 24, 14); + ctx.lineTo(width - 30, 34); + ctx.lineTo(width - 18, 34); + ctx.closePath(); + ctx.fill(); + ctx.font = "bold 12px sans-serif"; + ctx.textAlign = "center"; + ctx.fillText("N", width - 24, 46); + const target = 100 / view.scale; + const pow = 10 ** Math.floor(Math.log10(target)); + const meters = [1, 2, 5, 10].map((k) => k * pow).find((m) => m >= target) ?? target; + const px = meters * view.scale; + ctx.fillStyle = css("--color-surface", "#ffffff"); + ctx.globalAlpha = 0.85; + ctx.fillRect(width - 24 - px, height - 42, px + 16, 34); + ctx.globalAlpha = 1; + ctx.fillStyle = text; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(width - 16 - px, height - 16); + ctx.lineTo(width - 16, height - 16); + ctx.stroke(); + ctx.fillText(`${meters} m`, width - 16 - px / 2, height - 26); + ctx.textAlign = "start"; + } + + function draw(): void { + const dpr = window.devicePixelRatio || 1; + width = root.clientWidth; + height = root.clientHeight; + if (!width || !height) return; + if (canvas.width !== Math.round(width * dpr) || canvas.height !== Math.round(height * dpr)) { + canvas.width = Math.round(width * dpr); + canvas.height = Math.round(height * dpr); + } + ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + ctx.clearRect(0, 0, width, height); + const text = css("--color-text", "#1f2937"); + const accent = css("--color-primary", "#6d28d9"); + if (data && image) { + const g = data.grid; + const [left, top] = toScreen(g.x0 - g.dx / 2, g.y0 + (g.ny - 0.5) * g.dx); + ctx.imageSmoothingEnabled = false; + ctx.drawImage(image, left, top, g.nx * g.dx * view.scale, g.ny * g.dx * view.scale); + } + drawRoute(text, accent); + drawFurniture(text); + empty.hidden = Boolean(data); + } + + let drag: { x: number; y: number; view: View } | null = null; + canvas.addEventListener("wheel", (event) => { + event.preventDefault(); + const rect = canvas.getBoundingClientRect(); + const [mx, my] = toModel(event.clientX - rect.left, event.clientY - rect.top); + const k = Math.exp(-event.deltaY * 0.0015); + view = { scale: view.scale * k, cx: mx - (mx - view.cx) / k, cy: my - (my - view.cy) / k }; + draw(); + }); + canvas.addEventListener("pointerdown", (event) => { + drag = { x: event.clientX, y: event.clientY, view: { ...view } }; + canvas.setPointerCapture(event.pointerId); + }); + canvas.addEventListener("pointerup", () => (drag = null)); + canvas.addEventListener("dblclick", () => { + fit(); + draw(); + }); + canvas.addEventListener("pointerleave", () => (tip.hidden = true)); + canvas.addEventListener("pointermove", (event) => { + if (drag) { + view = { + ...drag.view, + cx: drag.view.cx - (event.clientX - drag.x) / view.scale, + cy: drag.view.cy + (event.clientY - drag.y) / view.scale, + }; + draw(); + return; + } + const rect = canvas.getBoundingClientRect(); + const [mx, my] = toModel(event.clientX - rect.left, event.clientY - rect.top); + const i = data ? cellIndex(data.grid, mx, my) : -1; + if (i < 0 || !data!.cls[i]) return void (tip.hidden = true); + const near = stations.reduce( + (best, s) => + !best || (s.x - mx) ** 2 + (s.y - my) ** 2 < (best.x - mx) ** 2 + (best.y - my) ** 2 + ? s + : best, + null, + ); + const m = data!.m[i]; + tip.textContent = [ + t(`E07_Class_${CLASS_KEYS[data!.cls[i] - 1]}` as LocaleKey), + `SI ${data!.si[i].toFixed(3)} ยท Fs ${data!.fs[i].toFixed(3)}`, + `${t("E07_Tip_Slope")} ${data!.slope[i].toFixed(1)}ยฐ${Number.isNaN(m) ? "" : ` ยท m ${m.toFixed(2)}`}`, + near ? `${t("E07_Tip_Station")} ${near.label ?? near.chainage_m.toFixed(1)}` : "", + ] + .filter(Boolean) + .join("\n"); + tip.style.left = `${event.clientX - rect.left + 14}px`; + tip.style.top = `${event.clientY - rect.top + 14}px`; + tip.hidden = false; + }); + + const resize = new ResizeObserver(() => { + const first = !width; + draw(); + if (first) { + fit(); + draw(); + } + }); + resize.observe(root); + const theme = new MutationObserver(draw); + theme.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] }); + + // ๊ฒ€์ฆ ์ฐฝ๊ตฌ โ€” ๋ชจ๋ธ ์ขŒํ‘œ โ†’ ํ™”๋ฉด(client) ์ขŒํ‘œ ยท ์ง€๊ธˆ ๊ฒฉ์ž ยท ์ธต + const probe = { + project: (x: number, y: number) => { + const rect = canvas.getBoundingClientRect(); + const [px, py] = toScreen(x, y); + return { x: rect.left + px, y: rect.top + py }; + }, + data: () => data, + layer: () => layer, + }; + (window as unknown as { __e07Map?: typeof probe }).__e07Map = probe; + + return { + root, + setRoute(nextLine, nextStations) { + line = nextLine; + stations = nextStations; + fit(); + draw(); + }, + setBand(halfWidthM) { + band = halfWidthM; + draw(); + }, + setResult(next) { + data = next; + buildImage(); + draw(); + }, + setLayer(next) { + layer = next; + buildImage(); + draw(); + }, + focus(chainage) { + picked = chainage; + const s = stations.find((x) => Math.abs(x.chainage_m - chainage) < 1e-6); + if (s) view = { ...view, cx: s.x, cy: s.y }; + draw(); + }, + dispose() { + resize.disconnect(); + theme.disconnect(); + if ((window as unknown as { __e07Map?: unknown }).__e07Map === probe) + delete (window as unknown as { __e07Map?: unknown }).__e07Map; + }, + }; +} diff --git a/E07_StabArea/E07_StabArea_UI_Page.ts b/E07_StabArea/E07_StabArea_UI_Page.ts new file mode 100644 index 000000000..e23acd3bb --- /dev/null +++ b/E07_StabArea/E07_StabArea_UI_Page.ts @@ -0,0 +1,497 @@ +/* ============================================================================= + * E07_StabArea_UI_Page.ts + * E07 ๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ ์ง€๋„(์š”๊ตฌ 345 ยท 351 ยท ์š”๊ตฌ์„œ 4-2 2์ฐจ ยท PLAN 536-2) โ€” B05~B09 ํ‹€(group_workflow). + * ์ขŒ = ํ† ์ธต ๋ฌผ์„ฑ(E02 ๋ฌผ์„ฑ ๊ณ ๋ฆ„ ยท ๋ฒ”์œ„) ยท ์‹œ๋‚˜๋ฆฌ์˜ค(ํ‘œ์ค€ ์Šต์œค์ง€์ˆ˜ T/R ยท ์ตœ์•… ์™„์ „ ํฌํ™”) ยท ๋…ธ์„  ๋  ยท [ํ•ด์„] ยท ์ง„ํ–‰๋ฅ  + * ๊ฐ€์šด๋ฐ = ํ‰๋ฉด ์ง€๋„(SINMAP ๋“ฑ๊ธ‰ ยท ์•ˆ์ „์œจ) ยท ๋ฒ”๋ก€ + * ์šฐ = ์š”์•ฝ(๋“ฑ๊ธ‰๋ณ„ ๋ฉด์  ยท ์œ„ํ—˜ ๋ฉด์ ) ยท ์ธก์ ๋ณ„ ์œ„ํ—˜ ํ‘œ(๋ˆ„๋ฆ„ = ์ง€๋„ ๊ทธ ์ธก์ ) + * ๊ณ„์‚ฐ = ์„œ๋ฒ„ ๋‹จ๋…(POST โ€ฆ/stab/area/analyze ยท ๋’ค์—์„œ) ยท ํ™”๋ฉด์€ ๊ฐ’ ยท ๊ทธ๋ฆผ๋งŒ. ์กฐ๊ฑด = sessionStorage. + * ========================================================================== */ + +import "./E07_StabArea_UI_Style.css"; +import { API_BASE_URL, CURRENT_PROJECT_ID_KEY, ROUTES } from "@config/config_frontend"; +import { attachCollapsible, createPanelCard } from "@ui/ui_template_collapsible"; +import { + createButton, + createInputField, + createSelectField, + el, + showToast, + type InputFieldHandle, +} from "@ui/ui_template_elements"; +import { t, type LocaleKey } from "@ui/ui_template_locale"; +import { createSidePanels } from "@ui/ui_template_side_panels"; +import { mountGroupStep } from "../A00_Common/group_workflow"; +import { createAreaMap, type MapLayer, type StationPoint } from "./E07_StabArea_UI_Map"; +import { + CLASS_COLORS, + CLASS_KEYS, + FS_STEPS, + LEVEL_COLORS, + LEVEL_KEYS, + decode, + type AreaResult, +} from "./logic/E07_StabArea_Raster"; + +interface Material { + id: string; + name: string; + gamma_t: number; + gamma_sat: number; + c: number; + phi: number; +} + +type Settings = Record; + +interface Overview { + editable: boolean; + defaults: Settings; + materials: Material[]; + line: number[][]; + stations: StationPoint[]; + result: AreaResult | null; +} + +interface Progress { + running: boolean; + percent: number; + message: string | null; + error: string | null; +} + +const NUMBER_FIELDS: [string, LocaleKey, "soil" | "rain" | "band"][] = [ + ["gamma_t", "E07_GammaT", "soil"], + ["gamma_sat", "E07_GammaSat", "soil"], + ["c", "E07_C", "soil"], + ["c_hi", "E07_CHi", "soil"], + ["phi", "E07_Phi", "soil"], + ["phi_hi", "E07_PhiHi", "soil"], + ["c_root", "E07_CRoot", "soil"], + ["soil_depth_m", "E07_Depth", "soil"], + ["tr_lo", "E07_TrLo", "rain"], + ["tr_hi", "E07_TrHi", "rain"], + ["band_m", "E07_Band", "band"], + ["cell_m", "E07_Cell", "band"], + ["level_lo_pct", "E07_LevelLo", "band"], + ["level_hi_pct", "E07_LevelHi", "band"], +]; + +const cacheKey = (projectId: string): string => `e07_settings:${projectId}`; +const shortLabel = (label: string | null, ch: number): string => + (label ?? `${ch.toFixed(1)} m`).replace(/^STA\./, "").replace(/\.0+$/, ""); + +async function api(path: string, init?: RequestInit): Promise { + const response = await fetch(`${API_BASE_URL}${path}`, { + credentials: "include", + headers: { "Content-Type": "application/json" }, + ...init, + }); + const body = (await response.json().catch(() => ({}))) as T & { detail?: unknown }; + if (!response.ok) { + const detail = body.detail; + const text = Array.isArray(detail) + ? detail.map((d) => (d as { msg?: string }).msg ?? "").join(" ยท ") + : detail; + throw new Error(typeof text === "string" && text ? text : `HTTP ${response.status}`); + } + return body; +} + +function swatch(color: string): HTMLElement { + const chip = el("i", { className: "e07-chip" }); + chip.style.background = color; + return chip; +} + +const SCENARIOS = ["sinmap", "saturated"]; +const scenarioName = (value: unknown): string => + SCENARIOS.includes(String(value)) ? t(`E07_Scenario_${value}` as LocaleKey) : String(value ?? ""); + +function legend(layer: MapLayer, scenario: unknown): HTMLElement { + const box = el("div", { className: "e07-legend" }); + box.append(el("strong", { text: t(layer === "cls" ? "E07_Legend_Class" : "E07_Legend_FS") })); + if (scenario) + box.append(el("small", { className: "e07-legend__sub", text: scenarioName(scenario) })); + const rows = + layer === "cls" + ? CLASS_KEYS.map((k, i) => ({ + color: CLASS_COLORS[i], + label: t(`E07_Class_${k}` as LocaleKey), + })) + : FS_STEPS.map((s, i) => ({ + color: s.color, + label: + i === 0 + ? `< ${s.below.toFixed(1)}` + : s.below === Infinity + ? `โ‰ฅ ${FS_STEPS[i - 1].below.toFixed(1)}` + : `${FS_STEPS[i - 1].below.toFixed(1)} โ€“ ${s.below.toFixed(1)}`, + })); + for (const row of rows) { + const item = el("span", { className: "e07-legend__row" }); + item.append(swatch(row.color), el("span", { text: row.label })); + box.append(item); + } + return box; +} + +export function renderE07StabArea(root: HTMLElement): void { + const projectId = localStorage.getItem(CURRENT_PROJECT_ID_KEY); + const panel = el("div", { className: "ui-sidebar-panel e07-panel" }); + const main = el("div", { className: "e07-page" }); + mountGroupStep(root, ROUTES.E07_STAB_AREA, { mainContent: main, leftPanel: panel }); + if (!projectId) + return void main.append(el("p", { className: "e07-empty", text: t("E07_NoProject") })); + + // ---- ๊ฐ€์šด๋ฐ ---- + const map = createAreaMap(); + let overview: Overview | null = null; + let layer: MapLayer = "cls"; + const legendSlot = el("div", { className: "e07-legend-slot" }); + const paintLegend = (): void => + legendSlot.replaceChildren(legend(layer, overview?.result?.settings.scenario)); + const toggle = el("div", { className: "e07-toggle" }); + const layerButtons = ( + [ + ["E07_Layer_Class", "cls"], + ["E07_Layer_FS", "fs"], + ] as [LocaleKey, MapLayer][] + ).map(([key, value], i) => { + const button = createButton({ label: t(key), variant: "ghost", size: "sm" }); + button.dataset.value = value; + button.classList.toggle("is-on", i === 0); + button.addEventListener("click", () => { + layerButtons.forEach((b) => b.classList.toggle("is-on", b === button)); + layer = value; + map.setLayer(layer); + paintLegend(); + }); + return button; + }); + toggle.append(...layerButtons); + const stage = el("div", { className: "e07-stage" }); + stage.append(map.root, legendSlot); + const center = el("div", { className: "e07-center" }); + center.append(toggle, stage); + paintLegend(); + + // ---- ์šฐ ---- + const resultBody = el("div", { className: "e07-result" }); + const side = createSidePanels({ + prefKey: "e07-stab-area-result", + panels: [{ id: "result", title: t("E07_Result_Title"), body: resultBody }], + defaultOpen: "result", + defaultWidth: 380, + follow: false, + }); + const row = el("div", { className: "ui-side-panels-row e07-row" }); + row.append(center, side.root); + main.append(row); + + // ---- ์ขŒ ---- + const fields = new Map(); + const cards = { + soil: createPanelCard(t("E07_Card_Soil"), false), + rain: createPanelCard(t("E07_Card_Rain"), false), + band: createPanelCard(t("E07_Card_Band"), false), + }; + const material = createSelectField({ label: t("E07_Material"), options: [], compact: true }); + const scenario = createSelectField({ + label: t("E07_Scenario"), + options: SCENARIOS.map((value) => ({ value, text: scenarioName(value) })), + compact: true, + }); + cards.soil.append(material.root); + cards.rain.append(scenario.root); + const sourceHint = el("p", { className: "e07-hint", text: t("E07_Defaults_Source") }); + for (const [key, label, card] of NUMBER_FIELDS) { + const field = createInputField({ label: t(label), type: "number", compact: true }); + field.input.step = "any"; + field.input.dataset.key = key; + fields.set(key, field); + cards[card].append(field.root); + } + const progressBar = el("progress", { className: "e07-progress", attrs: { max: "100" } }); + const progressText = el("p", { className: "e07-progress__text" }); + const progressBox = el("div", { className: "e07-progress-box" }); + progressBox.append(progressBar, progressText); + progressBox.hidden = true; + const defaultsButton = createButton({ + label: t("E07_Defaults"), + variant: "ghost", + onClick: () => overview && write(overview.defaults), + }); + const analyzeButton = createButton({ + label: t("E07_Analyze"), + variant: "filled", + onClick: () => void analyze(false), + }); + const reanalyzeButton = createButton({ + label: t("E07_Reanalyze"), + variant: "ghost", + onClick: () => void analyze(true), + }); + const actions = el("div", { className: "ui-sidebar-actions" }); + actions.append(defaultsButton, reanalyzeButton, analyzeButton); + const readOnly = el("p", { className: "e07-hint", text: t("E07_ReadOnly") }); + readOnly.hidden = true; + panel.append( + el("p", { className: "e07-hint", text: t("E07_Hint") }), + readOnly, + cards.soil, + cards.rain, + cards.band, + sourceHint, + progressBox, + actions, + ); + attachCollapsible(panel); + + // ---- ์ƒํƒœ ---- + let running = false; + + function read(): Settings { + const out: Settings = { material_id: material.select.value || null }; + out.scenario = scenario.select.value; + for (const [key, field] of fields) out[key] = Number(field.input.value); + return out; + } + + function write(values: Settings): void { + for (const [key, field] of fields) field.input.value = String(values[key] ?? ""); + material.select.value = String(values.material_id ?? ""); + scenario.select.value = SCENARIOS.includes(String(values.scenario)) + ? String(values.scenario) + : SCENARIOS[0]; + paintScenario(); + remember(); + } + + function paintScenario(): void { + const wet = scenario.select.value === "sinmap"; + for (const key of ["tr_lo", "tr_hi"]) fields.get(key)!.root.hidden = !wet; + map.setBand(Number(fields.get("band_m")!.input.value) || 100); + } + + const remember = (): void => sessionStorage.setItem(cacheKey(projectId), JSON.stringify(read())); + panel.addEventListener("input", remember); + scenario.select.addEventListener("change", () => { + paintScenario(); + remember(); + }); + fields.get("band_m")!.input.addEventListener("input", paintScenario); + material.select.addEventListener("change", () => { + const m = overview?.materials.find((x) => x.id === material.select.value); + if (!m) return; + const set = (key: string, v: number) => (fields.get(key)!.input.value = String(v)); + const rangeC = Number(overview!.defaults.c_hi) - Number(overview!.defaults.c); + const rangePhi = Number(overview!.defaults.phi_hi) - Number(overview!.defaults.phi); + set("gamma_t", m.gamma_t); + set("gamma_sat", m.gamma_sat); + set("c", m.c); + set("c_hi", m.c + rangeC); + set("phi", m.phi); + set("phi_hi", m.phi + rangePhi); + remember(); + }); + + function paintButtons(): void { + const editable = Boolean(overview?.editable); + analyzeButton.disabled = reanalyzeButton.disabled = !editable || running || !overview; + readOnly.hidden = !overview || editable; + } + + function paintResult(): void { + const result = overview?.result; + if (!result) { + resultBody.replaceChildren(el("p", { className: "e07-empty", text: t("E07_Result_None") })); + return; + } + const s = result.summary; + const list = el("dl", { className: "e07-summary" }); + const pairs: [LocaleKey, string][] = [ + ["E07_Scenario", scenarioName(result.settings.scenario)], + ["E07_Sum_FsMin", s.fs_min === null ? "-" : s.fs_min.toFixed(3)], + ["E07_Sum_Risk", `${Math.round(s.risk_area_m2).toLocaleString()} mยฒ`], + [ + "E07_Sum_Levels", + `${LEVEL_KEYS.map((k) => `${t(`E07_Level_${k}` as LocaleKey)} ${s.station_levels[k]}`).join(" ยท ")} / ${s.stations}`, + ], + [ + "E07_Sum_Covered", + `${Math.round(s.covered_area_m2).toLocaleString()} / ${Math.round(s.band_area_m2).toLocaleString()} mยฒ`, + ], + [ + "E07_Sum_Time", + `${result.elapsed_s.toFixed(1)} s ยท ${result.computed_at.replace("T", " ")}`, + ], + ]; + for (const [key, value] of pairs) + list.append( + el("dt", { text: t(key) }), + el("dd", { text: value, attrs: { "data-field": key } }), + ); + const areas = el("table", { className: "e07-table e07-areas" }); + const total = Object.values(s.areas_m2).reduce((a, b) => a + b, 0) || 1; + areas.append( + el("tr", { + children: [ + el("th", { text: t("E07_Col_Class") }), + el("th", { text: t("E07_Col_Area") }), + el("th", { text: "%" }), + ], + }), + ...CLASS_KEYS.map((k, i) => { + const name = el("td"); + name.append( + swatch(CLASS_COLORS[i]), + el("span", { text: t(`E07_Class_${k}` as LocaleKey) }), + ); + return el("tr", { + attrs: { "data-class": k }, + children: [ + name, + el("td", { text: Math.round(s.areas_m2[k] ?? 0).toLocaleString() }), + el("td", { text: (((s.areas_m2[k] ?? 0) / total) * 100).toFixed(1) }), + ], + }); + }), + ); + const table = el("table", { className: "e07-table e07-stations" }); + table.append( + el("tr", { + children: [ + el("th", { text: t("E07_Col_Station") }), + el("th", { text: t("E07_Col_FsMin") }), + el("th", { text: t("E07_Col_Ratio") }), + el("th", { text: t("E07_Col_Level") }), + el("th", { text: t("E07_Col_RiskArea") }), + ], + }), + ...result.stations.map((r) => { + const level = el("td"); + if (r.level) + level.append( + swatch(LEVEL_COLORS[r.level]), + el("span", { text: t(`E07_Level_${r.level}` as LocaleKey) }), + ); + const tr = el("tr", { + className: r.level === "high" ? "is-risk" : "", + attrs: { "data-chainage": String(r.chainage_m) }, + children: [ + el("td", { text: shortLabel(r.label, r.chainage_m) }), + el("td", { text: r.fs_min === undefined ? "-" : r.fs_min.toFixed(2) }), + el("td", { + text: r.risk_ratio === undefined ? "-" : (r.risk_ratio * 100).toFixed(1), + }), + level, + el("td", { + text: + r.risk_area_m2 === undefined ? "-" : Math.round(r.risk_area_m2).toLocaleString(), + }), + ], + }); + tr.addEventListener("click", () => { + table.querySelectorAll("tr.is-on").forEach((x) => x.classList.remove("is-on")); + tr.classList.add("is-on"); + map.focus(r.chainage_m); + }); + return tr; + }), + ); + const notes: HTMLElement[] = []; + if (result.stale) notes.push(el("p", { className: "e07-warn", text: t("E07_Result_Stale") })); + notes.push(el("p", { className: "e07-note", text: t("E07_Result_Note") })); + resultBody.replaceChildren( + list, + el("h4", { text: t("E07_Areas_Title") }), + areas, + el("h4", { text: t("E07_Stations_Title") }), + table, + ...notes, + ); + } + + async function refresh(): Promise { + overview = await api(`/projects/${projectId}/stab/area`); + material.setOptions( + overview.materials.map((m) => ({ value: m.id, text: m.name })), + String(overview.defaults.material_id ?? ""), + ); + map.setResult(overview.result ? decode(overview.result) : null); + paintButtons(); + paintResult(); + paintLegend(); + } + + async function poll(): Promise { + while (root.isConnected) { + const p = await api(`/projects/${projectId}/stab/area/progress`).catch(() => null); + if (!p) break; + running = p.running; + progressBox.hidden = !p.running; + progressBar.value = p.percent; + progressText.textContent = `${t("E07_Running")} ${p.percent}% ยท ${p.message ?? ""}`; + paintButtons(); + if (!p.running) { + if (p.error) showToast(p.error, "error"); + else showToast(t("E07_Done"), "success"); + await refresh(); + return; + } + await new Promise((resolve) => setTimeout(resolve, 500)); + } + } + + async function analyze(force: boolean): Promise { + try { + const body = await api<{ status: string }>(`/projects/${projectId}/stab/area/analyze`, { + method: "POST", + body: JSON.stringify({ ...read(), force }), + }); + if (body.status === "cached") { + showToast(t("E07_Cached"), "info"); + await refresh(); + return; + } + running = true; + paintButtons(); + progressBox.hidden = false; + void poll(); + } catch (error) { + showToast(error instanceof Error ? error.message : String(error), "error"); + } + } + + // ---- ์ฒ˜์Œ ---- + paintButtons(); + void (async () => { + try { + await refresh(); + } catch (error) { + main.replaceChildren( + el("p", { + className: "e07-empty", + text: error instanceof Error ? error.message : String(error), + }), + ); + return; + } + const ov = overview!; + const cached = JSON.parse( + sessionStorage.getItem(cacheKey(projectId)) ?? "null", + ) as Settings | null; + // ์˜› ์กฐ๊ฑด(๋น ์ง„ ์นธ ยท ์—†์–ด์ง„ ์‹œ๋‚˜๋ฆฌ์˜ค)์€ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์ฑ„์›€ + write({ ...ov.defaults, ...(cached ?? (ov.result?.settings as Settings | undefined)) }); + map.setRoute(ov.line, ov.stations); + const p = await api(`/projects/${projectId}/stab/area/progress`).catch(() => null); + if (p?.running) void poll(); + })(); + + const leave = new MutationObserver(() => { + if (root.isConnected && main.isConnected) return; + leave.disconnect(); + map.dispose(); + }); + leave.observe(document.body, { childList: true, subtree: true }); +} diff --git a/E07_StabArea/E07_StabArea_UI_Style.css b/E07_StabArea/E07_StabArea_UI_Style.css new file mode 100644 index 000000000..55436dfab --- /dev/null +++ b/E07_StabArea/E07_StabArea_UI_Style.css @@ -0,0 +1,236 @@ +/* ============================================================================= + * E07_StabArea_UI_Style.css + * E07 ๊ด‘์—ญ ์œ„ํ—˜ ์ง€๋„ โ€” ์ขŒ์ธก ์กฐ๊ฑด ยท ์ง„ํ–‰ / ๊ฐ€์šด๋ฐ ์ง€๋„ ยท ๋ฒ”๋ก€ / ์šฐ์ธก ์š”์•ฝ ยท ์ธก์  ํ‘œ + * ์ƒ‰์€ ํ…Œ๋งˆ ํ† ํฐ๋งŒ(๋ฐ์Œ ยท ์–ด๋‘์›€ ๊ฐ™์ด) ยท ๋“ฑ๊ธ‰ ยท ์•ˆ์ „์œจ ์ƒ‰๋งŒ logic ๊ณ ์ •์ƒ‰ + * ========================================================================== */ + +.e07-page { + height: 100dvh; + box-sizing: border-box; +} + +.e07-row { + height: 100%; +} + +.e07-center { + display: flex; + flex-direction: column; + gap: var(--spacing-8); + height: 100%; + min-height: 0; + padding: var(--spacing-12) 0 var(--spacing-12) var(--spacing-12); + box-sizing: border-box; +} + +.e07-toggle { + display: inline-flex; + gap: var(--spacing-4); +} + +.e07-toggle .ui-btn.is-on { + border-color: var(--color-primary); + background: var(--color-primary); + color: var(--color-primary-text); +} + +.e07-stage { + position: relative; + flex: 1 1 auto; + min-height: 320px; + border-radius: var(--radius-sm); + background: var(--color-surface-raised); + overflow: hidden; +} + +.e07-map { + position: absolute; + inset: 0; +} + +.e07-map__canvas { + display: block; + width: 100%; + height: 100%; + touch-action: none; + cursor: grab; +} + +.e07-map__tip { + position: absolute; + padding: var(--spacing-4) var(--spacing-8); + border-radius: var(--radius-sm); + background: var(--color-surface); + color: var(--color-text); + border: 1px solid var(--color-border); + font-size: 12px; + white-space: pre; + pointer-events: none; +} + +.e07-map__empty { + position: absolute; + inset: auto 0 45% 0; + margin: 0; + text-align: center; + color: var(--color-text-muted); + pointer-events: none; +} + +.e07-legend-slot { + position: absolute; + left: var(--spacing-8); + bottom: var(--spacing-8); + pointer-events: none; +} + +.e07-legend { + display: flex; + flex-direction: column; + gap: 2px; + padding: var(--spacing-8); + border-radius: var(--radius-sm); + background: var(--color-surface); + color: var(--color-text); + border: 1px solid var(--color-border); + font-size: 12px; +} + +.e07-legend__sub { + max-width: 180px; + color: var(--color-text-muted); +} + +.e07-legend__row { + display: flex; + align-items: center; + gap: var(--spacing-8); +} + +.e07-chip { + display: inline-block; + width: 14px; + height: 10px; + flex: none; + margin-right: var(--spacing-4); + border-radius: 2px; + border: 1px solid var(--color-border); + vertical-align: middle; +} + +/* ์ขŒ์ธก */ +.e07-hint, +.e07-note { + margin: 0 0 var(--spacing-8); + color: var(--color-text-muted); + font-size: 12px; + line-height: 1.5; +} + +.e07-panel .ui-field[hidden] { + display: none; +} + +.e07-progress-box { + display: flex; + flex-direction: column; + gap: var(--spacing-4); + margin: var(--spacing-8) 0; +} + +.e07-progress-box[hidden] { + display: none; +} + +.e07-progress { + width: 100%; +} + +.e07-progress__text { + margin: 0; + color: var(--color-text); + font-size: 12px; +} + +/* ์šฐ์ธก */ +.e07-result { + padding: var(--spacing-12); + color: var(--color-text); +} + +.e07-result h4 { + margin: var(--spacing-16) 0 var(--spacing-8); + font-size: 13px; +} + +.e07-summary { + display: grid; + grid-template-columns: auto 1fr; + gap: var(--spacing-8) var(--spacing-12); + margin: 0; + font-size: 13px; +} + +.e07-summary dt { + color: var(--color-text-muted); +} + +.e07-summary dd { + margin: 0; + font-weight: 600; + text-align: right; +} + +.e07-table { + width: 100%; + border-collapse: collapse; + font-size: 12px; +} + +.e07-table th, +.e07-table td { + padding: var(--spacing-4) var(--spacing-8); + border-bottom: 1px solid var(--color-border); + text-align: right; + white-space: nowrap; +} + +.e07-table th:first-child, +.e07-table td:first-child, +.e07-stations td:nth-child(4) { + text-align: left; +} + +.e07-table th { + color: var(--color-text-muted); + font-weight: 600; +} + +.e07-stations tr[data-chainage] { + cursor: pointer; +} + +.e07-stations tr[data-chainage]:hover { + background: var(--color-surface-raised); +} + +.e07-stations tr.is-risk td { + color: var(--color-danger); + font-weight: 600; +} + +.e07-stations tr.is-on { + outline: 2px solid var(--color-primary); + outline-offset: -2px; +} + +.e07-warn { + margin: var(--spacing-12) 0 0; + color: var(--color-danger); + font-size: 12px; +} + +.e07-empty { + margin: var(--spacing-16); + color: var(--color-text-muted); +} diff --git a/E07_StabArea/draft/E07_StabArea_Route.ts b/E07_StabArea/draft/E07_StabArea_Route.ts new file mode 100644 index 000000000..08b2db164 --- /dev/null +++ b/E07_StabArea/draft/E07_StabArea_Route.ts @@ -0,0 +1,16 @@ +/* ============================================================================= + * E07_StabArea_Route.ts + * ๊ธธ ์„ ์–ธ(PLAN 536-2) โ€” ๋ผ์šฐํ„ฐ๊ฐ€ ํŽ˜์ด์ง€ ํด๋”๋งˆ๋‹ค draft/*_Route.ts ๋ฅผ glob ํ•ด ๋“ฑ๋ก. + * ๋กœ๊ทธ์ธ ยท ํšŒ์‚ฌ ยท ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ํ•„์š” ยท ๊ผฌ๋ฆฌ๋ง ์—†์Œ ยท ๋ณธ๋ฌธ = E07 ๊ด‘์—ญ ์œ„ํ—˜ ์ง€๋„ ํŽ˜์ด์ง€ + * ========================================================================== */ + +import { ROUTES } from "@config/config_frontend"; +import type { DraftRoute } from "../../A00_Common/router"; + +export const ROUTE: DraftRoute = { + path: ROUTES.E07_STAB_AREA, + workflow: true, + project: true, + footerless: true, + load: async () => (await import("../E07_StabArea_UI_Page")).renderE07StabArea, +}; diff --git a/E07_StabArea/logic/E07_StabArea_Raster.ts b/E07_StabArea/logic/E07_StabArea_Raster.ts new file mode 100644 index 000000000..88ad25692 --- /dev/null +++ b/E07_StabArea/logic/E07_StabArea_Raster.ts @@ -0,0 +1,164 @@ +/* ============================================================================= + * E07_StabArea_Raster.ts + * E07 ๊ด‘์—ญ ์œ„ํ—˜ ์ง€๋„ ํ™”๋ฉด ๊ณ„์‚ฐ(PLAN 536-2) โ€” ์ €์žฅ๋ณธ ๊ฒฉ์ž ํ’€๊ธฐ ยท ๋“ฑ๊ธ‰ ยท ์•ˆ์ „์œจ ์ƒ‰ ยท RGBA ยท ์…€ ์ฐพ๊ธฐ. + * ๊ฒฉ์ž ๊ผด = ์„œ๋ฒ„ `E07_StabArea_Router.py` ๋จธ๋ฆฌ ์ฃผ์„(fs ยท si uint16 ร—1000 ยท cls uint8 ยท slope ร—10 ยท + * m ร—100 ยท ์ž‘์€ ๋ base64 ยท ํ–‰ 0 = ๋‚จ์ชฝ). ์ˆœ์ˆ˜ ํ•จ์ˆ˜๋งŒ(DOM ยท fetch ๊ธˆ์ง€). + * ========================================================================== */ + +export interface Grid { + x0: number; + y0: number; + dx: number; + nx: number; + ny: number; +} + +export type LevelKey = "low" | "mid" | "high"; + +export interface StationRow { + chainage_m: number; + label: string | null; + cells: number; + fs_min?: number; + si_min?: number; + worst_class?: number; + risk_area_m2?: number; + risk_ratio?: number; + level?: LevelKey; + slope_max_deg?: number; +} + +export interface AreaSummary { + areas_m2: Record; + band_area_m2: number; + covered_area_m2: number; + risk_area_m2: number; + fs_min: number | null; + station_levels: Record; + stations: number; +} + +export interface AreaResult { + stale?: boolean; + computed_at: string; + elapsed_s: number; + settings: Record; + grid: Grid; + fs: string; + si: string; + cls: string; + slope: string; + m: string; + stations: StationRow[]; + summary: AreaSummary; +} + +/** ํ’€์–ด ๋‘” ๊ฒฉ์ž โ€” ์ž๋ฃŒ ์—†์Œ = NaN(fs ยท si ยท slope ยท m) ยท 0(cls) */ +export interface Decoded { + grid: Grid; + fs: Float32Array; + si: Float32Array; + cls: Uint8Array; + slope: Float32Array; + m: Float32Array; +} + +/** ๋“ฑ๊ธ‰ ์—ด์‡ (์„œ๋ฒ„ CLASS_KEYS ์ฐจ๋ก€ ยท 1~6) */ +export const CLASS_KEYS = ["stable", "moderate", "quasi", "lower", "upper", "defended"] as const; +export const RISK_CLASS = 4; +/** ์ธก์  ๋“ฑ๊ธ‰(์„œ๋ฒ„ LEVEL_KEYS ์ฐจ๋ก€) ยท ์ƒ‰ */ +export const LEVEL_KEYS: readonly LevelKey[] = ["low", "mid", "high"]; +export const LEVEL_COLORS: Record = { + low: "#1a9850", + mid: "#fee08b", + high: "#d73027", +}; + +/** ๋“ฑ๊ธ‰ ์ƒ‰ 1~6 โ€” SINMAP ๊ด€๋ก€(์ดˆ๋ก โ†’ ๋นจ๊ฐ•) */ +export const CLASS_COLORS = ["#1a9850", "#91cf60", "#d9ef8b", "#fee08b", "#fc8d59", "#d73027"]; + +/** ์•ˆ์ „์œจ ์ƒ‰ ๋‹จ๊ณ„ โ€” 1.0 ยท 1.2 ยท 1.5 ๊ฒฝ๊ณ„ */ +export const FS_STEPS: readonly { below: number; color: string }[] = [ + { below: 1.0, color: "#a50026" }, + { below: 1.2, color: "#f46d43" }, + { below: 1.5, color: "#fee08b" }, + { below: 2.0, color: "#a6d96a" }, + { below: Infinity, color: "#1a9850" }, +]; + +function bytes(text: string): Uint8Array { + const raw = atob(text); + const out = new Uint8Array(raw.length); + for (let i = 0; i < raw.length; i += 1) out[i] = raw.charCodeAt(i); + return out; +} + +function u16(text: string, scale: number): Float32Array { + const b = bytes(text); + const view = new DataView(b.buffer); + const out = new Float32Array(b.length / 2); + for (let i = 0; i < out.length; i += 1) { + const v = view.getUint16(i * 2, true); + out[i] = v === 65535 ? NaN : v / scale; + } + return out; +} + +export function decode(result: AreaResult): Decoded { + const m = bytes(result.m); + return { + grid: result.grid, + fs: u16(result.fs, 1000), + si: u16(result.si, 1000), + cls: bytes(result.cls), + slope: u16(result.slope, 10), + m: Float32Array.from(m, (v) => (v === 255 ? NaN : v / 100)), + }; +} + +function rgb(hex: string): [number, number, number] { + const n = Number.parseInt(hex.slice(1), 16); + return [(n >> 16) & 255, (n >> 8) & 255, n & 255]; +} + +export function fsColor(value: number): string { + return (FS_STEPS.find((s) => value < s.below) ?? FS_STEPS[FS_STEPS.length - 1]).color; +} + +/** ๊ฒฉ์ž โ†’ RGBA(์ค„ 0 = ๋ถ์ชฝ ๋ ยท ์บ”๋ฒ„์Šค ๊ทธ๋ฆผ์šฉ) ยท ์ž๋ฃŒ ์—†์Œ = ํˆฌ๋ช… */ +export function rasterRgba(data: Decoded, layer: "cls" | "fs"): Uint8ClampedArray { + const { nx, ny } = data.grid; + const out = new Uint8ClampedArray(nx * ny * 4); + const classRgb = CLASS_COLORS.map(rgb); + const fsRgb = FS_STEPS.map((s) => rgb(s.color)); + for (let iy = 0; iy < ny; iy += 1) { + const row = ny - 1 - iy; + for (let ix = 0; ix < nx; ix += 1) { + const i = iy * nx + ix; + const k = data.cls[i]; + if (!k) continue; + const j = FS_STEPS.findIndex((s) => data.fs[i] < s.below); + const color = layer === "cls" ? classRgb[k - 1] : fsRgb[j < 0 ? fsRgb.length - 1 : j]; + const at = (row * nx + ix) * 4; + out[at] = color[0]; + out[at + 1] = color[1]; + out[at + 2] = color[2]; + out[at + 3] = 255; + } + } + return out; +} + +/** ๋ชจ๋ธ ์ขŒํ‘œ โ†’ ์…€ ๋ฒˆํ˜ธ(๊ฒฉ์ž ๋ฐ– = -1) */ +export function cellIndex(grid: Grid, x: number, y: number): number { + const ix = Math.round((x - grid.x0) / grid.dx); + const iy = Math.round((y - grid.y0) / grid.dx); + return ix < 0 || iy < 0 || ix >= grid.nx || iy >= grid.ny ? -1 : iy * grid.nx + ix; +} + +/** ๋“ฑ๊ธ‰๋ณ„ ์…€ ์ˆ˜(1~6) โ€” ๋ฒ”๋ก€ ยท ๊ฒ€์ฆ์šฉ */ +export function classCounts(data: Decoded): number[] { + const out = [0, 0, 0, 0, 0, 0]; + for (const k of data.cls) if (k) out[k - 1] += 1; + return out; +} diff --git a/common_util/common_util_help_router.py b/common_util/common_util_help_router.py index f08a44ba8..6960521b7 100644 --- a/common_util/common_util_help_router.py +++ b/common_util/common_util_help_router.py @@ -14,7 +14,7 @@ from fastapi import APIRouter, HTTPException router = APIRouter(prefix="/api/help", tags=["help"]) HELP_ROOT = Path(__file__).resolve().parent.parent / "resources" / "help" -PAGE_RE = re.compile(r"^(B0[2-9]|D0[1-4]|E0[1-5])$") +PAGE_RE = re.compile(r"^(B0[2-9]|D0[1-4]|E0[1-7])$") def read_help(page: str, root: Path = HELP_ROOT) -> list[dict[str, str]]: diff --git a/common_util/common_util_print_pdf.py b/common_util/common_util_print_pdf.py index af885cb90..0961e2a5f 100644 --- a/common_util/common_util_print_pdf.py +++ b/common_util/common_util_print_pdf.py @@ -58,6 +58,12 @@ def _pdf_options(job: dict[str, Any]) -> dict[str, Any]: options.update( display_header_footer=True, header_template="", footer_template=FOOTER ) + if job.get("header_template") or job.get("footer_template"): # ๋จธ๋ฆฌ๋ง ยท ๊ผฌ๋ฆฌ๋ง HTML(E05) + options.update( + display_header_footer=True, + header_template=job.get("header_template") or "", + footer_template=job.get("footer_template") or "", + ) return options @@ -102,7 +108,9 @@ def render_htmls_to_pdfs(jobs: list[dict[str, Any]], timeout_s: float = TIMEOUT_ """HTML ์—ฌ๋Ÿฌ ๋ฒŒ โ†’ PDF ์—ฌ๋Ÿฌ ๋ฒŒ(์ฐจ๋ก€ ๊ทธ๋Œ€๋กœ ยท ์ž์‹ ํ”„๋กœ์„ธ์Šค ํ•œ ๋ฒˆ). job = {html, page_size="A4", landscape=False, margins=None, page_numbers=False, - prepare_js=None, prepare_arg=None} โ€” prepare_js = PDF ์ง์ „ page.evaluate(์ค„์ž„ ๋“ฑ). + prepare_js=None, prepare_arg=None, header_template=None, footer_template=None} โ€” + prepare_js = PDF ์ง์ „ page.evaluate(์ค„์ž„ ๋“ฑ) ยท header/footer_template = ์ชฝ๋งˆ๋‹ค ๋จธ๋ฆฌ๋ง ยท ๊ผฌ๋ฆฌ๋ง + HTML(Playwright `pageNumber` ยท `totalPages` ์นธ ยท ์ฃผ๋ฉด page_numbers ๋Œ€์‹ ). """ jobs = [{"page_size": "A4", **job} for job in jobs] with _lock, tempfile.TemporaryDirectory() as tmp: diff --git a/common_util/common_util_project_copy.py b/common_util/common_util_project_copy.py index 12783a998..4cfe2e059 100644 --- a/common_util/common_util_project_copy.py +++ b/common_util/common_util_project_copy.py @@ -27,6 +27,7 @@ from uuid import uuid4 import aiomysql +from B05_Profile.B05_Profile_Engine_Sections_Names import find_cross_file from common_util.common_util_storage import resolve_stored_project_path from common_util.common_util_surface_confirmation import get_surface_confirmation_params @@ -42,6 +43,19 @@ _STABILITY_TREES = ( "B06_Section/cross_sections", "B04_PreProcess/drainage/edits", ) +_CROSS_DIR = "B06_Section/cross_sections" + + +def resolve_cross_file(root: Path, row: dict[str, Any]) -> Path | None: + """ํšก๋‹จ ํ–‰ โ†’ root ์•„๋ž˜ ํšก๋‹จ ํŒŒ์ผ โ€” ๊ฒฝ๋กœ ์นธ์ด ์—†๊ฑฐ๋‚˜(null) ยท ํŒŒ์ผ์ด ์—†๊ฑฐ๋‚˜ ยท ์ ˆ๋Œ€ ๊ฒฝ๋กœ(์˜› ๊ผด)๋ฉด + B06 ์ด๋ฆ„ ๊ทœ์น™(`find_cross_file` ยท ์˜› ์ •์ˆ˜ m ์ด๋ฆ„ ํฌํ•จ)์œผ๋กœ ์ฐพ์Œ ยท ๊ทธ๋ž˜๋„ ์—†์œผ๋ฉด None.""" + relative = row.get("cross_section_file_path") + if relative and not Path(relative).is_absolute() and (root / relative).is_file(): + return root / relative + if row.get("chainage_m") is None: + return None + found = find_cross_file(root / _CROSS_DIR, float(row["chainage_m"])) + return found if found.is_file() else None async def _rows(cursor, sql, args): @@ -424,6 +438,11 @@ async def copy_stability(connection, source_id: str, new_id: str, project_root: digest.update(STABILITY_DB_NAME.encode("utf-8") + b"\0" + db_text.encode("utf-8")) (target / STABILITY_DB_NAME).write_text(db_text, encoding="utf-8") sizes[STABILITY_DB_NAME] = len(db_text.encode("utf-8")) + missing = sorted( + float(row["chainage_m"] or 0.0) + for row in dump["cross_sections"] + if resolve_cross_file(target, row) is None + ) manifest = { "scope": "stability", "source_project_id": source_id, @@ -437,6 +456,9 @@ async def copy_stability(connection, source_id: str, new_id: str, project_root: "longitudinal_sections": len(dump["longitudinal_sections"]), }, "uuid_replaced": replaced, + #: ํšก๋‹จ ํŒŒ์ผ์„ ๋ชป ์ฐพ์€ ์ธก์ (๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ) โ€” E ๋Š” ๊ทธ ์ธก์ ์„ ใ€Œํšก๋‹จ ์—†์Œใ€์œผ๋กœ ๊ฑด๋„ˆ๋œ€ + "missing_cross_chainages": missing, + "warnings": [f"ํšก๋‹จ ์—†๋Š” ์ธก์  {len(missing)} ๊ฐœ โ€” ํ•ด์„์—์„œ ๋น ์ง"] if missing else [], "total_bytes": sum(sizes.values()), "elapsed_s": round(time.perf_counter() - started, 2), "files": sizes, diff --git a/common_util/common_util_project_kind.py b/common_util/common_util_project_kind.py index d4d4e997d..e09facfbe 100644 --- a/common_util/common_util_project_kind.py +++ b/common_util/common_util_project_kind.py @@ -1,29 +1,41 @@ """์ข…๋ฅ˜๋ณ„ ํ”„๋กœ์ ํŠธ ํ–‰ ๋งŒ๋“ค๊ธฐ โ€” D ์˜ˆ์ •๋…ธ์„  ยท E ์•ˆ์ •์„ฑํ‰๊ฐ€(์š”๊ตฌ 345). -์ž„๋„์„ค๊ณ„(ROAD)๋Š” B02 ๊ทธ๋Œ€๋กœ. ์ €์žฅ์†Œ ๊ฒฝ๋กœ ยท ์ดˆ๊ธฐ ํด๋”๋Š” B02 ํ•จ์ˆ˜๋ฅผ ๊ทธ๋Œ€๋กœ ์”€. +์ž„๋„์„ค๊ณ„(ROAD)๋Š” B02 ๊ทธ๋Œ€๋กœ. ์ €์žฅ์†Œ ๊ฒฝ๋กœ๋Š” B02 ํ•จ์ˆ˜๋ฅผ ์”€ ยท ์ดˆ๊ธฐ ํด๋”๋Š” ์ข…๋ฅ˜ ๋‹จ๊ณ„๋งŒ +(B ์–‘์‹ ๋ณต์‚ฌ ยท B0x ๋นˆ ํด๋” ์—†์Œ ยท D ๋Š” ์ง€ํ˜• ๋ฐฐ์น˜ B04_PreProcess ๋”ํ•จ). """ from __future__ import annotations from datetime import datetime +from pathlib import Path from typing import Any from uuid import uuid4 -from B02_ProjRegister.B02_ProjRegister_Repository import ( - _build_project_storage, - _initialize_project_storage, -) +from B02_ProjRegister.B02_ProjRegister_Repository import _build_project_storage from common_util.common_util_audit import record_audit from common_util.common_util_storage import get_project_stage_path from common_util.common_util_workflow_state import initialize_project_stages from config.config_db import get_db_pool KIND_STAGE_DIRS = { - "PLAN": ("D02_PlanBase", "D03_PlanAnalysis", "D04_PlanConfirm"), - "STABILITY": ("E02_StabGround", "E03_StabSection", "E04_Stab3D", "E05_StabReport"), + "PLAN": ("D02_PlanBase", "D03_PlanAnalysis", "D04_PlanConfirm", "B04_PreProcess"), + "STABILITY": ( + "E02_StabGround", + "E03_StabSection", + "E04_Stab3D", + "E05_StabReport", + "E06_StabRock", + "E07_StabArea", + ), } +def init_kind_storage(project_root: Path, kind: str) -> None: + """์ข…๋ฅ˜ ๋‹จ๊ณ„ ํด๋”๋งŒ โ€” B ์–‘์‹ ยท B0x ๋นˆ ํด๋” ยท workflow ยท manifest ์—†์Œ.""" + for stage_dir in KIND_STAGE_DIRS[kind]: + get_project_stage_path(str(project_root), stage_dir) + + async def create_kind_project( *, request: Any | None, @@ -92,9 +104,7 @@ async def create_kind_project( resource_ref=project_id, request=request, ) - _initialize_project_storage(project_root, project_id) - for stage_dir in KIND_STAGE_DIRS[kind]: - get_project_stage_path(str(project_root), stage_dir) + init_kind_storage(project_root, kind) await connection.commit() except Exception: await connection.rollback() diff --git a/common_util/common_util_storage.py b/common_util/common_util_storage.py index 59b65ffae..1d006136e 100644 --- a/common_util/common_util_storage.py +++ b/common_util/common_util_storage.py @@ -20,6 +20,8 @@ PROJECT_STORAGE_LAYOUT_V2 = ( ("B08_Quantity", "quantities"), ("B09_Estimation", "v1"), ) +#: D ยท E ํ”„๋กœ์ ํŠธ ํ‘œ์ง€ โ€” ์ƒ์„ฑ ๋•Œ ๋งŒ๋“œ๋Š” ๋‹จ๊ณ„ ํด๋”(common_util_project_kind) +KIND_STAGE_MARKERS = tuple(sorted(s for s in PROJECT_STORAGE_STAGE_DIRS if s[0] in "DE")) def get_project_stage_path(project_root: str, stage: str) -> str: @@ -37,8 +39,13 @@ def get_project_stage_path(project_root: str, stage: str) -> str: def ensure_project_storage_layout(project_root: str) -> None: - """์‹ ๊ทœ ์›Œํฌํ”Œ๋กœ์šฐ ์ €์žฅ์†Œ ํ•˜์œ„ ํด๋”๋ฅผ ๋ˆ„๋ฝ๋ถ„๋งŒ ์ƒ์„ฑํ•œ๋‹ค.""" + """์‹ ๊ทœ ์›Œํฌํ”Œ๋กœ์šฐ ์ €์žฅ์†Œ ํ•˜์œ„ ํด๋”๋ฅผ ๋ˆ„๋ฝ๋ถ„๋งŒ ์ƒ์„ฑํ•œ๋‹ค. + + D ยท E ํ”„๋กœ์ ํŠธ(D0x ยท E0x ๋‹จ๊ณ„ ํด๋”๊ฐ€ ์žˆ๋Š” ๋ฃจํŠธ)๋Š” B ํด๋”๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์Œ โ€” ์“ฐ๋Š” ๊ณณ์ด ์Šค์Šค๋กœ ๋งŒ๋“ฆ. + """ root = os.path.abspath(project_root) + if any(os.path.isdir(os.path.join(root, stage)) for stage in KIND_STAGE_MARKERS): + return for stage, subdir in PROJECT_STORAGE_LAYOUT_V2: if stage not in PROJECT_STORAGE_STAGE_DIRS: raise ValueError(f"ํ—ˆ์šฉ๋˜์ง€ ์•Š์€ ํ”„๋กœ์ ํŠธ ์ €์žฅ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค: {stage}") diff --git a/common_util/common_util_workflow_state.py b/common_util/common_util_workflow_state.py index 3c0c4c7d5..b6bb4f882 100644 --- a/common_util/common_util_workflow_state.py +++ b/common_util/common_util_workflow_state.py @@ -20,11 +20,11 @@ STAGE_KEYS = [ ] -# ์ข…๋ฅ˜๋ณ„ ๋‹จ๊ณ„(์š”๊ตฌ 345) โ€” ROAD = B03~B09 ยท PLAN = D02~D04 ยท STABILITY = E02~E05. +# ์ข…๋ฅ˜๋ณ„ ๋‹จ๊ณ„(์š”๊ตฌ 345) โ€” ROAD = B03~B09 ยท PLAN = D02~D04 ยท STABILITY = E02~E06. STAGE_KEYS_BY_KIND = { "ROAD": STAGE_KEYS, "PLAN": ["PLAN_BASE", "PLAN_ANALYSIS", "PLAN_CONFIRM"], - "STABILITY": ["STAB_GROUND", "STAB_SECTION", "STAB_3D", "STAB_REPORT"], + "STABILITY": ["STAB_GROUND", "STAB_SECTION", "STAB_3D", "STAB_REPORT", "STAB_ROCK"], } diff --git a/config/config_frontend.ts b/config/config_frontend.ts index 9d295ec1c..e256ad5e0 100644 --- a/config/config_frontend.ts +++ b/config/config_frontend.ts @@ -130,6 +130,8 @@ export const ROUTES = { E03_STAB_SECTION: "e03-stab-section", E04_STAB_3D: "e04-stab-3d", E05_STAB_REPORT: "e05-stab-report", + E06_STAB_ROCK: "e06-stab-rock", + E07_STAB_AREA: "e07-stab-area", } as const; /** D ์˜ˆ์ •๋…ธ์„  ๋‹จ๊ณ„ ์ฃผ์†Œ(D02 โ†’ D04 ยท ๋“ฑ๋ก D01 ์€ ๋‹จ๊ณ„ ๋ฐ–) */ @@ -139,12 +141,14 @@ export const PLAN_STEP_ROUTES = [ ROUTES.D04_PLAN_CONFIRM, ] as const; -/** E ์•ˆ์ •์„ฑํ‰๊ฐ€ ๋‹จ๊ณ„ ์ฃผ์†Œ(E02 โ†’ E05 ยท ์ƒ์„ฑ E01 ์€ ๋‹จ๊ณ„ ๋ฐ–) */ +/** E ์•ˆ์ •์„ฑํ‰๊ฐ€ ๋‹จ๊ณ„ ์ฃผ์†Œ(E02 โ†’ E06 ยท ๊ด‘์—ญ E07 ยท ์ƒ์„ฑ E01 ์€ ๋‹จ๊ณ„ ๋ฐ–) */ export const STAB_STEP_ROUTES = [ ROUTES.E02_STAB_GROUND, ROUTES.E03_STAB_SECTION, ROUTES.E04_STAB_3D, ROUTES.E05_STAB_REPORT, + ROUTES.E06_STAB_ROCK, + ROUTES.E07_STAB_AREA, ] as const; export type RouteKey = keyof typeof ROUTES; diff --git a/config/config_system.py b/config/config_system.py index 2fa29bb33..4c49991d3 100644 --- a/config/config_system.py +++ b/config/config_system.py @@ -220,6 +220,8 @@ PROJECT_STORAGE_STAGE_DIRS = frozenset( "E03_StabSection", "E04_Stab3D", "E05_StabReport", + "E06_StabRock", + "E07_StabArea", } ) diff --git a/docs/raw/plans/2026-10-25_plan_528-535.md b/docs/raw/plans/2026-10-25_plan_528-535.md new file mode 100644 index 000000000..8035c3a36 --- /dev/null +++ b/docs/raw/plans/2026-10-25_plan_528-535.md @@ -0,0 +1,63 @@ +# ๊ณ„ํš์„œ ์ด๊ด€ โ€” 528 ยท 529 ยท 530 ยท 534 ยท 535 (2026-10-25 ยท ์š”๊ตฌ 348 ยท ์™„๋ฃŒ ์ ˆ) + +## 528. ์‹ ๊ทœ ํ”„๋กœ์ ํŠธ ๊ทธ๋ฃน ๊ณตํ†ต โ€” ์ข…๋ฅ˜ ยท [+] ๋„ค ์นด๋“œ ยท ์‚ฐ๋ฆผ์ฒญ ยท ๊ณต์œ  ๋ณด๊ธฐ ยท ๋ณต์‚ฌ (์š”๊ตฌ 345 ยท 346 ยท ์š”๊ตฌ์„œ 345 2์žฅ ยท 528-1 ยท 3 ยท 5 sub1 ยท 528-2 sub2 ยท Opus high ยท 528-4 ๋’ค) + +- ์‚ฌ์šฉ์ž ํ™•์ •(์š”๊ตฌ 345 ยท 346): [+] = ์˜ˆ์ •๋…ธ์„  ยท ์ž„๋„์„ค๊ณ„ ยท ์‚ฌ๋ฐฉ์„ค๊ณ„ ยท ์•ˆ์ •์„ฑํ‰๊ฐ€ ์นด๋“œ(๊ทธ๋ฆผ + ์ œ๋ชฉ) ยท ์˜ˆ์ •๋…ธ์„  = ์‚ฐ๋ฆผ์ฒญ(*.go.kr) ยท ์‹œ์Šคํ…œ๊ด€๋ฆฌ์ž๋งŒ ยท ์ง€์ • ์—…์ฒด ๋ณด๊ธฐ๋งŒ ยท ์—ฐ๊ฒฐ ยท ์ƒ์„ฑ ์‹œ์  ๋ณต์‚ฌ ยท E ์—…๋ฐ์ดํŠธ ์—†์Œ. +- ์ •ํ•จ(๋ธŒ๋ ˆ์ธ ยท ์‚ฌ์šฉ์ž ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ): ์š”๊ตฌ์„œ 345 6์žฅ(์‚ฐ๋ฆผ์ฒญ = go.kr ์ „๋ถ€ ยท ๊ธฐ๊ด€๋„ ํšŒ์‚ฌ๋กœ ๋“ฑ๋ก ยท ์ง€์ • ํšŒ์‚ฌ ๊ตฌ์„ฑ์› ์ „๋ถ€ ๋ณด๊ธฐ ยท ๊ณต์œ  ํŒ์ •์€ ๊ณต์šฉ ์ ‘๊ทผ ํŒ์ • ํ•œ ๊ณณ). +- [x] 528-1 DB 020(projects ์ข…๋ฅ˜ ยท ์‚ฌ์—…์—ฐ๋„ ๋ ยท ์›๋ณธ ์นธ + ์˜ˆ์ •๋…ธ์„  ์—ฐ๋„ ๊ตฌ๊ฐ„ ยท ์ง€์ • ์—…์ฒด ํ‘œ) ยท ์‚ฐ๋ฆผ์ฒญ ํŒ์ • ํ•จ์ˆ˜ ยท B02 API ์ข…๋ฅ˜ ๊ฒ€์‚ฌ โ€” ์ง€ํ‚ด ์‹œํ—˜(ํŒ์ • ยท ์ผ๋ฐ˜ ์‚ฌ์šฉ์ž PLAN 403 ยท ์›๋ณธ B06 ๋ฏธ์™„๋ฃŒ 400). + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹œํ—˜ 19 ํ†ต๊ณผ ยท ๊ธฐ์กด B01 ยท ์ ‘๊ทผ ยท ๋กœ๊ทธ์ธ 63 ํ†ต๊ณผ ยท ์‹ค API: ์ผ๋ฐ˜ D01 403 ยท ์‚ฐ๋ฆผ์ฒญ PLAN(์†๋„ 20 ยท ๋‹จ๊ณ„ 3 ยท D ํด๋”) ยท E ์›๋ณธ B06 ๋ฏธ์™„๋ฃŒ 400 ยท ์™„๋ฃŒ = STABILITY ๋‹จ๊ณ„ 4 ยท ๊ณต์œ  DB ์— 020 ์ ์šฉ(๊ธฐ์กด 16ํ–‰ ROAD) +- [x] 528-2 [+] ๋„ค ์นด๋“œ ๋ชจ๋‹ฌ ยท B01 ์ข…๋ฅ˜ ์—ด ยท ์ข…๋ฅ˜๋ณ„ ์ฒซ ํ™”๋ฉด ยท ์ข…๋ฅ˜๋ณ„ ๋‹จ๊ณ„ ์‹œ๋“œ ยท storage ํด๋” ํ—ˆ์šฉ โ€” ORCA ์„ธ ์—ญํ• (์‚ฐ๋ฆผ์ฒญ ยท ์—…์ฒด ยท ์‹œ์Šคํ…œ๊ด€๋ฆฌ์ž). + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹ค API ์„ธ์…˜ ์‚ฐ๋ฆผ์ฒญ(go.kr ์ž„์‹œ ๊ณ„์ •) ยท ์‹œ์Šคํ…œ๊ด€๋ฆฌ์ž = ์ฐธ ยท D01 200 / ํšŒ์‚ฌ๊ด€๋ฆฌ์ž ยท ์‚ฌ์šฉ์ž = ๊ฑฐ์ง“ ยท D01 403 ยท ORCA ๋ชจ๋‹ฌ: ์‹œ์Šคํ…œ๊ด€๋ฆฌ์ž ์นด๋“œ ๋„ท ์ผฌ(์‚ฌ๋ฐฉ๋งŒ 0.45) ยท ํšŒ์‚ฌ๊ด€๋ฆฌ์ž ยท ์‚ฌ์šฉ์ž = ์˜ˆ์ •๋…ธ์„  0.45 + ๊นŒ๋‹ญ ํˆดํŒ ยท ๋ˆ„๋ฆ„ ์•Œ๋ฆผ ยท ์–ด๋‘์›€ ์„  ์ƒ‰ = ๊ธ€์ž ์ƒ‰ ยท ํ‘œ ์ข…๋ฅ˜ ์—ด ยท PLAN ํ–‰ ๋‹จ๊ณ„ ๋ˆ„๋ฆ„ โ†’ D03 ยท ๋ฐ›์€ ์˜ˆ์ •๋…ธ์„  2์ค„ ยท [๋ณด๊ธฐ] โ†’ D02 ยท E01 โ†’ E02 ยท DB STABILITY ๋‹จ๊ณ„ 4 ยท ๋งŒ๋“  ํ–‰ ๋ชจ๋‘ ์ง€์›€ +- [x] 528-3 ๊ณต์œ  ํšŒ์‚ฌ GET ๋งŒ ํŒ์ • ยท ๋Œ€์‹œ๋ณด๋“œ ใ€Œ๋ฐ›์€ ์˜ˆ์ •๋…ธ์„ ใ€ ๊ตฌ์—ญ โ€” ์‹œํ—˜(๊ณต์œ  GET 200 ยท PUT 403 ยท ๋ฏธ์ง€์ • 403) + ORCA. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹ค API: ์ง€์ • ํšŒ์‚ฌ GET 200 ยท PUT 403 ยท ๋ฏธ์ง€์ • GET 403 ยท ๋ฐ›์€ ์˜ˆ์ •๋…ธ์„  1๊ฑด(์ง€์ • ํšŒ์‚ฌ) ยท 0๊ฑด(๊ทธ ๋ฐ–) ยท ํ™”๋ฉด ๊ตฌ์—ญ์€ 528-2 +- [x] 528-4 ๊ณต์šฉ ๋ณต์‚ฌ ํ•จ์ˆ˜(๋ณต์ œ ๋„๊ตฌ ๋ณธ์ฒด ์ด์ „ ยท CLI ๊ป๋ฐ๊ธฐ ยท ๋ฒ”์œ„ ์ธ์ž) โ€” ์ž„์‹œ storage ์‹œํ—˜(์ƒˆ id ยท UUID ์น˜ํ™˜ ยท ์›๋ณธ ๊ทธ๋Œ€๋กœ). + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹œํ—˜ 2 ํ†ต๊ณผ(full: ํ‘œ 7 ํ–‰ ์ˆ˜ ๊ฐ™์Œ ยท ์ง€ํ‘œ๋ฉด id ๋‹ค์‹œ ์ด์Œ ยท UUID 0 ยท ์›๋ณธ ํ•ด์‹œ ๊ทธ๋Œ€๋กœ) ยท ์˜› ๋„๊ตฌ ๋Œ€ ์ƒˆ CLI ๊ฐ™์€ ์›๋ณธ: ํ‘œ 9 ยท ํŒŒ์ผ 234 ๊ฐ™์Œ(๋‹จ๊ณ„ params ๋Š” ์ž๋™์ฆ๊ฐ€ id ๋งŒ ๋‹ค๋ฆ„) +- [x] 528-5 ํšŒ์›๊ฐ€์ž… ๋ฉ”์ผ *.go.kr โ†’ ํšŒ์‚ฌ ใ€Œ์‚ฐ๋ฆผ์ฒญใ€ ์ž๋™ ์ž…๋ ฅ ยท ๊ณ ์ •(์š”๊ตฌ 347-8) + ์‚ฐ๋ฆผ์ฒญ ํšŒ์‚ฌ ํ–‰ ๋ฏธ๋ฆฌ ๋‘  โ€” ์ง€ํ‚ด ์‹œํ—˜(go.kr ๊ฐ€์ž… = ์‚ฐ๋ฆผ์ฒญ ํšŒ์‚ฌ ยท ๋‹ค๋ฅธ ํšŒ์‚ฌ ๋ณด๋‚ด๋„ ๋ฌด์‹œ) + ORCA ๊ฐ€์ž… ํ™”๋ฉด. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹œํ—˜: go.kr ์ธ์ฆ = ์‚ฐ๋ฆผ์ฒญ ยท ACTIVE ยท ๋ณด๋‚ธ ํšŒ์‚ฌ ๊ฐ’ ๋ฌด์‹œ ยท ๋‹ค๋ฅธ ๋ฉ”์ผ NO_COMPANY ยท ORCA ๊ฐ€์ž… ํ™”๋ฉด: Kim@Forest.GO.KR โ†’ ํšŒ์‚ฌ ์นธ ใ€Œ์‚ฐ๋ฆผ์ฒญใ€ readOnly ๋ณด์ž„ ยท gmail โ†’ ์ˆจ๊น€ +- [x] 528-6 D ยท E ์ƒ์„ฑ ๊ฐ€๋ณ๊ฒŒ(์š”๊ตฌ 351) โ€” B ์–‘์‹ ๋ณต์‚ฌ ยท B0x ๋นˆ ํด๋” ์—†์Œ(D = D0x + B04_PreProcess ยท E = E0x) ยท ๊ฒฝ๋กœ ํ’€์ด๊ฐ€ D ยท E ๋ฃจํŠธ์— B ํด๋” ์•ˆ ๋งŒ๋“ฆ ยท ROAD ๊ทธ๋Œ€๋กœ ยท ๊ธฐ์กด ๊ฒ€์ฆ D ยท E ์…‹ ์ •๋ฆฌ. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹œํ—˜ 528 25 ยท D03 ์ง€ํ˜• ยท ๋ถ„์„ ยท E01 ๋ณต์‚ฌ 19 ํ†ต๊ณผ ยท ์‹ค DB ์ƒ์„ฑ: E 31.03 MB(๋ณต์‚ฌ 20.27 + ์–‘์‹ 10.76) โ†’ 20.27 MB ยท B ํด๋” 0 ยท D 10.76 MB โ†’ 0 MB(D02~D04 + B04) ยท ๊ธฐ์กด ์…‹(๊ฒ€์ฆ_sub7 ยท ๊ฒ€์ฆ531 ยท ๊ฒ€์ฆ_sub8) ์–‘์‹ ยท ๋นˆ B ํด๋” ์ง€์›€ ยท ์‚ฌ์šฉ์ž D ยท E ์—†์Œ + +## 529. D ์˜ˆ์ •๋…ธ์„  โ€” ๋“ฑ๋ก ยท 1๋‹จ๊ณ„ ๊ธฐ์ค€ ์ง€์ • (D01 ยท D02 ยท ์š”๊ตฌ์„œ 345 3-1 ยท 3-2 ยท 529-1 ยท 2 sub2 ยท Opus high ยท 529-3 ๋’ค) + +- ์‚ฌ์šฉ์ž ํ™•์ •(์š”๊ตฌ 345 ยท 349): ๋“ฑ๋ก ์นธ(์‚ฌ์—…์—ฐ๋„ ๋ฒ”์œ„ ยท ์ง€์—ญ ยท ์ข…๋ฅ˜ ใ€Œ๋…ธ์„  ๊ณ„ํšใ€ ยท ์ž„๋„ ์ข…๋ฅ˜ ยท ์„ค๊ณ„์†๋„(๊ธฐ๋ณธ ์ตœ์ € ์†๋„) ยท ์ด๋ฆ„ ์ˆ˜๊ธฐ/์ž๋™ ยท ์‹œ์ž‘ ยท ์ข…๋ฃŒ ๋ˆ„๊ฐ€ ยท ๋„๋ฉด ์นธ ์—†์Œ) ยท ์—ฐ๋„๋ณ„ ์‹œ์  ยท ์ข…์ (์ง์ „ ์ข…์  = ๋‹ค์Œ ์‹œ์ ) ยท ์นด์นด์˜ค ๋Œ๊ธฐ ยท ์ขŒํ‘œ ์ž…๋ ฅ ยท B05~B09 ํ‹€ ์žฌํ™œ์šฉ. +- ์ •ํ•จ(๋ธŒ๋ ˆ์ธ): ํ‹€ ์•„์ด์ฝ˜ ยท [<][>] ยท ๋„์›€๋ง ์ธ์ž ยท ์นด์นด์˜ค ์ง€๋„ ui_template ์ด์ „ ยท ์ž‘์—… ์ขŒํ‘œ๊ณ„ ๊ฒฝ๋„ ์ž๋™(5185~5187) ยท ๋ณ€ํ™˜ ์„œ๋ฒ„ pyproj ยท ๊ทผ๊ฑฐ _๋ถ„์„_345-B. +- [x] 529-1 ํ‹€ ์ธ์ž + D ๊ฒฝ๋กœ + D01 ๋“ฑ๋ก(B02 ์ด๋ฆ„ ์กฐํ•ฉ๊ธฐ ยท ์ž„๋„ ์ข…๋ฅ˜ ยท ์„ค๊ณ„์†๋„ = B02 ์นธ ยท resolve_design_speed ์žฌํ™œ์šฉ) โ€” ORCA ๋“ฑ๋ก โ†’ DB ํ–‰ ยท ์ด๋ฆ„ ์ž๋™ ยท ์†๋„ ๊ธฐ๋ณธ 20 ยท B05 ํ‹€ ๊ทธ๋Œ€๋กœ. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ORCA D01: ์†๋„ ๊ธฐ๋ณธ 20(๊ฐ„์„  20/30/40 ยท ์ž‘์—… 20) ยท ์ด๋ฆ„ ใ€Œ2026~2028 ํ™•์ธ๊ตฐ ์ž‘์—…์ž„๋„ใ€ ์ž๋™ ยท ์†๋Œ„ ๋’ค ์ง€์—ญ๋งŒ ๊ฐˆ์•„ ๋ผ์›€ ยท ๋“ฑ๋ก โ†’ DB PLAN 2026~2028 ยท fire 30 ยท 0~2500 ยท ๋‹จ๊ณ„ 3 โ†’ D02 ยท D/E [<][>] ๋ฌถ์Œ ์•ˆ(D04 ยท E05 [>] ํ๋ฆผ) ยท [?] D03 ยท B05 ํ‹€ ๊ทธ๋Œ€๋กœ(B ์•„์ด์ฝ˜ ยท [<] ํ๋ฆผ ยท [?] 14 ํ•ญ๋ชฉ) ยท ์‹œํ—˜ 280-2 ยท 54-2 ยท 218-1 ํ†ต๊ณผ +- [x] 529-2 ์นด์นด์˜ค ์ง€๋„ ui_template ์ด์ „ ยท ํ‚ค ๊ธธ ๊ณต์šฉ โ€” B08 ์ด์ •ํ‘œ ์‹œํ—˜ + ORCA B08 ์ง€๋„ ๊ทธ๋Œ€๋กœ. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” B08 ์ด์ •ํ‘œ ์‹œํ—˜ 99 ํ†ต๊ณผ ยท ORCA B08 ๊ฑฐ๋ž˜์ฒ˜ ๋ชจ๋‹ฌ ์ง€๋„: /api/map/kakao-key ๋กœ SDK ์ ์žฌ ยท ์นธ ๋†’์ด 480 ยท ์‹œ์  ์•Œ์•ฝ ๊ทธ๋Œ€๋กœ +- [x] 529-3 ์ขŒํ‘œ ๋ณ€ํ™˜ common_util_crs + D02 ์ง€์  ์ •๋ณธ API(์ž‡๊ธฐ ยท ๋นˆ ์—ฐ๋„ ๊ฑฐ์ ˆ) + D02 ํ™”๋ฉด(์—ฐ๋„ ํ‘œ ยท ์ขŒํ‘œ ์นธ ยท ๋Œ๊ธฐ ยท ๊ตฌ๊ฐ„ ์„  ยท [>] ์ €์žฅ) โ€” ์ง€ํ‚ด ์‹œํ—˜(์™•๋ณต 1 mm ยท ์ž‡๊ธฐ) + ORCA ๋Œ๊ธฐ โ†” ํ‘œ ์ˆ˜์น˜. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹œํ—˜ 13 ํ†ต๊ณผ(์›์  3 ๋  ยท ์™•๋ณต 1 mm ยท ๋  ๊ฒฝ๊ณ„ ยท ์ž‡๊ธฐ ยท ๋นˆ ์—ฐ๋„ ยท ๋ฒ”์œ„ ยท ๊ถŒํ•œ ยท ๋ฐ”๋€ ์—ฐ๋„๋งŒ ๋น„์›€) ยท ์‹ค API ๋นˆ ์—ฐ๋„ 400 ยท ๋ฒ”์œ„ ๋ฐ– 400 ยท ์ž„๋„ ํ”„๋กœ์ ํŠธ 400 ยท ๋‹ค๋ฅธ ํšŒ์‚ฌ 403 ยท ORCA ์ง€๋„ ๋ˆ„๋ฆ„ 3๋ฒˆ = ์‹œ์  โ†’ ๊ฒฝ๊ณ„ โ†’ ์ข…์  ์ฐจ๋ก€ ยท ๋Œ๊ธฐ P1 โ†’ ํ‘œ 127.125 / 37.515 ยท ์ž…๋ ฅ โ†’ ํ‘œ์ง€ 127.165 ยท ์—ฐ๋„ ์„  ๋นจ๊ฐ• ยท ํŒŒ๋ž‘ ยท [>] ๋’ค D03 ยท DB 2027 ์‹œ์  = 2026 ์ข…์  (211050.971, 546176.272) EPSG 5186 = pyproj ๊ฐ’ ยท ๋งŒ๋“  ํ”„๋กœ์ ํŠธ ์ง€์›€ + +## 530. D ์˜ˆ์ •๋…ธ์„  โ€” ์ง€ํ˜• ๋ฐ›๊ธฐ ยท 2๋‹จ๊ณ„ ๋ถ„์„ (D03 ยท ์š”๊ตฌ์„œ 345 3-3 ยท 530-1 ยท 2 sub3 ยท Opus high ยท 530-3 ๋’ค) + +- ์‚ฌ์šฉ์ž ํ™•์ •(์š”๊ตฌ 345): [>] ๋•Œ ์ˆ˜์น˜์ง€ํ˜•๋„ ์™ธ๋ถ€ API ๋ฐ›์•„ ์˜๊ตฌ์ €์žฅ ยท ๋ฒ”์œ„ ๋„˜์œผ๋ฉด ๋” ๋ฐ›๊ธฐ ยท [๋ถ„์„] = B05 ์ถ”์ฒœ ๋กœ์ง ํ•œ ๋ฒŒ ยท ๋ณด์ž„ = ์ค‘์‹ฌ์„  ยท ์ธก์  ์Šค๋ƒ… ยท ํ‘œ๊ธฐ ยท ์ง€๋ฐ˜ ํšก๋‹จ ยท 3D(๊ตฌ์กฐ๋ฌผ X) ยท ๋…ธ๊ฒฌ ยท ์ ˆ์„ฑํ† ์„  ยท ์ข…๋‹จ ยท ๋ฐฐ์ˆ˜์œ ์—ญ X. +- ์ •ํ•จ(๋ธŒ๋ ˆ์ธ): ์ถ”์ฒœ ๊ธฐ์ค€ = ๋“ฑ๋ก ์ž„๋„ ์ข…๋ฅ˜ ยท ์„ค๊ณ„์†๋„ ยท ์ง€ํ˜• = D storage ์•ˆ B04_PreProcess ๋ฐฐ์น˜ ยท DEM ์—ฐ๋„ ๊ตฌ๊ฐ„๋ณ„ ยท ๊ฐˆ๋ž˜ ๋„ท ์—ฐ๋„๋งˆ๋‹ค ๊ณ ๋ฆ„ ยท ์—ฐ๋„ ๊ฒฝ๊ณ„ ๊บพ์ธ ์ฑ„ ยท ๊ณ„๋ฅ˜ ์ด๊ฒฉ ๋ฐ˜์˜ ยท ๋ณต๋„ ํญ D ๋งŒ ๋„“ํž˜(B ๊ธฐ๋ณธ ๊ทธ๋Œ€๋กœ) ยท ๊ทผ๊ฑฐ _๋ถ„์„_345-B. +- [x] 530-1 ์ง€ํ˜• ๋ฐ›๊ธฐ ์„œ๋น„์Šค(ํ•ฉ์ง‘ํ•ฉ ยท prune ์—†์Œ ยท ์ง€์  ์‚ฌ์ด ๋ฉ”์›€ ยท ์œ„์„ฑ) + ์—ฐ๋„๋ณ„ DEM ยท ๋ชจ๋ธ ํ–‰ โ€” ๊ฐ€์งœ ๋‚ด๋ ค๋ฐ›๊ธฐ ์‹œํ—˜(์˜› ๋„์—ฝ ์œ ์ง€ ยท ์ƒˆ ๋„์—ฝ๋งŒ ยท ๋น ์ง ์—†์Œ). + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์‹œํ—˜ 3 ํ†ต๊ณผ ยท ์‹ค์ธก(๊ฒ€์ฆ_sub3 ๋…ธ์„  ๊ทผ์ฒ˜ ๋‘ ์—ฐ๋„ 1.23 ยท 1.22 km ยท 5187) ๋ฐ›๊ธฐ 123 s ยท ๋„์—ฝ 12๋งค ยท ์œ„์„ฑ 1์žฅ ยท DEM 726ร—1935 ยท 1700ร—1400 ์นธ ยท ๋‹ค์‹œ ๋ฐ›๊ธฐ 0๋งค ยท ์œ„์„ฑ ๊ฑด๋„ˆ๋œ€ +- [x] 530-2 B05 ์—”์ง„ ์ธ์ž ์…‹(models_dir ยท cache_dir ยท ๋ณต๋„) + D ๋ถ„์„ API(๊ตฌ๊ฐ„ ๋ฐ˜๋ณต ยท ์ž‡๊ธฐ ยท ๋ˆ„๊ฐ€ ยท ์ง„ํ–‰๋ฅ  ยท ์ €์žฅ๋ณธ) โ€” B05 ์ถ”์ฒœ ์‹œํ—˜ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผ + ๊ฒฝ๊ณ„ ์ขŒํ‘œ ์ผ์น˜ ์‹œํ—˜. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” B05 ์ถ”์ฒœ ์‹œํ—˜ 11ํŒŒ์ผ ๊ทธ๋Œ€๋กœ ํ†ต๊ณผ ยท D ์‹œํ—˜ 7 ํ†ต๊ณผ ยท ์‹ค์ธก ๋‘ ๊ตฌ๊ฐ„ ๊ฐˆ๋ž˜ ๋„ท ๋ณต๋„ 100 ยท 200 ยท 300 m = 116 ยท 140 ยท 183 s ยท ๋ชป ์ง€ํ‚จ ๊ณณ 5 ยท 8 ยท 1 โ†’ ๋ณต๋„ 300 m ยท ๊ฐ™์€ ์ž…๋ ฅ ๋‹ค์‹œ 0.07 s ยท ๊ฒฝ๊ณ„ ์ขŒํ‘œ ์ผ์น˜ +- [x] 530-3 D03 ํ‰๋ฉด(MapRender ยท ์ธก์  ยท ์Šค๋ƒ… ยท ์ง€์  ๋Œ๊ธฐ ยท ๋ฒ”์œ„ ๋ฐ– ๋” ๋ฐ›๊ธฐ) + ์ง€๋ฐ˜๋งŒ ํšก๋‹จ + 3D ๋ชจ๋‹ฌ โ€” ORCA(์ธก์  ยท SVG ํ‘œ๊ณ  = ๊ฒฉ์ž ยท ๊ตฌ์กฐ๋ฌผ 0). + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” B05 ํšก๋‹จ ์‹œํ—˜ 48 ยท D ์‹œํ—˜ 30 ํ†ต๊ณผ ยท typecheck 0 ยท ORCA ์‚ฐ๋ฆผ์ฒญ ๊ณ„์ • ์ƒˆ ์˜ˆ์ •๋…ธ์„ (2026~2027): ์—ด์ž ๋ฐ›๊ธฐ ๋„์—ฝ 9๋งค ยท ๊ฐˆ๋ž˜ ๋„ท ์นด๋“œ ยท 2027 ํ† ๊ณต ์ตœ์†Œ ๊ณ ๋ฆ„ โ†’ [์ €์žฅ] DB ๊ฐˆ๋ž˜ ยท ๋ˆ„๊ฐ€ 2861.19~4218.94 = ํ™”๋ฉด ยท ์ธก์  150+0 ๋ˆ„๋ฆ„ SVG ์ง€๋ฐ˜์„  = ๊ฒฉ์ž ํ‘œ๊ณ  ์ฐจ 0.000001 m ยท 3D ์ฝ”๋ฆฌ๋„ ยท ๊ตฌ์กฐ๋ฌผ 0 ยท ์ข…์  1 km ๋Œ๊ธฐ โ†’ ๋ฒ”์œ„ ๋ฐ– ยท ๋” ๋ฐ›๊ธฐ ๋„์—ฝ 9 โ†’ 12๋งค ยท ๋ˆ ์ง€์  [๋ถ„์„] ยท [์ €์žฅ] = ์ •๋ณธ ์ข…์  ยท ๋…ธ์„  ๋ ๋ˆ„๊ฐ€ 5725.28 ยท ํ”„๋กœ์ ํŠธ ์ง€์›€ + +## 534. E ์•ˆ์ •์„ฑํ‰๊ฐ€ โ€” ๋‚ด์žฅ ์—‘์…€ ๋ณด๊ณ ์„œ (E05_StabReport ยท ์š”๊ตฌ 347-5 ยท 534-1 ยท 2 sub6 ยท Opus high ยท PDF ๋Š” 535 ๊ณต์šฉ ๋ชจ๋“ˆ ๋’ค ยท 534-3 ๋’ค) + +- ์‚ฌ์šฉ์ž ํ™•์ •(์š”๊ตฌ 347-5): ๋‚ด์žฅ ์—‘์…€ ์–‘์‹ํ™” ยท ํŽ˜์ด์ง€ ๊ตฌ๋ถ„ ยท ๋‚ด์šฉ ์ •๋ฆฌ ยท ์‚ฌ์šฉ์ž ๊ณ ์นจ ยท ๋ณด๊ธฐ = ๋‚ด์žฅ ์—‘์…€ ยท ๋‚ด๋ณด๋‚ด๊ธฐ = xlsx ยท PDF. +- ์ •ํ•จ(๋ธŒ๋ ˆ์ธ ยท ์‚ฌ์šฉ์ž ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ): ์‹œํŠธ ์—ฌ์„ฏ(ํ‘œ์ง€ ยท ์š”์•ฝ = ์‚ฌ์šฉ์ž ๋ชซ ยท ์ž…๋ ฅ ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด ยท 3D ์š”์•ฝ = ํ”„๋กœ๊ทธ๋žจ ๋ชซ) ยท [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] = ํ”„๋กœ๊ทธ๋žจ ๋ชซ ์‹œํŠธ ยท ๊ทธ๋ฆผ๋งŒ ๋‹ค์‹œ(์‚ฌ์šฉ์ž ๋ชซ์€ ์ˆ˜์‹์œผ๋กœ ๋”ฐ๋ผ๊ฐ ยท ์˜› ํŒ _์ง€๋‚œ/) ยท PDF = ์ธ์‡„ HTML โ†’ B09 Edge ์—”์ง„ ๊ณต์šฉํ™” ยท ๊ทธ๋ฆผ = ์„œ๋ฒ„ SVG โ†’ PNG(Playwright ยท PyMuPDF ์•ˆ ์”€ โ€” AGPL) ยท ์–‘์‹ = master_template/report/stab_report.json ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด = ์ž๋™(์Œ“๊ธฐ 10 m ยท ๊นŽ๊ธฐ 15 m ยท ์ตœ์†Œ ์•ˆ์ „์œจ ํ•˜์œ„ 5 ยท E03 ์—์„œ ๊ณ ์นจ) ยท ์นธ ์ž ๊ธˆ ์—†์Œ ยท ๋‚ด์žฅ ์—‘์…€ ๊ณ„์•ฝ ๊ณ ์นจ = 534 ์ฐฝ๋งŒ ยท ๊ทผ๊ฑฐ _๋ถ„์„_345-F(๊ทธ ํŒŒ์ผ์˜ E04 = E05). +- [x] 534-1 ๋‚ด์žฅ ์—‘์…€ ๊ณ„์•ฝ ๋”ํ•จ(์ธ์‡„ ์„ค์ • ยท ์ชฝ ๋‚˜๋ˆ” ยท ๊ทธ๋ฆผ ์นธ) + ๊ทธ๋ฆผ ๊ฒนํŒ ยท ์ชฝ ๋‚˜๋ˆ” ์ ์„  โ€” spreadsheet ์‹œํ—˜ ํ†ต๊ณผ + ํฌ๊ธฐ ์˜ˆ์‚ฐ. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์—”์ง„ ์‹œํ—˜ 9 ยท ํฌ๊ธฐ 52.0 KB/128 ยท ํ–‰ ๋„ฃ๊ธฐ ๋•Œ ์ชฝ๋‚˜๋ˆ” 4โ†’5 ยท ๊ทธ๋ฆผ B8:F12โ†’B9:F13 ยท ๊ฒนํŒ ์นธ ์ž๋ฆฌ 0 px ์ฐจ(์Šคํฌ๋กค ํฌํ•จ) ยท M02 ๋ฉด์ ์‚ฐ์ถœ ๊ทธ๋Œ€๋กœ +- [x] 534-2 PDF ์—”์ง„ ๊ณต์šฉํ™”(B09 ๋Š” ๋ถ€๋ฅด๊ธฐ๋งŒ) + ์ธ์‡„ HTML(TS ยท Node) + Workbook โ†’ xlsx โ€” B09 ์ธ์‡„ ์‹œํ—˜ ๊ทธ๋Œ€๋กœ + ๋˜์ฝ๊ธฐ ์‹œํ—˜(๋ณ‘ํ•ฉ ยท ์ชฝ ๋‚˜๋ˆ” ยท ๊ทธ๋ฆผ ยท ๋จธ๋ฆฌ ์ค„ ๋ฐ˜๋ณต). + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์ธ์‡„ HTML ๋ฉ์ด 3 ยท thead 1์ค„์”ฉ ยท ๋ณ‘ํ•ฉ ์ž˜๋ฆผ 2+2 ยท xlsx ๋˜์ฝ๊ธฐ(๋ณ‘ํ•ฉ 2 ยท row_breaks [6,12] ยท ๊ทธ๋ฆผ 1 ยท $2:$2 ยท ์‹) ยท PDF 3์ชฝ = ์ชฝ ๋‚˜๋ˆ” 2 + 1(๊ฐ€๋กœ) ยท PDF ์—”์ง„ = sub8 535-1 ๊ณต์šฉ ๋ชจ๋“ˆ +- [x] 534-3 ๋ณด๊ณ ์„œ ์–‘์‹ ยท ๋นŒ๋” ยท ๊ธธ ยท E05 ํ™”๋ฉด([์ €์žฅ] ยท [์ดˆ๊ธฐํ™”] ยท [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ยท [์—‘์…€] ยท [PDF]) โ€” ์‹œํ—˜(์ธก์  ์ˆ˜ = ์ค„ ์ˆ˜ ยท ์‚ฌ์šฉ์ž ๋ชซ ๋ถˆ๋ณ€) + ORCA(PDF ์ชฝ ์ˆ˜). + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ์–‘์‹(sub7 ยท ์‹œํŠธ ์—ฌ์„ฏ ยท ์ž๋ฆฌํ‘œ) + ๋นŒ๋” ยท ๊ธธ ยท ํ™”๋ฉด ์‹œํ—˜ 7(์ธก์  30 = ์ค„ 30 ยท N.G 4 ยท ๊ทธ๋ฆผ 4 ยท ๊ฐฑ์‹  ๋’ค ํ‘œ์ง€ ยท ๊ฐœ์š” ๊ทธ๋Œ€๋กœ ยท ๋ฎ์ž„ [์ธก์ ํ‘œ] ยท ํŒ 409 ยท xlsx ๋˜์ฝ๊ธฐ ยท PDF ์ชฝ = 3 + ์ธก์ ํ‘œ n + ๋‹จ๋ฉด 3 + 1) ยท ORCA ์ผ๋ฐ˜ ๊ณ„์ • ยท ๊ฒ€์ฆ_sub6 ๋ณต์‚ฌ E(์ธก์  57 ยท 2D = ๊ฐ™์€ ์—”์ง„ ยท ์ •ํ•œ ์ €์žฅ๋ณธ ๊ผด): ์‹œํŠธ 6 ยท ๋‹จ๋ฉด ๊ทธ๋ฆผ 3 ยท ํ‘œ์ง€ C10 ยท C12 ๊ณ ์นจ โ†’ [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ๋’ค ๋‚จ์Œ ยท ์ธก์ ํ‘œ ๊ณ ์นจ = ๋ฎ์ž„ ์•Œ๋ฆผ ยท ํ•ด์„ ๋ฐ”๋€œ ๋  ยท [์ดˆ๊ธฐํ™”] ยท PDF 10์ชฝ(3 + ์ธก์ ํ‘œ 3 + ๋‹จ๋ฉด 3 + 3D 1) ยท xlsx ์‹œํŠธ 6 ยท ์ธก์  ์ค„ 57 ยท N.G 28 ยท E ์ง€์›€ +- [x] 534-4 E ์ „ ํ๋ฆ„ ํ™•์ธ(์š”๊ตฌ 345 ยท 347 ยท 351 ยท sub1) โ€” ์ผ๋ฐ˜ ๊ณ„์ • ํšŒ์‚ฌ 3 ยท [+] โ†’ E01 โ†’ E05 ยท ์›๋ณธ ๋ฐ”๊ฟ”๋„ ๋ถˆ๋ณ€ ยท ๋‹ค๋ฅธ ํšŒ์‚ฌ 403 ยท ์‹œ์Šคํ…œ ๊ด€๋ฆฌ์ž ๋ณด๊ธฐ ยท ์ˆซ์ž ๋งž์ถค ยท ๊ณ ์นจ = E05 ๊ฐ€ E03 ์ •๋ณธ ์ €์žฅ๋ณธ(stab2d_summary ยท stab2d_station ยท ๋Œ€ํ‘œ ์ผฌ/๋”)์„ ๋ฐ”๋กœ ์ฝ์Œ ยท 3D์š”์•ฝ ๊ฐ™์€ ์กฐ๊ฑด ๋น„๊ต ยท E04 ํŒ์ • ยท ์ž…๋ ฅ์กฐ๊ฑด ์ ์šฉ ๊ตฌ๊ฐ„. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ๊ฒ€์ฆ_sub1 ํšŒ์‚ฌ 3 ์‚ฌ๋ณธ โ†’ E ์ƒ์„ฑ 3.0 s(ํ™”๋ฉด โ‰ค 5.8 s) ยท 16 MB ยท ์ธก์  63 = ์›๋ณธ 63 โˆ’ missing 0 ยท E03 182.6 s ยท ๊ฑด๊ธฐ N.G 13 ยท ์šฐ๊ธฐ 30(ํ™”๋ฉด ๊ฐ™์Œ) ยท ์ž„๊ณ„๋ฉด SVG 6์  = API ยท E05 ์ธก์  ์ค„ 63 ยท N.G 30 ยท ๋Œ€ํ‘œ 17 = E03 ์ผฌ ยท ์ž…๋ ฅ์กฐ๊ฑด = E02 ์ €์žฅ๊ฐ’(c 12 ยท ์ ํ† ์ธต ยท 0~100 m ยท ๋…ธ๋ฉด 12) ยท 3D/2D 1.7314 = 0.838/0.484 ยท ๊ด‘์—ญ์š”์•ฝ ๋ฉด์  = E07 ยท xlsx ์‹œํŠธ 7 ยท PDF 25์ชฝ ยท ์›๋ณธ B06 ๋ฐ”๊ฟˆ = E ์ง€๋ฌธ ๊ทธ๋Œ€๋กœ ยท ํšŒ์‚ฌ 1 E 403 ยท ๊ด€๋ฆฌ์ž 200 ยท ์‹œํ—˜ 40 ยท typecheck ยท ๋งŒ๋“  E ๋‘˜ ยท ์‚ฌ๋ณธ ์ง€์›€ ยท ๋‚จ์Œ = E04 ํ™”๋ฉด 2D ์ตœ์†Œ ยท ๋น„ ๋น„์–ด ๋ณด์ž„(fs_min ์นธ ์—†์Œ ยท sub7) +- ๊ณตํ†ต(532~534): ํ•ด์„ ์—”์ง„ ์ง์ ‘ ๋งŒ๋“ฆ ยท ์™ธ๋ถ€๋Š” BSD ยท MIT ยท Apache ๋งŒ(์š”๊ตฌ 350 ยท ์š”๊ตฌ์„œ 345 9์žฅ) ยท GPL ยท AGPL ยท LGPL ยท ๋น„์ƒ์—… ์ฝ”๋“œ ๊ธˆ์ง€. + +## 535. ๋ผ์ด์„ผ์Šค โ€” PDF ์—”์ง„ ๊ณต์šฉํ™” ยท B09 ๋„๋ฉด PDF ์˜ PyMuPDF(AGPL) ๊ต์ฒด (์š”๊ตฌ 350 ยท 351 ยท sub8 ยท Opus high) + +- ์‚ฌ์šฉ์ž ํ™•์ •(์š”๊ตฌ 351): ์ƒ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฌด๋ฃŒ PDF ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘ ํ’ˆ์งˆ ์ข‹์€ ๊ฒƒ์€ ๋ธŒ๋ ˆ์ธ์ด ์ •ํ•จ ยท B ๋Š” ์–ด์ฉ” ์ˆ˜ ์—†์„ ๋•Œ๋งŒ ๊ณ ์นจ(๋ผ์ด์„ผ์Šค๋ผ ๊ณ ์นจ). +- ์ •ํ•จ(๋ธŒ๋ ˆ์ธ): ๋งŒ๋“ค๊ธฐ = Playwright(Chromium PDF ยท Apache-2.0 ยท ๋ฒกํ„ฐ ยท ๊ธ€๊ผด ๋ฌป์Œ ยท B09 ์ธ์‡„์™€ ๊ฐ™์€ ์—”์ง„) ยท ํ•ฉ์น˜๊ธฐ ยท ์ชฝ ๋‹ค๋ฃจ๊ธฐ = pypdf(BSD ยท ์„ค์น˜๋จ) ยท ํฐ ๋„๋ฉด์—์„œ ๋А๋ฆฌ๋ฉด(์ธก์ •) ReportLab(BSD) ยท PyMuPDF ๋Š” ์ œํ’ˆ ์ฝ”๋“œ์—์„œ ์—†์•ฐ(์ง€์‹DB ๋ณ€ํ™˜ ์Šคํฌ๋ฆฝํŠธ๋Š” ๊ฐœ๋ฐœ ๋„๊ตฌ๋ผ ๋‘ ). +- [x] 535-1 PDF ์—”์ง„ ๊ณต์šฉํ™”(B09 ์ธ์‡„ ํ•จ์ˆ˜ โ†’ common_util/common_util_print_pdf.py ยท B09 ๋Š” ๋ถ€๋ฅด๊ธฐ๋งŒ) โ€” B09 ์ธ์‡„ ์‹œํ—˜ ๊ทธ๋Œ€๋กœ. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” test_302_1_print_frame 16 ํ†ต๊ณผ ยท ๊ณต์šฉ render_html_to_pdf ยท render_htmls_to_pdfs ยท merge_pdfs ยท sheet_title ยท print_setup +- [x] 535-2 B09 ๋„๋ฉด PDF PyMuPDF โ†’ SVG + Playwright ยท pypdf โ€” ์‹œํ—˜(์ชฝ ์ˆ˜ ยท ๊ธ€์ž ยท ์„  ์ˆ˜) + ์ „ํ›„ ๊ทธ๋ฆผ ๋Œ€์กฐ ยท ์‹œ๊ฐ„ ยท ์ œํ’ˆ ํด๋” pymupdf 0. + - ๊ฒ€์ฆ์™„๋ฃŒ โ€” ๊ฒ€์ฆ_sub8 9์žฅ(ํ‰๋ฉด๋„ ์ง€ํ˜• = ์˜› ํŒ๋„ ๋ชป ์ฝ์Œ ยท ๋บŒ) ์ชฝ 9/9 ยท ๊ธ€์ž 9์žฅ ๋ชจ๋‘ ๊ฐ™์Œ ยท ์„  ์กฐ๊ฐ ยฑ15 % ยท ์ชฝ ๊ทธ๋ฆผ ์ฐจ 0.9~3 %(๊ธ€ 0.76 pt ๋ฐ€๋ฆผ ยท ํ† ์ ๋„ ์„ธ์šด ๊ธ€ 116 ๊ฐœ = ํ™”๋ฉด ๋ฐฉํ–ฅ์œผ๋กœ ๋ฐ”๋กœ์žกํž˜ ยท ์˜› ํŒ์€ ๋’ค์ง‘ํž˜) ยท ํฌ๊ธฐ 391 โ†’ 413 KB ยท ๋ Œ๋” 1.1 โ†’ 2.4 s(๋ธŒ๋ผ์šฐ์ € ๋„์›€ +1.5 s ยท ์žฅ๋‹น 0.145 s ๊ฐ™์Œ) ยท ํฐ ์žฅ(์„  3๋งŒ ยท ๊ธ€ 1๋งŒ) 22.5 โ†’ 13.8 s ยท test_302_1 16 ยท 302_2 8 ํ†ต๊ณผ ยท ORCA ์ธ์‡„ ๋ฏธ๋ฆฌ๋ณด๊ธฐ 9์ชฝ ยท ์ œํ’ˆ ํด๋” pymupdf 0 diff --git a/docs/raw/์š”๊ตฌ_1025/์‚ฌ์šฉ์ž์š”๊ตฌ_1025.md b/docs/raw/์š”๊ตฌ_1025/์‚ฌ์šฉ์ž์š”๊ตฌ_1025.md index b8e7f2eae..63c5bca75 100644 --- a/docs/raw/์š”๊ตฌ_1025/์‚ฌ์šฉ์ž์š”๊ตฌ_1025.md +++ b/docs/raw/์š”๊ตฌ_1025/์‚ฌ์šฉ์ž์š”๊ตฌ_1025.md @@ -136,3 +136,29 @@ PDF ๋ฌด๋ฃŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ค‘์— ์‚ฌ์šฉ ๊ฐ€๋Šฅ ํ•œ๊ฒƒ์ค‘์— ํ’ˆ์งˆ์ด ์ข‹์€ ## ์š”๊ตฌ 352 (10-25 ยท ์‚ฌ์šฉ์ž) ๋„ˆ์ง€๊ธˆ ์˜ค๋ฅด์นด ๋ธŒ๋ผ์šฐ์ ธ ํƒญ์„ ๋ณต์ˆ˜๋กœ ์‹คํ–‰ํ•˜๊ณ  ์žˆ์–ด. ์žฌ์‚ฌ์šฉํ•ด. + +## ์š”๊ตฌ 353 (10-25 ยท ์‚ฌ์šฉ์ž) + +๋„ˆ ๋ธŒ๋ผ์šฐ์ ธ๋ฅผ ๋ฐ˜๋ณต์ ์œผ๋กœ ๋„์šฐ๊ณ  ์žˆ๋‹ค๊ณ  +๋ฒŒ์จ 2๋ฒˆ์งธ์•ผ + +## ์š”๊ตฌ 354 (10-25 ยท ์‚ฌ์šฉ์ž) + +์˜ค๋А์นด ๋ธŒ๋ผ์šฐ์ ธ๋Š” ๋ฒŒ์จ ๊ธฐ๋Šฅ์ด์žˆ๊ฑฐ๋‚˜ ์Šคํฌ๋ฆฝํŠธ๋กœ ์šด์šฉํ•˜๋Š”๊ฒŒ ์ข‹์•„๋ณด์ž„ + +## ์š”๊ตฌ 355 (10-25 ยท ์‚ฌ์šฉ์ž) + +ํด๋กœ๋“œ 5์‹œ๊ฐ„ ํ† ํฐ ์‚ฌ์šฉ ์ œํ•œ์— ๊ฑธ๋ฆด๊ฒƒ์œผ๋กœ ์˜ˆ์ƒ๋จ. ํ† ํฐ ํ’€๋ฆฐ ํ›„ ๋ฐ”๋กœ ๋„ˆ๋ฅผ ๊นจ์›Œ์ฃผ๋Š” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ. + +1์‹œ๊ฐ„์— ํ•œ๋ฒˆ์ด๋ฉด ๋จ + +## ์š”๊ตฌ 356 (10-25 ยท ์‚ฌ์šฉ์ž) + +ํ† ํฌ ํ•œ๋„ ๋ฆฌ๋ฐ‹ ๊ด€๋ จ ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์€ ๋‚ด๊ฐ€ on/off๋Š” ๋‚ด๊ฐ€ ์ง€์‹œํ•˜๊ฒ ์Œ. ํ˜„์žฌ๋Š” o. ๋ฉ”๋ชจ์—์„œ ์‚ญ์ œ. + +## ์š”๊ตฌ 357 (10-25 ยท ์‚ฌ์šฉ์ž) + +on ์ด ๋งž์Œ. ๊ทธ๋Ÿฐ๋ฐ shell command๊ฐ€ ์•„๋‹˜? +์‰˜๋กœ ๋ฐ”๊ฟ”์ค˜ ๋ญ๊ฐ€ ์‹คํ–‰์ค‘์ธ์ง€ ์˜ค๋ฅด์นด ide์— ํ‘œ์‹œ๋˜๋Š”๊ฒŒ ์ข‹๊ฒ ์–ด + +๊ทธ๋ง์ด ์•„๋‹ˆ๋ผ ์ง€๊ธˆ ๋„ˆ์™€์˜ ๋Œ€ํ™”์ฐฝ์—๋Š” ๊ฐ์‹œ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋Œ๋ฉด wait for worker reports ๋ผ๊ณ  shell command ํ•ญ๋ชฉ์ด ๋ณด์—ฌ ์ด๊ฑธ๋กœ ์‹คํ–‰์—ฌ๋ถ€๋ฅผ ํŒ๋‹จํ•˜๊ณ  ์‹ถ๋‹ค๊ณ  diff --git a/main.py b/main.py index f9d6e072d..a1ce11a71 100644 --- a/main.py +++ b/main.py @@ -83,7 +83,10 @@ from E01_StabRegister.E01_StabRegister_Router import router as e01_stab_register from E02_StabGround.E02_StabGround_Router import router as e02_stab_ground_router from E04_Stab3D.E04_Stab3D_Router import router as e04_stab_3d_router from E05_StabReport.E05_StabReport_Router import router as e05_stab_report_router +from E07_StabArea.E07_StabArea_Router import router as e07_stab_area_router from E03_StabSection.E03_StabSection_Router import router as e03_stab_section_router +from E03_StabSection.E03_StabSection_Router_WhatIf import router as e03_stab_whatif_router +from E06_StabRock.E06_StabRock_Router import router as e06_stab_rock_router from M01_MasterData.M01_MasterData_Router import router as m01_master_data_router from M02_MasterTemplete.M02_Labor_Router import router as m02_labor_router from M02_MasterTemplete.M02_MasterTemplete_Router import router as m02_master_template_router @@ -493,7 +496,10 @@ app.include_router(d04_plan_link_router, dependencies=protected_with_company) app.include_router(e02_stab_ground_router, dependencies=protected_with_company) app.include_router(e04_stab_3d_router, dependencies=protected_with_company) app.include_router(e05_stab_report_router, dependencies=protected_with_company) +app.include_router(e07_stab_area_router, dependencies=protected_with_company) app.include_router(e03_stab_section_router, dependencies=protected_with_company) +app.include_router(e03_stab_whatif_router, dependencies=protected_with_company) +app.include_router(e06_stab_rock_router, dependencies=protected_with_company) app.include_router(b05_corridor_router, dependencies=protected_with_company) app.include_router(b05_route_replan_router, dependencies=protected_with_company) app.include_router(b05_route_terrain_router, dependencies=protected_with_company) diff --git a/resources/help/D01/00_์„ค๋ช…์„œ.md b/resources/help/D01/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..0647091dd --- /dev/null +++ b/resources/help/D01/00_์„ค๋ช…์„œ.md @@ -0,0 +1,35 @@ +# ์˜ˆ์ •๋…ธ์„  ๋“ฑ๋ก ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ +- ์—ฌ๋Ÿฌ ํ•ด์— ๊ฑธ์นœ ์˜ˆ์ •๋…ธ์„ ์˜ ๊ธฐ๋ณธ ์ •๋ณด(์‚ฌ์—… ์—ฐ๋„ ยท ์ง€์—ญ ยท ์ž„๋„ ์ข…๋ฅ˜ ยท ์„ค๊ณ„์†๋„ ยท ์ด๋ฆ„)๋ฅผ ๋„ฃ๊ณ  ์ƒˆ ์˜ˆ์ •๋…ธ์„  ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“œ๋Š” ์ฒซ ํ™”๋ฉด์ž„. +- ๋Œ€์‹œ๋ณด๋“œ์˜ [+] ์—์„œ ใ€Œ์˜ˆ์ •๋…ธ์„ ใ€ ์นด๋“œ๋ฅผ ๊ณ ๋ฅด๋ฉด ์ด ํ™”๋ฉด์ด ์—ด๋ฆผ. +- ๋„๋ฉด ๊ด€๋ จ ์นธ์€ ์—†์Œ. ๋…ธ์„ ์˜ ์‹ค์ œ ์œ„์น˜๋Š” ๋งŒ๋“  ๋’ค ๋‹ค์Œ ํ™”๋ฉด(1๋‹จ๊ณ„ ๊ธฐ์ค€ ์ง€์ •)์—์„œ ์ •ํ•จ. +- ์˜ˆ์ •๋…ธ์„ ์€ **์‚ฐ๋ฆผ์ฒญ๋งŒ** ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ. ์‚ฐ๋ฆผ์ฒญ = ์‹œ์Šคํ…œ ๊ด€๋ฆฌ์ž ๋˜๋Š” ๋ฉ”์ผ ์ฃผ์†Œ๊ฐ€ `.go.kr` ๋กœ ๋๋‚˜๋Š” ์‚ฌ์šฉ์ž์ž„. + +## ์ž‘์—… ์ˆœ์„œ +1. ๋Œ€์‹œ๋ณด๋“œ์—์„œ [+] ๋ฅผ ๋ˆ„๋ฅด๊ณ  ใ€Œ์˜ˆ์ •๋…ธ์„ ใ€ ์นด๋“œ๋ฅผ ๊ณ ๋ฆ„. ์‚ฐ๋ฆผ์ฒญ์ด ์•„๋‹ˆ๋ฉด ์นด๋“œ๊ฐ€ ํ๋ฆฌ๊ฒŒ ๋ณด์ด๊ณ  ๋ˆ„๋ฅด๋ฉด ใ€Œ์˜ˆ์ •๋…ธ์„ ์€ ์‚ฐ๋ฆผ์ฒญ(์‹œ์Šคํ…œ ๊ด€๋ฆฌ์ž ยท go.kr ๋ฉ”์ผ)๋งŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ใ€ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ด. +2. ใ€Œ์‚ฌ์—… ์—ฐ๋„ ์‹œ์ž‘ใ€ ยท ใ€Œ์‚ฌ์—… ์—ฐ๋„ ๋ใ€ ์— ์‚ฌ์—… ๊ธฐ๊ฐ„์„ ๋„ฃ์Œ. ํ•œ ํ•ด์งœ๋ฆฌ๋ฉด ๋‘ ์นธ์„ ๊ฐ™์€ ํ•ด๋กœ ๋‘ . +3. ใ€Œ์‚ฌ์—… ์ง€์—ญใ€ ์„ ์ ์Œ. ํ•„์ˆ˜ ์นธ์ž„. +4. ใ€Œ์ž„๋„ ์ข…๋ฅ˜ใ€ ๋ฅผ ๊ณ ๋ฅด๊ณ , ๊ทธ์— ๋งž์ถฐ ใ€Œ์„ค๊ณ„์†๋„ใ€ ๋ฅผ ๊ณ ๋ฆ„. +5. ใ€Œํ”„๋กœ์ ํŠธ๋ช…ใ€ ์„ ํ™•์ธํ•จ. ์œ„ ์นธ์„ ์ฑ„์šฐ๋ฉด ์ž๋™์œผ๋กœ ์ด๋ฆ„์ด ๋งŒ๋“ค์–ด์ง€๋ฉฐ, ์ง์ ‘ ๊ณ ์ณ๋„ ๋จ. +6. ํ•„์š”ํ•˜๋ฉด ใ€Œ๋…ธ์„  ์‹œ์ž‘ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ (m)ใ€ ยท ใ€Œ๋…ธ์„  ์ข…๋ฃŒ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ (m)ใ€ ๋ฅผ ๋„ฃ์Œ. ๋น„์›Œ ๋‘์–ด๋„ ๋จ. +7. [๋“ฑ๋ก] ์„ ๋ˆ„๋ฆ„. ์นธ์— ๋ฌธ์ œ๊ฐ€ ์žˆ์œผ๋ฉด ํ•ด๋‹น ์นธ ์•„๋ž˜์— ๋นจ๊ฐ„ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ค๊ณ  ๋“ฑ๋ก๋˜์ง€ ์•Š์Œ. + +## ๋‹จ์ถ” +| ๋‹จ์ถ” | ํ•˜๋Š” ์ผ | +| --- | --- | +| [?] | ์ด ๋„์›€๋ง์„ ์—ถ | +| [์ทจ์†Œ] | ๋“ฑ๋กํ•˜์ง€ ์•Š๊ณ  ๋Œ€์‹œ๋ณด๋“œ๋กœ ๋Œ์•„๊ฐ | +| [๋“ฑ๋ก] | ์ž…๋ ฅํ•œ ๊ฐ’์œผ๋กœ ์˜ˆ์ •๋…ธ์„  ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค๊ณ  ๊ณง๋ฐ”๋กœ D02 ใ€Œ๊ธฐ์ค€ ์ง€์ •ใ€ ํ™”๋ฉด์œผ๋กœ ๊ฐ | + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• +- ๋“ฑ๋ก์— ์„ฑ๊ณตํ•˜๋ฉด ใ€Œํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.ใ€ ์•Œ๋ฆผ์ด ๋œจ๊ณ , ๋งŒ๋“  ํ”„๋กœ์ ํŠธ๊ฐ€ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋˜์–ด ใ€Œ๊ธฐ์ค€ ์ง€์ •ใ€ ํ™”๋ฉด(์ง€๋„ ํ™”๋ฉด)์ด ์—ด๋ฆผ. +- ๋Œ€์‹œ๋ณด๋“œ ํ”„๋กœ์ ํŠธ ํ‘œ์—๋„ ์ข…๋ฅ˜ ใ€Œ์˜ˆ์ •๋…ธ์„ ใ€ ์œผ๋กœ ์˜ฌ๋ผ๊ฐ. ํ‘œ์˜ ๋‹จ๊ณ„ ๋ง‰๋Œ€(๊ธฐ์ค€ ์ง€์ • ยท ๋…ธ์„  ๋ถ„์„ ยท ํ™•์ • ยท ๋‚ด๋ณด๋‚ด๊ธฐ)๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊ทธ ๋‹จ๊ณ„ ํ™”๋ฉด์œผ๋กœ ๊ฐ. +- ์‹คํŒจํ•˜๋ฉด ์„œ๋ฒ„๊ฐ€ ์•Œ๋ ค ์ค€ ๊นŒ๋‹ญ์ด ์•Œ๋ฆผ์œผ๋กœ ๋œจ๊ณ  ์ด ํ™”๋ฉด์— ๋จธ๋ฌพ. + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ +- ์นธ๋งˆ๋‹ค ๋œป๊ณผ ๊ธฐ๋ณธ๊ฐ’์€ ใ€Œ์นธ ๋œปใ€ ๋ฌธ์„œ์— ์žˆ์Œ. +- ์ž„๋„ ์ข…๋ฅ˜ ยท ์„ค๊ณ„์†๋„๋Š” ์ž„๋„์„ค๊ณ„(B ๊ทธ๋ฃน)์™€ ๊ฐ™์€ ๊ธฐ์ค€์„ ์”€. ๋“ฑ๋ก ๋’ค ์ด ํ™”๋ฉด์—์„œ ๊ณ ์น˜๋Š” ๋ฐฉ๋ฒ•์€ ์ฝ”๋“œ์—์„œ ํ™•์ธํ•˜์ง€ ๋ชปํ•จ. +- ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ(๋…ธ์„ ์„ ๋”ฐ๋ผ ์žฐ ๊ฑฐ๋ฆฌ) ๋‘ ์นธ์€ ์ €์žฅ๋งŒ ๋˜๋ฉฐ, ๋‹ค์Œ ํ™”๋ฉด๋“ค์—์„œ ์“ฐ์ด๋Š” ๊ณณ์€ ํ™•์ธํ•˜์ง€ ๋ชปํ•จ. ใ€Œ๋ถ„์„ ๋’ค ์‹ค์ œ ๋ˆ„๊ฐ€์™€ ๋‹ค๋ฅด๋ฉด ์ฐจ์ด ํ‘œ์‹œใ€ ๋Š” ์ค€๋น„ ์ค‘์œผ๋กœ ๋ด„. +- ๋“ฑ๋ก ์งํ›„์—๋Š” ๋…ธ์„  ์œ„์น˜๊ฐ€ ๋น„์–ด ์žˆ์Œ. ์ง€์ ์„ ์ •ํ•˜๊ณ  ์ €์žฅํ•ด์•ผ ๋ถ„์„ ๋‹จ๊ณ„๋กœ ๊ฐˆ ์ˆ˜ ์žˆ์Œ. +- ์ง€์ •๋ฐ›์€ ๋‹ค๋ฅธ ํšŒ์‚ฌ ๊ตฌ์„ฑ์›์€ ์˜ˆ์ •๋…ธ์„ ์„ ๋ณด๊ธฐ๋งŒ ํ•  ์ˆ˜ ์žˆ๊ณ  ๋งŒ๋“ค๊ฑฐ๋‚˜ ๊ณ ์น  ์ˆ˜ ์—†์Œ. diff --git a/resources/help/D01/10_์นธ_๋œป.md b/resources/help/D01/10_์นธ_๋œป.md new file mode 100644 index 000000000..692332260 --- /dev/null +++ b/resources/help/D01/10_์นธ_๋œป.md @@ -0,0 +1,36 @@ +# ์นธ ๋œป + +| ์นธ | ๋œป | ๊ธฐ๋ณธ๊ฐ’ | ๋ฒ”์œ„ ยท ๊ทœ์น™ | +| --- | --- | --- | --- | +| ์‚ฌ์—… ์—ฐ๋„ ์‹œ์ž‘ | ์‚ฌ์—… ์ฒซ ํ•ด | ์˜ฌํ•ด | 2000 ~ 2100 ์˜ ์ •์ˆ˜ | +| ์‚ฌ์—… ์—ฐ๋„ ๋ | ์‚ฌ์—… ๋งˆ์ง€๋ง‰ ํ•ด | ์˜ฌํ•ด | ์‹œ์ž‘ ์—ฐ๋„์™€ ๊ฐ™๊ฑฐ๋‚˜ ๋’ค์—ฌ์•ผ ํ•จ ยท 2100 ๊นŒ์ง€ | +| ์‚ฌ์—… ์ง€์—ญ | ์‚ฌ์—…์ด ์žˆ๋Š” ์ง€์—ญ ์ด๋ฆ„ | ๋น„์–ด ์žˆ์Œ | ํ•„์ˆ˜ ยท ๊ธ€์ž 100 ์ž๊นŒ์ง€ ยท ์˜ˆ: ์šธ์ง„๊ตฐ ๊ธˆ๊ฐ•์†ก๋ฉด | +| ์ข…๋ฅ˜ | ํ”„๋กœ์ ํŠธ ์ข…๋ฅ˜ | ใ€Œ๋…ธ์„  ๊ณ„ํšใ€ | ๊ณ ์ • ยท ๊ณ ์น  ์ˆ˜ ์—†์Œ | +| ์ž„๋„ ์ข…๋ฅ˜ | ๊ฐ„์„ ์ž„๋„ ยท ์‚ฐ๋ถˆ์ง„ํ™”์ž„๋„ ยท ์ž‘์—…์ž„๋„ ์ค‘ ํ•˜๋‚˜ | ๊ฐ„์„ ์ž„๋„(๋ชฉ๋ก ๋งจ ์œ„) | ์„ค๊ณ„์†๋„ ์„ ํƒ์ง€์™€ ์ด๋ฆ„ ์กฐํ•ฉ์— ์“ฐ์ž„ | +| ์„ค๊ณ„์†๋„ | ๋…ธ์„ ์„ ์„ค๊ณ„ํ•˜๋Š” ๊ธฐ์ค€ ์†๋„ | 20 km/h | ์•„๋ž˜ ํ‘œ ์ฐธ์กฐ | +| ํ”„๋กœ์ ํŠธ๋ช… | ๋Œ€์‹œ๋ณด๋“œ์— ๋ณด์ด๋Š” ์ด๋ฆ„ | ์ž๋™ ์กฐํ•ฉ | ํ•„์ˆ˜ ยท ๊ธ€์ž 255 ์ž๊นŒ์ง€ | +| ๋…ธ์„  ์‹œ์ž‘ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ (m) | ๋…ธ์„  ์ฒซ ์ง€์ ์˜ ์ธก์  ๊ฐ’ | ๋น„์–ด ์žˆ์Œ | 0 ์ด์ƒ ยท ์„ ํƒ | +| ๋…ธ์„  ์ข…๋ฃŒ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ (m) | ๋…ธ์„  ๋ ์ง€์ ์˜ ๊ณ„ํš ๋ชฉํ‘œ ๊ฑฐ๋ฆฌ | ๋น„์–ด ์žˆ์Œ | ์‹œ์ž‘ ๊ฐ’๋ณด๋‹ค ์ปค์•ผ ํ•จ ยท ์„ ํƒ | + +## ์„ค๊ณ„์†๋„ ์„ ํƒ์ง€ +| ์ž„๋„ ์ข…๋ฅ˜ | ๊ณ ๋ฅผ ์ˆ˜ ์žˆ๋Š” ๊ฐ’ | +| --- | --- | +| ๊ฐ„์„ ์ž„๋„ | 20 ยท 30 ยท 40 km/h | +| ์‚ฐ๋ถˆ์ง„ํ™”์ž„๋„ | 20 ยท 30 ยท 40 km/h | +| ์ž‘์—…์ž„๋„ | 20 km/h ๋งŒ | + +- ์ž„๋„ ์ข…๋ฅ˜๋ฅผ ๋ฐ”๊พธ๋ฉด ์„ค๊ณ„์†๋„ ๋ชฉ๋ก์ด ๋ฐ”๋€Œ๊ณ  ๊ฐ’์ด ๊ฐ€์žฅ ๋‚ฎ์€ ๊ฐ’(20 km/h)์œผ๋กœ ๋Œ์•„๊ฐ. +- ์ž„๋„์„ค๊ณ„(B ๊ทธ๋ฃน)์™€ ๊ฐ™์€ ๊ธฐ์ค€์ด๋ฉฐ, ํ—ˆ์šฉ๋˜์ง€ ์•Š๋Š” ๊ฐ’์ด ์„œ๋ฒ„์— ์˜ค๋ฉด 20 km/h ๋กœ ์ฒ˜๋ฆฌํ•จ. + +## ํ”„๋กœ์ ํŠธ๋ช… ์ž๋™ ์กฐํ•ฉ +- ใ€Œ์‚ฌ์—… ์—ฐ๋„ใ€ ยท ใ€Œ์‚ฌ์—… ์ง€์—ญใ€ ยท ใ€Œ์ž„๋„ ์ข…๋ฅ˜ใ€ ๋ฅผ ๋„์–ด ์ด์–ด ๋ถ™์ž„. ์˜ˆ: 2026~2028 ์šธ์ง„๊ตฐ ๊ธˆ๊ฐ•์†ก๋ฉด ๊ฐ„์„ ์ž„๋„ +- ์—ฐ๋„๊ฐ€ ์—ฌ๋Ÿฌ ํ•ด์ด๋ฉด ใ€Œ์‹œ์ž‘~๋ใ€ ๊ผด์ด๊ณ , ํ•œ ํ•ด์ด๋ฉด ํ•ด ํ•˜๋‚˜๋งŒ ๋“ค์–ด๊ฐ. +- ์ด๋ฆ„์„ ์ง์ ‘ ๊ณ ์น˜๊ธฐ ์ „์—๋Š” ์œ„ ์นธ์„ ๋ฐ”๊ฟ€ ๋•Œ๋งˆ๋‹ค ํ†ต์งธ๋กœ ๋‹ค์‹œ ๋งŒ๋“ค์–ด์ง. +- ์ด๋ฆ„์„ ๊ณ ์นœ ๋’ค์—๋Š” ์œ„ ์นธ์ด ๋ฐ”๋€Œ์–ด๋„ ์ž๋™ ์กฐ๊ฐ์ด ์ฐจ์ง€ํ•œ ๋ถ€๋ถ„๋งŒ ์ƒˆ ๊ฐ’์œผ๋กœ ๋ฐ”๋€Œ๊ณ , ์ง์ ‘ ๋„ฃ์€ ๊ธ€์€ ๊ทธ๋Œ€๋กœ ๋‚จ์Œ. ์ง์ ‘ ์ง€์šด ์กฐ๊ฐ์€ ๋˜์‚ด์•„๋‚˜์ง€ ์•Š์Œ. + +## ์ž…๋ ฅ ์˜ค๋ฅ˜ ์•ˆ๋‚ด +- ์‹œ์ž‘ ์—ฐ๋„๊ฐ€ 2000 ~ 2100 ๋ฐ–์ด๋ฉด ์ˆซ์ž ๋ฒ”์œ„ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ด. +- ๋ ์—ฐ๋„๊ฐ€ ์‹œ์ž‘๋ณด๋‹ค ์•ž์ด๋ฉด ใ€Œ๋ ์—ฐ๋„๋Š” ์‹œ์ž‘ ์—ฐ๋„์™€ ๊ฐ™๊ฑฐ๋‚˜ ๋’ค์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.ใ€ ๊ฐ€ ๋‚˜์˜ด. +- ์ง€์—ญ์ด๋‚˜ ์ด๋ฆ„์ด ๋น„๋ฉด ํ•„์ˆ˜ ์ž…๋ ฅ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ด. +- ์ข…๋ฃŒ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ๊ฐ€ ์‹œ์ž‘ ์ดํ•˜์ด๋ฉด ใ€Œ์ข…๋ฃŒ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ๋Š” ์‹œ์ž‘๋ณด๋‹ค ์ปค์•ผ ํ•ฉ๋‹ˆ๋‹ค.ใ€ ๊ฐ€ ๋‚˜์˜ด. +- ์‚ฐ๋ฆผ์ฒญ์ด ์•„๋‹Œ ๊ณ„์ •์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ์„œ๋ฒ„๊ฐ€ ใ€Œ์˜ˆ์ •๋…ธ์„ ์€ ์‚ฐ๋ฆผ์ฒญ๋งŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ใ€ ๋กœ ๋ง‰์Œ. ํšŒ์‚ฌ์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์€ ๊ณ„์ •๋„ ๋ง‰ํž˜. diff --git a/resources/help/D02/00_์„ค๋ช…์„œ.md b/resources/help/D02/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..a8eda8d5a --- /dev/null +++ b/resources/help/D02/00_์„ค๋ช…์„œ.md @@ -0,0 +1,59 @@ +# ๊ธฐ์ค€ ์ง€์ • ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ +- ์˜ˆ์ •๋…ธ์„  1๋‹จ๊ณ„. ์‚ฌ์—… ์—ฐ๋„๋งˆ๋‹ค ๋…ธ์„ ์˜ **์‹œ์ **๊ณผ **์ข…์ **์„ ์ง€๋„ ์œ„์— ์ •ํ•˜๋Š” ํ™”๋ฉด์ž„. +- ๊ฐ€์šด๋ฐ = ์นด์นด์˜ค๋งต(์ง€์ ๊ณผ ์—ฐ๋„๋ณ„ ์„ ์ด ๊ทธ๋ ค์ง). ์™ผ์ชฝ = ใ€Œ์—ฐ๋„ ์ง€์ ใ€ ํ‘œ(์ง€์  ์ด๋ฆ„ ยท ๊ฒฝ๋„ ยท ์œ„๋„ ์ž…๋ ฅ์นธ). +- ์ •ํ•œ ์ง€์ ์€ ๋‹ค์Œ ๋‹จ๊ณ„(๋…ธ์„  ๋ถ„์„)๊ฐ€ ๊ธธ์„ ์ฐพ๋Š” ๊ธฐ์ค€์ด ๋จ. +- ์ง€์  ์ €์žฅ์€ ์‚ฐ๋ฆผ์ฒญ๋งŒ ํ•  ์ˆ˜ ์žˆ์Œ. ์ง€์ •๋ฐ›์€ ๋‹ค๋ฅธ ํšŒ์‚ฌ๋Š” ๋ณด๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•˜๋ฉฐ ์ €์žฅํ•˜๋ฉด ใ€Œ์˜ˆ์ •๋…ธ์„  ์ง€์ ์€ ์‚ฐ๋ฆผ์ฒญ๋งŒ ๊ณ ์น  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ใ€ ๋กœ ๋ง‰ํž˜. + +## ์ž‘์—… ์ˆœ์„œ +1. ํ™”๋ฉด์ด ์—ด๋ฆฌ๋ฉด ์‚ฌ์—… ์—ฐ๋„ ์ˆ˜ + 1 ๊ฐœ์˜ ์ง€์  ์ค„์ด ํ‘œ์— ๋‚˜ํƒ€๋‚จ. ์˜ˆ: 2026 ~ 2027 ๋‘ ํ•ด์ด๋ฉด ใ€Œ2026 ์‹œ์ ใ€ ยท ใ€Œ2026 ์ข…์  ยท 2027 ์‹œ์ ใ€ ยท ใ€Œ2027 ์ข…์ ใ€ ์„ธ ์ค„. +2. ์ฒ˜์Œ์—๋Š” ๋น„์–ด ์žˆ๋Š” ์ฒซ ์ค„์ด ๋†“์„ ์ง€์ ์œผ๋กœ ์žกํ˜€ ์žˆ์Œ(์ค„์ด ๊ฐ•์กฐ๋˜์–ด ๋ณด์ž„). +3. ์ง€๋„๋ฅผ ๋ˆŒ๋Ÿฌ ๊ทธ ์ง€์ ์„ ๋†“์Œ. ๋†“์œผ๋ฉด ๋‹ค์Œ ๋นˆ ์ง€์ ์ด ์ž๋™์œผ๋กœ ๋†“์„ ์ง€์ ์ด ๋จ. +4. ๋†“์€ ๋’ค ๊ณ ์น˜๋ ค๋ฉด ์ง€๋„์˜ ํ‘œ์ง€๋ฅผ ๋Œ์–ด ์˜ฎ๊ธฐ๊ฑฐ๋‚˜, ํ‘œ์˜ ๊ฒฝ๋„ ยท ์œ„๋„ ์นธ์— ๊ฐ’์„ ์ง์ ‘ ๋„ฃ์Œ. +5. ๋ชจ๋“  ์ค„์ด ์ฑ„์›Œ์ง€๋ฉด ์—ฐ๋„๋งˆ๋‹ค ๋‹ค๋ฅธ ์ƒ‰์˜ ์„ ์ด ์ง€์  ์‚ฌ์ด๋ฅผ ์ด์Œ. +6. [์ €์žฅ] ๋˜๋Š” [>] ๋ฅผ ๋ˆ„๋ฆ„. + +## ์ง€์  ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ• +| ๋ฐฉ๋ฒ• | ํ•˜๋Š” ๋ฒ• | ๊ฒฐ๊ณผ | +| --- | --- | --- | +| ์ง€๋„ ๋ˆ„๋ฆ„ | ์ค„ ์˜† ๐Ÿ“ ๋กœ ๋†“์„ ์ง€์ ์„ ๊ณ ๋ฅธ ๋’ค ์ง€๋„๋ฅผ ๋ˆ„๋ฆ„ | ๊ทธ ์ž๋ฆฌ์— ํ‘œ์ง€๊ฐ€ ๋†“์ž„ | +| ํ‘œ์ง€ ๋Œ๊ธฐ | ์ง€๋„์˜ ํ‘œ์ง€๋ฅผ ๋Œ์–ด ๋†“์Œ | ํ‘œ์˜ ๊ฐ’์ด ๋”ฐ๋ผ ๋ฐ”๋€œ | +| ์ขŒํ‘œ ์ž…๋ ฅ | ๊ฒฝ๋„ ยท ์œ„๋„ ๋‘ ์นธ์— ๊ฐ’์„ ๋„ฃ๊ณ  ์นธ์„ ๋ฒ—์–ด๋‚จ | ๊ฐ’์ด ๋งž์œผ๋ฉด ํ‘œ์ง€๊ฐ€ ์˜ฎ๊ฒจ์ง ยท ํ‹€๋ฆฌ๋ฉด ์นธ์ด ๋นจ๊ฐ›๊ฒŒ ํ‘œ์‹œ๋จ | + +- ๋†“์„ ์ง€์ ์„ ๊ณ ๋ฅด์ง€ ์•Š๊ณ  ์ง€๋„๋ฅผ ๋ˆ„๋ฅด๋ฉด ใ€Œ์ง€๋„์— ๋†“์„ ์ง€์  ๊ณ ๋ฅด๊ธฐใ€ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ด. +- ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์—์„œ ๊ตฌํ•œ ์ขŒํ‘œ๋Š” ๊ฒฝ๋„ ยท ์œ„๋„(WGS84, ์†Œ์ˆ˜ 7 ์ž๋ฆฌ๊นŒ์ง€ ๋ฐ˜์˜)๋กœ ๋„ฃ์„ ๊ฒƒ. +- ๊ฒฝ๋„๋Š” 120 ~ 135, ์œ„๋„๋Š” 30 ~ 45 ๋ฒ”์œ„๋งŒ ๋ฐ›์Œ. + +## ์—ฐ๋„ ํ‘œ ์ฝ๋Š” ๋ฒ• +| ์ค„ ์ด๋ฆ„ | ๋œป | +| --- | --- | +| ์ฒซ ์—ฐ๋„ ์‹œ์  | ๋…ธ์„  ์ „์ฒด์˜ ์ถœ๋ฐœ์  | +| ์•ž ์—ฐ๋„ ์ข…์  ยท ๋‹ค์Œ ์—ฐ๋„ ์‹œ์  | ํ•œ ์ ์ด ๋‘ ์—ญํ• . ์•ž ์—ฐ๋„์˜ ๋์ด์ž ๋‹ค์Œ ์—ฐ๋„์˜ ์‹œ์ž‘ | +| ๋งˆ์ง€๋ง‰ ์—ฐ๋„ ์ข…์  | ๋…ธ์„  ์ „์ฒด์˜ ๋„์ฐฉ์  | + +- ์ง์ „ ์—ฐ๋„ ์ข…์ ๊ณผ ๋‹ค์Œ ์—ฐ๋„ ์‹œ์ ์€ ํ•ญ์ƒ ๊ฐ™์€ ์ ์ด๋ฉฐ, ํ•˜๋‚˜๋งŒ ์˜ฎ๊ธฐ๋ฉด ๋‘ ์—ฐ๋„์˜ ์„ ์ด ํ•จ๊ป˜ ์›€์ง์ž„. +- ์ค„ ์ƒ‰๊ณผ ์ง€๋„ ์„  ์ƒ‰์€ ์—ฐ๋„๋ณ„๋กœ ๋Œ€์‘ํ•จ(6 ์ƒ‰์„ ๋Œ๋ ค ์”€). + +## ๋‹จ์ถ” +| ๋‹จ์ถ” | ํ•˜๋Š” ์ผ | +| --- | --- | +| [?] | ์ด ๋„์›€๋ง | +| [<] | ์ด์ „ ๋‹จ๊ณ„. ์ด ํ™”๋ฉด์ด ์ฒซ ๋‹จ๊ณ„๋ผ ํ๋ฆผ | +| [>] | ์ง€์  ์ €์žฅ ๋’ค ๋‹ค์Œ ๋‹จ๊ณ„ ใ€Œ๋…ธ์„  ๋ถ„์„ใ€ ์œผ๋กœ ๊ฐ | +| [์ €์žฅ] | ์ง€์ ์„ ์ €์žฅํ•˜๊ณ  ์ขŒํ‘œ๊ณ„๋ฅผ ์ •ํ•จ | +| ๐Ÿ“ | ์ง€๋„๋ฅผ ๋ˆŒ๋ €์„ ๋•Œ ๋†“์„ ์ง€์ ์œผ๋กœ ์ด ์ค„์„ ๊ณ ๋ฆ„ | +| ์œ„ ๋‹จ๊ณ„ ๋ง‰๋Œ€ | ใ€Œ๊ธฐ์ค€ ์ง€์ •ใ€ ยท ใ€Œ๋…ธ์„  ๋ถ„์„ใ€ ยท ใ€Œํ™•์ • ยท ๋‚ด๋ณด๋‚ด๊ธฐใ€ ๋กœ ๋ฐ”๋กœ ์ด๋™ | + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• +- ์ €์žฅํ•˜๋ฉด ใ€Œ์ง€์ ์„ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.ใ€ ์•Œ๋ฆผ์ด ๋œธ. +- ํ‘œ ์•„๋ž˜ ํ•œ ์ค„์— ใ€Œ์ €์žฅ ์ขŒํ‘œ๊ณ„ EPSG:๋ฒˆํ˜ธใ€ ์™€ ์ฒซ ์—ฐ๋„ ์‹œ์ ์˜ X ยท Y(m)๊ฐ€ ๋‚˜์˜ด. ์ €์žฅ ์ „์—๋Š” ใ€Œ์•„์ง ์ €์žฅ ์•ˆ ํ•จใ€ ์ด ๋‚˜์˜ด. +- ์ €์žฅ ์ „ ๊ณ ์นœ ๊ฐ’์€ ์ด ํ™”๋ฉด์„ ๋‹ค์‹œ ์—ด์–ด๋„ ๊ฐ™์€ ๋ธŒ๋ผ์šฐ์ € ์•ˆ์—์„œ๋Š” ๋‚จ์•„ ์žˆ์Œ. ์„œ๋ฒ„์—๋Š” [์ €์žฅ] ์ด๋‚˜ [>] ๋ฅผ ๋ˆŒ๋Ÿฌ์•ผ ๋ฐ˜์˜๋จ(์ž๋™ ์ €์žฅ ์—†์Œ). + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ +- **[>] ์™€ ์ˆ˜์น˜์ง€ํ˜•๋„**: [>] ๋Š” ์ง€์ ์„ ์ €์žฅํ•œ ๋’ค ใ€Œ๋…ธ์„  ๋ถ„์„ใ€ ํ™”๋ฉด์„ ์—ถ. ์ˆ˜์น˜์ง€ํ˜•๋„(๋„์—ฝ) ยท ์œ„์„ฑ ์‚ฌ์ง„ ยท ์—ฐ๋„๋ณ„ ์ง€ํ˜•๋ฉด ๋ฐ›๊ธฐ๋Š” ๊ทธ ํ™”๋ฉด์ด ์—ด๋ฆด ๋•Œ ๋’ค์—์„œ ์‹œ์ž‘๋˜๋ฉฐ, ์ด๋ฏธ ๋ฐ›์€ ๋ฒ”์œ„ ์•ˆ์ด๋ฉด ๊ฑด๋„ˆ๋œ€. ์ง€์ ์„ ์ €์žฅํ•˜์ง€ ๋ชปํ•˜๋ฉด(์˜ˆ: ๋นˆ ์ง€์ ) ์ด ํ™”๋ฉด์— ๋จธ๋ฌพ. +- ๋นˆ ์ง€์ ์ด ๋‚จ์€ ์ฑ„ ์ €์žฅํ•˜๋ฉด ใ€Œโ—‹โ—‹โ—‹โ—‹๋…„ ์‹œ์ ์ด ๋น„์—ˆ์Šต๋‹ˆ๋‹ค.ใ€ ๋˜๋Š” ใ€Œโ—‹โ—‹โ—‹โ—‹๋…„ ์ข…์ ์ด ๋น„์—ˆ์Šต๋‹ˆ๋‹ค.ใ€ ๋กœ ๋ง‰ํž˜. +- ์—ฐ๋„ ๋ฒ”์œ„(์‹œ์ž‘ ยท ๋)๋Š” ๋“ฑ๋ก ๋•Œ ์ •ํ•ด์ง€๋ฉฐ ์ด ํ™”๋ฉด์—์„œ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์Œ. +- **์ด๋ฏธ ๋ถ„์„ ยท ํ™•์ •ํ•œ ์—ฐ๋„์˜ ์ง€์ ์„ ์˜ฎ๊ธฐ๋ฉด** ๊ทธ ์—ฐ๋„์˜ ๋ถ„์„ ๋…ธ์„ ๊ณผ ํ™•์ • ์ƒํƒœ๊ฐ€ ๋น„์›Œ์ง€๊ณ  ๋‹ค์‹œ ๋ถ„์„ํ•ด์•ผ ํ•จ. 1 mm ์ด๋‚ด ๋ณ€ํ™”๋Š” ๊ฐ™์€ ์ง€์ ์œผ๋กœ ๋ณด์•„ ์ง€์šฐ์ง€ ์•Š์Œ. +- ์ขŒํ‘œ๊ณ„๊ฐ€ ๋ฐ”๋€Œ๋Š” ๊ฒฝ์šฐ(ใ€Œ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐใ€ ๋ฌธ์„œ์˜ ์ขŒํ‘œ๊ณ„ ์ ˆ)์—๋Š” ๋ชจ๋“  ์—ฐ๋„์˜ ๋ถ„์„ ๋…ธ์„ ์ด ๋น„์›Œ์ง. +- ์ง€๋„๋Š” ์นด์นด์˜ค๋งต์„ ์”€. ์ ์ด ํ•˜๋‚˜๋„ ์—†์–ด๋„ ์šฐ๋ฆฌ๋‚˜๋ผ ๊ฐ€์šด๋ฐ์ฏค์„ ๋จผ์ € ๋ณด์—ฌ ์คŒ. diff --git a/resources/help/D02/10_์นธ_๋œป.md b/resources/help/D02/10_์นธ_๋œป.md new file mode 100644 index 000000000..24a2720a4 --- /dev/null +++ b/resources/help/D02/10_์นธ_๋œป.md @@ -0,0 +1,25 @@ +# ์นธ ๋œป + +## ์™ผ์ชฝ ใ€Œ์—ฐ๋„ ์ง€์ ใ€ ํ‘œ +| ์นธ | ๋œป | ๊ทœ์น™ | +| --- | --- | --- | +| ์ง€์  ์ด๋ฆ„ | ์–ด๋А ์—ฐ๋„์˜ ์‹œ์  ยท ์ข…์ ์ธ์ง€ | ์˜ˆ: ใ€Œ2026 ์‹œ์ ใ€ ยท ใ€Œ2026 ์ข…์  ยท 2027 ์‹œ์ ใ€ | +| ๐Ÿ“ | ์ง€๋„ ๋ˆ„๋ฆ„์ด ๋†“์„ ์ง€์ ์œผ๋กœ ๊ณ ๋ฆ„ | ๊ณ ๋ฅธ ์ค„์ด ๊ฐ•์กฐ๋จ | +| ๊ฒฝ๋„ | ๋™์„œ ์œ„์น˜(๋„) | 120 ~ 135 ยท ์†Œ์ˆ˜ ๊ฐ€๋Šฅ | +| ์œ„๋„ | ๋‚จ๋ถ ์œ„์น˜(๋„) | 30 ~ 45 ยท ์†Œ์ˆ˜ ๊ฐ€๋Šฅ | + +- ๊ฒฝ๋„ ยท ์œ„๋„ ์ค‘ ํ•˜๋‚˜๋งŒ ๋„ฃ์œผ๋ฉด ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ. ๋‘ ์นธ์ด ๋ชจ๋‘ ๋ฒ”์œ„ ์•ˆ์ผ ๋•Œ ๋ฐ˜์˜๋จ. +- ๋ฐ˜์˜๋  ๋•Œ ์†Œ์ˆ˜ 7 ์ž๋ฆฌ(์•ฝ 1 cm)๋กœ ๋ฐ˜์˜ฌ๋ฆผ๋จ. +- ๋ฒ”์œ„ ๋ฐ– ๊ฐ’์€ ๋‘ ์นธ์ด ๋ชจ๋‘ ๋นจ๊ฐ›๊ฒŒ ํ‘œ์‹œ๋˜๊ณ  ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ. + +## ์ง€๋„ ์œ„ +- ํ‘œ์ง€ ํ•˜๋‚˜ = ์ง€์  ํ•˜๋‚˜. ๋งˆ์šฐ์Šค๋กœ ๋Œ์–ด ์˜ฎ๊ธธ ์ˆ˜ ์žˆ์Œ. +- ์„  ํ•˜๋‚˜ = ์—ฐ๋„ ํ•˜๋‚˜(๊ทธ ํ•ด์˜ ์‹œ์ ์—์„œ ์ข…์ ๊นŒ์ง€). ์ƒ‰์€ ์—ฐ๋„๋ณ„๋กœ ๋‹ค๋ฆ„. +- ์ง€์ ์ด ํ•˜๋‚˜๋„ ์—†์œผ๋ฉด ์šฐ๋ฆฌ๋‚˜๋ผ ๊ฐ€์šด๋ฐ์ฏค์ด ๋จผ์ € ๋ณด์ž„. ์ง€์ ์„ ๋†“์œผ๋ฉด ์ฒ˜์Œ ํ•œ ๋ฒˆ๋งŒ ๊ทธ ์ง€์ ์— ๋งž์ถฐ ๋ณด์ด๊ณ , ๊ทธ ๋’ค๋กœ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋งž์ถ˜ ๋ณด๊ธฐ(์ค‘์‹ฌ ยท ํ™•๋Œ€)๋ฅผ ๊ทธ๋Œ€๋กœ ๋‘ . +- ์ง€๋„ ์•„๋ž˜ ์•ˆ๋‚ด๋ฌธ = ใ€Œ์ง€๋„๋ฅผ ๋ˆŒ๋Ÿฌ ์ง€์ ์„ ๋†“์Šต๋‹ˆ๋‹ค.ใ€ + +## ํ‘œ ์•„๋ž˜ ํ•œ ์ค„ +| ํ‘œ์‹œ | ๋œป | +| --- | --- | +| ์•„์ง ์ €์žฅ ์•ˆ ํ•จ | ์„œ๋ฒ„์— ์ง€์ ์ด ํ•œ ๋ฒˆ๋„ ์ €์žฅ๋˜์ง€ ์•Š์Œ | +| ์ €์žฅ ์ขŒํ‘œ๊ณ„ EPSG:๋ฒˆํ˜ธ ยท ์—ฐ๋„ X ยท Y | ์ €์žฅ๋œ ์ขŒํ‘œ๊ณ„์™€ ์ฒซ ์—ฐ๋„ ์‹œ์ ์˜ ํ‰๋ฉด์ขŒํ‘œ(m, ์†Œ์ˆ˜ 3 ์ž๋ฆฌ) | diff --git a/resources/help/D02/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md b/resources/help/D02/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md new file mode 100644 index 000000000..712ba6ee5 --- /dev/null +++ b/resources/help/D02/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md @@ -0,0 +1,34 @@ +# ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐ + +## ์ขŒํ‘œ๊ณ„ ์ฒ˜๋ฆฌ +- ์‚ฌ์šฉ์ž๊ฐ€ ๋„ฃ๋Š” ๊ฐ’์€ ๋ชจ๋‘ ๊ฒฝ๋„ ยท ์œ„๋„(WGS84)์ž„. ์ง€๋„(์นด์นด์˜ค๋งต)๋„ ์ด ๊ฐ’์œผ๋กœ ๊ทธ๋ ค์ง. +- ์ €์žฅํ•  ๋•Œ ์„œ๋ฒ„๊ฐ€ ๋ชจ๋“  ์ง€์ ์˜ **ํ‰๊ท  ๊ฒฝ๋„**๋ฅผ ๋ณด๊ณ  ํ•œ๊ตญ ํ‰๋ฉด์ขŒํ‘œ(TM) ๋  ํ•˜๋‚˜๋ฅผ ๊ณ ๋ฅธ ๋’ค, ๋ชจ๋“  ์ง€์ ์„ ๊ทธ ํ•œ ๊ฐ€์ง€ ์ขŒํ‘œ๊ณ„์˜ X(๋™์ชฝ) ยท Y(๋ถ์ชฝ) ๋ฏธํ„ฐ ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ” ์ €์žฅํ•จ. +- ๋ถ„์„๊ณผ ์ง€ํ˜• ๋ฐ›๊ธฐ๋Š” ์ด ํ‰๋ฉด์ขŒํ‘œ๋ฅผ ์”€. ์‚ฌ์šฉ์ž๊ฐ€ ๋ณ€ํ™˜์„ ๋”ฐ๋กœ ํ•  ํ•„์š”๋Š” ์—†์Œ. + +| ํ‰๊ท  ๊ฒฝ๋„ | ์ขŒํ‘œ๊ณ„ | ์ด๋ฆ„ | +| --- | --- | --- | +| 126ยฐ ๋ฏธ๋งŒ | EPSG:5185 | ์„œ๋ถ€ | +| 126ยฐ ์ด์ƒ 128ยฐ ๋ฏธ๋งŒ | EPSG:5186 | ์ค‘๋ถ€ | +| 128ยฐ ์ด์ƒ | EPSG:5187 | ๋™๋ถ€ | + +- ํ•œ ๋…ธ์„ ์€ ์—ฐ๋„๊ฐ€ ๋‹ฌ๋ผ๋„ ํ•ญ์ƒ ๊ฐ™์€ ์ขŒํ‘œ๊ณ„ ํ•œ ๋ฒŒ์„ ์”€(์—ฐ๋„๋งˆ๋‹ค ๋ ๋ฅผ ๋”ฐ๋กœ ์“ฐ์ง€ ์•Š์Œ). +- ๋™ํ•ด(5188)๋Š” ์“ฐ์ง€ ์•Š์Œ. +- ์ง€์ ์„ ์˜ฎ๊ฒจ ํ‰๊ท  ๊ฒฝ๋„๊ฐ€ 126ยฐ ๋‚˜ 128ยฐ ์„ ์„ ๋„˜์œผ๋ฉด ์ขŒํ‘œ๊ณ„๊ฐ€ ๋ฐ”๋€Œ๊ณ , ๊ทธ๋Ÿฌ๋ฉด ๋ชจ๋“  ์—ฐ๋„๊ฐ€ ๋‹ค์‹œ ์ €์žฅ๋˜๋ฉฐ ๋ถ„์„ ๋…ธ์„ ์ด ๋น„์›Œ์ง. +- ๋‚ด๋ณด๋‚ด๊ธฐ ๋•Œ์˜ ์ขŒํ‘œ๊ณ„(5179)๋Š” ํ™•์ • ยท ๋‚ด๋ณด๋‚ด๊ธฐ ๋‹จ๊ณ„์—์„œ ๋‹ค๋ฃธ. ์ด ํ™”๋ฉด์—์„œ๋Š” ์“ฐ์ง€ ์•Š์Œ. + +## ์ง์ „ ์—ฐ๋„ ์ข…์  = ๋‹ค์Œ ์—ฐ๋„ ์‹œ์  +- ํ™”๋ฉด: ์  ํ•˜๋‚˜๋ฅผ ๋‘ ์—ฐ๋„๊ฐ€ ํ•จ๊ป˜ ์“ฐ๋Š” ์ง€์  ๋ชฉ๋ก(์—ฐ๋„ ์ˆ˜ + 1 ๊ฐœ)์œผ๋กœ ๋‹ค๋ฃธ. +- ์„œ๋ฒ„: ์ €์žฅํ•  ๋•Œ ์ฒซ ์—ฐ๋„๋Š” ๋ณด๋‚ธ ์‹œ์ ์„ ์“ฐ๊ณ , ์ดํ›„ ์—ฐ๋„์˜ ์‹œ์ ์€ ํ•ญ์ƒ ์ง์ „ ์—ฐ๋„์˜ ์ข…์ ์œผ๋กœ ๋งž์ถค. ๋”ฐ๋กœ ๋‹ค๋ฅธ ๊ฐ’์„ ๋ณด๋‚ด๋„ ๋ฌด์‹œ๋จ. +- ์‚ฌ์—… ์—ฐ๋„ ๋ฐ–์˜ ์—ฐ๋„๊ฐ€ ์˜ค๊ฑฐ๋‚˜ ๊ฐ™์€ ์—ฐ๋„๊ฐ€ ๋‘ ๋ฒˆ ์˜ค๋ฉด ๊ฑฐ์ ˆ๋จ. +- ๋น„์–ด ์žˆ๋Š” ์‹œ์  ยท ์ข…์ ์ด ์žˆ์œผ๋ฉด ใ€Œโ—‹โ—‹โ—‹โ—‹๋…„ ์‹œ์ (์ข…์ )์ด ๋น„์—ˆ์Šต๋‹ˆ๋‹ค.ใ€ ๋กœ ๊ฑฐ์ ˆ๋จ. + +## ์ €์žฅ ๊ทœ์น™ +- ๋ฐ”๋€ ์—ฐ๋„๋งŒ ์ƒˆ๋กœ ์“ฐ๊ณ , ์•ˆ ๋ฐ”๋€ ์—ฐ๋„(1 mm ์ด๋‚ด)๋Š” ๊ทธ๋Œ€๋กœ ๋‘ . +- ๋ฐ”๋€ ์—ฐ๋„๋Š” ์ด๋ฏธ ์žˆ๋˜ ๋ถ„์„ ๋…ธ์„ ๊ณผ ํ™•์ • ์‹œ๊ฐ์„ ์ง€์›€. ํ•œ ๋ฌถ์Œ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด ์ผ๋ถ€๋งŒ ์ €์žฅ๋˜๋Š” ์ผ์€ ์—†์Œ. +- ์‚ฌ์—… ์—ฐ๋„ ๋ฐ–์œผ๋กœ ๋‚จ์•„ ์žˆ๋˜ ์—ฐ๋„ ํ–‰์€ ํ•จ๊ป˜ ์ง€์›€. +- ์ €์žฅ์€ ์‚ฐ๋ฆผ์ฒญ ๊ณ„์ •๋งŒ ๊ฐ€๋Šฅํ•จ(์‹œ์Šคํ…œ ๊ด€๋ฆฌ์ž ๋˜๋Š” `.go.kr` ๋ฉ”์ผ). + +## ํ•œ๊ณ„ +- ์ขŒํ‘œ ์ž…๋ ฅ์€ ๊ฒฝ์œ„๋„๋งŒ ๋ฐ›์Œ. ํ‰๋ฉด์ขŒํ‘œ(X ยท Y)๋ฅผ ์ง์ ‘ ๋„ฃ๋Š” ์นธ์€ ์—†์Œ. +- ์ง€๋„ ์œ„ ์œ„์น˜๋Š” ์นด์นด์˜ค๋งต ํ™”๋ฉด ๊ธฐ์ค€์ด๋ฉฐ, ์ •๋ฐ€ํ•œ ์ธก๋Ÿ‰ ๊ฐ’์ด ํ•„์š”ํ•˜๋ฉด ์ขŒํ‘œ ์ž…๋ ฅ์œผ๋กœ ๋„ฃ์„ ๊ฒƒ. +- ์ง€ํ˜• ๋ฐ›๊ธฐ์™€ ๋ถ„์„์— ๊ด€ํ•œ ์„ค๋ช…์€ ใ€Œ๋…ธ์„  ๋ถ„์„ใ€ ํ™”๋ฉด์˜ ๋„์›€๋ง์„ ๋ณผ ๊ฒƒ. diff --git a/resources/help/D03/00_์„ค๋ช…์„œ.md b/resources/help/D03/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..f5c9fc505 --- /dev/null +++ b/resources/help/D03/00_์„ค๋ช…์„œ.md @@ -0,0 +1,80 @@ +# ์˜ˆ์ •๋…ธ์„  2๋‹จ๊ณ„ ๋ถ„์„ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +- 1๋‹จ๊ณ„(๊ธฐ์ค€ ์ง€์ •)์—์„œ ์ •ํ•œ ์ง€์ ๋“ค์„ ์—ฐ๋„ ๊ตฌ๊ฐ„๋งˆ๋‹ค ์ด์–ด ์ฃผ๋Š” ๋…ธ์„ ์„ ์ž๋™์œผ๋กœ ์ฐพ์•„ ์คŒ. +- ์—ฐ๋„๋งˆ๋‹ค ๋…ธ์„  ํ›„๋ณด ๋„ท(์˜์„  ยท ํ† ๊ณต ์ตœ์†Œ ยท ๊ธฐ์šธ๊ธฐ ์ตœ์†Œ ยท ๋น„์ค‘ ๊ณ„์‚ฐ)์„ ๋งŒ๋“ค์–ด ๋ณด์—ฌ ์ฃผ๊ณ , ์—ฐ๋„๋ณ„๋กœ ํ•˜๋‚˜์”ฉ ๊ณ ๋ฆ„. +- ๊ณ ๋ฅธ ํ›„๋ณด๋ฅผ ์ด์–ด ๋ถ™์—ฌ ์ „์ฒด ์˜ˆ์ •๋…ธ์„  ํ•œ ์ค„๊ณผ ์ธก์ (20 m ๊ฐ„๊ฒฉ)์„ ํ‰๋ฉด์— ํ‘œ์‹œํ•จ. +- ์ธก์ ์„ ๋ˆ„๋ฅด๋ฉด ๊ทธ ์ž๋ฆฌ ์ง€๋ฐ˜์„  ํšก๋‹จ์„ ์•„๋ž˜์— ๋ณด์—ฌ ์ฃผ๊ณ , [3D] ๋กœ ์ง€ํ˜•๊ณผ ๋…ธ์„ ์„ ์ž…์ฒด๋กœ ๋ด„. +- ์ง€์ ์ด ๋งˆ์Œ์— ์•ˆ ๋“ค๋ฉด ํ‰๋ฉด์—์„œ ์ง์ ‘ ๋Œ์–ด ์˜ฎ๊ธด ๋’ค ๋‹ค์‹œ ๋ถ„์„ ๊ฐ€๋Šฅ. +- ๋…ธ์„  ํ›„๋ณด๋ฅผ ์ฐพ๋Š” ์—”์ง„์€ ์ž„๋„์„ค๊ณ„ ๋…ธ์„  ์ถ”์ฒœ(B05)๊ณผ ๊ฐ™์€ ์—”์ง„์ž„. ์ด ํ™”๋ฉด์—์„œ๋Š” ์‹œ์ž‘์ ๊ณผ ๋์ ๋งŒ ์ •ํ•ด์ง„ ๋‘ ์ง€์  ์‚ฌ์ด๋ฅผ ๊ตฌ๊ฐ„๋ณ„๋กœ ํ•œ ๋ฒˆ์”ฉ ์ฐพ์Œ. + +## ํ™”๋ฉด ๊ตฌ์„ฑ + +- ๊ฐ€์šด๋ฐ ์œ„ = ํ‰๋ฉด ์ง€๋„. ์œ„์„ฑ ์‚ฌ์ง„ ์œ„์— ๋“ฑ๊ณ ์„ , ์ง€์ , ๋…ธ์„ , ์ธก์ ์ด ๊ฒน์ณ ๋ณด์ž„. +- ๊ฐ€์šด๋ฐ ์•„๋ž˜ = ํšก๋‹จ ํŒ. ์ธก์ ์„ ๋ˆ„๋ฅด๋ฉด ์ฑ„์›Œ์ง€๋ฉฐ, ์ œ๋ชฉ ์ค„(ใ€Œํšก๋‹จใ€)์„ ๋ˆ„๋ฅด๋ฉด ์ ‘๊ณ  ํŽผ ์ˆ˜ ์žˆ์Œ. +- ์™ผ์ชฝ = ใ€Œ์ง€ํ˜•ใ€ ์นด๋“œ, ใ€Œ์—ฐ๋„๋ณ„ ๋…ธ์„ ใ€ ์นด๋“œ, ์•„๋ž˜์ชฝ ๋‹จ์ถ” [๋ถ„์„] [์ €์žฅ] [3D]. + +## ์ž‘์—… ์ˆœ์„œ + +1. ํ™”๋ฉด์„ ์—ด๋ฉด ์ž๋™์œผ๋กœ ์ง€ํ˜•์„ ํ™•์ธํ•จ. ์ง€์ ์ด ์ด๋ฏธ ๋ฐ›์€ ์ง€ํ˜• ๋ฒ”์œ„ ์•ˆ์ด๋ฉด ๊ทธ๋Œ€๋กœ ๋‘๊ณ , ์•„๋‹ˆ๋ฉด ๋’ค์—์„œ ์ง€ํ˜•์„ ๋ฐ›๊ธฐ ์‹œ์ž‘ํ•จ(์™ผ์ชฝ ใ€Œ์ง€ํ˜•ใ€ ์นด๋“œ์— ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ). +2. ์ง€์  ์œ„์น˜๊ฐ€ ๋งˆ์Œ์— ์•ˆ ๋“ค๋ฉด ํ‰๋ฉด์—์„œ ์ง€์ (์›)์„ ๋Œ์–ด ์˜ฎ๊น€. ์ง€์ ์ด ๋ฐ›์€ ์ง€ํ˜• ๋ฐ–์ด๋ฉด [๋ฒ”์œ„ ๋ฐ– โ€” ๋” ๋ฐ›๊ธฐ] ๋ฅผ ๋ˆŒ๋Ÿฌ ์ง€ํ˜•์„ ๋” ๋ฐ›์Œ. +3. [๋ถ„์„] ์„ ๋ˆ„๋ฆ„. ์—ฐ๋„ ๊ตฌ๊ฐ„๋งˆ๋‹ค ํ›„๋ณด ๋„ท์„ ์ฐพ์œผ๋ฉฐ, ์ง„ํ–‰๋ฅ ์ด ใ€Œ์ง€ํ˜•ใ€ ์นด๋“œ ์ž๋ฆฌ์— ํ‘œ์‹œ๋จ. +4. ใ€Œ์—ฐ๋„๋ณ„ ๋…ธ์„ ใ€ ์นด๋“œ์—์„œ ์—ฐ๋„๋งˆ๋‹ค ํ›„๋ณด ํ•˜๋‚˜๋ฅผ ๋ˆ„๋ฆ„(๋ˆŒ๋ฆฐ ์นด๋“œ๊ฐ€ ๊ฐ•์กฐ๋จ). ํ‰๋ฉด์˜ ๋…ธ์„ ๊ณผ ๋ˆ„๊ฐ€(๊ฑฐ๋ฆฌ) ์ˆซ์ž๊ฐ€ ๊ณง๋ฐ”๋กœ ๋ฐ”๋€œ. +5. ํ‰๋ฉด์˜ ์ธก์  ๋ˆˆ๊ธˆ์„ ๋ˆŒ๋Ÿฌ ํšก๋‹จ์„ ํ™•์ธํ•˜๊ณ , ํ•„์š”ํ•˜๋ฉด [3D] ๋กœ ํ™•์ธํ•จ. +6. ๋ชจ๋“  ์—ฐ๋„์— ๋…ธ์„ ์ด ์žˆ์œผ๋ฉด [์ €์žฅ] ์„ ๋ˆ„๋ฆ„. ์ €์žฅ ์ค‘์—๋Š” ํ™”๋ฉด ์ „์ฒด๊ฐ€ ์ž ๊น ์ž ๊น€. +7. ๋‹ค์Œ ๋‹จ๊ณ„(ํ™•์ • ยท ์—…์ฒด ยท ๋‚ด๋ณด๋‚ด๊ธฐ)๋กœ ๋„˜์–ด๊ฐ. + +## ๋‹จ์ถ” + +| ๋‹จ์ถ” | ํ•˜๋Š” ์ผ | +| --- | --- | +| ๋ถ„์„ | ์—ฐ๋„ ๊ตฌ๊ฐ„๋งˆ๋‹ค ๋…ธ์„  ํ›„๋ณด ๋„ท์„ ์ฐพ์Œ. ์ž…๋ ฅ(์ง€์  ยท ๊ธฐ์ค€ ยท ์ง€ํ˜•)์ด ์ด์ „๊ณผ ๊ฐ™์œผ๋ฉด ์ €์žฅ๋œ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ถˆ๋Ÿฌ์™€ ๋ฐ”๋กœ ๋๋‚จ. ๋ฐ›์€ ์ง€ํ˜• ๋ฐ–์— ์ง€์ ์ด ์žˆ์œผ๋ฉด ๋ˆŒ ์ˆ˜ ์—†์Œ | +| ์ €์žฅ | ์—ฐ๋„๋ณ„๋กœ ๊ณ ๋ฅธ ํ›„๋ณด๋ฅผ ์ •๋ณธ์œผ๋กœ ์ €์žฅํ•จ. ๋Œ์–ด ์˜ฎ๊ธด ์ง€์ ๋„ ์ด๋•Œ ํ•จ๊ป˜ ์ €์žฅ๋จ. ์–ด๋А ์—ฐ๋„๋“  ๋…ธ์„ ์ด ์—†์œผ๋ฉด ๋ˆŒ ์ˆ˜ ์—†์Œ(๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ์•ˆ๋‚ด ๋ฌธ๊ตฌ ํ‘œ์‹œ) | +| 3D | ์ง€ํ˜•๊ณผ ๋…ธ์„ ์„ ์ž…์ฒด๋กœ ๋ณด๋Š” ์ฐฝ์„ ์—ถ. ๋…ธ์„ ์ด ํ•˜๋‚˜๋ผ๋„ ์žˆ์–ด์•ผ ๋ˆŒ ์ˆ˜ ์žˆ์Œ | +| ๋ฒ”์œ„ ๋ฐ– โ€” ๋” ๋ฐ›๊ธฐ | ์ง€์ ์ด ๋ฐ›์€ ์ง€ํ˜• ๋ฐ–์ผ ๋•Œ๋งŒ ๋‚˜ํƒ€๋‚จ. ์ง€ํ˜•(์ˆ˜์น˜์ง€ํ˜•๋„ ยท ์œ„์„ฑ ยท ์—ฐ๋„๋ณ„ ๋†’์ด ์ž๋ฃŒ)์„ ๋” ๋ฐ›์Œ | + +- ์‚ฐ๋ฆผ์ฒญ ์†Œ์†์ด ์•„๋‹Œ ์‚ฌ์šฉ์ž(์ง€์ •๋ฐ›์€ ํšŒ์‚ฌ ๋“ฑ)์—๊ฒŒ๋Š” [๋ถ„์„] [์ €์žฅ] [๋ฒ”์œ„ ๋ฐ– โ€” ๋” ๋ฐ›๊ธฐ] ๊ฐ€ ๋ณด์ด์ง€ ์•Š์œผ๋ฉฐ ์ง€์ ๋„ ๋Œ ์ˆ˜ ์—†์Œ. ๋ณด๊ธฐ๋งŒ ๊ฐ€๋Šฅ. + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +- **ํ‰๋ฉด์˜ ์„  ๋ชจ์–‘** + - ๊ตต์€ ์„  = ์—ฐ๋„๋ณ„๋กœ ๊ณ ๋ฅธ ํ›„๋ณด๋ฅผ ์ด์€ ์˜ˆ์ •๋…ธ์„ . + - ํฐ ์ ์„ (์˜…์Œ) = ๊ณ ๋ฅด์ง€ ์•Š์€ ๋‹ค๋ฅธ ํ›„๋ณด๋“ค. ๋ถ„์„ ์ „์—๋Š” ์ง€์ ๋ผ๋ฆฌ ์ž‡๋Š” ์ ์„ ๋งŒ ๋ณด์ž„. + - ๋ˆ„๋ฅธ ์ธก์  = ๋ณด๋ผ์ƒ‰ ๊ณ ๋ฆฌ๋กœ ํ‘œ์‹œ๋จ. +- **์ง€์ (๋งˆ๋””) ์ƒ‰** + - ์ดˆ๋ก = ์ฒซ ์ง€์ (์‹œ์ ), ๋นจ๊ฐ• = ๋งˆ์ง€๋ง‰ ์ง€์ (์ข…์ ), ํŒŒ๋ž‘ = ์—ฐ๋„๊ฐ€ ๋ฐ”๋€Œ๋Š” ์ค‘๊ฐ„ ์ง€์ . + - ์ง€์  ๋‘˜๋ ˆ์— ๋นจ๊ฐ„ ๊ณ ๋ฆฌ์™€ ์ ์„  ๊ณ ๋ฆฌ = ๊ทธ ์ง€์ ์ด ๋ฐ›์€ ์ง€ํ˜• ๋ฐ–์ด๋ผ๋Š” ๋œป. ใ€Œ์ง€ํ˜•ใ€ ์นด๋“œ์— ใ€Œ๋นจ๊ฐ„ ๊ณ ๋ฆฌ ์ง€์ ์ด ๋ฐ›์€ ์ง€ํ˜• ๋ฐ–์ž…๋‹ˆ๋‹ค โ€” ๋” ๋ฐ›์•„์•ผ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค.ใ€ ๊ฐ€ ๋‚˜์˜ด. +- **๋“ฑ๊ณ ์„ ** = ๋„์—ฝ(์ˆ˜์น˜์ง€ํ˜•๋„) ๋“ฑ๊ณ ์„ . ๋…ธ๋ž€ ์ ์„ ๊ณผ ๋…ธ๋ž€ ์ˆซ์ž(๋†’์ด)๋กœ ๋ณด์ž„. ์ง€๋„๋ฅผ ํ™•๋Œ€ํ•˜๋ฉด ๋” ์ด˜์ด˜ํžˆ ๋ณด์ž„. +- **์—ฐ๋„๋ณ„ ๋…ธ์„  ์นด๋“œ** + - ์œ—์ค„ = ใ€Œ์—ฐ๋„ ยท ์‹œ์ž‘ ์ธก์  ~ ๋ ์ธก์  (์—ฐ์žฅ m)ใ€. ๋ถ„์„ ์ „์—๋Š” ใ€Œ๋ถ„์„ ์ „ โ€” [๋ถ„์„]์„ ๋ˆ„๋ฅด์„ธ์š”ใ€ ๊ฐ€ ๋ณด์ž„. + - ํ›„๋ณด ์นด๋“œ ํ•œ ์žฅ๋งˆ๋‹ค ใ€Œ์—ฐ์žฅ ยท ์ตœ๋Œ€ ๊ธฐ์šธ๊ธฐ(%) ยท ์œ„๋ฐ˜ ๊ฐœ์ˆ˜ใ€ ๋ฅผ ๋ณด์—ฌ ์คŒ. ์œ„๋ฐ˜ = ๊ธฐ์šธ๊ธฐ ๊ธฐ์ค€์„ ์ง€ํ‚ค์ง€ ๋ชปํ•œ ๊ณณ์˜ ์ˆ˜. + - ใ€Œ๋…ธ์„  ์—†์Œใ€ ์œผ๋กœ ํšŒ์ƒ‰ ์ฒ˜๋ฆฌ๋œ ์นด๋“œ = ๊ทธ ํ›„๋ณด๋Š” ๊ธธ์„ ์ฐพ์ง€ ๋ชปํ•จ. ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ๊นŒ๋‹ญ ํ‘œ์‹œ. ๋ˆ„๋ฅผ ์ˆ˜ ์—†์Œ. + - ๊ณ ๋ฅด์ง€ ์•Š๊ณ  ๋‘๋ฉด ๋„ค ํ›„๋ณด ์ค‘ ์ฐจ๋ก€(์˜์„  โ†’ ํ† ๊ณต ์ตœ์†Œ โ†’ ๊ธฐ์šธ๊ธฐ ์ตœ์†Œ โ†’ ๋น„์ค‘ ๊ณ„์‚ฐ)๋Œ€๋กœ ์ฒ˜์Œ ์„ฑ๊ณตํ•œ ํ›„๋ณด๊ฐ€ ์ž๋™์œผ๋กœ ์žกํž˜. +- **๋ˆ„๊ฐ€(์ธก์  ๋ฒˆํ˜ธ)** = ์•ž ์—ฐ๋„ ๋ = ๋‹ค์Œ ์—ฐ๋„ ์‹œ์ž‘์œผ๋กœ ์ด์–ด์„œ ์…ˆ. ์‹œ์ž‘ ๊ฐ’์€ ํ”„๋กœ์ ํŠธ ๋“ฑ๋ก ๋•Œ ์ •ํ•œ ๋…ธ์„  ์‹œ์ž‘ ๋ˆ„๊ฐ€. ์—ฐ๋„ ๊ฒฝ๊ณ„๋Š” ๊บพ์ธ ์ฑ„ ํ•œ ์ธก์ ์œผ๋กœ ํ‘œ์‹œ๋จ. +- **ํšก๋‹จ ํŒ** = ๋ˆ„๋ฅธ ์ธก์ ์˜ ์ง€๋ฐ˜์„  ๋‹จ๋ฉด. ์ œ๋ชฉ ์ค„์— ใ€Œ์—ฐ๋„ ยท ์ธก์  ์ด๋ฆ„ ยท ์ค‘์‹ฌ ์ง€๋ฐ˜๊ณ ใ€ ํ‘œ์‹œ. ์ขŒ์šฐ 20 m ๋ฒ”์œ„์˜ ์ง€๋ฐ˜๋งŒ ๋ณด์ด๋ฉฐ ์„ค๊ณ„์„ ์€ ์—†์Œ. + +## ๋ณด์ด๋Š” ๊ฒƒ๊ณผ ์•ˆ ๋ณด์ด๋Š” ๊ฒƒ + +| ๊ตฌ๋ถ„ | ๋‚ด์šฉ | +| --- | --- | +| ๋ณด์ž„ | ์œ„์„ฑ ์ง€๋„, ๋“ฑ๊ณ ์„ , ์ง€์ , ๋…ธ์„  ์ค‘์‹ฌ์„ , ์ธก์  ๋ˆˆ๊ธˆ๊ณผ ์ด๋ฆ„, ์ง€๋ฐ˜์„  ํšก๋‹จ, ์ง€ํ˜• + ์ค‘์‹ฌ์„  3D | +| ์•ˆ ๋ณด์ž„ | ๋…ธ๊ฒฌ, ์ ˆํ†  ยท ์„ฑํ† ์„ , ์ข…๋‹จ๋„, ๋ฐฐ์ˆ˜์œ ์—ญ, ๊ตฌ์กฐ๋ฌผ(3D ์—๋„ ์—†์Œ) | +| ์ด ํ™”๋ฉด์— ์นธ ์—†์Œ | ์ข…๋‹จ ๊ธฐ์šธ๊ธฐ ์ƒํ•œ ยท ์ตœ์†Œ ๊ณก์„  ๋ฐ˜์ง€๋ฆ„ ๋“ฑ ๊ธฐ์ค€๊ฐ’. ๋“ฑ๋ก ๋•Œ ์ •ํ•œ ์ž„๋„ ์ข…๋ฅ˜์™€ ์„ค๊ณ„์†๋„๋กœ ์ž๋™ ์ •ํ•ด์ง | + +## ์ง€์  ๋Œ๊ธฐ + +- ์ง€์ (์›) ์œ„์—์„œ ๋งˆ์šฐ์Šค ์™ผ์ชฝ ๋‹จ์ถ”๋ฅผ ๋ˆ„๋ฅธ ์ฑ„ ๋Œ๋ฉด ์ง€์ ์ด ๋”ฐ๋ผ ์›€์ง์ž„. ๋์ ์€ ์ด์›ƒ ์—ฐ๋„์˜ ์‹œ์ž‘์ ๊ณผ ํ•œ ์ ์œผ๋กœ ๋ฌถ์—ฌ ์žˆ์–ด ํ•จ๊ป˜ ์›€์ง์ž„. +- ์ง€์ ์„ ์˜ฎ๊ธฐ๋ฉด ๊ทธ ์ง€์  ์•ž ยท ๋’ค ์—ฐ๋„์˜ ๋ถ„์„ ๊ฒฐ๊ณผ๊ฐ€ ๋น„์›Œ์ง€๊ณ  ใ€Œ์ง€์ ์„ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค โ€” [๋ถ„์„] ๋’ค ์ธก์ ์„ ๋ˆ„๋ฅด์„ธ์š”.ใ€ ๊ฐ€ ํšก๋‹จ ํŒ์— ๋‚˜์˜ด. ๋‹ค์‹œ [๋ถ„์„] ์„ ๋ˆŒ๋Ÿฌ์•ผ ํ•จ. +- ์˜ฎ๊ธด ์œ„์น˜๋Š” [์ €์žฅ] ์ „๊นŒ์ง€ ์ด ๋ธŒ๋ผ์šฐ์ € ํƒญ์—๋งŒ ์ž„์‹œ๋กœ ๋‚จ์•„ ์žˆ์Œ(์ƒˆ๋กœ ๊ณ ์นจํ•ด๋„ ์œ ์ง€, ํƒญ์„ ๋‹ซ์œผ๋ฉด ์‚ฌ๋ผ์ง). [์ €์žฅ] ์„ ๋ˆŒ๋Ÿฌ์•ผ ์ •๋ณธ์ด ๋ฐ”๋€œ. +- ์†์ด ์กฐ๊ธˆ ๋–จ๋ฆฌ๋Š” ์ •๋„(๋ช‡ ํ”ฝ์…€)์˜ ์›€์ง์ž„์€ ๋Œ๊ธฐ๋กœ ์น˜์ง€ ์•Š๊ณ  ๋‹จ์ˆœ ๋ˆ„๋ฆ„์œผ๋กœ ์ฒ˜๋ฆฌํ•จ. +- ์ง€๋„ ์ž์ฒด๋Š” ์ง€์ ์ด ์•„๋‹Œ ๊ณณ์„ ๋Œ์–ด ์˜ฎ๊ธฐ๊ณ , ํœ ๋กœ ํ™•๋Œ€ ยท ์ถ•์†Œํ•จ. + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- [์ €์žฅ] ์€ ๋…ธ์„  ๋ชจ์–‘์„ ํ™”๋ฉด์—์„œ ๋ฐ›์ง€ ์•Š๊ณ , ๊ณ ๋ฅธ ํ›„๋ณด ์ด๋ฆ„๋งŒ ์„œ๋ฒ„์— ๋ณด๋ƒ„. ์„œ๋ฒ„๊ฐ€ ์ €์žฅ๋œ ๋ถ„์„ ๊ฒฐ๊ณผ๋กœ ๋…ธ์„ ์„ ๋‹ค์‹œ ์ด์–ด ๋ถ™์—ฌ ์ €์žฅํ•˜๋ฏ€๋กœ, [๋ถ„์„] ์„ ํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ๋Š” ์ €์žฅํ•  ์ˆ˜ ์—†์Œ(ใ€Œ๋ถ„์„ ์ €์žฅ๋ณธ์ด ์—†์Šต๋‹ˆ๋‹ค โ€” [๋ถ„์„]์„ ๋จผ์ € ํ•˜์„ธ์š”.ใ€). +- ์ฒ˜์Œ ์—ด ๋•Œ ์ง€์ ์ด ๋น„์–ด ์žˆ์œผ๋ฉด ใ€ŒN๋…„ ์ง€์ ์ด ๋น„์—ˆ์Šต๋‹ˆ๋‹ค โ€” 1๋‹จ๊ณ„(๊ธฐ์ค€ ์ง€์ •)์—์„œ ์ •ํ•˜์„ธ์š”.ใ€ ๋ผ๋Š” ๋ฌธ๊ตฌ๋งŒ ๋‚˜์˜ค๊ณ  ์ง„ํ–‰์ด ์•ˆ ๋จ. +- ์ง€ํ˜• ๋ฐ›๊ธฐ์™€ ๋ถ„์„์€ ์„œ๋ฒ„ ๋’ค์—์„œ ๋„๋Š” ์ž‘์—…์ž„. ํ™”๋ฉด์„ ๋‹ซ์•˜๋‹ค ๋‹ค์‹œ ์—ด์–ด๋„ ๋„๋Š” ์ค‘์ด๋ฉด ์ด์–ด์„œ ์ง„ํ–‰๋ฅ ์„ ๋ณด์—ฌ ์คŒ. +- ์œ„์„ฑ ์‚ฌ์ง„์€ ๋ชป ๋ฐ›์•„๋„ ์ง€ํ˜• ๋ฐ›๊ธฐ๊ฐ€ ๋ฉˆ์ถ”์ง€ ์•Š์Œ. ์œ„์„ฑ์ด ์—†์œผ๋ฉด ์–ด๋‘์šด ๋ฐ”ํƒ•์— ์ง€์ ๊ณผ ๋…ธ์„ ๋งŒ ๋ณด์ž„. +- 3D ์ฐฝ์€ ํ•œ ๋ฒˆ์— ํ•œ ์—ฐ๋„์”ฉ ๋ณด๋ฉฐ, ์ฐฝ ์œ„์ชฝ ์„ ํƒ์นธ์œผ๋กœ ์—ฐ๋„๋ฅผ ๋ฐ”๊ฟˆ. ์ฐฝ์„ ๋‹ซ์œผ๋ฉด 3D ํ™”๋ฉด ์ž์›์ด ์ •๋ฆฌ๋จ. +- ๋ฒ• ๊ธฐ์ค€์˜ ์„ธ๋ถ€ ์ˆ˜์น˜์™€ ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’์€ ใ€Œ30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’ใ€ ๋ฌธ์„œ ์ฐธ๊ณ . diff --git a/resources/help/D03/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md b/resources/help/D03/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md new file mode 100644 index 000000000..5f6a760a7 --- /dev/null +++ b/resources/help/D03/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md @@ -0,0 +1,52 @@ +# ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐ + +## ๋…ธ์„ ์„ ์ฐพ๋Š” ๋ฐฉ๋ฒ• + +- ์—ฐ๋„๋งˆ๋‹ค ๊ทธ ์—ฐ๋„์˜ ์‹œ์ ๊ณผ ์ข…์  ๋‘ ์  ์‚ฌ์ด๋ฅผ ํ•œ ๋ฒˆ์”ฉ ์ฐพ์Œ. ์—ฐ๋„ ์‚ฌ์ด์˜ ๊ฒฝ๊ณ„์ ์€ ์˜ฎ๊ธฐ์ง€ ์•Š์œผ๋ฏ€๋กœ ์•ž ์—ฐ๋„ ๋๊ณผ ๋‹ค์Œ ์—ฐ๋„ ์‹œ์ž‘์ด ์ •ํ™•ํžˆ ๋งž๋ฌผ๋ฆผ. +- ์ฐพ๋Š” ์—”์ง„์€ ์ž„๋„์„ค๊ณ„ ใ€Œ๋…ธ์„  ์ถ”์ฒœใ€(B05)๊ณผ ๊ฐ™์€ ์—”์ง„์ž„. ์ง€ํ˜• ๋†’์ด ์ž๋ฃŒ(์—ฐ๋„๋ณ„ ๋†’์ด ๊ฒฉ์ž)์—์„œ ๊ฒฝ์‚ฌ๋ฅผ ์ฝ๊ณ , ๊ธฐ์šธ๊ธฐ ยท ๊ณก์„  ยท ํ•˜์ฒœ ์ด๊ฒฉ์„ ์ง€ํ‚ค๋Š” ๊ธธ์„ ์ฐพ์•„ ๊ณก์„ ๊นŒ์ง€ ๋งž์ถ˜ ์ค‘์‹ฌ์„ ์„ ๋ƒ„. +- ํ›„๋ณด๋Š” ๋„ค ๊ฐˆ๋ž˜๋กœ ๊ฐ™์€ ์ž…๋ ฅ์—์„œ ๊ฐ๊ฐ ๊ตฌํ•จ. + +| ํ›„๋ณด | ๊ณ ๋ฅด๋Š” ๊ธฐ์ค€ | +| --- | --- | +| ์˜์„  | ๋ชฉํ‘œ ๊ธฐ์šธ๊ธฐ๋กœ ๋“ฑ๊ณ ์„ ์„ ๋น„์Šค๋“ฌํžˆ ๊ฑด๋„ˆ๊ฐ€๋Š” ์ผ์ •ํ•œ ๊ธฐ์šธ๊ธฐ ์„ . ๋ชฉํ‘œ ๊ธฐ์šธ๊ธฐ๋ฅผ ๋”ฐ๋กœ ์ฃผ์ง€ ์•Š์œผ๋ฏ€๋กœ ๊ธฐ์šธ๊ธฐ ์ƒํ•œ์„ ๋ชฉํ‘œ๋กœ ์‚ผ์Œ | +| ํ† ๊ณต ์ตœ์†Œ | ํ›„๋ณด ๊ธธ ์ค‘ ์ ˆํ†  + ์„ฑํ†  + ๋ฐ˜์ž… + ์‚ฌํ†  ๋ฌผ๋Ÿ‰์ด ๊ฐ€์žฅ ์ ์€ ๊ธธ | +| ๊ธฐ์šธ๊ธฐ ์ตœ์†Œ | ํ›„๋ณด ๊ธธ ์ค‘ ์ตœ๋Œ€ ์ข…๋‹จ ๊ธฐ์šธ๊ธฐ๊ฐ€ ๊ฐ€์žฅ ์ž‘์€ ๊ธธ(๊ฐ™์œผ๋ฉด ํ‰๊ท  ๊ธฐ์šธ๊ธฐ๊ฐ€ ์ž‘์€ ๊ธธ) | +| ๋น„์ค‘ ๊ณ„์‚ฐ | ์˜ˆ์ƒ ยท ํ† ๊ณต ยท ๊ธฐ์šธ๊ธฐ ์„ธ ๊ฐ€์ง€ ๋น„์šฉ์„ ๋น„์ค‘์œผ๋กœ ์„ž์–ด ํ•œ ๋ฒˆ ์ฐพ์€ ๊ธธ. ๋น„์ค‘์€ ๊ธฐ๋ณธ๊ฐ’ 50 : 25 : 25(%) ๊ทธ๋Œ€๋กœ ์”€ | + +- B05 ์˜ ๋‹ค๋ฅธ ํ›„๋ณด(์˜ˆ์ƒ๋…ธ์„  ์ถ”์  ยท ์•ˆ์ „ ์šฐ์„  ยท ๊ณ„๋ฅ˜ ํšก๋‹จ ์ตœ์†Œ ยท ๊ณต์‚ฌ๋น„ ์–ด๋ฆผ ์ตœ์†Œ)๋Š” ์ด ํ™”๋ฉด์—์„œ ์“ฐ์ง€ ์•Š์Œ. +- ์˜ˆ์ƒ๋…ธ์„  ์ถ”์ ์€ ๋‘ ์  ์‚ฌ์ด๊ฐ€ ์ง์„ ์ด ๋˜์–ด ์˜๋ฏธ๊ฐ€ ์—†์–ด ๋บ์Œ. + +## ์ถ”์ฒœ ๊ธฐ์ค€(์ž๋™์œผ๋กœ ์ •ํ•ด์ง€๋Š” ๊ฐ’) + +- ํ™”๋ฉด์— ๊ธฐ์ค€ ์นธ์ด ์—†์Œ. ํ”„๋กœ์ ํŠธ ๋“ฑ๋ก ๋•Œ ์ •ํ•œ ใ€Œ์ž„๋„ ์ข…๋ฅ˜ใ€์™€ ใ€Œ์„ค๊ณ„์†๋„ใ€๋กœ ์ž„๋„ ๋ฒ•์ • ํ•จ์ˆ˜๊ฐ€ ๊ฐ’์„ ๋ƒ„. +- ์„ค๊ณ„์†๋„๋Š” ์ข…๋ฅ˜๋ณ„๋กœ ๊ณ ๋ฅผ ์ˆ˜ ์žˆ๋Š” ๊ฐ’์ด ์ •ํ•ด์ ธ ์žˆ์Œ. ์ž‘์—…์ž„๋„๋Š” 20 km/h ํ•˜๋‚˜, ๊ฐ„์„  ยท ์‚ฐ๋ถˆ์ง„ํ™” ์ž„๋„๋Š” 20 ยท 30 ยท 40 km/h ์ค‘ ์„ ํƒ. ์ง€์ •ํ•˜์ง€ ์•Š์œผ๋ฉด 20 km/h. +- ์ง€ํ˜• ๊ตฌ๋ถ„์€ ๋“ฑ๋ก ์นธ์ด ์—†์–ด ๊ฐ€์žฅ ์™„ํ™”๋œ ์ชฝ(ํŠน์ˆ˜์ง€ํ˜•)์œผ๋กœ ๊ณ ์ •ํ•ด ์”€. + +| ์„ค๊ณ„์†๋„ | ์ข…๋‹จ ๊ธฐ์šธ๊ธฐ ์ƒํ•œ | ์ตœ์†Œ ๊ณก์„  ๋ฐ˜์ง€๋ฆ„ | +| --- | --- | --- | +| 20 km/h | 14 % | 12 m | +| 30 km/h | 12 % | 20 m | +| 40 km/h | 10 % | 40 m | + +- ํ•˜์ฒœ ์ด๊ฒฉ = 100 m. ๋„์—ฝ ํ•˜์ฒœ์„  ์ค‘ ์†Œํ•˜์ฒœ ยท ์ง€๋ฐฉํ•˜์ฒœ ยท ๊ตญ๊ฐ€ํ•˜์ฒœ๋งŒ ๋Œ€์ƒ. +- ํƒ์ƒ‰ ๋ณต๋„ ๋ฐ˜ํญ = 300 m. ๋‘ ์ง€์ ์„ ์ด์€ ์„  ์–‘์˜† 300 m ์•ˆ์—์„œ๋งŒ ๊ธธ์„ ์ฐพ์Œ. 100 m ยท 200 m ๋กœ๋Š” ๊ธฐ์ค€์„ ๋ชป ์ง€ํ‚ค๋Š” ๊ณณ์ด ๋‚จ์•„ 300 m ๋กœ ๋„“ํž˜. +- ์œ„๋ฐ˜ ๊ฐœ์ˆ˜ = ๊ธฐ์šธ๊ธฐ ๊ธฐ์ค€์„ ์ง€ํ‚ค์ง€ ๋ชปํ•œ ๊ณณ์˜ ์ˆ˜. ์ง€ํ˜•์ƒ ๊ธธ์ด ์—†์„ ๋•Œ๋งŒ ์–ด์ฉ” ์ˆ˜ ์—†์ด ๊ธฐ์ค€์„ ๋„˜์€ ๊ธธ์ด ๋‚˜์˜ด. + +## ์“ฐ๋Š” ์ง€ํ˜• ์ž๋ฃŒ + +- ์ˆ˜์น˜์ง€ํ˜•๋„ ๋„์—ฝ(์ถ•์ฒ™ 1/5,000)์„ ์ง€์  ๋‘˜๋ ˆ 3ร—3 ์žฅ์”ฉ ๋ฐ›์•„ ํ•œ ์žฅ์œผ๋กœ ๋ณ‘ํ•ฉํ•จ. ์ด๋ฏธ ๋ฐ›์€ ๋„์—ฝ์€ ๋‹ค์‹œ ๋ฐ›์ง€ ์•Š๊ณ , ์˜› ๋„์—ฝ๋„ ์ง€์šฐ์ง€ ์•Š์Œ. +- ์œ„์„ฑ ์ง€๋„(VWorld)๋Š” ๋ฐฐ๊ฒฝ ๊ทธ๋ฆผ์šฉ. ์ด๋ฏธ ๋ฐ›์€ ๋ฒ”์œ„๊ฐ€ ๋ฎ์œผ๋ฉด ๊ฑด๋„ˆ๋œ€. +- ์—ฐ๋„๋ณ„ ๋†’์ด ๊ฒฉ์ž๋Š” ๊ทธ ์—ฐ๋„ ๊ตฌ๊ฐ„ ์ƒ์ž + ํƒ์ƒ‰ ๋ณต๋„ 300 m + ์—ฌ์œ  50 m ๋ฒ”์œ„๋กœ ๋งŒ๋“ฆ. ์ง€์ ์ด ๊ฒฉ์ž ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ 300 m ์•ˆ์ชฝ์— ์žˆ์–ด์•ผ ใ€Œ๋ฐ›์€ ์ง€ํ˜• ์•ˆใ€ ์œผ๋กœ ํŒ์ •ํ•จ. +- ๋„์—ฝ ๋“ฑ๊ณ ์„ ์„ ํ‰ํ™œํ™”ํ•œ ๋†’์ด ๊ฒฉ์ž๋ผ ์‹ค์ œ ์ธก๋Ÿ‰ ์ง€ํ˜•๊ณผ ์ฐจ์ด๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ. + +## ์ €์žฅ ๋ฐฉ์‹ + +- ๊ฐ™์€ ์ž…๋ ฅ(์ง€์  ยท ๊ธฐ์ค€ ยท ์ง€ํ˜•)์ด๋ฉด ์ด์ „์— ๊ณ„์‚ฐํ•œ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋Œ€๋กœ ์จ์„œ [๋ถ„์„] ์ด ๋นจ๋ฆฌ ๋๋‚จ. ์ง€์ ์ด๋‚˜ ๊ธฐ์ค€์ด ๋ฐ”๋€Œ๋ฉด ๊ทธ ์—ฐ๋„๋งŒ ๋‹ค์‹œ ๊ณ„์‚ฐํ•จ. +- [์ €์žฅ] ์€ ์„œ๋ฒ„๊ฐ€ ์ €์žฅ๋œ ๋ถ„์„ ๊ฒฐ๊ณผ์—์„œ ๊ณ ๋ฅธ ํ›„๋ณด๋ฅผ ๋‹ค์‹œ ์ด์–ด ์ •๋ณธ์œผ๋กœ ๊ธฐ๋กํ•จ. ํ™”๋ฉด์—์„œ ๊ณ„์‚ฐํ•œ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ฐ›์•„ ์ ์ง€ ์•Š์Œ. +- ๋ˆ„๊ฐ€ ๊ณ„์‚ฐ, ํ›„๋ณด ๊ณ ๋ฆ„, ์ค‘์‹ฌ์„  ์ž‡๊ธฐ๋Š” ํ™”๋ฉด๊ณผ ์„œ๋ฒ„๊ฐ€ ๊ฐ™์€ ๊ทœ์น™์œผ๋กœ ๋”ฐ๋กœ ์…ˆ. ํ•œ์ชฝ๋งŒ ๊ณ ์น˜์ง€ ์•Š๋„๋ก ์ง์œผ๋กœ ๊ด€๋ฆฌํ•˜๋ฉฐ ์‹œํ—˜์œผ๋กœ ๋Œ€์กฐํ•จ. + +## ํ•œ๊ณ„ + +- ํ›„๋ณด ๋„ค ๊ฐœ๊ฐ€ ์„œ๋กœ ๋น„์Šทํ•œ ๊ธธ์ด ๋  ์ˆ˜ ์žˆ์Œ(๊ฐ€ํŒŒ๋ฅธ ์ง€ํ˜•์—์„œ ๊ธฐ์ค€์„ ์ง€ํ‚ค๋Š” ๊ธธ์ด ์ข์€ ๋  ํ•˜๋‚˜๋ฟ์ธ ๊ฒฝ์šฐ). +- ํ‰๋ฉด ๋…ธ์„ ๋งŒ ๋‹ค๋ฃธ. ์ข…๋‹จ(์„ธ๋กœ ๋‹จ๋ฉด) ยท ํ† ๊ณต๋Ÿ‰ ํ™•์ • ยท ๊ตฌ์กฐ๋ฌผ ๊ณ„ํš์€ ์ด ๋‹จ๊ณ„ ๋ฒ”์œ„๊ฐ€ ์•„๋‹ˆ๋ฉฐ, ์ž„๋„์„ค๊ณ„ ๋‹จ๊ณ„์—์„œ ๋‹ค๋ฃธ. +- ํšก๋‹จ์€ ์ง€๋ฐ˜์„ ๋งŒ ๋ณด์—ฌ ์ฃผ๋ฉฐ ์„ค๊ณ„ ๋‹จ๋ฉด์€ ์•„๋‹˜. diff --git a/resources/help/D03/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md b/resources/help/D03/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md new file mode 100644 index 000000000..8933630b4 --- /dev/null +++ b/resources/help/D03/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md @@ -0,0 +1,21 @@ +# ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ + +์•„๋ž˜ ๊ฐ’์€ ์ฝ”๋“œ์— ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋“ค์–ด ์žˆ์œผ๋‚˜ ์‚ฌ์šฉ์ž๊ฐ€ ์ตœ์ข… ํ™•์ •ํ•œ ๊ฐ’์ด ์•„๋‹˜. ๋ฐ”๊พธ๋ ค๋ฉด ๊ฐœ๋ฐœ ๋‹ด๋‹น์—๊ฒŒ ์š”์ฒญ. + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| ์ง€ํ˜• ๊ตฌ๋ถ„ | ํŠน์ˆ˜์ง€ํ˜•(๊ฐ€์žฅ ์™„ํ™”) ๊ณ ์ • | ์˜ˆ์ •๋…ธ์„  ๋“ฑ๋ก ํ™”๋ฉด์— ์ง€ํ˜• ๊ตฌ๋ถ„ ์นธ์ด ์—†์–ด ์ž„๋„์„ค๊ณ„ ๊ธฐ๋ณธ ๋Œ€์ฒด๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์”€ | ํ™•์ • ์ „ | +| ์ข…๋‹จ ๊ธฐ์šธ๊ธฐ ์ƒํ•œ | ์„ค๊ณ„์†๋„ 20 ยท 30 ยท 40 km/h ์— 14 ยท 12 ยท 10 % | ์ž„๋„ ๊ทœ์ • ๋ณ„ํ‘œ ์„ค๊ณ„์†๋„๋ณ„ ๊ฐ’ ์ค‘ ํŠน์ˆ˜์ง€ํ˜• ์—ด | ์ง€ํ˜• ๊ตฌ๋ถ„ ํ™•์ •์— ๋”ฐ๋ผ ๋ฐ”๋€œ | +| ์ตœ์†Œ ๊ณก์„  ๋ฐ˜์ง€๋ฆ„ | 12 ยท 20 ยท 40 m | ๊ฐ™์€ ๋ณ„ํ‘œ์˜ ํŠน์ˆ˜์ง€ํ˜• ์—ด | ์ง€ํ˜• ๊ตฌ๋ถ„ ํ™•์ •์— ๋”ฐ๋ผ ๋ฐ”๋€œ | +| ํ•˜์ฒœ ์ด๊ฒฉ | 100 m | ๋ฒ•์ • ์ˆ˜์น˜ ์—†์Œ. ํƒ€๋‹น์„ฑ ํ‰๊ฐ€ ๋ณ„ํ‘œ ๋น„๊ณ (์‚ฐ์ง€๊ณ„๋ฅ˜ 100 m ์ด๋‚ด ๊ตฌ๊ฐ„ ๋น„์œจ)๋ฅผ ํ›„๋ณด๋กœ ์”€ | ์ž„๋„์„ค๊ณ„์™€ ๊ฐ™์€ ๊ธฐ๋ณธ๊ฐ’ | +| ํƒ์ƒ‰ ๋ณต๋„ ๋ฐ˜ํญ | 300 m | ์˜ˆ์ •๋…ธ์„  ํ™”๋ฉด ์ „์šฉ๊ฐ’. ์‹ค์ œ ์ง€ํ˜•์œผ๋กœ ์‹œํ—˜ํ•ด ์ •ํ•จ | ์‹œํ—˜ ๊ฒฐ๊ณผ ๊ธฐ์ค€ | +| ๋น„์ค‘ ๊ณ„์‚ฐ ๋น„์ค‘ | ์˜ˆ์ƒ 50 ยท ํ† ๊ณต 25 ยท ๊ธฐ์šธ๊ธฐ 25 (%) | ์ž„๋„์„ค๊ณ„ ๊ธฐ๋ณธ ๋น„์ค‘ | ์ด ํ™”๋ฉด์—์„œ ๋ฐ”๊พธ๋Š” ์นธ ์—†์Œ | +| ์˜์„  ๋ชฉํ‘œ ๊ธฐ์šธ๊ธฐ | ๊ธฐ์šธ๊ธฐ ์ƒํ•œ๊ณผ ๊ฐ™๊ฒŒ | ์ž„๋„์„ค๊ณ„ ๊ธฐ๋ณธ ๋™์ž‘ | ์ด ํ™”๋ฉด์—์„œ ๋ฐ”๊พธ๋Š” ์นธ ์—†์Œ | +| ์ธก์  ๊ฐ„๊ฒฉ | 20 m | ์ž„๋„์„ค๊ณ„ ์ธก์  ๊ทœ์น™๊ณผ ๊ฐ™์Œ | ๊ณ ์ • | +| ํšก๋‹จ ํ‘œ์‹œ ํญ | ์ขŒ์šฐ ๊ฐ 20 m | ํ™”๋ฉด ๊ณ ์ •๊ฐ’ | ๊ณ ์ • | +| ์ง€ํ˜• ํŒ์ • ์—ฌ์œ  | ์ง€์ ์ด ์ง€ํ˜• ๊ฐ€์žฅ์ž๋ฆฌ์—์„œ 300 m ์•ˆ์ชฝ | ํƒ์ƒ‰ ๋ณต๋„์™€ ๊ฐ™์€ ๊ฐ’ | ๋ณต๋„ ๊ฐ’๊ณผ ํ•จ๊ป˜ ์›€์ง์ž„ | + +## ์ค€๋น„ ์ค‘ + +- ์˜ˆ์ •๋…ธ์„  ๋‹จ๊ณ„์—์„œ ์—ฐ๋„๋ณ„ ๋น„์ค‘์ด๋‚˜ ๋ชฉํ‘œ ๊ธฐ์šธ๊ธฐ๋ฅผ ์ง์ ‘ ์ •ํ•˜๋Š” ํ™”๋ฉด ์นธ์€ ์—†์Œ. ์ง€๊ธˆ์€ ๋ชจ๋‘ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๊ณ„์‚ฐํ•จ. +- 3D ๋ณด๊ธฐ์— ๋…ธ๊ฒฌ ยท ์ ˆ์„ฑํ† ๋ฉด ยท ๊ตฌ์กฐ๋ฌผ์„ ์˜ฌ๋ฆฌ๋Š” ๊ธฐ๋Šฅ์€ ์—†์Œ. diff --git a/resources/help/D04/00_์„ค๋ช…์„œ.md b/resources/help/D04/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..8127e7330 --- /dev/null +++ b/resources/help/D04/00_์„ค๋ช…์„œ.md @@ -0,0 +1,81 @@ +# ์˜ˆ์ •๋…ธ์„  ํ™•์ • ยท ์—…์ฒด ยท ๋‚ด๋ณด๋‚ด๊ธฐ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +- 2๋‹จ๊ณ„(๋ถ„์„)์—์„œ ์ €์žฅํ•œ ์—ฐ๋„๋ณ„ ๋…ธ์„ ์„ ํ•œ๋ˆˆ์— ํ™•์ธํ•จ(์—ฐ์žฅ ยท ์‹œ์  ยท ์ข…์  ยท ๋ˆ„๊ฐ€ ยท ํ™•์ • ์—ฌ๋ถ€). +- ์—ฐ๋„๋งˆ๋‹ค ์ผ์„ ๋งก๊ธธ ์—…์ฒด๋ฅผ ์ง€์ •ํ•จ. ์•„์ด์Šฌ๋กœ์— ๋“ฑ๋ก๋œ ํšŒ์‚ฌ๋ฅผ ๊ณ ๋ฅด๊ฑฐ๋‚˜, ์—…์ฒด๋ช…๊ณผ ๋ฉ”์ผ ์ฃผ์†Œ๋ฅผ ์ง์ ‘ ์ ๋Š” ์ˆ˜๊ธฐ ์ง€์ • ์ค‘ ์„ ํƒ. ํ•œ ์—ฐ๋„์— ์—…์ฒด ์—ฌ๋Ÿฟ ์ง€์ • ๊ฐ€๋Šฅ. +- [ํ™•์ •] ์œผ๋กœ ์—ฐ๋„ ๋…ธ์„ ์„ ํ™•์ •ํ•˜๊ณ , ์ง€์ • ์—…์ฒด์— ๋…ธ์„  ํŒŒ์ผ์ด ๋“  ์••์ถ• ํŒŒ์ผ(zip)์„ ๋ฉ”์ผ๋กœ ๋ณด๋ƒ„. +- [๋‚ด๋ณด๋‚ด๊ธฐ] ๋กœ ๋…ธ์„  ํŒŒ์ผ ๋ฌถ์Œ์„ ๋‚ด๋ ค๋ฐ›์Œ. +- ์—…์ฒด ์ชฝ์—์„œ๋Š” ๋ฐ›์€ ์˜ˆ์ •๋…ธ์„ ์„ ์ž๊ธฐ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋กœ ์—ฐ๊ฒฐํ•ด ์„ค๊ณ„๋ฅผ ์ด์–ด ๊ฐ(์•„๋ž˜ ใ€Œ๋ฐ›์€ ์—…์ฒด์˜ ์ž„๋„ ์—ฐ๊ฒฐใ€ ์ฐธ๊ณ ). + +## ํ™”๋ฉด ๊ตฌ์„ฑ + +- ๊ฐ€์šด๋ฐ = ์—ฐ๋„ ํ‘œ. ํ”„๋กœ์ ํŠธ ์ด๋ฆ„์ด ์œ„์— ํฌ๊ฒŒ ๋‚˜์˜ค๊ณ , ํ‘œ ์—ด์€ ์—ฐ๋„ ยท ์—ฐ์žฅ(m) ยท ์‹œ์ (์œ„๋„, ๊ฒฝ๋„) ยท ์ข…์ (์œ„๋„, ๊ฒฝ๋„) ยท ๋ˆ„๊ฐ€(m) ยท ํ™•์ • ยท ์ง€์ • ์—…์ฒด. +- ์™ผ์ชฝ ์œ„ = ใ€Œํ™•์ •ํ•  ์—ฐ๋„ใ€ ์นด๋“œ. ๋ถ„์„ [์ €์žฅ] ์ด ๋œ ์—ฐ๋„๋งˆ๋‹ค ์„ ํƒ ์ƒ์ž๊ฐ€ ์žˆ์Œ. ์•„์ง ํ™•์ •ํ•˜์ง€ ์•Š์€ ์—ฐ๋„๋Š” ์ฒ˜์Œ๋ถ€ํ„ฐ ์„ ํƒ๋˜์–ด ์žˆ์Œ. +- ์™ผ์ชฝ ์•„๋ž˜ = ๋‹จ์ถ” [๋‚ด๋ณด๋‚ด๊ธฐ] [ํ™•์ •] [์ €์žฅ]. + +## ์ž‘์—… ์ˆœ์„œ + +1. ์—ฐ๋„ ํ‘œ์—์„œ ์—ฐ๋„๋งˆ๋‹ค ์—ฐ์žฅ๊ณผ ๋ˆ„๊ฐ€๋ฅผ ํ™•์ธํ•จ. ๋ถ„์„์„ ์ €์žฅํ•˜์ง€ ์•Š์€ ์—ฐ๋„๋Š” ๋ˆ„๊ฐ€ ์นธ์— ใ€Œ๋…ธ์„  ๋ถ„์„ [์ €์žฅ] ์ „ใ€ ์ด ๋‚˜์˜ค๋ฉฐ, ํ™•์ •ํ•  ์—ฐ๋„ ๋ชฉ๋ก์—๋„ ๋‚˜ํƒ€๋‚˜์ง€ ์•Š์Œ. +2. ์ง€์ • ์—…์ฒด ์นธ์—์„œ ์—…์ฒด๋ฅผ ์ถ”๊ฐ€ํ•จ. + - ๋“ฑ๋ก ํšŒ์‚ฌ: ใ€Œ์—…์ฒด(๋“ฑ๋ก ํšŒ์‚ฌ ์ฐพ๊ธฐ ๋˜๋Š” ์ด๋ฆ„)ใ€ ์นธ์— ๊ธ€์ž๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ๊ฐ€์ž…๋œ ํšŒ์‚ฌ ์ด๋ฆ„์ด ๋ชฉ๋ก์œผ๋กœ ๋œธ. ๋ชฉ๋ก์—์„œ ๊ณ ๋ฅด๋ฉด ๋“ฑ๋ก ํšŒ์‚ฌ๋กœ ์ง€์ •๋จ. ๋ฉ”์ผ ์นธ์„ ๋น„์šฐ๋ฉด ๊ทธ ํšŒ์‚ฌ์˜ ๋Œ€ํ‘œ ๋ฉ”์ผ์ด ์“ฐ์ž„. + - ์ˆ˜๊ธฐ ์ง€์ •: ์—…์ฒด๋ช…๊ณผ ๋ฉ”์ผ ์ฃผ์†Œ(@ ํฌํ•จ)๋ฅผ ๋‘˜ ๋‹ค ์ ์Œ. ์—†์œผ๋ฉด ใ€Œ๋“ฑ๋ก ํšŒ์‚ฌ๊ฐ€ ์•„๋‹ˆ๋ฉด ์—…์ฒด๋ช…๊ณผ ๋ฉ”์ผ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.ใ€ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ค๊ณ  ์ถ”๊ฐ€๋˜์ง€ ์•Š์Œ. + - [์ถ”๊ฐ€] ๋ฅผ ๋ˆ„๋ฅด๋ฉด ๊ทธ ์—ฐ๋„ ์นธ์— ์—…์ฒด ์ด๋ฆ„ + ๋ฉ”์ผ์ด ํ‘œ์‹œ๋จ. ๊ฐ™์€ ์—…์ฒด๋ฅผ ๋‘ ๋ฒˆ ๋„ฃ์–ด๋„ ํ•œ ๋ฒˆ๋งŒ ๋“ค์–ด๊ฐ. + - ์ž˜๋ชป ๋„ฃ์€ ์—…์ฒด๋Š” ์—…์ฒด ์˜† [ร—] ๋กœ ๋บŒ. +3. [์ €์žฅ] ์„ ๋ˆ„๋ฅด๋ฉด ์ง€์ • ์—…์ฒด๊ฐ€ ์„œ๋ฒ„์— ์ €์žฅ๋จ(ใ€Œ์ง€์ • ์—…์ฒด๋ฅผ ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.ใ€). ์ €์žฅ ์ „์—๋Š” ํ‘œ ์œ„์— ใ€Œ์ €์žฅ ์•ˆ ๋œ ๋ณ€๊ฒฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.ใ€ ๊ฐ€ ํ‘œ์‹œ๋จ. +4. ใ€Œํ™•์ •ํ•  ์—ฐ๋„ใ€ ์—์„œ ํ™•์ •ํ•  ์—ฐ๋„๋ฅผ ๊ณ ๋ฅด๊ณ  [ํ™•์ •] ์„ ๋ˆ„๋ฆ„. ํ™•์ธ ์ฐฝ(ใ€Œ๊ณ ๋ฅธ ์—ฐ๋„๋ฅผ ํ™•์ •ํ•˜๊ณ  ์ง€์ • ์—…์ฒด์— ๋…ธ์„  ๋ฌถ์Œ์„ ๋ฉ”์ผ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์ง„ํ–‰ํ• ๊นŒ์š”?ใ€)์—์„œ ์ง„ํ–‰์„ ๋ˆ„๋ฅด๋ฉด ํ™•์ •๊ณผ ๋ฉ”์ผ ๋ฐœ์†ก์ด ์ด๋ค„์ง. ์ €์žฅํ•˜์ง€ ์•Š์€ ์—…์ฒด ๋ณ€๊ฒฝ์ด ์žˆ์œผ๋ฉด ์ž๋™์œผ๋กœ ๋จผ์ € ์ €์žฅ๋จ. +5. ํ•„์š”ํ•˜๋ฉด [๋‚ด๋ณด๋‚ด๊ธฐ] ๋กœ ํŒŒ์ผ์„ ๋‚ด๋ ค๋ฐ›์•„ ๋ณด๊ด€ํ•จ. + +## ๋‹จ์ถ” + +| ๋‹จ์ถ” | ํ•˜๋Š” ์ผ | +| --- | --- | +| ๋‚ด๋ณด๋‚ด๊ธฐ | ใ€Œํ”„๋กœ์ ํŠธ์ด๋ฆ„_์˜ˆ์ •๋…ธ์„ .zipใ€ ์„ ๋‚ด๋ ค๋ฐ›์Œ. ์‚ฐ๋ฆผ์ฒญ์€ ๋ชจ๋“  ์—ฐ๋„์™€ ์ „์ฒด ๋…ธ์„ ์„ ๋ฐ›์Œ. ์ง€์ •๋ฐ›์€ ํšŒ์‚ฌ๋Š” ํ™•์ •๋œ ์ž๊ธฐ ์—ฐ๋„๋งŒ ๋ฐ›์Œ | +| ํ™•์ • | ๊ณ ๋ฅธ ์—ฐ๋„๋ฅผ ํ™•์ • ์ฒ˜๋ฆฌํ•˜๊ณ  ์ง€์ • ์—…์ฒด์— ๋ฉ”์ผ์„ ๋ณด๋ƒ„. ๊ณ ๋ฅธ ์—ฐ๋„๊ฐ€ ์—†์œผ๋ฉด ใ€Œํ™•์ •ํ•  ์—ฐ๋„๋ฅผ ๊ณ ๋ฅด์„ธ์š”.ใ€ ์•ˆ๋‚ด | +| ์ €์žฅ | ์—ฐ๋„๋ณ„ ์ง€์ • ์—…์ฒด ๋ชฉ๋ก์„ ์„œ๋ฒ„์— ์ €์žฅํ•จ | + +- ์—…์ฒด ์ถ”๊ฐ€์™€ ์‚ญ์ œ๋Š” [์ €์žฅ] ์„ ๋ˆ„๋ฅด๊ธฐ ์ „๊นŒ์ง€ ์ด ๋ธŒ๋ผ์šฐ์ € ํƒญ์—๋งŒ ์ž„์‹œ๋กœ ๋‚จ์Œ. ํƒญ์„ ๋‹ซ์œผ๋ฉด ์‚ฌ๋ผ์ง€๋ฏ€๋กœ [์ €์žฅ] ์„ ๋ˆ„๋ฅผ ๊ฒƒ. +- [์ €์žฅ] [ํ™•์ •] ์€ ์‚ฐ๋ฆผ์ฒญ ์†Œ์† ์‚ฌ์šฉ์ž๋งŒ ๊ฐ€๋Šฅ. ๊ทธ ๋ฐ–์˜ ์‚ฌ์šฉ์ž๊ฐ€ ๋ˆ„๋ฅด๋ฉด ใ€Œ์‚ฐ๋ฆผ์ฒญ๋งŒ ์˜ˆ์ •๋…ธ์„ ์„ ํ™•์ • ยท ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.ใ€ ์•ˆ๋‚ด๊ฐ€ ๋‚˜์˜ด. + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +- **ํ™•์ • ์—ด** = ํ™•์ •ํ•œ ๋‚ ์งœ์™€ ์‹œ๊ฐ(ํ•œ๊ตญ ์‹œ๊ฐ„). ๋น„์–ด ์žˆ์œผ๋ฉด ์•„์ง ํ™•์ • ์ „. +- **์ง€์ • ์—…์ฒด ์—ด์˜ ํ‘œ์‹œ** + - ใ€ŒAisloใ€ = ์•„์ด์Šฌ๋กœ ๋“ฑ๋ก ํšŒ์‚ฌ๋กœ ์ง€์ •๋œ ์—…์ฒด. + - ใ€Œ๋ฉ”์ผ ๋ณด๋ƒ„ใ€ = ํ™•์ • ๋•Œ ๋ฉ”์ผ์ด ๋‚˜๊ฐ”๋‹ค๋Š” ํ‘œ์‹œ. ๊ฐ™์€ ์—…์ฒด์—๋Š” ๋‹ค์‹œ ํ™•์ •ํ•ด๋„ ์ค‘๋ณต ๋ฐœ์†กํ•˜์ง€ ์•Š์Œ. + - ใ€Œ์—ฐ๊ฒฐ๋จใ€ = ๊ทธ ์—…์ฒด๊ฐ€ ์ด ์—ฐ๋„ ๋…ธ์„ ์„ ์ž๊ธฐ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋กœ ๊ฐ€์ ธ๊ฐ€ ์—ฐ๊ฒฐํ–ˆ๋‹ค๋Š” ํ‘œ์‹œ. +- **[ํ™•์ •] ํ›„ ์•ˆ๋‚ด ๋ฌธ๊ตฌ** = ใ€Œํ™•์ • โ€” ๋ฉ”์ผ sent 2 ยท already_sent 1ใ€ ๊ผด๋กœ ์—…์ฒด๋ณ„ ์ฒ˜๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ๊ฐœ์ˆ˜๋กœ ๋ณด์—ฌ ์คŒ. + +| ๊ฒฐ๊ณผ ์ด๋ฆ„ | ๋œป | +| --- | --- | +| sent | ๋ฉ”์ผ ๋ฐœ์†ก ์„ฑ๊ณต | +| already_sent | ์ด๋ฏธ ๋ณด๋‚ธ ์—…์ฒด์—ฌ์„œ ๋‹ค์‹œ ๋ณด๋‚ด์ง€ ์•Š์Œ | +| no_email | ๋ฉ”์ผ ์ฃผ์†Œ๊ฐ€ ์—†์–ด ๋ณด๋‚ด์ง€ ๋ชปํ•จ(๋“ฑ๋ก ํšŒ์‚ฌ์˜ ๋Œ€ํ‘œ ๋ฉ”์ผ์ด ๋น„์–ด ์žˆ๋Š” ๊ฒฝ์šฐ ๋“ฑ) | +| failed | ๋ฉ”์ผ ๋ฐœ์†ก ์‹คํŒจ | + +## ๋ณด๋‚ด์ง€๋Š” ๋ฉ”์ผ + +- ์ œ๋ชฉ: ใ€Œ[์˜ˆ์ •๋…ธ์„  ํ™•์ •] ํ”„๋กœ์ ํŠธ์ด๋ฆ„ N๋…„ใ€. +- ๋ณธ๋ฌธ: ํ”„๋กœ์ ํŠธ ยท ์‚ฌ์—… ์ง€์—ญ ยท ์—ฐ๋„ ยท ์—ฐ์žฅ(km ์™€ m) ์š”์•ฝ๊ณผ ์ฒจ๋ถ€ ํŒŒ์ผ ๋ชฉ๋ก, ์••์ถ•์„ ํ’€๋ฉด ๋…ธ์„  ์ž…๋ ฅ์— ๋ฐ”๋กœ ์˜ฌ๋ฆด ์ˆ˜ ์žˆ๋‹ค๋Š” ์•ˆ๋‚ด ๋ฌธ๊ตฌ. +- ์ฒจ๋ถ€: ๊ทธ ์—…์ฒด์˜ ์—ฐ๋„ ํ•œ ํ•ด ๋ถ„๋Ÿ‰ zip 1๊ฐœ(ํŒŒ์ผ ์ด๋ฆ„ ใ€Œํ”„๋กœ์ ํŠธ์ด๋ฆ„_์˜ˆ์ •๋…ธ์„ _์—ฐ๋„.zipใ€). ๋‚ด์šฉ์€ ใ€Œ10_๋‚ด๋ณด๋‚ด๊ธฐ_ํŒŒ์ผ_๊ตฌ์„ฑใ€ ์ฐธ๊ณ . + +## ๋ฐ›์€ ์—…์ฒด์˜ ์ž„๋„ ์—ฐ๊ฒฐ + +- ์ง€์ •๋ฐ›์€ ๋“ฑ๋ก ํšŒ์‚ฌ๋Š” ๋Œ€์‹œ๋ณด๋“œ์˜ ใ€Œ๋ฐ›์€ ์˜ˆ์ •๋…ธ์„ ใ€ ๊ตฌ์—ญ์—์„œ ํ™•์ •๋œ ์—ฐ๋„๋ฅผ ๋ด„. ์—ด์€ ์˜ˆ์ •๋…ธ์„  ยท ์—ฐ๋„ ยท ํ™•์ • ยท ์—ฐ๊ฒฐ ์ƒํƒœ(์—ฐ๊ฒฐ๋จ / ๋ฏธ์—ฐ๊ฒฐ / ์—…๋ฐ์ดํŠธ ์žˆ์Œ) ยท ์ž‘์—…. +- [๋ณด๊ธฐ] = ์˜ˆ์ •๋…ธ์„  ํ™”๋ฉด์„ ๋ณด๊ธฐ๋งŒ ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋กœ ์—ถ. +- [์—ฐ๊ฒฐ] = ๋ชจ๋‹ฌ์—์„œ ๋‘ ๊ฐ€์ง€ ์ค‘ ์„ ํƒ. + - ใ€Œ์ƒˆ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋กœใ€: ์˜ˆ์ •๋…ธ์„  ์ด๋ฆ„ + ์—ฐ๋„๋กœ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค๊ณ  ๋…ธ์„  ์ž…๋ ฅ ๋‹จ๊ณ„(B03)๋กœ ์ด๋™. ์˜ˆ์ •๋…ธ์„ ์˜ ์„ค๊ณ„์†๋„(20 ยท 30 ยท 40 km/h)๊ฐ€ ์ƒˆ ํ”„๋กœ์ ํŠธ์˜ ์„ค๊ณ„์†๋„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋“ค์–ด๊ฐ. + - ใ€Œ๊ธฐ์กด ํ”„๋กœ์ ํŠธ์— ์—ฐ๊ฒฐใ€: ์šฐ๋ฆฌ ํšŒ์‚ฌ์˜ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋ฅผ ๊ณจ๋ผ ๊ทธ ํ”„๋กœ์ ํŠธ์— ์—ฐ๊ฒฐ. ์ž„๋„ ํ”„๋กœ์ ํŠธ๊ฐ€ ์—†์œผ๋ฉด ใ€Œ์šฐ๋ฆฌ ํšŒ์‚ฌ ์ž„๋„ ํ”„๋กœ์ ํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.ใ€ ํ‘œ์‹œ. +- ์—ฐ๊ฒฐํ•˜๋ฉด ๊ทธ ์—ฐ๋„ ๋…ธ์„ (shapefile ํ•œ ๋ฒŒ)์ด ๋…ธ์„  ์ž…๋ ฅ ๋‹จ๊ณ„์˜ ์ž…๋ ฅ ํŒŒ์ผ๋กœ ๋“ค์–ด๊ฐ. ํŒŒ์ผ์„ ์ง์ ‘ ์˜ฌ๋ฆฐ ๊ฒƒ๊ณผ ๊ฐ™์€ ๊ธธ์ด๋ผ ์ดํ›„ ์ฒ˜๋ฆฌ๋„ ๊ฐ™์Œ. ์ง€ํ˜• ์ž๋ฃŒ๋Š” ์—…์ฒด๊ฐ€ ๋”ฐ๋กœ ์˜ฌ๋ ค์•ผ ํ•จ. +- ์ด๋ฏธ ๋ถ„์„ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ๋Š” ์ž„๋„ ํ”„๋กœ์ ํŠธ์— ์—ฐ๊ฒฐํ•˜๋ฉด ๊ธฐ์กด ๊ฒฐ๊ณผ๋ฅผ ์ง€์›Œ๋„ ๋˜๋Š”์ง€ ๋ฌป๋Š” ํ™•์ธ ์ฐฝ์ด ๋จผ์ € ๋‚˜์˜ด. ์ทจ์†Œํ•˜๋ฉด ์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๋€Œ์ง€ ์•Š์Œ. +- ์ž„๋„ ํ”„๋กœ์ ํŠธ ๋…ธ์„  ์ž…๋ ฅ ํ™”๋ฉด์˜ [์˜ˆ์ •๋…ธ์„ ์—์„œ ๊ฐ€์ ธ์˜ค๊ธฐ] ๋‹จ์ถ”๋กœ๋„ ๊ฐ™์€ ์ผ์„ ํ•  ์ˆ˜ ์žˆ์Œ. ์ง€์ •๋ฐ›์€ ์—ฐ๋„๋ฅผ ๊ณ ๋ฅด๋ฉด ๋…ธ์„ ์ด ์ž…๋ ฅ๋จ. +- ์—ฐ๊ฒฐ ์‹œ์ ์˜ ๋…ธ์„ ์ด ๋ณต์‚ฌ๋˜๋ฏ€๋กœ, ์‚ฐ๋ฆผ์ฒญ์ด ๋’ค์— ๋…ธ์„ ์„ ๋ฐ”๊ฟ”๋„ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋Š” ์ž๋™์œผ๋กœ ๋ฐ”๋€Œ์ง€ ์•Š์Œ. ๋Œ€์‹  ใ€Œ์—…๋ฐ์ดํŠธ ์žˆ์Œใ€ ํ‘œ์‹œ๊ฐ€ ๋œจ๋ฉฐ, ๋…ธ์„  ์ž…๋ ฅ ํ™”๋ฉด ์œ„์ชฝ ๋ ์—์„œ [๋‹ค์‹œ ๊ฐ€์ ธ์˜ค๊ธฐ] ๋กœ ์ง์ ‘ ๋ฐ˜์˜ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•จ. +- ํ•œ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋Š” ํ•œ ์—ฐ๋„์—๋งŒ ์—ฐ๊ฒฐ๋จ. ๋‹ค๋ฅธ ์—ฐ๋„๋กœ ๋‹ค์‹œ ์—ฐ๊ฒฐํ•˜๋ฉด ์•ž์˜ ์—ฐ๊ฒฐ์€ ํ’€๋ฆผ. + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- ํ™•์ • ์‹œ๊ฐ์€ [ํ™•์ •] ์„ ๋ˆ„๋ฅผ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ ๊ธฐ๋ก๋จ. ์ด๋ฏธ ๋ฉ”์ผ์„ ๋ณด๋‚ธ ์—…์ฒด์—๋Š” ๋‹ค์‹œ ๋ณด๋‚ด์ง€ ์•Š์Œ(๋‹ค์‹œ ๋ณด๋‚ด๋Š” ๋‹จ์ถ”๋Š” ํ™”๋ฉด์— ์—†์Œ). +- ํ™•์ •์€ ๋ถ„์„ [์ €์žฅ] ์ด ๋œ ์—ฐ๋„๋งŒ ๊ฐ€๋Šฅ. ์ €์žฅํ•˜์ง€ ์•Š์€ ์—ฐ๋„๋ฅผ ์–ต์ง€๋กœ ํ™•์ •ํ•˜๋ ค ํ•˜๋ฉด ใ€Œ๋ถ„์„ [์ €์žฅ]์ด ์•ˆ ๋œ ์—ฐ๋„์ž…๋‹ˆ๋‹คใ€ ์˜ค๋ฅ˜๊ฐ€ ๋‚˜์˜ด. +- [์ €์žฅ] ์€ ํ‘œ์— ์žˆ๋Š” ๋ชจ๋“  ์—ฐ๋„์˜ ์ง€์ • ์—…์ฒด๋ฅผ ํ™”๋ฉด ๋‚ด์šฉ๋Œ€๋กœ ๋งž์ถค. ์—…์ฒด๋ฅผ ๋ชจ๋‘ ๋บ€ ์—ฐ๋„๋Š” ์ง€์ •์ด ๋น„์›Œ์ง. +- ๊ฐ™์€ ์—…์ฒด ํŒ์ •: ๋“ฑ๋ก ํšŒ์‚ฌ๋Š” ํšŒ์‚ฌ ๋ฒˆํ˜ธ, ์ˆ˜๊ธฐ ์—…์ฒด๋Š” ๋ฉ”์ผ ์ฃผ์†Œ(๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์—†์Œ). +- ๋‚ด๋ณด๋‚ด๊ธฐ๋Š” ์„œ๋ฒ„์— ์ €์žฅ๋œ ๋…ธ์„ ๋งŒ ์‚ฌ์šฉํ•จ. 2๋‹จ๊ณ„์—์„œ [์ €์žฅ] ํ•˜์ง€ ์•Š์€ ๋ณ€๊ฒฝ์€ ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ. diff --git a/resources/help/D04/10_๋‚ด๋ณด๋‚ด๊ธฐ_ํŒŒ์ผ_๊ตฌ์„ฑ.md b/resources/help/D04/10_๋‚ด๋ณด๋‚ด๊ธฐ_ํŒŒ์ผ_๊ตฌ์„ฑ.md new file mode 100644 index 000000000..edfcf1655 --- /dev/null +++ b/resources/help/D04/10_๋‚ด๋ณด๋‚ด๊ธฐ_ํŒŒ์ผ_๊ตฌ์„ฑ.md @@ -0,0 +1,64 @@ +# ๋‚ด๋ณด๋‚ด๊ธฐ ํŒŒ์ผ ๊ตฌ์„ฑ + +## ํ•œ๋ˆˆ์— + +- ๋‚ด๋ ค๋ฐ›๋Š” ํŒŒ์ผ ์ด๋ฆ„ = ใ€Œํ”„๋กœ์ ํŠธ์ด๋ฆ„_์˜ˆ์ •๋…ธ์„ .zipใ€. ์ด๋ฆ„์— ์“ธ ์ˆ˜ ์—†๋Š” ๊ธ€์ž๋Š” ๋ฐ‘์ค„(_)๋กœ ๋ฐ”๋€œ. +- ์••์ถ• ์•ˆ์— ์—ฐ๋„ ์ด๋ฆ„์˜ ํด๋”๊ฐ€ ์—ฐ๋„๋งˆ๋‹ค ํ•˜๋‚˜, ๊ทธ๋ฆฌ๊ณ  ใ€Œ์ „์ฒดใ€ ํด๋”๊ฐ€ ํ•˜๋‚˜ ๋“ค์–ด ์žˆ์Œ(์ „์ฒด ํด๋”๋Š” ์‚ฐ๋ฆผ์ฒญ๋งŒ ๋ฐ›์Œ). +- ์—…์ฒด์— ๋ณด๋‚ด๋Š” ๋ฉ”์ผ ์ฒจ๋ถ€ zip ์€ ํ•œ ์—ฐ๋„ ๋ถ„๋Ÿ‰์ด๋ฉฐ, ํด๋” ์—†์ด ๊ทธ ์—ฐ๋„ ํŒŒ์ผ๋“ค์ด ๋ฐ”๋กœ ๋“ค์–ด ์žˆ์Œ. + +| ์œ„์น˜ | ๋‚ด์šฉ | +| --- | --- | +| ์—ฐ๋„ ํด๋”(์˜ˆ: 2026) | ๊ทธํ•ด ์‹œ์ ์—์„œ ์ข…์ ๊นŒ์ง€ ์ค‘์‹ฌ์„  ํ•œ ์ค„(shapefile ํ•œ ๋ฒŒ) + ์ขŒํ‘œํ‘œ ์—‘์…€ | +| ์ „์ฒด ํด๋” | ๋ชจ๋“  ์—ฐ๋„๋ฅผ ์ด์€ ํ•œ ์ค„(shapefile ํ•œ ๋ฒŒ, ๋ฐœ์ฃผ์ฒ˜ ๋ณด๊ด€์šฉ) + ๋ชจ๋“  ์—ฐ๋„๊ฐ€ ๋‹ด๊ธด ์ขŒํ‘œํ‘œ ์—‘์…€ | + +## shapefile ํ•œ ๋ฒŒ + +- ํ™•์žฅ์ž ๋‹ค์„ฏ ๊ฐ€์ง€๊ฐ€ ํ•œ ๋ฒŒ์ž„: .shp(์„  ๋ชจ์–‘) ยท .shx(์ƒ‰์ธ) ยท .dbf(์†์„ฑ ํ‘œ) ยท .prj(์ขŒํ‘œ๊ณ„ ์ •์˜) ยท .cpg(๊ธ€์ž ์ฝ”๋“œ). ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ์•ˆ ๋˜๋ฏ€๋กœ ๊ฐ™์ด ๋ณด๊ด€ํ•  ๊ฒƒ. +- ํŒŒ์ผ ์ด๋ฆ„ = ใ€Œ๋Œ€์ƒ์ง€_์ง€์—ญ.์—ฐ์žฅkm_์—ฐ๋„ใ€. ์˜ˆ: ์—ฐ์žฅ์ด 2.2 km ์ด๋ฉด ใ€Œ๋Œ€์ƒ์ง€_์ง€์—ญ.2.2_2026ใ€. ์ „์ฒด ๋…ธ์„ ์€ ์—ฐ๋„ ์ž๋ฆฌ์— ใ€Œ์ „์ฒดใ€. +- ์ขŒํ‘œ๊ณ„ = UTM-K(EPSG:5179). ํ”„๋กœ์ ํŠธ๊ฐ€ ๋‹ค๋ฅธ ์ขŒํ‘œ๊ณ„๋กœ ์ž‘์—…ํ–ˆ๋”๋ผ๋„ ๋‚ด๋ณด๋‚ผ ๋•Œ 5179 ๋กœ ๋ฐ”๋€œ. +- ๊ธ€์ž ์ฝ”๋“œ = cp949(ํ•œ๊ธ€ ์œˆ๋„์šฐ ํ‘œ์ค€). ์—‘์…€ ยท ์ง€๋ฆฌ์ •๋ณด ํ”„๋กœ๊ทธ๋žจ์—์„œ ํ•œ๊ธ€ ์นธ ์ด๋ฆ„์ด ๊นจ์ง€์ง€ ์•Š์Œ. +- ์„  ๋ชจ์–‘ = ํ‰๋ฉด ์ค‘์‹ฌ์„  ํ•œ ์ค„(๋†’์ด ๊ฐ’ ์—†์Œ), ๊ธฐ๋ก ํ•œ ๊ฑด. +- ์–‘์‹์€ 2026๋…„ ์ž„๋„ ์šฉํ™” ์˜ˆ์ •๋…ธ์„  ํŒŒ์ผ(๋Œ€์ƒ์ง€_์ผ์›”.์šฉํ™”.2.2)๊ณผ ๊ฐ™์€ ๊ผด์ž„. ์ขŒํ‘œ๊ณ„ ์ •์˜(.prj)๋Š” ๊ทธ ํŒŒ์ผ ์›๋ฌธ์„ ๊ทธ๋Œ€๋กœ ์”€. + +### ์†์„ฑ ํ‘œ ์นธ + +| ์นธ ์ด๋ฆ„ | ๋œป | ํ˜•์‹ | +| --- | --- | --- | +| ๋Œ€์ƒ์ง€ | ใ€Œ์‚ฌ์—…์ง€์—ญ.์—ฐ์žฅkmใ€ ๊ธ€์ž(์˜ˆ: ์ง€์—ญ.2.2). 20๋ฐ”์ดํŠธ(ํ•œ๊ธ€ 10์ž ์•ˆํŒŽ)๋ฅผ ๋„˜์œผ๋ฉด ๋’ค๊ฐ€ ์ž˜๋ฆผ | ๊ธ€์ž 20 | +| ๊ธธ์ด | ๋…ธ์„  ์—ฐ์žฅ, ๋‹จ์œ„ km(์†Œ์ˆ˜ ์…‹์งธ ์ž๋ฆฌ) | ์ˆซ์ž 11.3 | +| ์—ฐ๋„ | ์‚ฌ์—… ์—ฐ๋„. ์ „์ฒด ๋…ธ์„ ์€ ๋น„์–ด ์žˆ์Œ | ์ •์ˆ˜ 4 | +| ์‹œ์ ๋ˆ„๊ฐ€ | ๊ทธ ์„ ์ด ์‹œ์ž‘ํ•˜๋Š” ์ธก์  ๊ฐ’(m) | ์ˆซ์ž 11.2 | +| ์ข…์ ๋ˆ„๊ฐ€ | ๊ทธ ์„ ์ด ๋๋‚˜๋Š” ์ธก์  ๊ฐ’(m) | ์ˆซ์ž 11.2 | + +- ๋Œ€์ƒ์ง€ ยท ๊ธธ์ด ๋‘ ์นธ์€ ์šฉํ™” ์–‘์‹๊ณผ ๊ฐ™๊ณ , ์—ฐ๋„ ยท ์‹œ์ ๋ˆ„๊ฐ€ ยท ์ข…์ ๋ˆ„๊ฐ€ ๋Š” ์ด ํ”„๋กœ๊ทธ๋žจ์ด ๋ง๋ถ™์ธ ์นธ์ž„. +- ๋ˆ„๊ฐ€ ๊ฐ’์€ 2๋‹จ๊ณ„์—์„œ ์ด์–ด ์…ˆํ•œ ๊ฐ’ ๊ทธ๋Œ€๋กœ์ž„. ์•ž ์—ฐ๋„ ์ข…์  ๋ˆ„๊ฐ€ = ๋‹ค์Œ ์—ฐ๋„ ์‹œ์  ๋ˆ„๊ฐ€. + +## ์ขŒํ‘œํ‘œ ์—‘์…€ + +- ํŒŒ์ผ ์ด๋ฆ„ = ใ€Œ์ขŒํ‘œํ‘œ_ํ”„๋กœ์ ํŠธ์ด๋ฆ„.xlsxใ€, ์‹œํŠธ ์ด๋ฆ„ ใ€Œ์ขŒํ‘œํ‘œใ€. +- ์ฒซ ์ค„์— ใ€Œํ”„๋กœ์ ํŠธ์ด๋ฆ„ ์˜ˆ์ •๋…ธ์„  ์ขŒํ‘œํ‘œ (UTM-K EPSG:5179)ใ€ ์ œ๋ชฉ, ๋‘˜์งธ ์ค„์ด ์—ด ์ด๋ฆ„, ์ดํ›„ ์—ฐ๋„๋งˆ๋‹ค ํ•œ ์ค„. +- ์—ฐ๋„ ํด๋”์˜ ์ขŒํ‘œํ‘œ๋Š” ๊ทธ ์—ฐ๋„ ํ•œ ์ค„, ์ „์ฒด ํด๋”์˜ ์ขŒํ‘œํ‘œ๋Š” ๋ชจ๋“  ์—ฐ๋„๊ฐ€ ์ค„๋งˆ๋‹ค ๋“ค์–ด ์žˆ์Œ. + +| ์—ด | ๋œป | +| --- | --- | +| ์—ฐ๋„ | ์‚ฌ์—… ์—ฐ๋„ | +| ์‹œ์  X ยท ์‹œ์  Y | ์‹œ์ž‘์  UTM-K ์ขŒํ‘œ(m) | +| ์ข…์  X ยท ์ข…์  Y | ๋์  UTM-K ์ขŒํ‘œ(m) | +| ์‹œ์  ๊ฒฝ๋„ ยท ์‹œ์  ์œ„๋„ | ์‹œ์ž‘์  ์œ„๊ฒฝ๋„(๋„) | +| ์ข…์  ๊ฒฝ๋„ ยท ์ข…์  ์œ„๋„ | ๋์  ์œ„๊ฒฝ๋„(๋„) | +| ์‹œ์ ๋ˆ„๊ฐ€(m) ยท ์ข…์ ๋ˆ„๊ฐ€(m) | ์ด์–ด ์…ˆํ•œ ์ธก์  ๊ฐ’ | +| ์—ฐ์žฅ(m) | ๊ทธ ์—ฐ๋„ ๋…ธ์„  ๊ธธ์ด | + +## ๋ณด๋Š” ์‚ฌ๋žŒ์— ๋”ฐ๋ฅธ ์ฐจ์ด + +| ์‚ฌ์šฉ์ž | ๋ฐ›๋Š” ๋ฒ”์œ„ | +| --- | --- | +| ์‚ฐ๋ฆผ์ฒญ | ๋ถ„์„์ด ์ €์žฅ๋œ ๋ชจ๋“  ์—ฐ๋„ ํด๋” + ์ „์ฒด ํด๋” | +| ์ง€์ •๋ฐ›์€ ๋“ฑ๋ก ํšŒ์‚ฌ | ํ™•์ •๋˜์—ˆ๊ณ  ์ž๊ธฐ ํšŒ์‚ฌ๊ฐ€ ์ง€์ •๋œ ์—ฐ๋„ ํด๋”๋งŒ. ์ „์ฒด ํด๋” ์—†์Œ. ๋ฐ›์„ ์—ฐ๋„๊ฐ€ ์—†์œผ๋ฉด ใ€Œ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ์—ฐ๋„๊ฐ€ ์—†์Šต๋‹ˆ๋‹คใ€ ๋กœ ๊ฑฐ์ ˆ๋จ | +| ์ €์žฅ๋œ ๋ถ„์„์ด ์ „ํ˜€ ์—†์„ ๋•Œ | ใ€Œ๋ถ„์„ [์ €์žฅ]์ด ๋œ ์—ฐ๋„๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.ใ€ ๋กœ ๊ฑฐ์ ˆ๋จ | + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- ํŒŒ์ผ์€ ์„œ๋ฒ„์— ์ €์žฅ๋œ ๋…ธ์„ ์œผ๋กœ๋งŒ ๋งŒ๋“ค๋ฉฐ, ํ™”๋ฉด์—์„œ ์•„์ง ์ €์žฅํ•˜์ง€ ์•Š์€ ๋…ธ์„ ์€ ๋ฐ˜์˜๋˜์ง€ ์•Š์Œ. +- ์ „์ฒด ๋…ธ์„ ์€ ์•ž ์—ฐ๋„ ๋์ ๊ณผ ๋‹ค์Œ ์—ฐ๋„ ์‹œ์ž‘์ ์ด ๊ฒน์น˜๋ฏ€๋กœ ๊ฒน์น˜๋Š” ํ•œ ์ ์„ ๋นผ๊ณ  ์ด์–ด ๋ถ™์ž„. +- ์ž„๋„ ํ”„๋กœ์ ํŠธ๋กœ ์—ฐ๊ฒฐํ•  ๋•Œ๋Š” ์ขŒํ‘œํ‘œ ์—†์ด shapefile ํ•œ ๋ฒŒ๋งŒ ๋…ธ์„  ์ž…๋ ฅ์— ๋“ค์–ด๊ฐ. diff --git a/resources/help/D04/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md b/resources/help/D04/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md new file mode 100644 index 000000000..4e12a87dd --- /dev/null +++ b/resources/help/D04/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md @@ -0,0 +1,18 @@ +# ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ + +์•„๋ž˜ ํ•ญ๋ชฉ์€ ์ฝ”๋“œ์˜ ํ˜„์žฌ ๋™์ž‘์ด๋ฉฐ, ๋ฐ”๊พธ๋ ค๋ฉด ๊ฐœ๋ฐœ ๋‹ด๋‹น์—๊ฒŒ ์š”์ฒญ. + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| ๋‚ด๋ณด๋‚ด๊ธฐ ์ขŒํ‘œ๊ณ„ | UTM-K(EPSG:5179) ๊ณ ์ • | ์šฉํ™” ์˜ˆ์ •๋…ธ์„  ์–‘์‹ | ์–‘์‹ ๋”ฐ๋ฆ„ | +| ๊ธ€์ž ์ฝ”๋“œ | cp949 | ์šฉํ™” ์˜ˆ์ •๋…ธ์„  ์–‘์‹ | ์–‘์‹ ๋”ฐ๋ฆ„ | +| ๋ง๋ถ™์€ ์†์„ฑ ์นธ | ์—ฐ๋„ ยท ์‹œ์ ๋ˆ„๊ฐ€ ยท ์ข…์ ๋ˆ„๊ฐ€ | ์ด ํ”„๋กœ๊ทธ๋žจ์—์„œ ์ถ”๊ฐ€ | ์—…์ฒด ์ชฝ ์š”์ฒญ ์‹œ ์กฐ์ • ๊ฐ€๋Šฅ | +| ์ขŒํ‘œํ‘œ ์—‘์…€ | ์—ฐ๋„ ํด๋” ยท ์ „์ฒด ํด๋”์— ํฌํ•จ, ์„ ํƒ ๊ธฐ๋Šฅ์€ ์•„๋‹˜ | ์š”๊ตฌ์„œ๋Š” ใ€Œ์„ ํƒใ€ ์œผ๋กœ ์ ์—ˆ์œผ๋‚˜ ์‹ค์ œ๋กœ๋Š” ํ•ญ์ƒ ๋งŒ๋“ฆ | ํ™•์ • ์ „ | +| ๋ฉ”์ผ ์žฌ๋ฐœ์†ก | ์ด๋ฏธ ๋ณด๋‚ธ ์—…์ฒด์—๋Š” ๋‹ค์‹œ ๋ณด๋‚ด์ง€ ์•Š์Œ, ํ™”๋ฉด์— ์žฌ๋ฐœ์†ก ๋‹จ์ถ” ์—†์Œ | ์„œ๋ฒ„์— ์žฌ๋ฐœ์†ก ์˜ต์…˜์€ ์žˆ์œผ๋‚˜ ํ™”๋ฉด์— ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์Œ | ์ค€๋น„ ์ค‘ | +| ์—ฐ๊ฒฐ ์‹œ ์—ฐ๋„ ์ธก์  | ์—ฐ๊ฒฐํ•œ ์ž„๋„ ํ”„๋กœ์ ํŠธ์—์„œ ์ธก์ ์ด ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ• ์ง€ | ์ด์–ด ๋ฐ›๊ธฐ ๊ธฐ๋Šฅ์ด ํ•„์š”ํ•ด ๋’ค๋กœ ๋ฏธ๋ฃฌ ํ•ญ๋ชฉ | ์ค€๋น„ ์ค‘ | +| ์„ค๊ณ„์†๋„ ์ „๋‹ฌ | ์ƒˆ ์ž„๋„ ํ”„๋กœ์ ํŠธ๋กœ ์—ฐ๊ฒฐํ•  ๋•Œ ์˜ˆ์ •๋…ธ์„  ์„ค๊ณ„์†๋„๊ฐ€ ๋…ธ์„  ๋‹จ๊ณ„(B05) ์„ค์ •์—๋„ ๋“ค์–ด๊ฐ. ๊ธฐ์กด ํ”„๋กœ์ ํŠธ๋Š” ํ”„๋กœ์ ํŠธ ์„ค๊ณ„์†๋„ ์นธ์ด ๋น„์–ด ์žˆ์„ ๋•Œ๋งŒ ์ฑ„์›€ | ์ž„๋„์„ค๊ณ„ ์„ค์ •๊ณผ ๊ฐ™์€ ๊ฐ’ ๋ฒ”์œ„ | ๋™์ž‘ ์ค‘ | + +## ์ค€๋น„ ์ค‘ + +- ์ง€์ •๋ฐ›์€ ์—…์ฒด์— ํ™•์ • ๋ฉ”์ผ ์™ธ ๋ณ„๋„ ์•Œ๋ฆผ์„ ๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์€ ์—†์Œ. +- ์—…์ฒด ์ง€์ • ์ด๋ ฅ(๋ˆ„๊ฐ€ ์–ธ์ œ ๋ฐ”๊ฟจ๋Š”์ง€)์„ ๋ณด์—ฌ ์ฃผ๋Š” ํ™”๋ฉด์€ ์—†์Œ. diff --git a/resources/help/E01/00_์„ค๋ช…์„œ.md b/resources/help/E01/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..0998ead39 --- /dev/null +++ b/resources/help/E01/00_์„ค๋ช…์„œ.md @@ -0,0 +1,55 @@ +# ์•ˆ์ •์„ฑํ‰๊ฐ€ ๋งŒ๋“ค๊ธฐ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +- ํšก๋‹จ ์„ค๊ณ„(B06)๊นŒ์ง€ ๋๋‚œ ์ž„๋„ ํ”„๋กœ์ ํŠธ ํ•˜๋‚˜๋ฅผ ๊ณจ๋ผ ์ƒˆ ์•ˆ์ •์„ฑํ‰๊ฐ€ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ฆ. +- ๋งŒ๋“œ๋Š” ์ˆœ๊ฐ„์˜ ์›๋ณธ ์ž๋ฃŒ๋ฅผ ์•ˆ์ •์„ฑํ‰๊ฐ€ ์ชฝ์— ๋”ฐ๋กœ ๋ณต์‚ฌํ•ด ๋‘ (๋™๊ฒฐ ๋ณต์‚ฌ). ๊ทธ ๋’ค ํ•ด์„์€ ๋ชจ๋‘ ์ด ๋ณต์‚ฌ๋ณธ์œผ๋กœ ํ•จ. +- ๋Œ€์‹œ๋ณด๋“œ [+] ์ƒˆ ํ”„๋กœ์ ํŠธ โ†’ ใ€Œ์•ˆ์ •์„ฑํ‰๊ฐ€ใ€๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ด ํ™”๋ฉด์ด ์—ด๋ฆผ. + +## ์ž‘์—… ์ˆœ์„œ + +1. **๋Œ€์ƒ ์ž„๋„ ํ”„๋กœ์ ํŠธ** ์นธ์—์„œ ์›๋ณธ ์ž„๋„๋ฅผ ๊ณ ๋ฆ„. ๋ชฉ๋ก ๊ธ€ = ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ ยท ์ง€์—ญ ยท ์‚ฌ์—… ์—ฐ๋„(์ตœ๊ทผ์— ๊ณ ์นœ ํ”„๋กœ์ ํŠธ๊ฐ€ ์œ„). +2. **ํ”„๋กœ์ ํŠธ๋ช…** ์นธ์„ ํ™•์ธํ•จ. ์†๋Œ€์ง€ ์•Š์œผ๋ฉด ใ€Œ์›๋ณธ ์ด๋ฆ„ ์•ˆ์ •์„ฑํ‰๊ฐ€ใ€๋กœ ์ž๋™์œผ๋กœ ์ฑ„์›Œ์ง โ€” ์›ํ•˜๋Š” ์ด๋ฆ„์œผ๋กœ ๋ฐ”๊ฟ”๋„ ๋จ. +3. [๋“ฑ๋ก]์„ ๋ˆ„๋ฆ„. ๋ณต์‚ฌํ•˜๋Š” ๋™์•ˆ ํ™”๋ฉด ์ „์ฒด๊ฐ€ ์ž ๊ธฐ๊ณ , ๋๋‚˜๋ฉด ใ€Œํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.ใ€ ์•Œ๋ฆผ์ด ๋œธ. +4. ์ƒˆ ํ”„๋กœ์ ํŠธ๊ฐ€ ํ˜„์žฌ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋˜๊ณ  ์ฒซ ๋‹จ๊ณ„ ใ€Œ์ง€๋ฐ˜ ์กฐ๊ฑดใ€(E02) ํ™”๋ฉด์œผ๋กœ ๋„˜์–ด๊ฐ. + +## ๋‹จ์ถ” + +| ๋‹จ์ถ” | ํ•˜๋Š” ์ผ | +| --- | --- | +| [?] | ์ด ์„ค๋ช…์„œ๋ฅผ ์—ถ | +| [์ทจ์†Œ] | ๋งŒ๋“ค์ง€ ์•Š๊ณ  ๋Œ€์‹œ๋ณด๋“œ๋กœ ๋Œ์•„๊ฐ | +| [๋“ฑ๋ก] | ๊ณ ๋ฅธ ์ž„๋„๋ฅผ ๋ณต์‚ฌํ•ด ์•ˆ์ •์„ฑํ‰๊ฐ€ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ฆ. ๊ณ ๋ฅผ ์ž„๋„๊ฐ€ ์—†์œผ๋ฉด ํ๋ฆฌ๊ฒŒ ๊บผ์ ธ ์žˆ์Œ | + +## ๋ชฉ๋ก์— ๋‚˜์˜ค๋Š” ์ž„๋„ + +- ์šฐ๋ฆฌ ํšŒ์‚ฌ ์ž„๋„ ์„ค๊ณ„ ํ”„๋กœ์ ํŠธ ์ค‘ **ํšก๋‹จ ์„ค๊ณ„(B06) ๋‹จ๊ณ„๊ฐ€ ๋๋‚œ ๊ฒƒ๋งŒ** ๋‚˜์˜ด. +- ๊ณ ๋ฅผ ์ž„๋„๊ฐ€ ํ•˜๋‚˜๋„ ์—†์œผ๋ฉด ์นธ ์•„๋ž˜์— ใ€Œํšก๋‹จ ์„ค๊ณ„(B06)๊นŒ์ง€ ๋๋‚œ ์ž„๋„ ํ”„๋กœ์ ํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.ใ€๊ฐ€ ๋ณด์ž„ โ€” ๋จผ์ € ์ž„๋„ ํ”„๋กœ์ ํŠธ์—์„œ ํšก๋‹จ ์„ค๊ณ„๋ฅผ ๋๋‚ผ ๊ฒƒ. +- ์ง€์—ญ ยท ๋„๋กœ ์ข…๋ฅ˜ ยท ์‚ฌ์—… ์—ฐ๋„๋Š” ์›๋ณธ ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ฌผ๋ ค๋ฐ›์Œ. + +## ๋ณต์‚ฌํ•˜๋Š” ๊ฒƒ ยท ์•ˆ ํ•˜๋Š” ๊ฒƒ + +ํ•ด์„์— ์“ฐ๋Š” ์ž๋ฃŒ๋งŒ ๋ณต์‚ฌํ•จ(๋ณดํ†ต ์ˆ˜์‹ญ MB ์•ˆ์ชฝ). + +| ๋ณต์‚ฌํ•จ | ์•ˆ ํ•จ | +| --- | --- | +| ์›๋ณธ ํ”„๋กœ์ ํŠธ ์ •๋ณด ยท ๋‹จ๊ณ„ ์ƒํƒœ ยท ์ง€ํ‘œ๋ฉด ํ™•์ • ๊ฐ’ | ์›๋ณธ ์ ๊ตฐ(LAS) ํŒŒ์ผ | +| ์ตœ์‹  ๋…ธ์„ ๊ณผ ์ธก์  ยท ์ข…๋‹จ ยท ํšก๋‹จ ์ž๋ฃŒ | ์ƒ์„ธ ์„ค๊ณ„(B07) ยท ์ˆ˜๋Ÿ‰(B08) ยท ๋‚ด์—ญ(B09) | +| ๋…ธ์„  ํด๋”(๊ตฌ์กฐ๋ฌผ ๋ฐฐ์น˜ ํฌํ•จ) ยท ์ข…ํšก๋‹จ ํŒŒ์ผ | ์“ฐ์ด์ง€ ์•Š์€ ๋‹ค๋ฅธ ์ง€ํ˜• ๊ฒฉ์ž | +| ์›๋ณธ ์„ค๊ณ„์— ์“ฐ์ธ ์ง€ํ˜• ๊ฒฉ์ž(DTM) ํ•˜๋‚˜ | | +| ์ตœ์‹  ๋…ธ์„  3D ๋ฉด ยท ๋ฐฐ์ˆ˜ ํŽธ์ง‘๋ถ„ | | + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +- [๋“ฑ๋ก]์ด ๋๋‚˜๋ฉด ์ง€๋ฐ˜ ์กฐ๊ฑด(E02) ํ™”๋ฉด์ด ์—ด๋ฆฌ๊ณ , ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ยท ๊ธฐ์ค€์•ˆ์ „์œจ ๋“ฑ์€ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ, ์ธก์ ๋ณ„ ํ† ์ธต ๋‘๊ป˜๋Š” ๋ณต์‚ฌํ•œ B06 ๊ฐ’์œผ๋กœ ์ฑ„์›Œ์ ธ ์žˆ์Œ. +- ์›๋ณธ ํšก๋‹จ ํŒŒ์ผ์ด ์—†๋Š” ์ธก์ ์ด ์žˆ์œผ๋ฉด E02 ยท E03 ํ™”๋ฉด ์œ„์— **ใ€Œํšก๋‹จ ์—†๋Š” ์ธก์  N ๊ฐœ โ€” ํ•ด์„์—์„œ ๋น ์งใ€** ๋ ๊ฐ€ ๋ณด์ž„. ๋ ๋ฅผ ๋ˆ„๋ฅด๋ฉด ๋น ์ง„ ์ธก์ ์˜ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ(m)๊ฐ€ ํŽผ์ณ์ง. +- ๋น ์ง„ ์ธก์ ์€ ํ•ด์„ ๋ชฉ๋ก์— ๋‚˜์˜ค์ง€ ์•Š์Œ. ๋‚˜๋จธ์ง€ ์ธก์  ํ•ด์„์—๋Š” ์˜ํ–ฅ ์—†์Œ. + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- **์—…๋ฐ์ดํŠธ ์—†์Œ**: ๋งŒ๋“  ๋’ค ์›๋ณธ ์ž„๋„๋ฅผ ๊ณ ์ณ๋„ ์ด ์•ˆ์ •์„ฑํ‰๊ฐ€๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์Œ(๋งŒ๋“  ์ˆœ๊ฐ„์˜ ์ž๋ฃŒ๋กœ ๊ณ ์ •). +- ์›๋ณธ์˜ ๋ฐ”๋€ ์„ค๊ณ„๋กœ ๋‹ค์‹œ ํ‰๊ฐ€ํ•˜๋ ค๋ฉด ์ด ํ™”๋ฉด์—์„œ **์•ˆ์ •์„ฑํ‰๊ฐ€๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค ๊ฒƒ**. +- ๋ณต์‚ฌ์— ์‹คํŒจํ•˜๋ฉด ใ€Œ์›๋ณธ ์ž๋ฃŒ ๋ณต์‚ฌ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.ใ€ ์•Œ๋ฆผ์ด ๋œจ๊ณ , ๋งŒ๋“ค๋‹ค ๋งŒ ํ”„๋กœ์ ํŠธ๋Š” ์ž๋™์œผ๋กœ ์ง€์›Œ์ง โ€” ๋‹ค์‹œ [๋“ฑ๋ก]ํ•˜๋ฉด ๋จ. +- ์›๋ณธ ์ž„๋„ ํ”„๋กœ์ ํŠธ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ์“ฐ์ง€ ์•Š์Œ(์ฝ๊ธฐ๋งŒ ํ•จ). +- ์•ˆ์ •์„ฑํ‰๊ฐ€์˜ ์ง€๋ฐ˜ ๊ฐ’์€ **๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ**์ด ๊ธฐ๋ณธ์ž„ โ€” ๋‹ค์Œ ๋‹จ๊ณ„(E02)์—์„œ ํ˜„์žฅ ์—ฌ๊ฑด์— ๋งž๊ฒŒ ํ™•์ธ ยท ์ˆ˜์ •ํ•  ๊ฒƒ. +- ํšก๋‹จ ์—†๋Š” ์ธก์ ์„ ์‚ด๋ฆฌ๋ ค๋ฉด ์›๋ณธ ์ž„๋„์—์„œ ๊ทธ ์ธก์  ํšก๋‹จ์„ ๋‹ค์‹œ ๋งŒ๋“  ๋’ค ์•ˆ์ •์„ฑํ‰๊ฐ€๋ฅผ ์ƒˆ๋กœ ๋งŒ๋“ค ๊ฒƒ. diff --git a/resources/help/E02/00_์„ค๋ช…์„œ.md b/resources/help/E02/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..385338eda --- /dev/null +++ b/resources/help/E02/00_์„ค๋ช…์„œ.md @@ -0,0 +1,50 @@ +# ์ง€๋ฐ˜ ์กฐ๊ฑด ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +- 2D ํ•ด์„(E03)์— ๋“ค์–ด๊ฐˆ ์ง€๋ฐ˜ ์กฐ๊ฑด์„ ์ •ํ•จ โ€” ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ยท ๊ธฐ์ค€์•ˆ์ „์œจ ยท ๋…ธ๋ฉด ํ•˜์ค‘ ยท ์ง€์ง„ ยท ์ง€ํ•˜์ˆ˜ ยท ์ธต ๋‘๊ป˜. +- ์ฒ˜์Œ ์—ด๋ฉด ๊ธฐ๋ณธ๊ฐ’(๋ฌธํ—Œ ์ถ”์ •์น˜)๊ณผ ๋ณต์‚ฌํ•œ B06 ์ธก์  ๊ฐ’์œผ๋กœ ์ฑ„์›Œ์ ธ ์žˆ์Œ. ์นธ๋งˆ๋‹ค ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ. +- ์ขŒ์ธก = ์•ˆ๋‚ด ๊ธ€ ยท ์ƒํƒœ ๊ธ€ ยท [์ดˆ๊ธฐํ™”] [์ €์žฅ]. +- ๊ฐ€์šด๋ฐ = ์นด๋“œ ๋‹ค์„ฏ โ€” ใ€Œ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ใ€ ยท ใ€Œ๊ธฐ์ค€์•ˆ์ „์œจใ€ ยท ใ€Œ๋…ธ๋ฉด ํ•˜์ค‘ ยท ์ง€์ง„ใ€ ยท ใ€Œ์ง€ํ•˜์ˆ˜ใ€ ยท ใ€Œ์ธต ๋‘๊ป˜ ยท ๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐใ€. + +## ์ž‘์—… ์ˆœ์„œ + +1. **์ง€๋ฐ˜๋ฌผ์„ฑ์น˜** ์นด๋“œ์—์„œ ์ง€์ธต๋ณ„ ฮณt ยท ฮณsat ยท c ยท ฯ† ๋ฅผ ํ™•์ธํ•จ. ํ˜„์žฅ ๊ฐ’์ด ์žˆ์œผ๋ฉด ๊ณ ์ณ ๋„ฃ๊ณ , ํ•„์š”ํ•˜๋ฉด [์ง€์ธต ์ถ”๊ฐ€]. +2. ๊ฐ™์€ ์นด๋“œ ์•„๋ž˜ **B06 ์ง€๋ฐ˜ โ†’ ์ง€์ธต** ์ค„์—์„œ B06 ์˜ ํ† ์‚ฌ ยท ๋ฆฌํ•‘์•” ยท ๋ฐœํŒŒ์•” ยท ์„ฑํ† ์ฒด๊ฐ€ ์–ด๋А ์ง€์ธต์œผ๋กœ ํ•ด์„๋ ์ง€ ๊ณ ๋ฆ„. +3. **๊ธฐ์ค€์•ˆ์ „์œจ** ์นด๋“œ์—์„œ ์กฐ๊ฑด(๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์‹œ ยท ๋‹จ๊ธฐ)๋ณ„ ๊ธฐ์ค€์„ ํ™•์ธํ•จ. +4. **๋…ธ๋ฉด ํ•˜์ค‘ ยท ์ง€์ง„** ์นด๋“œ์—์„œ ๋…ธ๋ฉด ํ•˜์ค‘(kPa)๊ณผ ์ง€์ง„ ๊ฒ€ํ†  ์ผฌ/๋” ยท ์ˆ˜ํ‰์ง€์ง„๊ณ„์ˆ˜ kh ๋ฅผ ์ •ํ•จ. +5. **์ง€ํ•˜์ˆ˜** ์นด๋“œ์—์„œ ๊ฑด๊ธฐ ์ง€ํ•˜์ˆ˜์™€ ์šฐ๊ธฐ ๊ทœ์น™(๋„ท ์ค‘ ํ•˜๋‚˜)์„ ๊ณ ๋ฆ„. +6. **์ธต ๋‘๊ป˜ ยท ๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐ** ์นด๋“œ์—์„œ ํŠน์ • ๊ตฌ๊ฐ„๋งŒ ์ง€์ธต ยท ํ† ์ธต ๋‘๊ป˜๋ฅผ ๋ฐ”๊ฟ€ ๊ฒƒ์ด ์žˆ์œผ๋ฉด [๊ตฌ๊ฐ„ ์ถ”๊ฐ€]. ์•„๋ž˜ **์ธก์ ๋ณ„ ์ ์šฉ ์ง€์ธต** ํ‘œ๋กœ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•จ. +7. ์ขŒ์ธก [์ €์žฅ]์„ ๋ˆ„๋ฆ„. ์ €์žฅํ•ด์•ผ 2D ํ•ด์„(E03)์— ์“ฐ์ž„. + +## ๋‹จ์ถ” + +| ๋‹จ์ถ” | ์ž๋ฆฌ | ํ•˜๋Š” ์ผ | +| --- | --- | --- | +| [์ €์žฅ] | ์ขŒ์ธก ์•„๋ž˜ | ํ™”๋ฉด ๊ฐ’์„ ๊ฒ€์‚ฌํ•œ ๋’ค ์ž‘์—…๋ณธ์œผ๋กœ ์ €์žฅํ•จ. ํ‹€๋ฆฐ ์นธ์ด ์žˆ์œผ๋ฉด ์ €์žฅํ•˜์ง€ ์•Š๊ณ  ์•Œ๋ฆผ | +| [์ดˆ๊ธฐํ™”] | ์ขŒ์ธก ์•„๋ž˜ | ํ™•์ธ ์ฐฝ ๋’ค ์ดˆ๊ธฐ๊ฐ’(๋งŒ๋“ค ๋•Œ ๊ธฐ๋ณธ๊ฐ’ + B06 ์ธก์ )์œผ๋กœ ๋˜๋Œ๋ฆผ. ์ €์žฅํ•œ ๊ฐ’์ด ์‚ฌ๋ผ์ง | +| [?] [<] [>] | ์ขŒ์ธก ์•„๋ž˜ | ์„ค๋ช…์„œ ยท ์ด์ „ ๋‹จ๊ณ„ ยท ๋‹ค์Œ ๋‹จ๊ณ„(2D ํ•ด์„) | +| [์ง€์ธต ์ถ”๊ฐ€] | ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ์นด๋“œ | ใ€Œ์ƒˆ ์ง€์ธต nใ€ ์ค„์„ ๋ง๋ถ™์ž„(ํ’ํ™”ํ†  ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์‹œ์ž‘) | +| [์ง€์›€] | ํ‘œ ์ค„ ๋ | ๊ทธ ์ค„์„ ์ง€์›€. ์“ฐ์ด๋Š” ์ง€์ธต์€ ์ง€์šธ ์ˆ˜ ์—†์Œ | +| [๊ธฐ๋ณธ๊ฐ’] | ์นด๋“œ๋งˆ๋‹ค | ๊ทธ ์นด๋“œ ๊ฐ’๋งŒ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋˜๋Œ๋ฆผ(๋‹ค๋ฅธ ์นด๋“œ๋Š” ๊ทธ๋Œ€๋กœ) | +| [๊ตฌ๊ฐ„ ์ถ”๊ฐ€] | ์ธต ๋‘๊ป˜ ์นด๋“œ | ๋ฎ์–ด์“ฐ๊ธฐ ๊ตฌ๊ฐ„ ํ•œ ์ค„(์ฒซ ์ธก์  ~ ๋ ์ธก์ )์„ ๋ง๋ถ™์ž„ | +| [์ˆ˜์œ„์„  ๊ตฌ๊ฐ„ ์ถ”๊ฐ€] ยท [์  ์ถ”๊ฐ€] | ์ง€ํ•˜์ˆ˜ ์นด๋“œ | ์šฐ๊ธฐ ๊ทœ์น™์ด ใ€Œ์‚ฌ์šฉ์ž ์ˆ˜์œ„์„ ใ€์ผ ๋•Œ๋งŒ ๋ณด์ž„ | + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +- ์นธ์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด **๊ธฐ๋ณธ๊ฐ’๊ณผ ๊ทผ๊ฑฐ**(์ง€์‹DB 07 ยท KDS 11 70 05 ๋“ฑ)๊ฐ€ ๋ณด์ž„. ์‚ฌ์šฉ์ž๊ฐ€ ๋”ํ•œ ์ง€์ธต์€ ใ€Œ์‚ฌ์šฉ์ž ์ง€์ธต โ€” ์ถœ์ฒ˜ ์—†์Œใ€. +- ํ‹€๋ฆฐ ๊ฐ’์€ ์นธ ํ…Œ๋‘๋ฆฌ๊ฐ€ **๋นจ๊ฐ›๊ฒŒ** ๋˜๊ณ , ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ๊นŒ๋‹ญ(ใ€Œ๋ฒ”์œ„ ๋ฐ– (0 ~ 60)ใ€ ๋“ฑ)์ด ๋ณด์ž„. ์ขŒ์ธก์— ใ€Œ์ž…๋ ฅ๊ฐ’์„ ํ™•์ธํ•˜์„ธ์š” โ€” ํ‹€๋ฆฐ ์นธ Nใ€. +- ๋ฐ”๊พธ๊ณ  ์ €์žฅํ•˜์ง€ ์•Š์œผ๋ฉด ์ขŒ์ธก์— ใ€Œ์ €์žฅ ์•ˆ ๋œ ๋ณ€๊ฒฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.ใ€๊ฐ€ ๋ณด์ž„. +- **์ธก์ ๋ณ„ ์ ์šฉ ์ง€์ธต** ํ‘œ = ์ธก์ ๋งˆ๋‹ค ์‹ค์ œ๋กœ ํ•ด์„์— ๋“ค์–ด๊ฐˆ ์œ„ ์ง€์ธต ยท ์•„๋ž˜ ์ง€์ธต ยท ํ† ์ธต ๋‘๊ป˜. ๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐ๊ฐ€ ๊ฑธ๋ฆฐ ์ค„์€ ๋ฐ”ํƒ•์ƒ‰์ด ๋‹ค๋ฆ„. ์•„๋ž˜ ์ง€์ธต์ด ์—†์œผ๋ฉด ใ€Œ์ „๋ถ€ ์œ„ ์ง€์ธตใ€. +- ๊ณต์œ ๋ฐ›์€ ํšŒ์‚ฌ(๋ณด๊ธฐ๋งŒ)๋Š” ์นธ๊ณผ ๋‹จ์ถ”๊ฐ€ ๊บผ์ง€๊ณ  ใ€Œ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.ใ€๊ฐ€ ๋ณด์ž„. + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- **๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ**: ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ๊ธฐ๋ณธ๊ฐ’์€ ๊ตญ๋‚ด ์„ค๊ณ„ ํŽธ๋žŒ ยท ํ•™์ˆ  ์ž๋ฃŒ์˜ ๋Œ€ํ‘œ๊ฐ’์ž„. ํ˜„์žฅ ์‹œ์ถ” ยท ์‹œํ—˜ ๊ฐ’์ด ์•„๋‹ˆ๋ฏ€๋กœ ๊ฒฐ๊ณผ๋Š” ์ฐธ๊ณ ์šฉ โ€” ํ˜„์žฅ ์กฐ์‚ฌ ๊ฐ’์ด ์žˆ์œผ๋ฉด ๊ผญ ๋ฐ”๊ฟ” ๋„ฃ์„ ๊ฒƒ. +- ์ž๋™์ €์žฅ ์—†์Œ. ๋ฐ”๊พผ ๊ฐ’์€ ์ด ๋ธŒ๋ผ์šฐ์ € ํƒญ์—๋งŒ ์ž„์‹œ๋กœ ๋‚จ๊ณ , [์ €์žฅ]ํ•ด์•ผ E03 [ํ•ด์„]์— ์“ฐ์ž„. +- ํ™”๋ฉด ์œ„ ใ€Œํšก๋‹จ ์—†๋Š” ์ธก์  N ๊ฐœ โ€” ํ•ด์„์—์„œ ๋น ์งใ€ ๋  = ์›๋ณธ์— ํšก๋‹จ ํŒŒ์ผ์ด ์—†์–ด ํ•ด์„์—์„œ ๋น ์ง€๋Š” ์ธก์ . ๋ˆ„๋ฅด๋ฉด ๋ชฉ๋ก์ด ํŽผ์ณ์ง. +- ์ธก์ ์˜ B06 ์ง€๋ฐ˜ ยท B06 ํ† ์ธต ๋‘๊ป˜๋Š” ๋ณต์‚ฌ๋ณธ ๊ฐ’์ด๋ผ ํ™”๋ฉด์—์„œ ๊ณ ์น  ์ˆ˜ ์—†์Œ โ€” ๋ฐ”๊พธ๋ ค๋ฉด ๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ์“ธ ๊ฒƒ. +- ์ €์žฅ ๋’ค E03 ์—์„œ [ํ•ด์„]์„ ๋‹ค์‹œ ๋ˆ„๋ฅด๋ฉด ๊ฐ’์ด ๋ฐ”๋€ ์ธก์ ๋งŒ ๋‹ค์‹œ ๊ณ„์‚ฐํ•จ. +- ๊ธฐ์ค€์•ˆ์ „์œจ ํ‘œ์˜ ใ€Œ๋‹จ๊ธฐใ€ ์ค„ ยท ใ€Œ์Œ“๊ธฐ(์—ฐ์•ฝ์ง€๋ฐ˜)ใ€ ์นธ์€ ๊ฐ’๋งŒ ๋ณด๊ด€ํ•จ โ€” ์ง€๊ธˆ 2D ํ•ด์„ ํŒ์ •์€ ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์‹œ์˜ ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ์นธ๋งŒ ์”€. +- ์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜ ๊ทœ์น™(KDS ๊ธฐ๋ณธ ์™ธ ๊ฐˆ๋ž˜ ํฌํ•จ)์€ ์ง€๊ธˆ E03 ํšก๋‹จ ๊ทธ๋ฆผ์˜ ์ˆ˜์œ„์„ ์— ๋ฐ˜์˜๋จ. ์•ˆ์ „์œจ ๊ณ„์‚ฐ์€ ์•„์ง ๋ชจ๋“  ์ธก์ ์„ ใ€Œ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”ใ€๋กœ ์…ˆ โ€” ๊ณ ๋ฅธ ๊ฐˆ๋ž˜์˜ ๊ณ„์‚ฐ ๋ฐ˜์˜์€ ์ค€๋น„ ์ค‘. +- ์นธ ๋œป ยท ๋ฒ”์œ„๋Š” ใ€Œ์นธ ๋œปใ€, ๊ทผ๊ฑฐ ยท ์ˆ˜์‹์€ ใ€Œ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐใ€, ํ™•์ • ์•ˆ ๋œ ๊ฐ’ ๋ชฉ๋ก์€ ใ€Œ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ใ€ ํ•ญ๋ชฉ์— ์žˆ์Œ. diff --git a/resources/help/E02/10_์นธ_๋œป.md b/resources/help/E02/10_์นธ_๋œป.md new file mode 100644 index 000000000..872615c53 --- /dev/null +++ b/resources/help/E02/10_์นธ_๋œป.md @@ -0,0 +1,75 @@ +# ์นธ ๋œป + +## ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ํ‘œ + +| ์นธ | ๋œป | ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„ | +| --- | --- | --- | +| ์ง€์ธต | ์ง€์ธต ์ด๋ฆ„ | 1~30 ์ž ยท ๊ฐ™์€ ์ด๋ฆ„ ์•ˆ ๋จ | +| ฮณt (kN/mยณ) | ์Šต์œค ๋‹จ์œ„์ค‘๋Ÿ‰ โ€” ๋ฌผ์— ์ž ๊ธฐ์ง€ ์•Š์€ ํ™ 1 mยณ ์˜ ๋ฌด๊ฒŒ | 10 ~ 30 | +| ฮณsat (kN/mยณ) | ํฌํ™” ๋‹จ์œ„์ค‘๋Ÿ‰ โ€” ๋ฌผ์— ์ž ๊ธด ํ™ 1 mยณ ์˜ ๋ฌด๊ฒŒ. ์ง€ํ•˜์ˆ˜์œ„ ์•„๋ž˜์—์„œ ์”€ | 10 ~ 30 ยท ฮณt ์ด์ƒ | +| c (kPa) | ์ ์ฐฉ๋ ฅ โ€” ํ™ ์•Œ๊ฐฑ์ด๋ผ๋ฆฌ ๋ถ™์žก๋Š” ํž˜ | 0 ~ 1,000 | +| ฯ† (ยฐ) | ๋‚ด๋ถ€๋งˆ์ฐฐ๊ฐ โ€” ํ™์ด ์„œ๋กœ ๋น„๋น„๋ฉฐ ๋ฒ„ํ‹ฐ๋Š” ์ •๋„(ํด์ˆ˜๋ก ๋‹จ๋‹จํ•จ) | 0 ~ 60 | +| ์ง€ํ•˜์ˆ˜ ๋ฌด๋ฆฌ | ใ€Œํ† ์‚ฌ ยท ํ’ํ™”์•”ใ€ ๋˜๋Š” ใ€Œ์•”๋ฐ˜ใ€. ์šฐ๊ธฐ ๊ทœ์น™์ด KDS ๊ธฐ๋ณธ์ผ ๋•Œ ์–ด๋А ์ˆ˜์œ„ ๊ทœ์น™์„ ์“ธ์ง€ ๊ฐ€๋ฆ„ | ๋‘˜ ์ค‘ ํ•˜๋‚˜ | + +๊ธฐ๋ณธ ์ง€์ธต ์—ฌ์„ฏ = ํ’ํ™”ํ†  ยท ํ’ํ™”์•” ยท ์—ฐ์•” ยท ๊ฒฝ์•” ยท ์„ฑํ†  ยท ๋ถ•์ ํ† . ๊ฐ’์€ ใ€Œ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ใ€ ํ•ญ๋ชฉ์— ์žˆ์Œ. + +## B06 ์ง€๋ฐ˜ โ†’ ์ง€์ธต + +| ์นธ | ๋œป | ๊ธฐ๋ณธ | +| --- | --- | --- | +| ํ† ์‚ฌ (๊ฒฝ๊ณ„ ์œ„) | B06 ํšก๋‹จ์˜ ์•” ๊ฒฝ๊ณ„ ์œ„ ํ™(๋ชจ๋“  ์ธก์ ) | ํ’ํ™”ํ†  | +| ๋ฆฌํ•‘์•” (๊ฒฝ๊ณ„ ์•„๋ž˜) | B06 ๋ฆฌํ•‘์•” ์ธก์ ์˜ ์•” ๊ฒฝ๊ณ„ ์•„๋ž˜ | ํ’ํ™”์•” | +| ๋ฐœํŒŒ์•” (๊ฒฝ๊ณ„ ์•„๋ž˜) | B06 ๋ฐœํŒŒ์•” ์ธก์ ์˜ ์•” ๊ฒฝ๊ณ„ ์•„๋ž˜ | ์—ฐ์•” | +| ์„ฑํ† ์ฒด | ์Œ“๊ธฐ๋กœ ์ƒˆ๋กœ ์˜ฌ๋ฆฐ ํ™ | ์„ฑํ†  | + +## ์ธต ๋‘๊ป˜ ยท ๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐ + +| ์นธ | ๋œป | +| --- | --- | +| ์‹œ์ž‘ (m) ยท ๋ (m) | ๋ฎ์–ด์“ธ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ๊ตฌ๊ฐ„. ์–‘ ๋ ์ธก์  ํฌํ•จ ยท ๋์€ ์‹œ์ž‘๋ณด๋‹ค ์ปค์•ผ ํ•จ(0 ~ 100,000) | +| ์œ„ ์ง€์ธต | ๊ตฌ๊ฐ„ ์•ˆ ์ธก์ ์˜ ์œ„ ์ง€์ธต. ใ€ŒB06 ๊ทธ๋Œ€๋กœใ€ = ๋ฐ”๊พธ์ง€ ์•Š์Œ | +| ์•„๋ž˜ ์ง€์ธต | ๊ตฌ๊ฐ„ ์•ˆ ์ธก์ ์˜ ์•„๋ž˜ ์ง€์ธต. ใ€ŒB06 ๊ทธ๋Œ€๋กœใ€ = ๋ฐ”๊พธ์ง€ ์•Š์Œ | +| ํ† ์ธต ๋‘๊ป˜ (m) | ์›์ง€๋ฐ˜์—์„œ ์•„๋ž˜ ์ง€์ธต ์œ—๋ฉด๊นŒ์ง€ ๊นŠ์ด. ๋นˆ ์นธ = B06 ๊ทธ๋Œ€๋กœ(0 ~ 50) | + +- ์ค„์ด ์—ฌ๋Ÿฟ ๊ฒน์น˜๋ฉด **๋’ค ์ค„์ด ์ด๊น€**. +- ํ† ์‚ฌ ์ธก์ ์€ B06 ํ† ์ธต ๋‘๊ป˜๊ฐ€ ์—†์–ด ใ€Œ์ „๋ถ€ ์œ„ ์ง€์ธตใ€์ž„. ํ† ์‚ฌ ์ธก์ ์— ์•„๋ž˜ ์ง€์ธต์„ ๋„ฃ์œผ๋ ค๋ฉด ์•„๋ž˜ ์ง€์ธต๊ณผ ํ•จ๊ป˜ ํ† ์ธต ๋‘๊ป˜๋„ ๋„ฃ์„ ๊ฒƒ. +- ์“ฐ์ด๋Š” ์ง€์ธต(B06 ์ง€๋ฐ˜ ์—ฐ๊ฒฐ ยท ๊ตฌ๊ฐ„์—์„œ ๊ณ ๋ฅธ ์ง€์ธต)์€ ๋ฌผ์„ฑ ํ‘œ์—์„œ ์ง€์šธ ์ˆ˜ ์—†์Œ. + +## ์ธก์ ๋ณ„ ์ ์šฉ ์ง€์ธต (์ฝ๊ธฐ ์ „์šฉ) + +| ์นธ | ๋œป | +| --- | --- | +| ์ธก์  | ์ธก์  ๋ฒˆํ˜ธ(์˜ˆ `5+20`) | +| B06 ์ง€๋ฐ˜ | ๋ณต์‚ฌํ•œ B06 ํšก๋‹จ์˜ ์ง€๋ฐ˜ ๊ตฌ๋ถ„(ํ† ์‚ฌ ยท ๋ฆฌํ•‘์•” ยท ๋ฐœํŒŒ์•”) | +| B06 ํ† ์ธต ๋‘๊ป˜ (m) | B06 ์•” ๊ฒฝ๊ณ„ ๊นŠ์ด. ํ† ์‚ฌ ์ธก์ ์€ ใ€Œ-ใ€ | +| ์œ„ ์ง€์ธต ยท ์•„๋ž˜ ์ง€์ธต ยท ํ† ์ธต ๋‘๊ป˜ (m) | ์—ฐ๊ฒฐ ยท ๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐ๋ฅผ ๋ชจ๋‘ ๋ฐ˜์˜ํ•œ ์ตœ์ข… ๊ฐ’ | + +## ๊ธฐ์ค€์•ˆ์ „์œจ + +- ์ค„ = ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์‹œ ยท ๋‹จ๊ธฐ ยท ์นธ = ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ยท ์Œ“๊ธฐ(์—ฐ์•ฝ์ง€๋ฐ˜). +- ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š” ๋ฒ”์œ„ = 1.0 ~ 3.0. +- ์ง€๊ธˆ 2D ํ•ด์„ ํŒ์ •์€ ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์‹œ ์ค„์˜ ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ์นธ๋งŒ ์”€. + +## ๋…ธ๋ฉด ํ•˜์ค‘ ยท ์ง€์ง„ + +| ์นธ | ๋œป | ๊ธฐ๋ณธ ยท ๋ฒ”์œ„ | +| --- | --- | --- | +| ๋…ธ๋ฉด ํ•˜์ค‘ (kPa) | ๋…ธ๋ฉด ์–‘ ๋ ์‚ฌ์ด์— ๊ณ ๋ฅด๊ฒŒ ์–น๋Š” ์ฐจ๋Ÿ‰ ยท ํ†ตํ–‰ ํ•˜์ค‘ | 10 ยท 0 ~ 100 | +| ์ง€์ง„ ๊ฒ€ํ†  | ์ผœ๋ฉด E03 ์— ใ€Œ์ง€์ง„ใ€ ์กฐ๊ฑด์ด ์ƒ๊น€ | ๋” | +| ์ˆ˜ํ‰์ง€์ง„๊ณ„์ˆ˜ kh | ํ™ ๋ฌด๊ฒŒ์˜ ๋ช‡ ๋ฐฐ๋ฅผ ์˜†์œผ๋กœ ๋ฏธ๋Š” ํž˜์œผ๋กœ ๋ณผ์ง€. ์ง€์ง„ ๊ฒ€ํ† ๋ฅผ ์ผฐ์„ ๋•Œ๋งŒ ๋„ฃ์Œ | 0 ยท 0 ~ 0.5 | + +## ์ง€ํ•˜์ˆ˜ + +| ์นธ | ๋œป | ๊ธฐ๋ณธ | +| --- | --- | --- | +| ๊ฑด๊ธฐ ์ง€ํ•˜์ˆ˜ | ๊ฑด๊ธฐ ยท ์ง€์ง„ ์‹œ ์ˆ˜์œ„ ๊ทœ์น™(์—†์Œ ยท ์ง€ํ‘œ ํฌํ™” ยท ๋น„ํƒˆ ๋†’์ด ยฝ ํฌํ™”) | ์—†์Œ | +| ์šฐ๊ธฐ ๊ทœ์น™ | ๋„ท ์ค‘ ํ•˜๋‚˜ โ€” KDS ๊ธฐ๋ณธ ยท ์ง€ํ‘œ ์•„๋ž˜ ๊นŠ์ด = ๋น„ํƒˆ ๋†’์ด ร— ๋น„์œจ ยท ๊ฐ„๊ทน์ˆ˜์••๋น„ r_u ยท ์‚ฌ์šฉ์ž ์ˆ˜์œ„์„  | KDS ๊ธฐ๋ณธ | +| ์šฐ๊ธฐ ยท ํ† ์‚ฌ ยท ํ’ํ™”์•” | KDS ๊ธฐ๋ณธ์ผ ๋•Œ, ๋น„ํƒˆ ๊ฒ‰ ์ง€์ธต์ด ใ€Œํ† ์‚ฌ ยท ํ’ํ™”์•”ใ€ ๋ฌด๋ฆฌ์ธ ์ธก์ ์˜ ๊ทœ์น™ | ์ง€ํ‘œ ํฌํ™” | +| ์šฐ๊ธฐ ยท ์•”๋ฐ˜ | KDS ๊ธฐ๋ณธ์ผ ๋•Œ, ๋น„ํƒˆ ๊ฒ‰ ์ง€์ธต์ด ใ€Œ์•”๋ฐ˜ใ€ ๋ฌด๋ฆฌ์ธ ์ธก์ ์˜ ๊ทœ์น™ | ๋น„ํƒˆ ๋†’์ด ยฝ ํฌํ™” | +| ๊นŠ์ด ๋น„์œจ | ์ˆ˜์œ„ = ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ์—์„œ (๋น„ํƒˆ ๋†’์ด ร— ๋น„์œจ)๋งŒํผ ์•„๋ž˜ | 0.5 ยท 0 ~ 1 | +| r_u | ๊ฐ„๊ทน์ˆ˜์•• = ๊ทธ ์ž๋ฆฌ ํ™ ๋ฌด๊ฒŒ ์••๋ ฅ ร— r_u | 0.2 ยท 0 ~ 0.9 | +| ์ˆ˜์œ„์„  ๊ตฌ๊ฐ„ โ€” ์‹œ์ž‘ ยท ๋ (m) | ์ˆ˜์œ„์„ ์„ ์“ธ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ๊ตฌ๊ฐ„. ๋ฎ์ง€ ์•Š์€ ์ธก์ ์€ KDS ๊ธฐ๋ณธ ๊ทœ์น™ | ์—†์Œ | +| ์  (offset ยท ๊นŠ์ด) | offset = ์ค‘์‹ฌ์„ ์—์„œ ๊ฑฐ๋ฆฌ(m ยท ์ขŒ +, โˆ’200 ~ 200) ยท ๊นŠ์ด = ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ ์•„๋ž˜(m, 0 ~ 50). ์ ์€ ๋‘˜ ์ด์ƒ ยท offset ์€ ์ž‘์€ ์ชฝ๋ถ€ํ„ฐ ์ปค์ง€๊ฒŒ | [โˆ’10 : 1] ยท [10 : 1] ๋กœ ์‹œ์ž‘ | + +- ๋น„ํƒˆ ๊ฒ‰ ์ง€์ธต = ํ† ์ธต ๋‘๊ป˜๊ฐ€ 0 ์ธ ์ธก์ ์€ ์•„๋ž˜(์•”) ์ง€์ธต, ๊ทธ ๋ฐ–์€ ์œ„ ์ง€์ธต. +- ์ˆ˜์œ„์„  ๊ตฌ๊ฐ„์ด ๊ฒน์น˜๋ฉด ๋’ค ์ค„์ด ์ด๊น€. diff --git a/resources/help/E02/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md b/resources/help/E02/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md new file mode 100644 index 000000000..eb8f3ee94 --- /dev/null +++ b/resources/help/E02/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md @@ -0,0 +1,60 @@ +# ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐ + +## ์ง€๋ฐ˜ ๊ฐ’์ด ํ•ด์„์— ๋“ค์–ด๊ฐ€๋Š” ๊ธธ + +- ๋น„ํƒˆ ์•ˆ ํ™๋ฉ์ด๋ฅผ ์„ธ๋กœ๋กœ ์ž˜๊ฒŒ ๋‚˜๋ˆˆ ์กฐ๊ฐ(์ ˆํŽธ)๋งˆ๋‹ค ๋ฌด๊ฒŒ์™€ ๋ฒ„ํ‹ฐ๋Š” ํž˜์„ ์…ˆ(์ž์„ธํ•œ ํ•ด์„ ๋ฐฉ๋ฒ•์€ E03 ์„ค๋ช…์„œ ใ€Œ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐใ€). +- **๋ฌด๊ฒŒ** = ์ง€์ธต๋ณ„ (๋‹จ์œ„์ค‘๋Ÿ‰ ร— ๊ทธ ์ง€์ธต ๋‘๊ป˜)์˜ ํ•ฉ ร— ์ ˆํŽธ ํญ + ๋…ธ๋ฉด ํ•˜์ค‘. ์ง€ํ•˜์ˆ˜์œ„ ์œ„๋Š” ฮณt, ์•„๋ž˜๋Š” ฮณsat ๋ฅผ ์”€. +- **๋ฒ„ํ‹ฐ๋Š” ํž˜**(Mohr-Coulomb ๊ฐ•๋„) = c ร— ๋ฐ”๋‹ฅ ๊ธธ์ด + (๋ฐ”๋‹ฅ์„ ๋ˆ„๋ฅด๋Š” ํž˜ โˆ’ ๋ฌผ์ด ๋– ๋ฐ›์น˜๋Š” ํž˜) ร— tanฯ†. +- ๋ฌผ์ด ๋– ๋ฐ›์น˜๋Š” ํž˜(๊ฐ„๊ทน์ˆ˜์••) = ๋ฌผ ๋‹จ์œ„์ค‘๋Ÿ‰ 9.81 kN/mยณ ร— ์ˆ˜์œ„์—์„œ ์ ˆํŽธ ๋ฐ”๋‹ฅ๊นŒ์ง€ ๊นŠ์ด. ๋ฌผ์ด ์ฐจ๋ฉด ๋ฒ„ํ‹ฐ๋Š” ํž˜์ด ์ค„์–ด ์•ˆ์ „์œจ์ด ๋‚ฎ์•„์ง. +- **์•ˆ์ „์œจ** = ๋ฒ„ํ‹ฐ๋Š” ํž˜ ํ•ฉ รท ๋ฏธ๋„๋Ÿฌ์ง€๊ฒŒ ํ•˜๋Š” ํž˜ ํ•ฉ. 1 ๋ณด๋‹ค ์ž‘์œผ๋ฉด ๋ฌด๋„ˆ์ง€๋Š” ์ชฝ, ๊ธฐ์ค€์•ˆ์ „์œจ ์ด์ƒ์ด๋ฉด O.K. + +## ์ง€์ธต์„ ๋‚˜๋ˆ„๋Š” ๋ฐฉ๋ฒ• + +- ์•” ๊ฒฝ๊ณ„ = ์›์ง€๋ฐ˜ ์„ ์„ ํ† ์ธต ๋‘๊ป˜๋งŒํผ ๊ทธ๋Œ€๋กœ ์•„๋ž˜๋กœ ๋‚ด๋ฆฐ ์„ (B06 ๋ฌผ๋Ÿ‰ ๊ฐ€๋ฆ„๊ณผ ๊ฐ™์€ ์ •์˜). +- ๊ฒฝ๊ณ„ ์œ„ = ์œ„ ์ง€์ธต(๊ธฐ๋ณธ ํ’ํ™”ํ† ) ยท ๊ฒฝ๊ณ„ ์•„๋ž˜ = ์•„๋ž˜ ์ง€์ธต(๋ฆฌํ•‘์•” ์ธก์  = ํ’ํ™”์•” ยท ๋ฐœํŒŒ์•” ์ธก์  = ์—ฐ์•”) ยท ์Œ“๊ธฐ ํ™ = ์„ฑํ† . +- B06 ํ† ์ธต ๋‘๊ป˜๋Š” ๋ฌผ๋Ÿ‰์„ ๊ฐ€๋ฅด๋ ค๊ณ  ์ •ํ•œ ๊ฐ’(๊ธฐ๋ณธ 0.5 m)์ด๋ผ ์‹ค์ œ ์ง€์ธต ๋‘๊ป˜์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ โ€” ๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐ๋กœ ๊ณ ์น  ๊ฒƒ. + +## ๊ธฐ์ค€์•ˆ์ „์œจ โ€” KDS 11 70 05 (๊ฑด์„ค๊ณต์‚ฌ ๋น„ํƒˆ๋ฉด ์„ค๊ณ„๊ธฐ์ค€) + +| ์กฐ๊ฑด | ๊นŽ๊ธฐ | ์Œ“๊ธฐ | ์Œ“๊ธฐ(์—ฐ์•ฝ์ง€๋ฐ˜) | ์ง€ํ•˜์ˆ˜ ยท ํ•˜์ค‘ ์กฐ๊ฑด | +| --- | --- | --- | --- | --- | +| ๊ฑด๊ธฐ | 1.5 | 1.5 | 1.3 | ๋น„ํƒˆ ์•ˆ์— ์ง€ํ•˜์ˆ˜ ์—†์Œ | +| ์šฐ๊ธฐ | 1.2 | 1.3 | 1.2 | ํ† ์‚ฌ ยท ํ’ํ™”์•” = ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™” ยท ์•”๋ฐ˜ = ๋น„ํƒˆ ๋†’์ด ยฝ ๊นŠ์ด๊นŒ์ง€ ํฌํ™” | +| ์ง€์ง„ ์‹œ | 1.1 | 1.1 | 1.1 | ๊ฑด๊ธฐ ์ˆ˜์œ„ + ์ˆ˜ํ‰์ง€์ง„๊ณ„์ˆ˜ kh | +| ๋‹จ๊ธฐ | 1.1 | 1.1 | 1.1 | 1 ๋…„ ๋ฏธ๋งŒ ยท ์‹œ๊ณต ์ค‘ | + +- ๊ทผ๊ฑฐ ํ‘œ = KDS 11 70 05 ํ‘œ 4.3-1(์Œ“๊ธฐ ์ผ๋ฐ˜) ยท 4.3-2(์—ฐ์•ฝ์ง€๋ฐ˜ ์Œ“๊ธฐ) ยท 4.3-3(๊นŽ๊ธฐ). +- ์šฐ๊ธฐ ๊นŽ๊ธฐ 1.2 = ์ง€ํ•˜์ˆ˜์œ„๋ฅผ ์ •ํ•ด ๋‘๊ณ  ํ•ด์„ํ•˜๋Š” ๋ฐฉ์‹์˜ ๊ฐ’(๊ฐ•์šฐ ์นจํˆฌ ํ•ด์„์„ ๋”ฐ๋กœ ํ•˜๋ฉด 1.3). ์ง€ํ•˜์ˆ˜ ์นด๋“œ์˜ KDS ๊ธฐ๋ณธ ๊ทœ์น™๊ณผ ์ง์„ ์ด๋ฃธ โ€” ๋‹ค๋ฅธ ์šฐ๊ธฐ ๊ทœ์น™์„ ๊ณ ๋ฅด๋ฉด ๊ธฐ์ค€์•ˆ์ „์œจ๋„ ํ•จ๊ป˜ ๊ฒ€ํ† ํ•  ๊ฒƒ. + +## ์ง€ํ•˜์ˆ˜ ๊ทœ์น™ ๋„ท + +1. **KDS ๊ธฐ๋ณธ** โ€” ๋น„ํƒˆ ๊ฒ‰ ์ง€์ธต์ด ํ† ์‚ฌ ยท ํ’ํ™”์•”์ด๋ฉด ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”, ์•”๋ฐ˜์ด๋ฉด ๋น„ํƒˆ ๋†’์ด ยฝ ๊นŠ์ด๊นŒ์ง€ ํฌํ™”(KDS 11 70 05 ํ‘œ 4.3-3). +2. **์ง€ํ‘œ ์•„๋ž˜ ๊นŠ์ด = ๋น„ํƒˆ ๋†’์ด ร— ๋น„์œจ** โ€” ์ˆ˜์œ„ = ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ์—์„œ (๋น„ํƒˆ ๋†’์ด ร— ๋น„์œจ)๋งŒํผ ์•„๋ž˜. ์ขŒ ยท ์šฐ ๋น„ํƒˆ์€ ๊ฐ์ž ๋†’์ด๋กœ ์…ˆ. ๊ทผ๊ฑฐ ์—†์Œ ยท ์‚ฌ์šฉ์ž ํŒ๋‹จ(0.5 = KDS ์•”๋ฐ˜ ยฝ ์™€ ๊ฐ™์€ ๊ฐ’). +3. **๊ฐ„๊ทน์ˆ˜์••๋น„ r_u** โ€” ์ˆ˜์œ„์„  ์—†์ด ๊ฐ„๊ทน์ˆ˜์•• = r_u ร— ๊ทธ ์ž๋ฆฌ ํ™ ๋ฌด๊ฒŒ ์••๋ ฅ(ฮณ ร— ๊นŠ์ด). ๊ทผ๊ฑฐ ์—†์Œ ยท ์‚ฌ์šฉ์ž ํŒ๋‹จ. +4. **์‚ฌ์šฉ์ž ์ˆ˜์œ„์„ ** โ€” ๊ตฌ๊ฐ„๋งˆ๋‹ค ์ (offset ยท ์ง€ํ‘œ ์•„๋ž˜ ๊นŠ์ด)์„ ์ด์€ ์„ . ์  ์‚ฌ์ด๋Š” ๊ณง์€ ์„ . ๋ฎ์ง€ ์•Š์€ ์ธก์ ์€ KDS ๊ธฐ๋ณธ ๊ทœ์น™. + +- ๊ฑด๊ธฐ ยท ์ง€์ง„ ์‹œ๋Š” ใ€Œ๊ฑด๊ธฐ ์ง€ํ•˜์ˆ˜ใ€ ๊ทœ์น™(๊ธฐ๋ณธ ์—†์Œ)์„ ์”€. +- ์ง€๊ธˆ E03 ์•ˆ์ „์œจ ๊ณ„์‚ฐ์€ ์šฐ๊ธฐ๋ฅผ ๋ชจ๋“  ์ธก์  ใ€Œ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”ใ€๋กœ ์…ˆ โ€” ์œ„ ๊ทœ์น™์€ ํšก๋‹จ ๊ทธ๋ฆผ์˜ ์ˆ˜์œ„์„ ์—๋งŒ ๋ฐ˜์˜๋จ(๊ณ„์‚ฐ ๋ฐ˜์˜์€ ์ค€๋น„ ์ค‘). ํฌํ™”๋Š” ๊ฐ€์žฅ ๋ถˆ๋ฆฌํ•œ ์ชฝ ๊ฐ€์ •์ž„. + +## ๋…ธ๋ฉด ํ•˜์ค‘ + +- ๋…ธ๋ฉด ์–‘ ๋ ์‚ฌ์ด์— ๊ณ ๋ฅด๊ฒŒ ์–น๋Š” ํ•˜์ค‘(๊ธฐ๋ณธ 10 kPa = 1 mยฒ ์— ์•ฝ 1 t). +- ๊ทผ๊ฑฐ = ์˜น๋ฒฝ ๋’ค ์ƒ์žฌํ•˜์ค‘ 10 kN/mยฒ ๋ฅผ ๋นŒ๋ ค ์”€(์ง€์‹DB ๊ตฌ์กฐ๋ฌผ ์•ˆ์ •๊ณ„์‚ฐ ยท KDS 11 80 05). ์ž„๋„ ์ฐจ๋Ÿ‰ ํ•˜์ค‘์˜ ๊ณ ์œ  ๊ทผ๊ฑฐ๋Š” ์—†์Œ. + +## ์ง€์ง„ + +- ์œ ์‚ฌ์ •์  ํ•ด์„ โ€” ํ™ ๋ฌด๊ฒŒ ร— kh ๋งŒํผ ์˜†์œผ๋กœ ๋ฏธ๋Š” ํž˜์„ ๋”ํ•ด ์…ˆ. +- KDS 11 90 00 1.8.1 โ‘ก โ€” ๋ฌด๋„ˆ์กŒ์„ ๋•Œ ์˜ํ–ฅ ๋ฒ”์œ„์— ์ฃผ์š” ๊ตฌ์กฐ๋ฌผ์ด ์—†์œผ๋ฉด ์ ์šฉํ•˜์ง€ ์•Š์Œ. ์ž„๋„๋Š” ๋ฐœ์ฃผ์ž์™€ ํ˜‘์˜ํ•  ๊ฒƒ ยท ๊ธฐ๋ณธ ๋”. +- kh ๋Š” ์ง€๋ฐ˜ ๊ฐ€์†๋„์˜ ๋น„์œจ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ๋„ฃ์Œ(์ง€์‹DB 02 ์˜ˆ์‹œ 0.05 ~ 0.15). + +## ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ๊ธฐ๋ณธ๊ฐ’ ๊ทผ๊ฑฐ + +- ์ง€์‹DB 07(์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ์›๋ฌธ ๊ทผ๊ฑฐ) ยง3 ยท ยง4 ์ข…ํ•ฉ ๋Œ€์กฐํ‘œ โ€” ใ€Œ๋„๋กœ์„ค๊ณ„์š”๋ นใ€ ยท ใ€Œ๋„๋กœ๊ต์‹œ๋ฐฉ์„œใ€ ยท ์„œ์šธ์‹œ ใ€Œ์ง€๋ฐ˜์กฐ์‚ฌํŽธ๋žŒใ€(2006) ยท ํ•œ๊ตญ์ง€๋ฐ˜๊ณตํ•™ํšŒ ์‚ฌ๋ฉด์•ˆ์ • ํ•™์ˆ ๋ฐœํ‘œํšŒ ยท ํ•œ๊ตญ๋„๋กœ๊ณต์‚ฌ ใ€Œ๋„๋กœ์„ค๊ณ„์‹ค๋ฌดํŽธ๋žŒใ€(1996) ยท ใ€Œ๊ฑด์„ค๊ณต์‚ฌ ๋น„ํƒˆ๋ฉด ์„ค๊ณ„๊ธฐ์ค€ ํ•ด์„คใ€ ยท Hoek & Bray. +- ์ง€์‹DB 04(ํ‘œ์ค€ ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ํ›„๋ณด๊ตฐ ยท ํ—ˆ์šฉ์•ˆ์ „์œจ) โ€” ์ง€์ธต๋ณ„ ๋ฒ”์œ„์™€ ํ›„๋ณด๊ฐ’. +- ์นธ๋งˆ๋‹ค ๊ทผ๊ฑฐ ๋ฌธ์žฅ์€ ์นธ์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ๋ณด์ž„. + +## ํ•œ๊ณ„ + +- ํ˜„์žฅ ์‹œ์ถ” ยท ์‹œํ—˜ ์—†์ด ๋ฌธํ—Œ ๋Œ€ํ‘œ๊ฐ’์„ ์”€ โ€” ๊ฐ™์€ ์ด๋ฆ„์˜ ์ง€์ธต๋„ ํ˜„์žฅ๋งˆ๋‹ค ๊ฐ’์ด ํฌ๊ฒŒ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ. +- ์ธก์ ๋งˆ๋‹ค ์ง€์ธต์€ ์œ„ ยท ์•„๋ž˜ ๋‘ ์ธต๊นŒ์ง€๋งŒ ๋‚˜๋ˆ”(๊ตฌ๊ฐ„ ๋ฎ์–ด์“ฐ๊ธฐ๋„ ๋‘ ์ธต). +- ๊ฐ•์šฐ ์นจํˆฌ ํ•ด์„(์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ์ˆ˜์œ„ ๋ณ€ํ™”)์€ ํ•˜์ง€ ์•Š์Œ. diff --git a/resources/help/E02/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md b/resources/help/E02/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md new file mode 100644 index 000000000..631fd941c --- /dev/null +++ b/resources/help/E02/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md @@ -0,0 +1,58 @@ +# ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ + +์•„๋ž˜ ๊ฐ’์€ ๋ฌธํ—Œ ยท ๊ธฐ์ค€์—์„œ ๊ณ ๋ฅธ **์ถ”์ • ๊ธฐ๋ณธ๊ฐ’**์ž„. ํ˜„์žฅ ์‹œํ—˜ ๊ฐ’์ด ์•„๋‹ˆ๋ฉฐ, ๋ฐœ์ฃผ์ฒ˜ ยท ์„ค๊ณ„์ž ํ™•์ธ ์ „ ์ƒํƒœ์ž„. ํ™”๋ฉด์—์„œ ์–ธ์ œ๋“  ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ. + +## ์ง€๋ฐ˜๋ฌผ์„ฑ์น˜ ๊ธฐ๋ณธ๊ฐ’ + +| ์ง€์ธต | ฮณt (kN/mยณ) | ฮณsat (kN/mยณ) | c (kPa) | ฯ† (ยฐ) | ๋ฌธํ—Œ ๋ฒ”์œ„(์ง€์‹DB 07 ยง4) | ์ƒํƒœ | +| --- | --- | --- | --- | --- | --- | --- | +| ํ’ํ™”ํ†  | 18.0 | 19.0 | 15 | 30 | ฮณt 18~19 ยท c 5~20 ยท ฯ† 27~32 | ๋ฌธํ—Œ ์ถ”์ • ยท ํ™•์ • ์ „ | +| ํ’ํ™”์•” | 20.0 | 21.0 | 30 | 33 | ฮณt 19.5~21 ยท c 10~30 ยท ฯ† 30~36 | ๋ฌธํ—Œ ์ถ”์ • ยท ํ™•์ • ์ „ | +| ์—ฐ์•” | 24.0 | 24.5 | 150 | 35 | ฮณt 22~24 ยท c 50~300 ยท ฯ† 33~40 | ๋ฌธํ—Œ ์ถ”์ • ยท ํ™•์ • ์ „ | +| ๊ฒฝ์•” | 26.0 | 26.0 | 300 | 40 | ฮณt 25~27 ยท c 200 ์ด์ƒ ยท ฯ† 40~45 | ๋ฌธํ—Œ ์ถ”์ • ยท ํ™•์ • ์ „ | +| ์„ฑํ†  | 18.0 | 19.0 | 10 | 28 | ฮณt 18~19 ยท c 0~15 ยท ฯ† 28~30 | ๋ฌธํ—Œ ์ถ”์ • ยท ํ™•์ • ์ „ | +| ๋ถ•์ ํ†  | 17.5 | 18.5 | 5 | 27 | ฮณt 17~18 ยท c 0~10 ยท ฯ† 25~28 | ๋ฌธํ—Œ ์ถ”์ • ยท ํ™•์ • ์ „ | + +- ์ถœ์ฒ˜ ๋ฌธ์žฅ์€ ์นธ์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ๋ณด์ž„. + +## ์ง€์ธต ์—ฐ๊ฒฐ ยท ๋‘๊ป˜ + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| B06 ํ† ์‚ฌ โ†’ | ํ’ํ™”ํ†  | ์š”๊ตฌ์„œ 345 6์žฅ ยท ๋ถ„์„ 345-D | ๋ธŒ๋ ˆ์ธ ์ œ์•ˆ ยท ํ™•์ • ์ „ | +| B06 ๋ฆฌํ•‘์•” โ†’ | ํ’ํ™”์•” | ๊ฐ™์Œ | ๋ธŒ๋ ˆ์ธ ์ œ์•ˆ ยท ํ™•์ • ์ „ | +| B06 ๋ฐœํŒŒ์•” โ†’ | ์—ฐ์•” | ๊ฐ™์Œ | ๋ธŒ๋ ˆ์ธ ์ œ์•ˆ ยท ํ™•์ • ์ „ | +| B06 ์„ฑํ† ์ฒด โ†’ | ์„ฑํ†  | ๊ฐ™์Œ | ๋ธŒ๋ ˆ์ธ ์ œ์•ˆ ยท ํ™•์ • ์ „ | +| ์•” ์ธก์  ํ† ์ธต ๋‘๊ป˜ | B06 ์•” ๊ฒฝ๊ณ„ ๊นŠ์ด(๊ธฐ๋ณธ 0.5 m) | B06 ๋ฌผ๋Ÿ‰ ๊ฐ€๋ฆ„์šฉ ๊ฐ’ โ€” ์ง€์ธต ์กฐ์‚ฌ ๊ฐ’ ์•„๋‹˜ | ํ™•์ธ ํ•„์š” | + +## ๊ธฐ์ค€์•ˆ์ „์œจ + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์‹œ ยท ๋‹จ๊ธฐ | ใ€Œ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐใ€ ํ‘œ | KDS 11 70 05 ํ‘œ 4.3-1 ยท 4.3-2 ยท 4.3-3 | ๊ธฐ์ค€๊ฐ’ ยท ์‚ฌ์šฉ์ž ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ | +| ์šฐ๊ธฐ ๊นŽ๊ธฐ | 1.2 | ๊ธฐ๋ณธ๊ฐ’ ๊ทผ๊ฑฐ ๊ธ€ = ์ง€ํ•˜์ˆ˜์œ„ ๊ฒฐ์ • ๋ฐฉ์‹ 1.2(๊ฐ•์šฐ ์นจํˆฌ ํ•ด์„ 1.3) | ์ง€์‹DB 04 ํ‘œ๋Š” ใ€Œ์ง€ํ‘œ ํฌํ™” 1.2 ยท ์ผ๋ฐ˜ ์ง€ํ•˜์ˆ˜์œ„ 1.3ใ€๋กœ ํ’€์–ด ์ ์Œ โ€” ์–ด๋А ํ•ด์„์„ ๋”ฐ๋ฅผ์ง€ ํ™•์ธ ํ•„์š” | + +## ํ•˜์ค‘ ยท ์ง€์ง„ + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| ๋…ธ๋ฉด ํ•˜์ค‘ | 10 kPa | ์˜น๋ฒฝ ๋’ค ์ƒ์žฌํ•˜์ค‘ 10 kN/mยฒ ์ค€์šฉ(KDS 11 80 05) ยท ์ž„๋„ ์ฐจ๋Ÿ‰ ํ•˜์ค‘ ๊ณ ์œ  ๊ทผ๊ฑฐ ์—†์Œ | ๋ธŒ๋ ˆ์ธ ์ œ์•ˆ ยท ํ™•์ • ์ „ | +| ์ง€์ง„ ๊ฒ€ํ†  | ๋” | KDS 11 90 00 1.8.1 โ‘ก ยท ์ž„๋„๋Š” ๋ฐœ์ฃผ์ž ํ˜‘์˜ | ๋ฐœ์ฃผ์ž ํ˜‘์˜ ํ•„์š” | +| ์ˆ˜ํ‰์ง€์ง„๊ณ„์ˆ˜ kh | 0 | ์‚ฌ์šฉ์ž ์ž…๋ ฅ ๋ชซ | ์ผค ๋•Œ ๊ฐ’ ์ •ํ•  ๊ฒƒ | + +## ์ง€ํ•˜์ˆ˜ ๊ทœ์น™ + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| ๊ฑด๊ธฐ ์ง€ํ•˜์ˆ˜ | ์—†์Œ | KDS 11 70 05 ํ‘œ 4.3-3 | ๊ธฐ์ค€๊ฐ’ | +| ์šฐ๊ธฐ ๊ทœ์น™ | KDS ๊ธฐ๋ณธ | KDS 11 70 05 ํ‘œ 4.3-3 | ๊ธฐ์ค€๊ฐ’ | +| ์šฐ๊ธฐ ยท ํ† ์‚ฌ ยท ํ’ํ™”์•” | ์ง€ํ‘œ ํฌํ™” | KDS 11 70 05 ํ‘œ 4.3-3 | ๊ธฐ์ค€๊ฐ’ | +| ์šฐ๊ธฐ ยท ์•”๋ฐ˜ | ๋น„ํƒˆ ๋†’์ด ยฝ ํฌํ™” | KDS 11 70 05 ํ‘œ 4.3-3 | ๊ธฐ์ค€๊ฐ’ | +| ๊นŠ์ด ๋น„์œจ | 0.5 | ๊ทผ๊ฑฐ ์—†์Œ(KDS ์•”๋ฐ˜ ยฝ ์™€ ๊ฐ™์€ ๊ฐ’) | ์‚ฌ์šฉ์ž ์ž…๋ ฅ | +| r_u | 0.2 | ๊ทผ๊ฑฐ ์—†์Œ | ์‚ฌ์šฉ์ž ์ž…๋ ฅ | +| ์ง€ํ•˜์ˆ˜ ๋ฌด๋ฆฌ | ํ’ํ™”ํ†  ยท ํ’ํ™”์•” ยท ์„ฑํ†  ยท ๋ถ•์ ํ†  โ†’ ใ€Œํ† ์‚ฌ ยท ํ’ํ™”์•”ใ€, ์—ฐ์•” ยท ๊ฒฝ์•” โ†’ ใ€Œ์•”๋ฐ˜ใ€ | ๊ธฐ๋ณธ๊ฐ’ ํŒŒ์ผ | ํ™•์ • ์ „ | + +## ๊ณ„์‚ฐ์— ์•„์ง ์•ˆ ๋“ค์–ด๊ฐ„ ๊ฐ’ + +- ์šฐ๊ธฐ ๊ทœ์น™(KDS ๊ธฐ๋ณธ์˜ ์•”๋ฐ˜ ยฝ ยท ๊นŠ์ด ๋น„์œจ ยท r_u ยท ์‚ฌ์šฉ์ž ์ˆ˜์œ„์„ )์€ ์ง€๊ธˆ E03 ํšก๋‹จ ๊ทธ๋ฆผ์˜ ์ˆ˜์œ„์„ ์—๋งŒ ๋ณด์ž„. ์•ˆ์ „์œจ ๊ณ„์‚ฐ์€ ๋ชจ๋“  ์ธก์ ์„ ใ€Œ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”ใ€๋กœ ์…ˆ โ€” ๊ณ„์‚ฐ ๋ฐ˜์˜์€ ์ค€๋น„ ์ค‘. +- ๊ธฐ์ค€์•ˆ์ „์œจ ใ€Œ๋‹จ๊ธฐใ€ ์ค„ ยท ใ€Œ์Œ“๊ธฐ(์—ฐ์•ฝ์ง€๋ฐ˜)ใ€ ์นธ์€ 2D ํŒ์ •์— ์•„์ง ์•ˆ ์“ฐ์ž„. diff --git a/resources/help/E03/00_์„ค๋ช…์„œ.md b/resources/help/E03/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..a4407ce83 --- /dev/null +++ b/resources/help/E03/00_์„ค๋ช…์„œ.md @@ -0,0 +1,69 @@ +# 2D ํ•ด์„ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +- ๋ชจ๋“  ์ธก์  ํšก๋‹จ์—์„œ ๋น„ํƒˆ์ด ๋ฏธ๋„๋Ÿฌ์งˆ ์œ„ํ—˜์„ 2D ํ•œ๊ณ„ํ‰ํ˜•ํ•ด์„์œผ๋กœ ์…ˆ โ€” ์ธก์  ยท ์ขŒ์šฐ ๋น„ํƒˆ ยท ์กฐ๊ฑด(๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„)๋งˆ๋‹ค ์ตœ์†Œ ์•ˆ์ „์œจ๊ณผ ํŒ์ •(O.K ยท N.G). +- ๊นŽ๊ธฐ ๋น„ํƒˆ ยท ์Œ“๊ธฐ ๋น„ํƒˆ์„ ๋”ฐ๋กœ ์…ˆ. ์˜น๋ฒฝ ยท ์„์ถ• ๋“ฑ ๋ฒฝ์ด ์žˆ๋Š” ์ธก์ ์€ ๋ฒฝ์„ ๋„ฃ์–ด ์…ˆ. +- ใ€Œ๋ณด๊ฐ• ๊ฒ€ํ† ใ€ ํƒญ์—์„œ ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ •์„ ๋‹ค์‹œ ํ•ด์„ํ•˜๊ณ , ๋ฒฝ์˜ ์™ธ์  ์•ˆ์ •(ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ)์„ ๊ฒ€ํ† ํ•จ. +- ํ™”๋ฉด = ์ขŒ ์ธก์  ๋ชฉ๋ก ยท ์œ„ ํ‰๋ฉด ์œ„ํ—˜ ๋  ยท ๊ฐ€์šด๋ฐ ํšก๋‹จ ยท ์šฐ ๊ฒฐ๊ณผ. + +## ์ž‘์—… ์ˆœ์„œ + +### ํ•ด์„ + +1. ์ง€๋ฐ˜ ์กฐ๊ฑด(E02)์—์„œ ๊ฐ’์„ ํ™•์ธํ•˜๊ณ  [์ €์žฅ]ํ•ด ๋‘  โ€” ์ €์žฅํ•œ ๊ฐ’์œผ๋กœ๋งŒ ๊ณ„์‚ฐํ•จ. +2. ์ขŒ์ธก ์•„๋ž˜ [ํ•ด์„]์„ ๋ˆ„๋ฆ„. ํ™”๋ฉด์ด ์ž ๊ธฐ๊ณ  ใ€Œํ•ด์„ ์ค‘ n% ยท ์ธก์  k / Nใ€ ์ง„ํ–‰๋ฅ ์ด ๋ณด์ž„. ์ธก์  ์ˆ˜์‹ญ ๊ฐœ๋ฉด ๋ช‡ ๋ถ„ ๊ฑธ๋ฆผ. +3. ๋๋‚˜๋ฉด ใ€Œํ•ด์„์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค.ใ€ ์•Œ๋ฆผ ๋’ค ๋ชฉ๋ก ยท ํ‰๋ฉด ๋  ยท ํšก๋‹จ์ด ์ฑ„์›Œ์ง. ์ฒ˜์Œ ๋ณด์ด๋Š” ์ธก์  = ์ง€๊ธˆ ์กฐ๊ฑด์—์„œ ์•ˆ์ „์œจ์ด ๊ฐ€์žฅ ๋‚ฎ์€ ์ธก์ . +4. ์ขŒ์ธก ์œ„ **์กฐ๊ฑด**์—์„œ ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ(์ง€์ง„ ๊ฒ€ํ† ๋ฅผ ์ผฐ์œผ๋ฉด ์ง€์ง„๋„)๋ฅผ ๊ณ ๋ฆ„. ๋ชฉ๋ก ยท ํ‰๋ฉด ๋  ยท ํšก๋‹จ์ด ๊ทธ ์กฐ๊ฑด์œผ๋กœ ๋ฐ”๋€œ. +5. ๋ชฉ๋ก ์ค„์ด๋‚˜ ํ‰๋ฉด ๋ ์˜ ์ธก์  ๊ทผ์ฒ˜๋ฅผ ๋ˆŒ๋Ÿฌ ์ธก์ ์„ ๊ณ ๋ฆ„. ๊ฐ€์šด๋ฐ ์œ„ ๋น„ํƒˆ ์นธ์—์„œ ใ€Œ์ขŒ ๋น„ํƒˆ ยท ๊นŽ๊ธฐใ€ ๋“ฑ ๋น„ํƒˆ์„ ๊ณ ๋ฆ„(์ฒ˜์Œ = ์•ˆ์ „์œจ ๋‚ฎ์€ ์ชฝ). +6. ํšก๋‹จ์˜ ์ ˆํŽธ์ด๋‚˜ ์šฐ์ธก ์ ˆํŽธํ‘œ ์ค„์„ ๋ˆ„๋ฅด๋ฉด ๊ทธ ์ ˆํŽธ์˜ ํž˜์ด ์ž์œ ๋ฌผ์ฒด๋„ ยท ๊ทธ๋ž˜ํ”„์— ๋ณด์ž„. +7. ๋ณด๊ณ ์„œ(E05)์— ๋„ฃ์„ ๋Œ€ํ‘œ ๋‹จ๋ฉด์€ ๋ชฉ๋ก ใ€Œ๋Œ€ํ‘œใ€ ์นธ ๋ณ„(โ˜… ยท โ˜†)์„ ๋ˆŒ๋Ÿฌ ์ผœ๊ณ  ๋ˆ ๋’ค [๋Œ€ํ‘œ ๋‹จ๋ฉด ์ €์žฅ]. +8. [3D ๋ณด๊ธฐ]๋กœ ๋…ธ์„  3D ๋ฉด ์œ„ ์•ˆ์ „์œจ ์ƒ‰์„ ๋ด„. + +### ๋ณด๊ฐ• ๊ฒ€ํ†  + +1. ์ธก์ ์„ ๊ณ ๋ฅด๊ณ  ๊ฐ€์šด๋ฐ ์œ„ ๋ณด๊ธฐ ์นธ์—์„œ ใ€Œ๋ณด๊ฐ• ๊ฒ€ํ† ใ€๋ฅผ ๊ณ ๋ฆ„. ์šฐ์ธก์ด ๋ณด๊ฐ• ๊ฒ€ํ†  ํŒ์œผ๋กœ ๋ฐ”๋€œ. +2. **๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ •**: ๋น„ํƒˆ ยท ๊ธฐ์šธ๊ธฐ 1:n ยท ์†Œ๋‹จ ํญ ยท ์†Œ๋‹จ ๋†’์ด๋ฅผ ๋„ฃ๊ณ  [๊ฐ€์ • ํ•ด์„]. ๋˜๋Š” [์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ]๋กœ ์ง€๊ธˆ ์กฐ๊ฑด ๊ธฐ์ค€์•ˆ์ „์œจ์„ ๋งž์ถ”๋Š” ๊ฐ€์žฅ ๊ธ‰ํ•œ ๊ธฐ์šธ๊ธฐ๋ฅผ ์ฐพ์Œ(์ˆ˜์‹ญ ์ดˆ ~ 1 ๋ถ„). +3. **๋ฒฝ ์™ธ์  ์•ˆ์ •**: ๋ฒฝ ์žˆ๋Š” ์ธก์ ์—์„œ๋งŒ ๋ณด์ž„. ๋ณด๊ฐ• ๊ฒ€ํ†  ํƒญ์„ ์—ด๋ฉด Coulomb ยท ๋ฐฐ๋ฉด ์ˆ˜์œ„ 0 ์œผ๋กœ ํ•œ ๋ฒˆ ์ž๋™ ๊ฒ€ํ† ํ•จ. ํ† ์•• ยท ๋ฐฐ๋ฉด ์ˆ˜์œ„๋ฅผ ๋ฐ”๊ฟ” [์™ธ์  ์•ˆ์ • ๊ฒ€ํ† ]. +4. ๋„ค์ผ ยท ์•ต์ปค ๋ณด๊ฐ• ๊ฐ€์ •์€ ์ค€๋น„ ์ค‘. + +## ๋‹จ์ถ” + +| ๋‹จ์ถ” ยท ์นธ | ์ž๋ฆฌ | ํ•˜๋Š” ์ผ | +| --- | --- | --- | +| ์กฐ๊ฑด ยท ์ •๋ ฌ | ์ขŒ์ธก ์œ„ | ์กฐ๊ฑด ๊ณ ๋ฆ„ ยท ใ€Œ์ธก์  ์ˆœใ€ ๋˜๋Š” ใ€Œ์•ˆ์ „์œจ ๋‚ฎ์€ ์ˆœใ€ | +| N.G ๋งŒ | ์ขŒ์ธก ์œ„ | ์ง€๊ธˆ ์กฐ๊ฑด์—์„œ N.G ์ธ ์ธก์ ๋งŒ ๋ณด์ž„ | +| [ํ•ด์„] | ์ขŒ์ธก ์•„๋ž˜ | ์ „ ์ธก์  ํ•ด์„. ์ž…๋ ฅ์ด ๊ฐ™์œผ๋ฉด ใ€Œ์ž…๋ ฅ์ด ๊ฐ™์•„ ๋‹ค์‹œ ์…€ ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค.ใ€ ยท ๋ฐ”๋€ ์ธก์ ๋งŒ ๋‹ค์‹œ ์…ˆ | +| [3D ๋ณด๊ธฐ] | ์ขŒ์ธก ์•„๋ž˜ | ๋…ธ์„  3D ๋ฉด ์ƒ‰์น  ์ฐฝ์„ ์—ถ(ํ•ด์„ ๋’ค์—๋งŒ ์ผœ์ง) ยท [๋‹ซ๊ธฐ]๋กœ ๋‹ซ์Œ | +| [๋Œ€ํ‘œ ๋‹จ๋ฉด ์ €์žฅ] | ์ขŒ์ธก ์•„๋ž˜ | ๋ณ„ ์ผฌ/๋”์„ ์ €์žฅํ•จ(๋ฐ”๊พผ ๊ฒƒ์ด ์žˆ์„ ๋•Œ๋งŒ ์ผœ์ง) | +| ๋น„ํƒˆ ์นธ | ๊ฐ€์šด๋ฐ ์œ„ | ๋ณผ ๋น„ํƒˆ(์ขŒ ยท ์šฐ ยท ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ) | +| ๋ณด๊ธฐ ์นธ | ๊ฐ€์šด๋ฐ ์œ„ | ใ€Œํ•ด์„ ๊ฒฐ๊ณผใ€ โ†” ใ€Œ๋ณด๊ฐ• ๊ฒ€ํ† ใ€ | +| ๋“ฑ์•ˆ์ „์œจ ์ง€๋„ ยท ์›ํ˜ธ ์ค‘์‹ฌ ๊ฒฉ์ž ยท ๋‹ค๋ฅธ ํ›„๋ณด | ๊ฐ€์šด๋ฐ ์œ„ | ํšก๋‹จ ๊ทธ๋ฆผ์— ์ผœ๊ณ  ๋”(๊ธฐ๋ณธ = ์ง€๋„ ์ผฌ ยท ๊ฒฉ์ž ๋” ยท ํ›„๋ณด ์ผฌ) | +| [๊ฐ€์ • ํ•ด์„] ยท [์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ] | ๋ณด๊ฐ• ๊ฒ€ํ†  | ๋น„ํƒˆ ๊ฐ€์ • ์žฌํ•ด์„ | +| [์™ธ์  ์•ˆ์ • ๊ฒ€ํ† ] | ๋ณด๊ฐ• ๊ฒ€ํ†  | ๋ฒฝ ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ ๊ฒ€ํ†  | + +- ํšก๋‹จ ๊ทธ๋ฆผ: ํœ  = ํ™•๋Œ€ ยท ์ถ•์†Œ ยท ๊ฐ€์šด๋ฐ ๋‹จ์ถ” ๋Œ๊ธฐ = ์˜ฎ๊ธฐ๊ธฐ ยท ๋‘ ๋ฒˆ ๋ˆ„๋ฆ„ = ์ฒ˜์Œ ๋ฐฐ์œจ. ๋งˆ์šฐ์Šค ์•„๋ž˜ ใ€Œ๊ฑฐ๋ฆฌ ยท ELใ€(offset ยท ํ‘œ๊ณ ) ํ‘œ์‹œ. + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +- **O.K** = ์ตœ์†Œ ์•ˆ์ „์œจ โ‰ฅ ๊ธฐ์ค€์•ˆ์ „์œจ(E02) ยท **N.G** = ๊ทธ๋ณด๋‹ค ์ž‘์Œ ยท ใ€Œ-ใ€ = ํ•ด์„ ์—†์Œ ยท ใ€Œ์˜ค๋ฅ˜ใ€ = ๊ทธ ์ธก์  ๊ณ„์‚ฐ ์‹คํŒจ(์ค„์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ๊นŒ๋‹ญ). +- ํŒ์ •์— ์“ฐ๋Š” ์•ˆ์ „์œจ = Bishop ๊ฐ„ํŽธ๋ฒ• ์ตœ์†Œ๊ฐ’(์›ํ˜ธ ยท ๋น„์›ํ˜ธ ์ค‘ ์ž‘์€ ์ชฝ). ์ธก์  ํŒ์ • = ๊ทธ ์กฐ๊ฑด ์ขŒ ยท ์šฐ ๋น„ํƒˆ ์ค‘ ํ•˜๋‚˜๋ผ๋„ N.G ๋ฉด N.G. +- **์ขŒ์ธก ๋ชฉ๋ก**: ์ธก์  ยท ์ขŒ ยท ์šฐ(๊นŽ ยท ์Œ“) ยท ์กฐ๊ฑด๋ณ„ ์ตœ์†Œ ์•ˆ์ „์œจ ยท ํŒ์ • ยท ๋Œ€ํ‘œ. โ–ฆ = ๋ฒฝ ์žˆ๋Š” ์ธก์ . +- **ํ‰๋ฉด ์œ„ํ—˜ ๋ **: ๋…ธ์„ ์„ ์ง€๊ธˆ ์กฐ๊ฑด ํŒ์ • ์ƒ‰์œผ๋กœ ์น ํ•จ(์ดˆ๋ก O.K ยท ๋นจ๊ฐ• N.G ยท ํšŒ์ƒ‰ ํ•ด์„ ์—†์Œ). ์˜ค๋ฅธ์ชฝ ์œ„์— ํŒ์ •๋ณ„ ์ธก์  ์ˆ˜. ๊ณ ๋ฅธ ์ธก์ ์€ ๋™๊ทธ๋ผ๋ฏธ. +- **ํšก๋‹จ ์ƒ‰(๋“ฑ์•ˆ์ „์œจ)**: ์›ํ˜ธ ์ค‘์‹ฌ ์ž๋ฆฌ๋งˆ๋‹ค ์ตœ์†Œ ์•ˆ์ „์œจ์„ ๊ธฐ์ค€์•ˆ์ „์œจ ๋Œ€๋น„ ์ผ๊ณฑ ๋ ๋กœ ์น ํ•จ(๋ฐ์€ ํ™”๋ฉด ๊ธฐ์ค€) โ€” ์ง„ํ•œ ๋นจ๊ฐ•(๊ธฐ์ค€์˜ 0.8 ๋ฐฐ ๋ฏธ๋งŒ) ยท ๋นจ๊ฐ•(0.8 ~ 0.9) ยท ๋ถ„ํ™(0.9 ~ 1.0) ยท ์—ฐํšŒ์ƒ‰(1.0 ~ 1.1) ยท ์—ฐํŒŒ๋ž‘(1.1 ~ 1.25) ยท ํŒŒ๋ž‘(1.25 ~ 1.5) ยท ์ง„ํŒŒ๋ž‘(1.5 ๋ฐฐ ์ด์ƒ). ๋ฒ”๋ก€์—๋Š” ์‹ค์ œ ์•ˆ์ „์œจ ๊ฐ’์œผ๋กœ ์ ํž˜. +- **ํšก๋‹จ ์„ **: ๊ตต์€ ์„  = ์ž„๊ณ„ ํ™œ๋™๋ฉด(Fs ๊ฐ’ ยท ์›ํ˜ธ ์ค‘์‹ฌ Xc ยท Zc ยท ๋ฐ˜๊ฒฝ R) ยท ์˜…์€ ์„  = ๋‹ค๋ฅธ ๋ฐฉ๋ฒ• ยท ๋‹ค๋ฅธ ๊ผด ํ›„๋ณด ยท ์ ์„  = ์ง€ํ•˜์ˆ˜์œ„ ยท ๋…ธ๋ฉด ์œ„ ๋ˆˆ๊ธˆ = ๋…ธ๋ฉด ํ•˜์ค‘ ยท ํšŒ์ƒ‰ ๋น—๊ธˆ = ๋ฒฝ. +- **์šฐ์ธก ๊ฒฐ๊ณผ**: ์ตœ์†Œ ์•ˆ์ „์œจ ยท ํŒ์ • ยท ๊ธฐ์ค€์•ˆ์ „์œจ ยท ๋ฐฉ๋ฒ• ยท ์ˆ˜๋ ด ยท ๋น„ํƒˆ ๋†’์ด ยท ์›ํ˜ธ ์ค‘์‹ฌ ยท ๋ฐ˜๊ฒฝ ยท ๋ฐฉ๋ฒ•๋ณ„ ์ตœ์†Œ ์•ˆ์ „์œจ ํ‘œ(Fellenius ยท Bishop ยท Janbu ๋ณด์ • ยท Spencer) ยท ์ ˆํŽธํ‘œ ยท ์ ˆํŽธ ์ž์œ ๋ฌผ์ฒด๋„ ยท ์ ˆํŽธ๋ ฅ ๊ทธ๋ž˜ํ”„. ์นธ ๋œป์€ ใ€Œ์นธ ๋œปใ€ ํ•ญ๋ชฉ. +- **3D ๋ณด๊ธฐ**: ๋…ธ์„  ๋ฉด์„ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ธก์ ์˜ (์ตœ์†Œ ์•ˆ์ „์œจ รท ๊ธฐ์ค€์•ˆ์ „์œจ) ๋  ์ƒ‰์œผ๋กœ ์น ํ•จ ยท ํ•ด์„ ์—†๋Š” ์ธก์ ์€ ํšŒ์ƒ‰. ํ‘œ์‹œ๋งŒ ํ•จ. +- **๋Œ€ํ‘œ ๋‹จ๋ฉด ๋ณ„**: ์ฑ„์šด ๋ณ„ โ˜… = ์ผฌ ยท ๋ฐ‘์ค„ = ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ”๊ฟˆ. ์ž๋™์œผ๋กœ ์ผœ์ง€๋Š” ์ธก์  = ์Œ“๊ธฐ ๋†’์ด 10 m ์ด์ƒ ยท ๊นŽ๊ธฐ ๋†’์ด 15 m ์ด์ƒ ยท ์ตœ์†Œ ์•ˆ์ „์œจ ํ•˜์œ„ 5 ๊ณณ. +- ์ขŒ์ธก ์ƒํƒœ ๊ธ€: ใ€Œํ•ด์„ ์‹œ๊ฐใ€ ยท ใ€Œ์ง€๋ฐ˜ ์กฐ๊ฑด์ด ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค โ€” [ํ•ด์„]์„ ๋‹ค์‹œ ๋ˆ„๋ฅด์„ธ์š”ใ€(๋ณด์ด๋Š” ๊ฒฐ๊ณผ๋Š” ์˜› ๊ฒฐ๊ณผ) ยท ใ€Œ๋Œ€ํ‘œ ๋‹จ๋ฉด ๋ฐ”๋€œ โ€” ์ €์žฅ ์•ˆ ๋จใ€. + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- **๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ**: ์ง€๋ฐ˜ ๊ฐ’์ด ๋ฌธํ—Œ ๋Œ€ํ‘œ๊ฐ’์ด๋ผ ์•ˆ์ „์œจ์€ ์„ค๊ณ„ ๊ฒ€ํ† ์šฉ ์ฐธ๊ณ ๊ฐ’์ž„. ํ˜„์žฅ ์กฐ์‚ฌ ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ” ๋‹ค์‹œ ํ•ด์„ํ•  ๊ฒƒ. +- ํ•ด์„์€ ์„œ๋ฒ„๊ฐ€ ํ•จ ยท ํ™”๋ฉด์€ ๊ฒฐ๊ณผ๋งŒ ๋ณด์ž„. ๊ฐ™์€ ์ž…๋ ฅ์ด๋ฉด ๋‹ค์‹œ ์…ˆํ•˜์ง€ ์•Š์Œ. +- ์šฐ๊ธฐ ๊ณ„์‚ฐ = ์ง€๊ธˆ์€ ๋ชจ๋“  ์ธก์  ใ€Œ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”ใ€. E02 ์—์„œ ๊ณ ๋ฅธ ์šฐ๊ธฐ ๊ทœ์น™์€ ํšก๋‹จ ๊ทธ๋ฆผ์˜ ์ง€ํ•˜์ˆ˜์œ„ ์ ์„ ์—๋งŒ ๋ฐ˜์˜๋จ(๊ณ„์‚ฐ ๋ฐ˜์˜์€ ์ค€๋น„ ์ค‘). r_u ๋ฅผ ๊ณจ๋ž์œผ๋ฉด ๊ทธ๋ฆผ ์•„๋ž˜์— ใ€Œ์ง€ํ•˜์ˆ˜ = ๊ฐ„๊ทน์ˆ˜์••๋น„ r_u โ€ฆใ€ ๊ธ€๋งŒ ๋ณด์ž„. +- ํƒ์ƒ‰ ๋ฒ”์œ„ = ๋…ธ๋ฉด ๋ฐ˜๋Œ€์ชฝ ๋ ~ ๋น„ํƒˆ ๋ ๋ฐ”๊นฅ (5 m ์™€ ๋น„ํƒˆ ๋†’์ด 2 ๋ฐฐ ์ค‘ ํฐ ๊ฐ’)๊นŒ์ง€ โ€” ์„ค๊ณ„ ๋น„ํƒˆ์„ ์ง€๋‚˜๋Š” ๋ฉด๋งŒ ๋ด„. ๋…ธ์„  ๋ฐ– ์ž์—ฐ ๋น„ํƒˆ ์ „์ฒด ๋ถ•๊ดด๋Š” ๊ด‘์—ญ ์œ„ํ—˜(E07) ๋ชซ. +- ๊นŠ์ด 0.3 m ๋ฏธ๋งŒ์œผ๋กœ ๊ฒ‰๋งŒ ๊ธ๋Š” ๋ฉด์€ ๋ถ•๊ดด๋กœ ๋ณด์ง€ ์•Š์Œ. +- ๋ฒฝ์€ ์•„์ฃผ ๋‹จ๋‹จํ•œ ๋ฌผ์ฒด๋กœ ๋„ฃ์–ด ํ™œ๋™๋ฉด์ด ๋ฒฝ์„ ์ง€๋‚˜์ง€ ์•Š์Œ โ€” ๋ฒฝ ์ž์ฒด ์•ˆ์ •์€ ๋ณด๊ฐ• ๊ฒ€ํ† ์˜ ใ€Œ๋ฒฝ ์™ธ์  ์•ˆ์ •ใ€์œผ๋กœ ๋ด„. +- ๋ณด๊ฐ• ๊ฐ€์ •์€ ๋”ฐ๋กœ ์ €์žฅ๋˜๋Š” ๊ฒ€ํ† ์šฉ ๊ฒฐ๊ณผ์ž„ โ€” ์„ค๊ณ„ ยท ์›๋ณธ ํ•ด์„ ๊ฒฐ๊ณผ ยท ์ž„๋„ ํ”„๋กœ์ ํŠธ๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์Œ. ๋จผ์ € ์ง€๊ธˆ ์ž…๋ ฅ์œผ๋กœ [ํ•ด์„]์ด ๋๋‚˜ ์žˆ์–ด์•ผ ํ•จ. ๋ฒฝ์ด ์„  ๋น„ํƒˆ์€ ๊ฐ€์ • ํ•ด์„ ๋Œ€์‹  ๋ฒฝ ์™ธ์  ์•ˆ์ •์œผ๋กœ ๊ฒ€ํ† ํ•  ๊ฒƒ. +- ๊ณต์œ ๋ฐ›์€ ํšŒ์‚ฌ(๋ณด๊ธฐ๋งŒ)๋Š” [ํ•ด์„] ยท ๋ณ„ ยท ๋ณด๊ฐ• ๋‹จ์ถ”๊ฐ€ ๊บผ์ง. diff --git a/resources/help/E03/10_์นธ_๋œป.md b/resources/help/E03/10_์นธ_๋œป.md new file mode 100644 index 000000000..7f7ba573c --- /dev/null +++ b/resources/help/E03/10_์นธ_๋œป.md @@ -0,0 +1,73 @@ +# ์นธ ๋œป + +## ์ขŒ์ธก ์ธก์  ๋ชฉ๋ก + +| ์นธ | ๋œป | +| --- | --- | +| ์ธก์  | ์ธก์  ๋ฒˆํ˜ธ ยท โ–ฆ = ๋ฒฝ(์˜น๋ฒฝ ยท ์„์ถ• ๋“ฑ) ์žˆ๋Š” ์ธก์  | +| ์ขŒ ยท ์šฐ | ์ขŒ ๋น„ํƒˆ ยท ์šฐ ๋น„ํƒˆ ์—ญํ• (๊นŽ = ๊นŽ๊ธฐ ยท ์Œ“ = ์Œ“๊ธฐ) | +| ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ | ์กฐ๊ฑด๋ณ„ ๊ทธ ์ธก์  ์ตœ์†Œ ์•ˆ์ „์œจ(์ขŒ ยท ์šฐ ์ค‘ ์ž‘์€ ๊ฐ’ ยท Bishop) | +| ํŒ์ • | ์ง€๊ธˆ ์กฐ๊ฑด์˜ O.K ยท N.G ยท ใ€Œ-ใ€(ํ•ด์„ ์—†์Œ) ยท ใ€Œ์˜ค๋ฅ˜ใ€ | +| ๋Œ€ํ‘œ | ๋ณด๊ณ ์„œ ๋Œ€ํ‘œ ๋‹จ๋ฉด ๋ณ„. ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ์ž๋™์œผ๋กœ ์ผœ์ง„ ๊นŒ๋‹ญ(์Œ“๊ธฐ 10 m ์ด์ƒ ยท ๊นŽ๊ธฐ 15 m ์ด์ƒ ยท ์ตœ์†Œ ์•ˆ์ „์œจ ํ•˜์œ„ 5 ยท ์‚ฌ์šฉ์ž ์ง€์ •) | + +## ์šฐ์ธก ๊ฒฐ๊ณผ + +| ์นธ | ๋œป | +| --- | --- | +| ์ตœ์†Œ ์•ˆ์ „์œจ | ๊ณ ๋ฅธ ๋น„ํƒˆ ยท ์กฐ๊ฑด์—์„œ Bishop ๊ฐ„ํŽธ๋ฒ•์œผ๋กœ ์ฐพ์€ ๊ฐ€์žฅ ๋‚ฎ์€ ์•ˆ์ „์œจ(์†Œ์ˆ˜ ์…‹์งธ ์ž๋ฆฌ) | +| ํŒ์ • | ์ตœ์†Œ ์•ˆ์ „์œจ โ‰ฅ ๊ธฐ์ค€์•ˆ์ „์œจ์ด๋ฉด O.K | +| ๊ธฐ์ค€์•ˆ์ „์œจ | E02 ๊ธฐ์ค€์•ˆ์ „์œจ ํ‘œ์˜ ๊ทธ ์กฐ๊ฑด ยท ๊นŽ๊ธฐ ๋˜๋Š” ์Œ“๊ธฐ ์นธ | +| ๋ฐฉ๋ฒ• | ใ€ŒBishop ยท ์›ํ˜ธใ€ ๋˜๋Š” ใ€ŒBishop ยท ๋น„์›ํ˜ธใ€ โ€” ์ž„๊ณ„๋ฉด ๊ผด | +| ์ˆ˜๋ ด | ๋ฐ˜๋ณต ๊ณ„์‚ฐ์ด ๋‹ต์— ๋‹ฟ์•˜๋Š”์ง€(์˜ˆ ยท ์•„๋‹ˆ์˜ค)์™€ ๋ฐ˜๋ณต ํšŸ์ˆ˜ | +| ๋น„ํƒˆ ๋†’์ด | ๋…ธ๋ฉด ๋์—์„œ ๋น„ํƒˆ ๋(์›์ง€๋ฐ˜๊ณผ ๋งŒ๋‚˜๋Š” ์ )๊นŒ์ง€ ๋†’์ด์ฐจ(m) | +| ๋ฒฝ ์žˆ์Œ | ์ด ์ธก์  ๋‹จ๋ฉด์— ๋„ฃ์€ ๋ฒฝ ์ˆ˜ | +| ์›ํ˜ธ ์ค‘์‹ฌ ยท ๋ฐ˜๊ฒฝ | ์ž„๊ณ„ ์›ํ˜ธ์˜ ์ค‘์‹ฌ(offset, ํ‘œ๊ณ )๊ณผ ๋ฐ˜์ง€๋ฆ„(m). ๋น„์›ํ˜ธ๋ฉด ์•ˆ ๋ณด์ž„ | +| ๋ฐฉ๋ฒ•๋ณ„ ํ‘œ | Fellenius ยท Bishop ยท Janbu ๋ณด์ • ยท Spencer ๊ฐ๊ฐ์˜ ์ตœ์†Œ ์•ˆ์ „์œจ(์›ํ˜ธ ยท ๋น„์›ํ˜ธ ์ค‘ ์ž‘์€ ๊ฐ’) | + +## ์ ˆํŽธํ‘œ + +์ž„๊ณ„ ํ™œ๋™๋ฉด ์œ„ ํ™์„ ์„ธ๋กœ๋กœ ๋‚˜๋ˆˆ ์กฐ๊ฐ(์ ˆํŽธ)๋งˆ๋‹ค ํ•œ ์ค„. ์ค„์„ ๋ˆ„๋ฅด๋ฉด ๊ทธ ์ ˆํŽธ์„ ๊ณ ๋ฆ„. + +| ์นธ | ๋œป ยท ๋‹จ์œ„ | +| --- | --- | +| # | ์ ˆํŽธ ๋ฒˆํ˜ธ | +| ํญ b | ์ ˆํŽธ ๊ฐ€๋กœ ํญ(m) | +| ๋†’์ด h | ์ ˆํŽธ ๊ฐ€์šด๋ฐ ๋†’์ด(m) | +| ฮฑยฐ | ์ ˆํŽธ ๋ฐ”๋‹ฅ ๊ธฐ์šธ๊ธฐ(ยฐ) | +| W+Q | ์ ˆํŽธ ๋ฌด๊ฒŒ + ๋…ธ๋ฉด ํ•˜์ค‘(kN/m โ€” ๋‹จ๋ฉด 1 m ํญ๋‹น) | +| u | ๋ฐ”๋‹ฅ ๊ฐ„๊ทน์ˆ˜์••(kPa) | +| N' | ๋ฐ”๋‹ฅ์„ ๋ˆ„๋ฅด๋Š” ์œ ํšจ ์ˆ˜์ง๋ ฅ(kN/m) โ€” ๋ฌผ์ด ๋– ๋ฐ›์น˜๋Š” ๋ชซ์„ ๋บ€ ํž˜ | +| S | ๋ฐ”๋‹ฅ์—์„œ ๋™์›๋œ ์ „๋‹จ๋ ฅ = ๋ฒ„ํ‹ธ ์ˆ˜ ์žˆ๋Š” ํž˜ รท ์•ˆ์ „์œจ(kN/m) | +| c ยท ฯ†ยฐ | ๋ฐ”๋‹ฅ์ด ์ง€๋‚˜๋Š” ์ง€์ธต์˜ ์ ์ฐฉ๋ ฅ(kPa) ยท ๋‚ด๋ถ€๋งˆ์ฐฐ๊ฐ(ยฐ) | + +## ์ ˆํŽธ ์ž์œ ๋ฌผ์ฒด๋„ ยท ์ ˆํŽธ๋ ฅ ๊ทธ๋ž˜ํ”„ + +- ์ž์œ ๋ฌผ์ฒด๋„ = ๊ณ ๋ฅธ ์ ˆํŽธ ํ•˜๋‚˜๋ฅผ ํฌ๊ฒŒ ๊ทธ๋ฆฌ๊ณ  ํž˜์„ ํ™”์‚ด๋กœ ๋ณด์ž„. ํ™”์‚ด ๊ธธ์ด = ํž˜ ํฌ๊ธฐ ๋น„๋ก€(kN/m). +- ํ™”์‚ด ์ด๋ฆ„: W = ๋ฌด๊ฒŒ ยท Q = ๋…ธ๋ฉด ํ•˜์ค‘ ยท N = ์œ ํšจ ์ˆ˜์ง๋ ฅ ยท U = ๋ฐ”๋‹ฅ ์ˆ˜์•• ยท S = ์ „๋‹จ ์ €ํ•ญ ยท EL ยท ER = ์–‘์˜† ์ˆ˜ํ‰๋ ฅ ยท XL ยท XR = ์–‘์˜† ์—ฐ์ง๋ ฅ(๊ฐ’์ด ์žˆ์„ ๋•Œ๋งŒ). +- ๊ทธ๋ž˜ํ”„ = ์ ˆํŽธ๋งˆ๋‹ค ๋ฐ”๋‹ฅ์˜ ์œ ํšจ ์ˆ˜์ง์‘๋ ฅ ฯƒ' ยท ๊ฐ„๊ทน์ˆ˜์•• u ยท ๋™์› ์ „๋‹จ์‘๋ ฅ ฯ„(kPa ํ•œ ์ถ•). ๊ฐ€๋กœ = ๊ฑฐ๋ฆฌ(offset). ๊ธฐ๋‘ฅ์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ์„ธ ๊ฐ’์ด ๋ณด์ž„. + +## ๋ณด๊ฐ• ๊ฒ€ํ†  โ€” ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ • + +| ์นธ | ๋œป | +| --- | --- | +| ๋น„ํƒˆ | ๊ฐ€์ •์„ ๋„ฃ์„ ๋น„ํƒˆ(์ขŒ ยท ์šฐ ยท ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ) | +| ๊ธฐ์šธ๊ธฐ 1:n | ๋น„ํƒˆ ์ „์ฒด๋ฅผ ๋ฐ”๊ฟ€ ๊ธฐ์šธ๊ธฐ n(์„ธ๋กœ 1 ์— ๊ฐ€๋กœ n). ๋นˆ ์นธ = ์ง€๊ธˆ ๊ฐ’ ยท 0.1 ~ 4 | +| ์†Œ๋‹จ ํญ m | ์†Œ๋‹จ ํ•œ ์นธ ํญ(0 ~ 10 m) | +| ์†Œ๋‹จ ๋†’์ด m ๋งˆ๋‹ค | ๋ช‡ m ๋†’์ด๋งˆ๋‹ค ์†Œ๋‹จ์„ ๋‘˜์ง€(0 ~ 30 m). ํญ ยท ๋†’์ด ๋‘˜ ๋‹ค ๋„ฃ์–ด์•ผ ์†Œ๋‹จ์ด ์ƒ๊น€ | +| ๊ฒฐ๊ณผ ๊ธ€ | ใ€Œ1:์ง€๊ธˆ โ†’ 1:๊ฐ€์ • ยท ์†Œ๋‹จ ํญ / ๋†’์ด ยท ์ฐพ์€ ๊ฒฐ๊ณผ(ํšŒ) ยท ๊ฑธ๋ฆฐ ์ดˆใ€ | +| ๊ฒฐ๊ณผ ํ‘œ | ์กฐ๊ฑด๋งˆ๋‹ค ์ „ Fs ยท ํ›„ Fs ยท ๊ธฐ์ค€์•ˆ์ „์œจ ยท ์ „ ํŒ์ • ยท ํ›„ ํŒ์ • | +| ๊ฐ€์ • ๋‹จ๋ฉด | ๋ฐ”๋€ ๋น„ํƒˆ๊ณผ ๊ทธ ์ž„๊ณ„๋ฉด ๊ทธ๋ฆผ(์ง€๊ธˆ ์กฐ๊ฑด) | + +- [์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ] ๋ชฉํ‘œ = ์ง€๊ธˆ ์กฐ๊ฑด ๊ธฐ์ค€์•ˆ์ „์œจ. ์ง€๊ธˆ ๊ธฐ์šธ๊ธฐ๋ถ€ํ„ฐ ์„ฑ๊ธด ๊ฑธ์Œ์œผ๋กœ ์™„๋งŒํ•˜๊ฒŒ ํ•˜๋ฉฐ ์ฒ˜์Œ ๋งž๋Š” ๊ธฐ์šธ๊ธฐ๋ฅผ ์ฐพ๊ณ , ๊ทธ ์•ž ์นธ ์‚ฌ์ด๋ฅผ ๋ฐ˜์”ฉ ์ขํ˜€ 0.05 ๋‹จ์œ„๋กœ ๋งž์ถค. ๋น„ํƒˆ์ด ๊ธ‰ํ•œ ์ž์—ฐ ์‚ฌ๋ฉด์—์„œ๋Š” ์™„๋งŒํ•˜๊ฒŒ ํ• ์ˆ˜๋ก ๊นŽ๊ธฐ ๋†’์ด๊ฐ€ ์ปค์ ธ ์˜คํžˆ๋ ค ์•ˆ์ „์œจ์ด ๋‚ฎ์•„์งˆ ์ˆ˜๋„ ์žˆ์Œ. ๋ชป ์ฐพ์œผ๋ฉด ใ€Œ๋ชป ์ฐพ์Œ โ€” ์›์ง€๋ฐ˜์„ ๋งŒ๋‚˜๋Š” ๊ฐ€์žฅ ์™„๋งŒํ•œ 1:n ๊นŒ์ง€ ๋ด„ใ€. + +## ๋ณด๊ฐ• ๊ฒ€ํ†  โ€” ๋ฒฝ ์™ธ์  ์•ˆ์ • (KDS 11 80 05) + +| ์นธ | ๋œป | +| --- | --- | +| ํ† ์•• | ๋ฒฝ ๋’ค ํ™์ด ๋ฏธ๋Š” ํž˜ ๊ณ„์‚ฐ๋ฒ• โ€” Coulomb(๊ธฐ๋ณธ) ยท Rankine | +| ๋ฐฐ๋ฉด ์ˆ˜์œ„ รท ๋ฒฝ ๋†’์ด | ๋ฒฝ ๋’ค ๋ฌผ ๋†’์ด ๋น„์œจ(0 = ๋ฌผ ์—†์Œ ยท 1 = ๋ฒฝ ๊ผญ๋Œ€๊ธฐ๊นŒ์ง€) | +| ๋จธ๋ฆฌ ๊ธ€ | ๋ฒฝ ์ด๋ฆ„ ยท ํ‰์ƒ์‹œ ๋˜๋Š” ์ง€์ง„ ์‹œ ยท H(๋ฒฝ ๋†’์ด m) ยท B(๋ฐ”๋‹ฅ ํญ m) ยท Ka(์ฃผ๋™ํ† ์••๊ณ„์ˆ˜) ยท KAE(์ง€์ง„ ์‹œ ํ† ์••๊ณ„์ˆ˜) ยท e(๋ฐ”๋‹ฅ ํ•ฉ๋ ฅ์ด ๊ฐ€์šด๋ฐ์„œ ๋ฒ—์–ด๋‚œ ๊ฑฐ๋ฆฌ m) ยท q_max(์ตœ๋Œ€ ์ง€๋ฐ˜ ๋ฐ˜๋ ฅ kPa) | +| ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ | ์ค„๋งˆ๋‹ค ์•ˆ์ „์œจ ยท ๊ธฐ์ค€ ยท ํŒ์ • | + +- ๊ธฐ์ค€(KDS 11 80 05): ํ‰์ƒ์‹œ ํ™œ๋™ 1.5 ยท ์ „๋„ 2.0 ยท ์ง€์ง€๋ ฅ 3.0 ยท ์ง€์ง„ ์‹œ ํ™œ๋™ 1.2 ยท ์ „๋„ 1.5 ยท ์ง€์ง€๋ ฅ 2.0. +- ์ง€์ง„ ์‹œ ์ค„์€ E02 ์—์„œ ์ง€์ง„ ๊ฒ€ํ† ๋ฅผ ์ผœ๊ณ  kh ๊ฐ€ 0 ๋ณด๋‹ค ํด ๋•Œ๋งŒ ๋‚˜์˜ด. diff --git a/resources/help/E03/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md b/resources/help/E03/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md new file mode 100644 index 000000000..b3d132a89 --- /dev/null +++ b/resources/help/E03/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md @@ -0,0 +1,81 @@ +# ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐ + +## ํ•œ๊ณ„ํ‰ํ˜•ํ•ด์„ โ€” ์ ˆํŽธ๋ฒ• + +- ๊ฐ€์ƒ์˜ ๋ฏธ๋„๋Ÿผ๋ฉด(ํ™œ๋™๋ฉด) ์œ„ ํ™๋ฉ์ด๋ฅผ ์„ธ๋กœ ์กฐ๊ฐ(์ ˆํŽธ) 50 ๊ฐœ๋กœ ๋‚˜๋ˆ”. +- ์ ˆํŽธ๋งˆ๋‹ค ๋ฌด๊ฒŒ W(์ง€์ธต๋ณ„ ๋‹จ์œ„์ค‘๋Ÿ‰ ร— ๋‘๊ป˜ ร— ํญ + ๋…ธ๋ฉด ํ•˜์ค‘) ยท ๋ฐ”๋‹ฅ ์ˆ˜์•• u ยท ๋ฐ”๋‹ฅ ๊ฐ•๋„ c ยท ฯ† ๋ฅผ ์…ˆ. +- **์•ˆ์ „์œจ Fs** = ๋ฒ„ํ‹ฐ๋Š” ํž˜(๋˜๋Š” ๋ชจ๋ฉ˜ํŠธ) ํ•ฉ รท ๋ฏธ๋„๋Ÿฌ์ง€๊ฒŒ ํ•˜๋Š” ํž˜(๋˜๋Š” ๋ชจ๋ฉ˜ํŠธ) ํ•ฉ. 1 ๋ฏธ๋งŒ = ๋ฌด๋„ˆ์ง€๋Š” ์ชฝ. +- ๋ฒ„ํ‹ฐ๋Š” ํž˜ = c ร— ๋ฐ”๋‹ฅ ๊ธธ์ด + (๋ฐ”๋‹ฅ์„ ๋ˆ„๋ฅด๋Š” ํž˜ โˆ’ ์ˆ˜์•• ร— ๋ฐ”๋‹ฅ ๊ธธ์ด) ร— tanฯ† (Mohr-Coulomb). +- ์ˆ˜์•• u = 9.81 kN/mยณ ร— ์ง€ํ•˜์ˆ˜์œ„์—์„œ ์ ˆํŽธ ๋ฐ”๋‹ฅ๊นŒ์ง€ ๊นŠ์ด. ์ง€ํ•˜์ˆ˜์œ„ ์•„๋ž˜ ํ™ ๋ฌด๊ฒŒ๋Š” ฮณsat ๋กœ ์…ˆ. +- ๊ทผ๊ฑฐ = ์ง€์‹DB 02(ํ† ์‚ฌ ยท ๋ณตํ•ฉ ๋น„ํƒˆ๋ฉด ํ•œ๊ณ„ํ‰ํ˜•ํ•ด์„ ์ˆ˜์‹ ๋ช…์„ธ) ยง1 ยท ยง2. + +## ๋„ค ๊ฐ€์ง€ ๋ฐฉ๋ฒ• + +| ๋ฐฉ๋ฒ• | ํ™”๋ฉด ์ด๋ฆ„ | ์›๋ฆฌ(์‰ฌ์šด ๋ง) | ์“ฐ์ž„ | +| --- | --- | --- | --- | +| Bishop ๊ฐ„ํŽธ๋ฒ• | Bishop | ์ ˆํŽธ ์˜† ์ˆ˜ํ‰๋ ฅ์€ ๋„ฃ๊ณ  ์—ฐ์ง๋ ฅ์€ ๋บŒ ยท ์› ์ค‘์‹ฌ ๋‘˜๋ ˆ ๋ชจ๋ฉ˜ํŠธ๋กœ ํ‰ํ˜• | **๊ธฐ๋ณธ ยท ํŒ์ •** | +| Janbu ๊ฐ„ํŽธ๋ฒ• + ๋ณด์ • | Janbu ๋ณด์ • | ์ˆ˜ํ‰ ยท ์—ฐ์ง ํž˜ ํ‰ํ˜•๋งŒ์œผ๋กœ ์…ˆ โ†’ ๋ฉด ๋ชจ์–‘ ๋ณด์ •๊ณ„์ˆ˜ f0 ๋ฅผ ๊ณฑํ•จ | ๋น„์›ํ˜ธ(๊บพ์ธ ๋ฉด) ํƒ์ƒ‰ ยท ๋น„๊ต | +| Fellenius ๋ฒ• | Fellenius | ์ ˆํŽธ ์‚ฌ์ด ํž˜์„ ๋ชจ๋‘ ๋บŒ ยท ๋ฐ˜๋ณต ์—†์ด ํ•œ ๋ฒˆ์— ์…ˆ ยท ๋ณดํ†ต ๋‚ฎ๊ฒŒ(๋ณด์ˆ˜์ ์œผ๋กœ) ๋‚˜์˜ด | ๋น„๊ต | +| Spencer ๋ฒ• | Spencer | ๋ชจ๋ฉ˜ํŠธ ยท ํž˜ ํ‰ํ˜•์„ ํ•จ๊ป˜ ๋งž์ถค(์ ˆํŽธ ์‚ฌ์ด ํž˜ ๊ธฐ์šธ๊ธฐ ฮป ๋ฅผ ํ•จ๊ป˜ ์ฐพ์Œ) | ๋น„์›ํ˜ธ ํƒ์ƒ‰ ยท ๋น„๊ต | + +- ์ด ๋ฐ–์˜ ๋ฐฉ๋ฒ•(GLE ยท Morgenstern-Price ๋“ฑ)์€ ์“ฐ์ง€ ์•Š์Œ. +- **Bishop ์‹ ๋งํ’€์ด**: Fs = ฮฃ[(c ร— ํญ + (W โˆ’ u ร— ํญ) ร— tanฯ†) รท mฮฑ] รท ฮฃ(W ร— sinฮฑ + ์ง€์ง„ ํ•ญ). mฮฑ = cosฮฑ + sinฮฑ ร— tanฯ† รท Fs. ์–‘์ชฝ์— Fs ๊ฐ€ ์žˆ์–ด ๋งž์„ ๋•Œ๊นŒ์ง€ ๋˜ํ’€์ดํ•ด ์…ˆ. +- ์ง„์ถœ๋ถ€๊ฐ€ ๊ธ‰ํ•ด ์–ด๋А ์ ˆํŽธ์˜ mฮฑ ๊ฐ€ 0.2 ๋ณด๋‹ค ์ž‘์•„์ง€๋Š” ๋ฉด์€ ๊ฐ’์ด ํ„ฐ์ง€๋ฏ€๋กœ ๋ฒ„๋ฆผ(์ƒ์šฉ ํ”„๋กœ๊ทธ๋žจ ๊ด€๋ก€). +- **Janbu ๋ณด์ •๊ณ„์ˆ˜** f0 = 1 + b1 ร— (d/L โˆ’ 1.4 ร— (d/L)ยฒ). L = ๋ฉด ์–‘ ๋์„ ์ด์€ ์ง์„  ๊ธธ์ด ยท d = ๊ทธ ์ง์„ ์—์„œ ๋ฉด๊นŒ์ง€ ๊ฐ€์žฅ ๊นŠ์€ ๊ฑฐ๋ฆฌ ยท b1 = 0.31(c ๊ฐ€ ๋ชจ๋‘ 0) ยท 0.69(ฯ† ๊ฐ€ ๋ชจ๋‘ 0) ยท 0.50(๊ทธ ๋ฐ–). +- **์ง€์ง„**: ์ ˆํŽธ๋งˆ๋‹ค ๋ฌด๊ฒŒ ร— kh ๋งŒํผ ์˜†์œผ๋กœ ๋ฏธ๋Š” ํž˜์„ ๋”ํ•จ(์œ ์‚ฌ์ •์ ). + +## ์ž„๊ณ„ ํ™œ๋™๋ฉด ์ฐพ๊ธฐ + +- **์›ํ˜ธ** โ€” ๋‘ ๋ฐฉ์‹์„ ํ•จ๊ป˜ ์”€. +- Entry & Exit: ๋น„ํƒˆ ์œ„ ยท ์•„๋ž˜ ์ง„์ž… ยท ์ง„์ถœ ์  25 ๊ฐœ์”ฉ์„ ์—ฌ๋Ÿฌ ๊ฐ๋„์˜ ์›ํ˜ธ๋กœ ์ด์Œ. +- Grid & Radius: ์›ํ˜ธ ์ค‘์‹ฌ ๊ฒฉ์ž 15 ร— 15 ์ž๋ฆฌ๋งˆ๋‹ค ๋ฐ˜์ง€๋ฆ„ 10 ๊ฐ€์ง€. +- ์œ„ ํ›„๋ณด๋ฅผ ์ ˆํŽธ 30 ๊ฐœ๋กœ ๋น ๋ฅด๊ฒŒ ๊ฑฐ๋ฅธ ๋’ค, ์•ˆ์ „์œจ ๋‚ฎ์€ 30 ๊ฐœ๋ฅผ ์ถœ๋ฐœ์ ์œผ๋กœ ์ฐจ๋ถ„์ง„ํ™”(๋ฌด๋ฆฌ ์ตœ์ ํ™”)๋กœ ๋‹ค๋“ฌ์–ด ์ ˆํŽธ 50 ๊ฐœ๋กœ ์ตœ์ข… ์…ˆ. +- **๋น„์›ํ˜ธ** โ€” ๊ผญ์ง“์  4 ๊ฐœ ๊บพ์€์„ . ์ถœ๋ฐœ์  = ๊ฐ€์žฅ ๋‚ฎ์€ ์›ํ˜ธ + ๋ธ”๋ก ๋ชจ์–‘ ๋ฉด. Janbu ยท Spencer ๋กœ ๊ผญ์ง“์ ์„ ์˜ฎ๊ฒจ ๊ฐ€๋ฉฐ ์ตœ์†Œ๋ฅผ ์ฐพ๊ณ , ์ฐพ์€ ๋ฉด์—์„œ Fellenius ยท Bishop ๊ฐ’๋„ ์…ˆ. +- ๋ฐฉ๋ฒ•๋งˆ๋‹ค ์›ํ˜ธ ยท ๋น„์›ํ˜ธ ์ค‘ ์ž‘์€ ๊ฐ’์ด ๊ทธ ๋ฐฉ๋ฒ•์˜ ์ตœ์†Œ ์•ˆ์ „์œจ ยท ํŒ์ •์€ Bishop ์ตœ์†Œ ์•ˆ์ „์œจ. +- ๊ฐ™์€ ์ž…๋ ฅ์ด๋ฉด ๋Š˜ ๊ฐ™์€ ๋‹ต์ด ๋‚˜์˜ค๋„๋ก ๋ฌด์ž‘์œ„ ์”จ์•—์„ ๊ณ ์ •ํ•จ. +- **๋“ฑ์•ˆ์ „์œจ ์ง€๋„** = ์›ํ˜ธ ์ค‘์‹ฌ ๊ฒฉ์ž ์ž๋ฆฌ๋งˆ๋‹ค ๊ทธ ์ค‘์‹ฌ์œผ๋กœ ๊ทธ๋ฆฐ ์›ํ˜ธ ์ค‘ Bishop ์ตœ์†Œ ์•ˆ์ „์œจ. +- ๊นŠ์ด 0.3 m ๋ฏธ๋งŒ์œผ๋กœ ๊ฒ‰๋งŒ ๊ธ๋Š” ๋ฉด์€ ๋บŒ(ํ‘œํ†  ๋ฒ—๊ฒจ์ง์€ ๋ถ•๊ดด๋กœ ๋ณด์ง€ ์•Š์Œ). +- ๊ทผ๊ฑฐ = ์ง€์‹DB 02 ยง3(Grid & Radius ยท Entry & Exit). ์ƒ์šฉ ๋Œ€์‘ = SLOPE/W ยท Slide ์˜ ๊ฐ™์€ ์ด๋ฆ„ ๋ฐฉ์‹. + +## ์กฐ๊ฑด ยท ๋น„ํƒˆ + +| ์กฐ๊ฑด | ์ง€ํ•˜์ˆ˜ ยท ํ•˜์ค‘ | ๊ธฐ์ค€์•ˆ์ „์œจ(E02 ๊ธฐ๋ณธ ยท ๊นŽ๊ธฐ / ์Œ“๊ธฐ) | +| --- | --- | --- | +| ๊ฑด๊ธฐ | ์ง€ํ•˜์ˆ˜ ์—†์Œ | 1.5 / 1.5 | +| ์šฐ๊ธฐ | ์ง€๊ธˆ์€ ๋ชจ๋“  ์ธก์  ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™” | 1.2 / 1.3 | +| ์ง€์ง„(E02 ์—์„œ ์ผฐ์„ ๋•Œ) | ๊ฑด๊ธฐ ์ˆ˜์œ„ + ์ˆ˜ํ‰์ง€์ง„๊ณ„์ˆ˜ kh | 1.1 / 1.1 | + +- ๊ธฐ์ค€ = KDS 11 70 05 ํ‘œ 4.3-1 ยท 4.3-2 ยท 4.3-3(์ง€์‹DB 04 ยง2). ๊ฐ’์€ E02 ์—์„œ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ. +- ์ขŒ ยท ์šฐ ๋น„ํƒˆ์„ ๋”ฐ๋กœ ์…ˆ โ€” ๊นŽ๊ธฐ = ์‚ฐ ์ชฝ์—์„œ ๋…ธ๋ฉด ์ชฝ์œผ๋กœ ๋ฏธ๋„๋Ÿฌ์ง ยท ์Œ“๊ธฐ = ๋…ธ๋ฉด์—์„œ ๋ฐ”๊นฅ์ชฝ์œผ๋กœ ๋ฏธ๋„๋Ÿฌ์ง. +- ํƒ์ƒ‰ ๋ฒ”์œ„ = ๋…ธ๋ฉด ๋ฐ˜๋Œ€์ชฝ ๋ ~ ๋น„ํƒˆ ๋ ๋ฐ”๊นฅ (5 m ์™€ ๋น„ํƒˆ ๋†’์ด 2 ๋ฐฐ ์ค‘ ํฐ ๊ฐ’). +- ๋…ธ๋ฉด ํ•˜์ค‘ = ๋…ธ๋ฉด ์–‘ ๋ ์‚ฌ์ด ๊ณ ๋ฅธ ํ•˜์ค‘(E02 ยท ๊ธฐ๋ณธ 10 kPa). +- ํŒ์ • = Bishop ์ตœ์†Œ ์•ˆ์ „์œจ โ‰ฅ ๊ทธ ์กฐ๊ฑด ยท ๊ทธ ๋น„ํƒˆ(๊นŽ๊ธฐ ยท ์Œ“๊ธฐ) ๊ธฐ์ค€์•ˆ์ „์œจ์ด๋ฉด O.K. + +## ๋ฒฝ ๋„ฃ๊ธฐ + +- B06 ์— ๋†“์ธ ์˜น๋ฒฝ ยท ์„์ถ• ยท ๋Œ์Œ“๊ธฐ ยท ๊ด€ ๊ธฐ์Šญ๋ง‰์ด ๋‹จ๋ฉด์„ B06 ํ™”๋ฉด๊ณผ ๊ฐ™์€ ๊ณ„์‚ฐ์œผ๋กœ ๊ทธ๋ ค ๋„ฃ์Œ. +- ๋ฒฝ = ๋‹จ์œ„์ค‘๋Ÿ‰ 23 kN/mยณ ยท c 1,000 kPa ยท ฯ† 45ยฐ ์˜ ์•„์ฃผ ๋‹จ๋‹จํ•œ ๋ฌผ์ฒด โ€” ํ™œ๋™๋ฉด์ด ๋ฒฝ์„ ์ง€๋‚˜์ง€ ์•Š๊ณ  ๋ฒฝ ๋’ค ยท ์•„๋ž˜๋กœ ๋Ž. + +## ๋ณด๊ฐ• ๊ฒ€ํ†  + +- **๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ** โ€” ๋น„ํƒˆ ์‹œ์ž‘์ (๊นŽ๊ธฐ = ์ธก๊ตฌ ๋ฐ”๊นฅ ยท ์Œ“๊ธฐ = ๋…ธ๋ฉด ๋)๋ถ€ํ„ฐ 1:n ๊ธฐ์šธ๊ธฐ(+ ๋†’์ด๋งˆ๋‹ค ์†Œ๋‹จ)๋กœ ์›์ง€๋ฐ˜์„ ๋งŒ๋‚  ๋•Œ๊นŒ์ง€ ๋‹ค์‹œ ๊ทธ๋ ค ๊ฐ™์€ ๋ฐฉ๋ฒ• ยท ์กฐ๊ฑด์œผ๋กœ ๋‹ค์‹œ ํ•ด์„ํ•จ. 2 ๋‹จ ๊ฒฝ์‚ฌ(์•” ยท ํ† ์‚ฌ)๋Š” ํ•œ ๊ธฐ์šธ๊ธฐ๋กœ ๊ฐˆ์Œ. +- **๋ฒฝ ์™ธ์  ์•ˆ์ •**(KDS 11 80 05 ยท ์ง€์‹DB 04 ยง2) โ€” ํ† ์•• = Coulomb ๋˜๋Š” Rankine ยท ์ง€์ง„ ์‹œ = Mononobe-Okabe + ๋ฒฝ ๊ด€์„ฑ kh ร— ๋ฒฝ ๋ฌด๊ฒŒ. +- ํ™œ๋™ ์•ˆ์ „์œจ = (๋ฐ”๋‹ฅ ์—ฐ์ง๋ ฅ ร— ๋งˆ์ฐฐ๊ณ„์ˆ˜ + ๋ฐ”๋‹ฅ ์ ์ฐฉ ร— ํญ) รท ์ˆ˜ํ‰๋ ฅ ํ•ฉ(์•ž์ชฝ ์ˆ˜๋™ํ† ์•• ๋บŒ). +- ์ „๋„ ์•ˆ์ „์œจ = ์•ž๊ตฝ ๋‘˜๋ ˆ ๋ฒ„ํ‹ฐ๋Š” ๋ชจ๋ฉ˜ํŠธ รท ๋„˜๊ธฐ๋Š” ๋ชจ๋ฉ˜ํŠธ. +- ์ง€์ง€๋ ฅ ์•ˆ์ „์œจ = Meyerhof ๊ทนํ•œ ์ง€์ง€๋ ฅ(์œ ํšจ ํญ ยท ๊ฒฝ์‚ฌ ๋ณด์ •) รท ์ตœ๋Œ€ ์ง€๋ฐ˜ ๋ฐ˜๋ ฅ. +- ๊ธฐ์ค€ = ํ‰์ƒ์‹œ ํ™œ๋™ 1.5 ยท ์ „๋„ 2.0 ยท ์ง€์ง€๋ ฅ 3.0 ยท ์ง€์ง„ ์‹œ 1.2 ยท 1.5 ยท 2.0. +- ๊ฐ€์ •: ๋ฒฝ ๋’ค ํ™ ์ ์ฐฉ 0(์•ˆ์ „ ์ชฝ) ยท ๋ฒฝ ๋‹จ์œ„์ค‘๋Ÿ‰ 23 kN/mยณ ยท ๋ฒฝ ๋’ค ์ƒ์žฌํ•˜์ค‘ = ์Œ“๊ธฐ ์ชฝ ๋ฒฝ๋งŒ ๋…ธ๋ฉด ํ•˜์ค‘ ยท ์ˆ˜์•• = ๋ฐฐ๋ฉด ์ˆ˜์œ„๋งŒํผ ์ •์ˆ˜์•• + ๋ฐ”๋‹ฅ ์–‘์••๋ ฅ. + +## ๊ฒ€์ฆ + +- ์ƒ์šฉ ํ”„๋กœ๊ทธ๋žจ ๊ฒ€์ฆ ์˜ˆ์ œ 12 ๋ฌธ์ œ(40 ๊ฐœ ๊ฐ’)์™€ ๋น„๊ตํ•ด ์ฐจ์ด 2.2 % ์•ˆ. +- ๋ฒฝ ์™ธ์  ์•ˆ์ •์€ ๊ต๊ณผ์„œ ์†๊ณ„์‚ฐ๊ณผ ๋น„๊ตํ•ด ์ฐจ์ด 2 % ์•ˆ. + +## ๊ฐ€์ • ยท ํ•œ๊ณ„ + +- ์ง€๋ฐ˜ ๊ฐ’ = ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ. +- 2D ํ•ด์„์ด๋ผ ๋น„ํƒˆ ์–‘์˜†์ด ๋ถ™์žก์•„ ์ฃผ๋Š” ํšจ๊ณผ(๋๋‹จ ํšจ๊ณผ)๋Š” ๋น ์ง โ€” 3D ํ•ด์„(E04) ๋ชซ. +- ์šฐ๊ธฐ๋Š” ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”๋กœ ์…ˆ(๊ฐ€์žฅ ๋ถˆ๋ฆฌํ•œ ์ชฝ). E02 ์šฐ๊ธฐ ๊ทœ์น™ ๊ฐˆ๋ž˜(์•”๋ฐ˜ ยฝ ยท ๊นŠ์ด ๋น„์œจ ยท r_u ยท ์ˆ˜์œ„์„ )์˜ ๊ณ„์‚ฐ ๋ฐ˜์˜์€ ์ค€๋น„ ์ค‘. +- ๊ฐ•์šฐ ์นจํˆฌ ํ•ด์„(์‹œ๊ฐ„์— ๋”ฐ๋ฅธ ์ˆ˜์œ„ ๋ณ€ํ™”) ยท ์ธ์žฅ๊ท ์—ด์€ ๋„ฃ์ง€ ์•Š์Œ. +- ์•”๋ฐ˜ ์ ˆ๋ฆฌ๋ฅผ ๋”ฐ๋ผ ๋ฏธ๋„๋Ÿฌ์ง€๋Š” ํ‰๋ฉด ยท ์๊ธฐ ํŒŒ๊ดด๋Š” ์•”๋ฐ˜ ํ•ด์„(E06) ๋ชซ. +- ๋„ค์ผ ยท ์•ต์ปค ๋ณด๊ฐ• ๊ฐ€์ •์€ ์ค€๋น„ ์ค‘. diff --git a/resources/help/E04/00_์„ค๋ช…์„œ.md b/resources/help/E04/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..43ae4e0e2 --- /dev/null +++ b/resources/help/E04/00_์„ค๋ช…์„œ.md @@ -0,0 +1,68 @@ +# 3D ํ•ด์„ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +- ๋…ธ์„  ํ•œ ๊ตฌ๊ฐ„(๋ณดํ†ต ์•ฝ 100 m)์„ ๊ณจ๋ผ ์ง€ํ˜• ์ „์ฒด๋ฅผ ์ž…์ฒด๋กœ ๋ณด๊ณ  ๊ฐ€์žฅ ์œ„ํ—˜ํ•œ 3์ฐจ์› ํ™œ๋™๋ฉด(ํ™์ด ๋ฏธ๋„๋Ÿฌ์ ธ ๋‚ด๋ฆฌ๋Š” ๋ฐ”๋‹ฅ๋ฉด)์„ ์ฐพ์Œ. +- ๋•…์„ 1 m ๊ธฐ๋‘ฅ ๊ฒฉ์ž๋กœ ๋‚˜๋ˆ  ์…€๋งˆ๋‹ค ใ€Œ๊ทธ ์…€์„ ์ง€๋‚˜๋Š” ํ™œ๋™๋ฉด ์ค‘ ๊ฐ€์žฅ ๋‚ฎ์€ ์•ˆ์ „์œจใ€์„ ์ƒ‰ ์ง€๋„๋กœ ๋ณด์—ฌ ์คŒ. +- ๊ฐ€์žฅ ์œ„ํ—˜ํ•œ ํ™œ๋™๋ฉด์˜ ์•ˆ์ „์œจ ยท ๋ฌด๋„ˆ์งˆ ํ™์˜ ์–‘(์ž„๊ณ„ ์ฒด์ ) ยท ๊ฐ™์€ ๊ตฌ๊ฐ„ 2D(E03) ๊ฒฐ๊ณผ์™€์˜ ๋น„๋ฅผ ์˜ค๋ฅธ์ชฝ์— ๋ณด์—ฌ ์คŒ. +- ๊ณ„์‚ฐ์€ ๋ชจ๋‘ ์„œ๋ฒ„๊ฐ€ ํ•จ. ํ™”๋ฉด์€ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋ฆฌ๊ธฐ๋งŒ ํ•จ. + +## ์ž‘์—… ์ˆœ์„œ + +1. ์™ผ์ชฝ **๊ตฌ๊ฐ„** ์นด๋“œ ๋ชฉ๋ก์—์„œ ๊ตฌ๊ฐ„ ํ•˜๋‚˜๋ฅผ ๋ˆ„๋ฆ„(์˜ˆ: `0 โ€“ 100 m`). ์ง์ ‘ ์ •ํ•˜๋ ค๋ฉด ์•„๋ž˜ **์‹œ์ž‘ (m)** ยท **๋ (m)** ์นธ์— ์ˆซ์ž๋ฅผ ๋„ฃ์Œ. +2. **ํƒ์ƒ‰ ์„ค์ •** ์นด๋“œ์—์„œ **์กฐ๊ฑด**(๊ฑด๊ธฐ ยท ์šฐ๊ธฐ)๊ณผ **ํ™œ๋™๋ฉด ๋ชจ์–‘**์„ ๊ณ ๋ฆ„. ์ฒ˜์Œ์—๋Š” ๊ธฐ๋ณธ๊ฐ’ ๊ทธ๋Œ€๋กœ ์จ๋„ ๋จ. +3. ํ•„์š”ํ•˜๋ฉด **๊ณ ๊ธ‰ ์„ค์ •** ์นด๋“œ๋ฅผ ํŽผ์ณ ๊ธฐ๋‘ฅ ํฌ๊ธฐ ยท ๊ตฌ ์ค‘์‹ฌ ๊ฒฉ์ž ยท ๋ถ€ํ”ผ ๋ฒ”์œ„๋ฅผ ๋ฐ”๊ฟˆ(์นธ ๋œป์€ ใ€Œ์นธ ๋œปใ€ ๋ฌธ์„œ). +4. ์™ผ์ชฝ ์•„๋ž˜ **[ํ•ด์„]** ์„ ๋ˆ„๋ฆ„. ์ง„ํ–‰ ๋ง‰๋Œ€์— ใ€Œํ•ด์„ ์ค‘ n% ยท ํ•ฉ์„ฑ ์ง€ํ˜• โ†’ ๊ฑฐ์นœ ํƒ์ƒ‰ โ†’ ๊ณ ์šด ํƒ์ƒ‰ โ†’ ํƒ€์›์ฒดใ€ ์ฐจ๋ก€๊ฐ€ ๋ณด์ž„. +5. ๋๋‚˜๋ฉด ใ€Œ3D ํ•ด์„์ด ๋๋‚ฌ์Šต๋‹ˆ๋‹ค.ใ€ ์•Œ๋ฆผ๊ณผ ํ•จ๊ป˜ ๊ฐ€์šด๋ฐ ์ง€๋„์— ์ƒ‰์ด ์น ํ•ด์ง€๊ณ  ์˜ค๋ฅธ์ชฝ **๊ตฌ๊ฐ„ ๊ฒฐ๊ณผ** ๊ฐ€ ์ฑ„์›Œ์ง. +6. ๊ฐ€์šด๋ฐ ์œ„ **[3D ๋ณด๊ธฐ]** ๋กœ ๋ฐ”๊ฟ” ์ž…์ฒด ์ง€ํ˜• ์œ„์˜ ์ž„๊ณ„ ํ™œ๋™๋ฉด์„ ํ™•์ธํ•จ. +7. ๋‹ค๋ฅธ ๊ตฌ๊ฐ„๋„ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด์„ํ•จ. ํ•ด์„ํ•œ ๊ตฌ๊ฐ„์€ ๋ชฉ๋ก์— ์ƒ‰ ์ ๊ณผ `Fs` ๊ฐ’์ด ๋ถ™์Œ. +8. ๋ณด๊ณ ์„œ์— ๋„ฃ์œผ๋ ค๋ฉด E05 ๋ณด๊ณ ์„œ ํ™”๋ฉด์—์„œ **[๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]** ์„ ๋ˆ„๋ฆ„(3D์š”์•ฝ ์‹œํŠธ์— ๊ตฌ๊ฐ„๋งˆ๋‹ค ํ•œ ์ค„). + +## ๋‹จ์ถ” + +| ์ž๋ฆฌ | ๋‹จ์ถ” | ํ•˜๋Š” ์ผ | +| --- | --- | --- | +| ์™ผ์ชฝ ์•„๋ž˜ | **[ํ•ด์„]** | ๊ณ ๋ฅธ ๊ตฌ๊ฐ„์„ ํ•ด์„ํ•จ. ์ž…๋ ฅ(์ง€๋ฐ˜ ์กฐ๊ฑด ยท ํƒ์ƒ‰ ์„ค์ • ยท ๊ตฌ๊ฐ„)์ด ์ €์žฅ๋œ ๊ฒฐ๊ณผ์™€ ๊ฐ™์œผ๋ฉด ๋‹ค์‹œ ์…ˆํ•˜์ง€ ์•Š๊ณ  ใ€Œ์ž…๋ ฅ์ด ๊ฐ™์•„ ์ €์žฅ๋œ ๊ฒฐ๊ณผ๋ฅผ ์—ฝ๋‹ˆ๋‹ค.ใ€ | +| ์™ผ์ชฝ ์•„๋ž˜ | **[๋‹ค์‹œ ํ•ด์„]** | ์ž…๋ ฅ์ด ๊ฐ™์•„๋„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์…ˆ | +| ์ง„ํ–‰ ๋ง‰๋Œ€ ์˜† | **[๋Š๊ธฐ]** | ๋Œ๊ณ  ์žˆ๋Š” ํ•ด์„์„ ๋ฉˆ์ถค. ์ „์— ์ €์žฅ๋œ ๊ฒฐ๊ณผ๋Š” ๊ทธ๋Œ€๋กœ ๋‚จ์Œ | +| ๊ณ ๊ธ‰ ์„ค์ • ์นด๋“œ | **[๊ธฐ๋ณธ๊ฐ’]** | ํƒ์ƒ‰ ์„ค์ • ์นธ์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋˜๋Œ๋ฆผ(๊ตฌ๊ฐ„ ์‹œ์ž‘ ยท ๋์€ ๊ทธ๋Œ€๋กœ ยท ์กฐ๊ฑด์€ ๊ฑด๊ธฐ) | +| ๊ฐ€์šด๋ฐ ์œ„ | **[ํ‰๋ฉด ์ง€๋„]** ยท **[3D ๋ณด๊ธฐ]** | ์œ„์—์„œ ๋ณธ ์ƒ‰ ์ง€๋„์™€ ์ž…์ฒด ๋ณด๊ธฐ๋ฅผ ๋ฐ”๊ฟˆ | +| ๊ฐ€์šด๋ฐ ์œ„ | **[์ตœ์†Œ ์•ˆ์ „์œจ]** ยท **[์ž„๊ณ„ ์ฒด์ ]** | ์ง€๋„ ยท 3D ์ƒ‰์น  ๊ธฐ์ค€์„ ๋ฐ”๊ฟˆ | +| 3D ๋ณด๊ธฐ ์œ„ | **์ž„๊ณ„ ํ™œ๋™๋ฉด** ์ฒดํฌ | ์ผฌ = ํ™œ๋™์ฒด๋ฅผ ๊ฑท์–ด๋‚ธ ์ง€ํ‘œ + ๋ถ‰์€ ํ™œ๋™๋ฉด ยท ๋” = ์›๋ž˜ ์ง€ํ‘œ๋งŒ | + +- ํ‰๋ฉด ์ง€๋„: ๋งˆ์šฐ์Šค ํœ  = ํ™•๋Œ€ ยท ์ถ•์†Œ, ๋Œ๊ธฐ = ์˜ฎ๊น€, ๋‘ ๋ฒˆ ๋ˆ„๋ฆ„ = ํ™”๋ฉด ๋งž์ถค, ์…€ ์œ„์— ์˜ฌ๋ฆฌ๋ฉด ๊ทธ ์…€์˜ ์ตœ์†Œ ์•ˆ์ „์œจ ยท ์ž„๊ณ„ ์ฒด์  ยท ๊ฐ€๊นŒ์šด ์ธก์ ์ด ๋œธ. +- 3D ๋ณด๊ธฐ: ๋Œ๊ธฐ = ๋Œ๋ฆผ, ํœ  = ํ™•๋Œ€ ยท ์ถ•์†Œ. ์˜ค๋ฅธ์ชฝ ์œ„ ๋‚˜์นจ๋ฐ˜์ด ๋ฐฉ์œ„๋ฅผ ์•Œ๋ ค ์คŒ. + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +- **ํ‰๋ฉด ์ง€๋„**: ์…€ ์ƒ‰ = ๊ทธ ์…€์„ ์ง€๋‚˜๋Š” ํ™œ๋™๋ฉด ์ค‘ ๊ฐ€์žฅ ๋‚ฎ์€ ์•ˆ์ „์œจ. ๋ถ‰์„์ˆ˜๋ก ์œ„ํ—˜. ์ ์„  ์œค๊ณฝ = ๊ตฌ๊ฐ„ ์ „์ฒด์—์„œ ๊ฐ€์žฅ ์œ„ํ—˜ํ•œ ํ™œ๋™๋ฉด์ด ๋•…์„ ์ž๋ฅด๋Š” ๋ฒ”์œ„, ๋™๊ทธ๋ผ๋ฏธ์™€ `Fs` ๊ธ€ = ๊ทธ ํ™œ๋™๋ฉด ์ž๋ฆฌ. ๊ตต์€ ์„  = ๊ณ ๋ฅธ ๊ตฌ๊ฐ„. +- **์•ˆ์ „์œจ ์ƒ‰ ๋‹จ๊ณ„**: 1.0 ๋ฏธ๋งŒ ยท 1.0~1.2 ยท 1.2~1.3 ยท 1.3~1.5 ยท 1.5~2.0 ยท 2.0~3.0 ยท 3.0 ์ด์ƒ ยท ํšŒ์ƒ‰ = ๊ฐ’ ์—†์Œ(ํ™œ๋™๋ฉด์ด ๋‹ฟ์ง€ ์•Š์€ ์…€). 1.3(์šฐ๊ธฐ) ยท 1.5(๊ฑด๊ธฐ)๊ฐ€ ๊ธฐ์ค€ ๊ฒฝ๊ณ„. +- **์ž„๊ณ„ ์ฒด์  ์ƒ‰ ๋‹จ๊ณ„**: 10 mยณ ๋ฏธ๋งŒ ยท 10~100 ยท 100~1,000 ยท 1,000~10,000 ยท 10,000 mยณ ์ด์ƒ โ€” ์ง™์„์ˆ˜๋ก ํฐ ๋ถ•๊ดด. +- **3D ๋ณด๊ธฐ**: ์ง€ํ‘œ ์ƒ‰ = ์ง€๊ธˆ ๊ณ ๋ฅธ ์ธต(์•ˆ์ „์œจ ๋˜๋Š” ์ฒด์ ) ยท ํŒŒ๋ž€ ๊ด€ = ๋…ธ์„  ์ค‘์‹ฌ์„  ยท ์ง™์€ ๋ถ‰์€ ๋ฉด = ์ž„๊ณ„ ํ™œ๋™๋ฉด(์ง€ํ‘œ ์•„๋ž˜ ๋ถ€๋ถ„๋งŒ). +- **์˜ค๋ฅธ์ชฝ ๊ตฌ๊ฐ„ ๊ฒฐ๊ณผ**: + +| ์ค„ | ๋œป | +| --- | --- | +| 3D ์ตœ์†Œ ์•ˆ์ „์œจ | ๊ตฌ๊ฐ„์—์„œ ์ฐพ์€ ๊ฐ€์žฅ ๋‚ฎ์€ 3D ์•ˆ์ „์œจ | +| ํŒ์ • | ใ€Œ๋งŒ์กฑใ€ ๋˜๋Š” ใ€Œ๋ถˆ๋งŒ์กฑใ€ ยท ๊ด„ํ˜ธ ์•ˆ = ์กฐ๊ฑด๊ณผ ๊ธฐ์ค€(์˜ˆ: ๊ฑด๊ธฐ โ‰ฅ 1.50) | +| 2D ์ตœ์†Œ ์•ˆ์ „์œจ (๊ฐ™์€ ๊ตฌ๊ฐ„) | E03 ์ธก์  ๊ฒฐ๊ณผ ์ค‘ ์ด ๊ตฌ๊ฐ„ ์•ˆ ๊ฐ€์žฅ ๋‚ฎ์€ ๊ฐ’ ยท ์—†์œผ๋ฉด ใ€Œ2D ๊ฒฐ๊ณผ ์—†์Œใ€ | +| 3D / 2D ๋น„ | 3D ์ตœ์†Œ รท 2D ์ตœ์†Œ ยท 1 ๋ณด๋‹ค ํฌ๋ฉด 2D ๊ฐ€ ์•ˆ์ „ ์ชฝ(๋ณด์ˆ˜์ ) | +| ์ž„๊ณ„ ์ฒด์  | ๊ฐ€์žฅ ์œ„ํ—˜ํ•œ ํ™œ๋™๋ฉด ์œ„ ํ™์˜ ๋ถ€ํ”ผ(mยณ) | +| ๋‹ค์Šค๋ฆฐ ๋ฐฉ๋ฒ• | ํŒ์ •์— ์“ด ๋ฐฉ๋ฒ•(3D Bishop ๋˜๋Š” 3D Janbu ๋ณด์ •) | +| 3D Bishop ยท 3D Janbu ๋ณด์ • ยท Hovland (๋น„๊ต) | ๊ฐ™์€ ํ™œ๋™๋ฉด์„ ์„ธ ๋ฐฉ๋ฒ•์œผ๋กœ ์…ˆํ•œ ๊ฐ’ | +| ์œ„์น˜ (๊ฐ€๊นŒ์šด ์ธก์ ) | ๊ฐ€์žฅ ์œ„ํ—˜ํ•œ ํ™œ๋™๋ฉด์— ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ธก์  | +| ์…€ ์ˆ˜ ยท ์‹œํ—˜ ํ™œ๋™๋ฉด ์ˆ˜ ยท ๊ณ„์‚ฐ ์‹œ๊ฐ„ | ๊ณ„์‚ฐ ๊ทœ๋ชจ | + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- **๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ** โ€” ์ง€๋ฐ˜ ๋ฌผ์„ฑ์น˜๋Š” ๋ฌธํ—Œ ยท ๊ธฐ์ค€๊ฐ’์„ ์ ์šฉํ•œ ์ถ”์ •์น˜์ด๋ฉฐ ํ˜„์žฅ ์กฐ์‚ฌ ยท ์‹œํ—˜ ์„ฑ๊ณผ๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ์•Š์•˜์Œ. ์•ˆ์ „์œจ์€ ๋น„๊ต ยท ๊ฒ€ํ† ์šฉ์œผ๋กœ ๋ณผ ๊ฒƒ. +- ๊ฒฐ๊ณผ๋Š” ์„œ๋ฒ„์— ๊ตฌ๊ฐ„๋งˆ๋‹ค ์ž๋™ ๋ณด๊ด€๋จ(๋”ฐ๋กœ [์ €์žฅ] ์—†์Œ). ํƒ์ƒ‰ ์„ค์ • ์นธ ๊ฐ’์€ ์ด ๋ธŒ๋ผ์šฐ์ € ํƒญ์—๋งŒ ๊ธฐ์–ต๋จ. +- E02 ์ง€๋ฐ˜ ์กฐ๊ฑด์„ ๋ฐ”๊พธ๋ฉด ๊ทธ ๊ตฌ๊ฐ„ ๊ฒฐ๊ณผ ์•„๋ž˜ ใ€Œ์ž…๋ ฅ(์ง€๋ฐ˜ ์กฐ๊ฑด ยท ํƒ์ƒ‰ ์„ค์ • ยท ์—”์ง„)์ด ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค โ€” ๋‹ค์‹œ ํ•ด์„ํ•˜์„ธ์š”.ใ€ ๊ฒฝ๊ณ ๊ฐ€ ๋œจ๊ณ  ๋ชฉ๋ก์— ใ€Œ์˜› ๊ฒฐ๊ณผใ€ ๊ฐ€ ๋ถ™์Œ. **[ํ•ด์„]** ์„ ๋‹ค์‹œ ๋ˆ„๋ฅผ ๊ฒƒ. +- 3D ๋Š” E02 ์—์„œ **[์ €์žฅ]** ํ•œ ์ง€๋ฐ˜ ์กฐ๊ฑด์„ ์”€. ํ•ด์„ ์ „ E02 ์—์„œ [์ €์žฅ] ์„ ํ•œ ๋ฒˆ ๋ˆŒ๋Ÿฌ ๋‘˜ ๊ฒƒ. +- ํ•œ ๋ฒˆ์— ํ•ด์„ํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ๊ฐ„์€ 500 m ์ดํ•˜. ๊ตฌ๊ฐ„ ์•ˆ ์ธก์ ์ด ๋‘˜ ์ด์ƒ์ด์–ด์•ผ ํ•จ. +- ํ•ด์„ ํ•˜๋‚˜์— ์ˆ˜์‹ญ ์ดˆ ~ ์ˆ˜ ๋ถ„ ๊ฑธ๋ฆผ(๊ตฌ๊ฐ„ ๊ธธ์ด ยท ํƒ์ƒ‰ ํญ ยท ๊ธฐ๋‘ฅ ํฌ๊ธฐ์— ๋”ฐ๋ผ). ํ•œ ํ”„๋กœ์ ํŠธ์—์„œ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜๋งŒ ๋Ž. +- ใ€Œ์กฐ๊ฑด์— ๋งž๋Š” ํ™œ๋™๋ฉด์ด ์—†์Šต๋‹ˆ๋‹ค โ€” ๋ถ€ํ”ผ ยท ๋†’์ด ๋ฒ”์œ„๋ฅผ ๋„“ํžˆ์„ธ์š”.ใ€ ๊ฐ€ ๋œจ๋ฉด ๊ณ ๊ธ‰ ์„ค์ •์˜ ์ตœ์†Œ ยท ์ตœ๋Œ€ ๋ถ€ํ”ผ๋‚˜ ์ค‘์‹ฌ ๋†’์ด ๋ฒ”์œ„๋ฅผ ๋„“ํž ๊ฒƒ. +- ์šฐ๊ธฐ๋Š” ใ€Œ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”ใ€ ํ•œ ๊ฐ€์ง€๋กœ ์…ˆ โ€” E02 ์˜ ์ง€ํ•˜์ˆ˜ ๊ทœ์น™(๊นŠ์ด ๋น„์œจ ยท r_u ยท ์ˆ˜์œ„์„ )์€ 3D ์— ์“ฐ์ง€ ์•Š์Œ. ์ง€์ง„ ์กฐ๊ฑด ยท ์˜น๋ฒฝ ยท ์„์ถ•์€ 3D ์— ๋„ฃ์ง€ ์•Š์Œ. +- 2D ๋Œ€์กฐ ๊ฐ’์€ E03 ์ธก์ ๋งˆ๋‹ค ๊ธฐ์ค€ ๋Œ€๋น„ ๊ฐ€์žฅ ๋‚ฎ์€ ์กฐ๊ฑด ยท ๋น„ํƒˆ์˜ ์•ˆ์ „์œจ์ด๋ผ ์กฐ๊ฑด์„ ๋งž์ถ”์ง€ ์•Š์Œ. ๊ฐ™์€ ์กฐ๊ฑด๋ผ๋ฆฌ ๊ฒฌ์ค€ ํ‘œ๋Š” E05 ๋ณด๊ณ ์„œ ใ€Œ3D์š”์•ฝใ€ ์‹œํŠธ์— ์žˆ์Œ. +- ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ใ€Œ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.ใ€ ๊ฐ€ ๋œจ๊ณ  [ํ•ด์„] ๋‹จ์ถ”๊ฐ€ ๊บผ์ง. +- ์“ฐ์ธ ๋ฐฉ๋ฒ• ยท ๊ทผ๊ฑฐ ยท ํ•œ๊ณ„๋Š” ใ€Œ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐใ€ ๋ฌธ์„œ, ์•„์ง ์‚ฌ์šฉ์ž ํ™•์ • ์ „์ธ ๊ฐ’์€ ใ€Œ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ใ€ ๋ฌธ์„œ ์ฐธ๊ณ . diff --git a/resources/help/E04/10_์นธ_๋œป.md b/resources/help/E04/10_์นธ_๋œป.md new file mode 100644 index 000000000..b89c0e2c4 --- /dev/null +++ b/resources/help/E04/10_์นธ_๋œป.md @@ -0,0 +1,51 @@ +# 3D ํ•ด์„ ์นธ ๋œป + +## ๊ตฌ๊ฐ„ ์นด๋“œ + +| ์นธ | ๋œป | ๊ธฐ๋ณธ๊ฐ’ ยท ๋ฒ”์œ„ | +| --- | --- | --- | +| ๊ตฌ๊ฐ„ ๋ชฉ๋ก | ์ธก์ ์„ ์•ž์—์„œ๋ถ€ํ„ฐ 100 m ์ด์ƒ์”ฉ ๋ฌถ์€ ์ž๋™ ๊ตฌ๊ฐ„ ยท ๊ฒฝ๊ณ„๋Š” ์ธก์  ยท ๋์— 50 m ๋ฏธ๋งŒ์ด ๋‚จ์œผ๋ฉด ์•ž ๊ตฌ๊ฐ„์— ๋ถ™์ž„ | ์ž๋™ | +| ์‹œ์ž‘ (m) | ํ•ด์„ ๊ตฌ๊ฐ„ ์‹œ์ž‘ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ | ์ฒซ ๊ตฌ๊ฐ„ ์‹œ์ž‘ | +| ๋ (m) | ํ•ด์„ ๊ตฌ๊ฐ„ ๋ ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ | ์‹œ์ž‘๋ณด๋‹ค ์ปค์•ผ ํ•จ ยท ๊ตฌ๊ฐ„ ๊ธธ์ด 500 m ์ดํ•˜ | + +- ๋ชฉ๋ก ํ•ญ๋ชฉ = `์‹œ์ž‘ โ€“ ๋ m` ยท ์ƒ‰ ์ (ํ•ด์„ํ•œ ๊ตฌ๊ฐ„์˜ ์ตœ์†Œ ์•ˆ์ „์œจ ์ƒ‰) ยท `Fs 0.00` ยท ์ž…๋ ฅ์ด ๋ฐ”๋€Œ์—ˆ์œผ๋ฉด ใ€Œ์˜› ๊ฒฐ๊ณผใ€. +- ์‹œ์ž‘ ยท ๋์„ ์ง์ ‘ ๋„ฃ์–ด ํ•ด์„ํ•œ ๊ตฌ๊ฐ„๋„ ๋ชฉ๋ก ๋์— ๋”ํ•ด์ง. + +## ํƒ์ƒ‰ ์„ค์ • ์นด๋“œ + +| ์นธ | ๋œป | ๊ธฐ๋ณธ๊ฐ’ ยท ๋ฒ”์œ„ | +| --- | --- | --- | +| ์กฐ๊ฑด | ๊ฑด๊ธฐ = ์ง€ํ•˜์ˆ˜ ์—†์Œ ยท ์šฐ๊ธฐ (์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”) = ์ง€ํ•˜์ˆ˜์œ„๋ฉด์„ ์ง€ํ‘œ์— ๋‘  | ๊ฑด๊ธฐ | +| ํ™œ๋™๋ฉด ๋ชจ์–‘ | ๊ตฌ = ๊ณต ๋ชจ์–‘ ๋ฉด๋งŒ ยท ๊ตฌ + ํƒ€์›์ฒด = ๊ณต ๋ชจ์–‘์œผ๋กœ ์ฐพ์€ ์œ„ํ—˜ ์ž๋ฆฌ์—์„œ ์˜†์œผ๋กœ ๋Š˜๋ฆฐ ํƒ€์›์ฒด๋„ ๋ด„ | ๊ตฌ + ํƒ€์›์ฒด | +| ํƒ€์›์ฒด ๊ธธ์ด๋น„ | ํƒ€์›์ฒด๋ฅผ ํ™œ๋™ ๋ฐฉํ–ฅ(๋น„ํƒˆ ์•„๋ž˜์ชฝ)์— ์ง๊ฐ์ธ ์˜† ๋ฐฉํ–ฅ์œผ๋กœ ๋ช‡ ๋ฐฐ ๋Š˜๋ฆด์ง€ | 2.0 ยท 1.1~5.0 | +| ํƒ์ƒ‰ ํญ (์ค‘์‹ฌ์„  ยฑm) | ํ™œ๋™๋ฉด ์ค‘์‹ฌ์„ ์ฐพ๋Š” ๋ฒ”์œ„ = ๋…ธ์„  ์ค‘์‹ฌ์„  ์ขŒ์šฐ ์ด ๊ฑฐ๋ฆฌ ์•ˆ | 30 m ยท 5~100 m | + +## ๊ณ ๊ธ‰ ์„ค์ • ์นด๋“œ(์ ‘ํž˜) + +| ์นธ | ๋œป | ๊ธฐ๋ณธ๊ฐ’ ยท ๋ฒ”์œ„ | +| --- | --- | --- | +| ๊ธฐ๋‘ฅ ํฌ๊ธฐ (m) | ๋•…์„ ๋‚˜๋ˆ„๋Š” ์ •์‚ฌ๊ฐ ๊ธฐ๋‘ฅ(์…€) ํ•œ ๋ณ€ ยท ์ž‘์„์ˆ˜๋ก ์ •๋ฐ€ํ•˜๋‚˜ ๋А๋ฆผ | 1.0 m ยท 0.5~5 m | +| ๊ตฌ ์ค‘์‹ฌ ๊ฐ„๊ฒฉ (m) | ๊ฐ€์žฅ ๊ณ ์šด ํƒ์ƒ‰์—์„œ ๊ตฌ ์ค‘์‹ฌ๋ผ๋ฆฌ ์ˆ˜ํ‰ ๊ฐ„๊ฒฉ | 2.0 m ยท 0.5~10 m | +| ์ค‘์‹ฌ ๋†’์ด ์ตœ์†Œ (m) | ๊ตฌ ์ค‘์‹ฌ์„ ์ง€ํ‘œ ์œ„ ๋ช‡ m ๋ถ€ํ„ฐ ๋‘˜์ง€ | 2 m ยท 0~100 m | +| ์ค‘์‹ฌ ๋†’์ด ์ตœ๋Œ€ (m) | ๊ตฌ ์ค‘์‹ฌ์„ ์ง€ํ‘œ ์œ„ ๋ช‡ m ๊นŒ์ง€ ๋‘˜์ง€ | 30 m ยท 1~200 m | +| ์ค‘์‹ฌ ๋†’์ด ๊ฐ„๊ฒฉ (m) | ์ค‘์‹ฌ ๋†’์ด๋ฅผ ๋ช‡ m ์”ฉ ์˜ฌ๋ ค ๋ณผ์ง€ | 2 m ยท 0.5~20 m | +| ๋ฐ˜์ง€๋ฆ„ ์ฆ๋ถ„ (m) | ๊ตฌ ๋ฐ˜์ง€๋ฆ„์„ ๋ช‡ m ์”ฉ ํ‚ค์›Œ ๋ณผ์ง€ | 1.0 m ยท 0.2~5 m | +| ์ตœ๋Œ€ ๊นŠ์ด (m) | ๋ฐ˜์ง€๋ฆ„ ์ƒํ•œ = ์ค‘์‹ฌ ๋†’์ด + ์ด ๊ฐ’ ยท ํ™œ๋™๋ฉด์ด ๋•…์† ์–ผ๋งˆ๋‚˜ ๊นŠ์ด ๊ฐˆ ์ˆ˜ ์žˆ๋Š”์ง€ | 15 m ยท 1~60 m | +| ์ตœ์†Œ ๋ถ€ํ”ผ (mยณ) | ์ด๋ณด๋‹ค ์ž‘์€ ํ™œ๋™์ฒด๋Š” ์…ˆ์—์„œ ๋บŒ(์•„์ฃผ ์–•์€ ํ‘œ๋ฉด ๊ธํž˜ ์ œ์™ธ) | 10 mยณ ยท 0 ์ด์ƒ | +| ์ตœ๋Œ€ ๋ถ€ํ”ผ (mยณ) | ์ด๋ณด๋‹ค ํฐ ํ™œ๋™์ฒด๋Š” ์…ˆ์—์„œ ๋บŒ ยท ๊ทธ ์ค‘์‹ฌ์—์„œ ๋” ํฐ ๋ฐ˜์ง€๋ฆ„๋„ ๊ทธ๋งŒ ๋ด„ | 10,000 mยณ ยท ์ตœ์†Œ๋ณด๋‹ค ์ปค์•ผ ํ•จ | + +- **[๊ธฐ๋ณธ๊ฐ’]** = ์œ„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋˜๋Œ๋ฆผ(์‹œ์ž‘ ยท ๋ ๊ทธ๋Œ€๋กœ ยท ์กฐ๊ฑด ๊ฑด๊ธฐ). +- ์ตœ์†Œ๊ฐ€ ์ตœ๋Œ€๋ณด๋‹ค ํฌ๋ฉด ใ€Œํƒ์ƒ‰ ๋ฒ”์œ„์˜ ์ตœ์†Œ๊ฐ€ ์ตœ๋Œ€๋ณด๋‹ค ํฝ๋‹ˆ๋‹ค.ใ€ ๋กœ ๊ฑฐ์ ˆ๋จ. +- ์นธ ๊ฐ’์€ ์ด ๋ธŒ๋ผ์šฐ์ € ํƒญ์— ๋ฐ”๋กœ ๊ธฐ์–ต๋จ(์„œ๋ฒ„์—๋Š” [ํ•ด์„] ๋•Œ ๊ฒฐ๊ณผ์™€ ํ•จ๊ป˜ ๋‚จ์Œ). + +## ํ™”๋ฉด์— ์•ˆ ๋‚˜์˜ค๋Š” ๊ณ ์ •๊ฐ’ + +| ๊ฐ’ | ๋œป | ์ง€๊ธˆ ๊ฐ’ | +| --- | --- | --- | +| ๊ฑฐ์นœ ํƒ์ƒ‰ ๋ฐฐ์ˆ˜ | ์ฒ˜์Œ์—” ๊ตฌ ์ค‘์‹ฌ ๊ฐ„๊ฒฉ์˜ 4 ๋ฐฐ๋กœ ์„ฑ๊ธฐ๊ฒŒ ๋ณด๊ณ , ์œ„ํ—˜ํ•œ ๊ณณ ๋‘˜๋ ˆ๋งŒ ์ ˆ๋ฐ˜์”ฉ ์ขํ˜€ ๊ฐ | 4 ๋ฐฐ ยท ๊ณ ์šด ๋‹จ๊ณ„ 4 ๋ฒˆ๊นŒ์ง€ | +| ์…€ ์•ˆ ํ‘œ๋ณธ | ์…€๋งˆ๋‹ค 2 ร— 2 ์ ์—์„œ ํ™œ๋™๋ฉด ๋†’์ด๋ฅผ ๋ด ๊ฐ€์žฅ์ž๋ฆฌ ์…€์„ ๋ถ€๋ถ„ ๊ธฐ๋‘ฅ์œผ๋กœ ์…ˆ | 2 ร— 2 | +| ๊ณ ์šด ํƒ์ƒ‰ ๋Œ€์ƒ | ์•ˆ์ „์œจ์ด 2.0 ๋˜๋Š” ์ง€๊ธˆ๊นŒ์ง€ ์ตœ์†Œ์˜ 1.2 ๋ฐฐ๋ณด๋‹ค ๋‚ฎ์€ ์ž๋ฆฌ๋งŒ ๋” ์ด˜์ด˜ํžˆ ยท ํƒ€์›์ฒด๋„ ๊ทธ ์ž๋ฆฌ๋งŒ | 2.0 ยท 1.2 ๋ฐฐ | +| Janbu ๋ณด์ • ์ „ํ™˜ | ํ™œ๋™๋ฉด ๋ฐ”๋‹ฅ์ด ๋ชป ๋šซ๋Š” ์•”๋ฐ˜์— ์ž˜๋ฆฐ ๋ฉด์ ์ด 10 % ์ด์ƒ์ด๋ฉด 3D Janbu ๋ณด์ • ๊ฐ’์„ ๊ฐ™์ด ๋ด„ | 10 % | +| ํ™ ๋‘๊ป˜(ํ† ์‚ฌ ์ธก์ ) | ์•” ๊ฒฝ๊ณ„ ์ •๋ณด๊ฐ€ ์—†๋Š” ์ธก์ ์˜ ํ™ ๋‘๊ป˜ | 3 m | +| ๋…ธ๋ฉด ํ•˜์ค‘ | E02 ๋…ธ๋ฉด ํ•˜์ค‘(์—†์œผ๋ฉด 10 kPa)์„ ๋…ธ๋ฉด ๋  ์…€์— ์–น์Œ | E02 ๊ฐ’ | +| ๊ธฐ์ค€ ์•ˆ์ „์œจ | E02 ๊ธฐ์ค€ ์•ˆ์ „์œจ ์ค‘ ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ๊ฐ€์šด๋ฐ ํฐ ๊ฐ’(์—„ํ•œ ์ชฝ) | ๊ฑด๊ธฐ 1.5 ยท ์šฐ๊ธฐ 1.3(๊ธฐ๋ณธ) | diff --git a/resources/help/E04/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md b/resources/help/E04/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md new file mode 100644 index 000000000..c1706c8ec --- /dev/null +++ b/resources/help/E04/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md @@ -0,0 +1,74 @@ +# 3D ํ•ด์„ ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐ + +## ํ•œ๋ˆˆ์— + +| ๋‹จ๊ณ„ | ์“ฐ์ธ ๋ฐฉ๋ฒ• | ๊ทผ๊ฑฐ | +| --- | --- | --- | +| ์ž…๋ ฅ ์ง€ํ˜• | ๋ณต์‚ฌ DEM + ๋…ธ์„  ๊ณ„ํš๋ฉด(B06 ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ) ํ•ฉ์„ฑ | E01 ๋ณต์‚ฌ๋ณธ ยท B06 ํšก๋‹จ ์„ค๊ณ„ | +| ์ง€์ธต | ์ง€์ธต ๊ฒฝ๊ณ„ ๊ฒฉ์ž(B06 ์•” ๊ฒฝ๊ณ„๋ฅผ ์ธก์  ์‚ฌ์ด ๋ณด๊ฐ„) | B06 ์•” ๊ฒฝ๊ณ„ ยท E02 ๋ฌผ์„ฑ | +| ํ™œ๋™๋ฉด ์ฐพ๊ธฐ | Scoops3D ๋ฐฉ์‹ ๊ตฌ ๊ฒฉ์ž + ๋ถ€ํ”ผ ์ œ์•ฝ ยท ํƒ€์›์ฒด ๋ง๋ด„ | Reid ์™ธ(2015) USGS TM 14-A1 | +| ์•ˆ์ „์œจ(๊ธฐ๋ณธ) | Hungr 3D Bishop ๊ฐ„ํŽธ๋ฒ• | Hungr(1987) ยท Hungr ์™ธ(1989) | +| ์•ˆ์ „์œจ(์•”๋ฐ˜์— ์ž˜๋ฆฐ ๋ฉด) | 3D Janbu ๊ฐ„ํŽธ๋ฒ• + ๋ณด์ • ๊ณ„์ˆ˜ | Hungr ์™ธ(1989) ยท Janbu(1973) | +| ์•ˆ์ „์œจ(๋น„๊ต) | Hovland 3D ๋‹จ์ˆœ ๊ธฐ๋‘ฅ๋ฒ• | Hovland(1977) | + +- ์ง€์‹DB ๊ทผ๊ฑฐ: `์•ˆ์ •์„ฑํ‰๊ฐ€/05_3D_์‚ฌ๋ฉด์•ˆ์ •ํ•ด์„_๋…ผ๋ฌธ_๊ธฐ์ˆ ์ •๋ณด` 1์žฅ(๊ธฐ๋‘ฅ๋ฒ• ์ˆ˜์‹) ยท 3์žฅ(Scoops3D) ยท 6.1์ ˆ(๊ตฌ ๊ฒฉ์ž ํƒ์ƒ‰) ยท 8์žฅ(3D/2D ๋น„). +- ๊ณ„์‚ฐ ์ฝ”๋“œ๋Š” ์ˆ˜์‹๋งŒ ์˜ฎ๊ฒจ ์ง์ ‘ ์งฌ(์™ธ๋ถ€ ํ•ด์„ ํ”„๋กœ๊ทธ๋žจ ์•ˆ ์”€). + +## ์ž…๋ ฅ ์ง€ํ˜• ๋งŒ๋“ค๊ธฐ + +- ์›์ง€ํ˜• = ์ž„๋„ ํ”„๋กœ์ ํŠธ์—์„œ ๋ณต์‚ฌํ•ด ์˜จ DEM(์ง€ํ˜• ๊ฒฉ์ž). +- ์ธก์ ๋งˆ๋‹ค B06 ์„ค๊ณ„์„ (๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ๋‹จ๋ฉด)๊ณผ ์›์ง€ํ˜•์˜ ๋†’์ด ์ฐจ๋ฅผ ๊ตฌํ•˜๊ณ , ์ด์›ƒ ๋‘ ์ธก์  ์‚ฌ์ด๋Š” ํšก๋‹จ์„ ์„ ์„œ๋กœ ์ด์€ ๋ฉด ์œ„์—์„œ ๊ทธ ์ฐจ๋ฅผ ๊ณ ๋ฅด๊ฒŒ ์ด์–ด ๋ถ™์ž„. +- ๊ทธ๋ž˜์„œ ์ธก์  ์œ„ ๋‹จ๋ฉด์€ B06 ์„ค๊ณ„ ๋‹จ๋ฉด๊ณผ ๊ฐ™๊ณ , ๋…ธ์„  ๋  ๋ฐ–์€ ์›์ง€ํ˜• ๊ทธ๋Œ€๋กœ์ž„. +- ๋น„ํƒˆ์ด ํšก๋‹จ ๋์—์„œ ์›์ง€ํ˜•๊ณผ ์•ˆ ๋งŒ๋‚˜๋ฉด ๋ ๊ธฐ์šธ๊ธฐ๋กœ ์›์ง€ํ˜•๊ณผ ๋งŒ๋‚  ๋•Œ๊นŒ์ง€ ๋Š˜๋ฆผ(์ตœ๋Œ€ 60 m) โ€” ๋  ๋์— ๊ฐ€์งœ ์ ˆ๋ฒฝ์ด ์ƒ๊ธฐ์ง€ ์•Š๊ฒŒ. +- ํ•ด์„ ๊ฒฉ์ž๋Š” ๊ธฐ๋‘ฅ ํฌ๊ธฐ(๊ธฐ๋ณธ 1 m) ์ •์‚ฌ๊ฐ์œผ๋กœ ๋‹ค์‹œ ๋œธ. ๊ตฌ๊ฐ„ ์•ž๋’ค ์ธก์  ํ•˜๋‚˜์”ฉ์„ ๋” ๋ถ™์—ฌ ๊ตฌ๊ฐ„ ๋ ์ง€ํ˜•์ด ๋Š๊ธฐ์ง€ ์•Š๊ฒŒ ํ•จ. + +## ์ง€์ธต ๊ฒฝ๊ณ„ + +- ์œ„์—์„œ ์•„๋ž˜๋กœ ์„ฑํ†  ยท ํ’ํ™”ํ†  ยท ๋ฆฌํ•‘์•”(ํ’ํ™”์•” ๋ฌผ์„ฑ) ยท ๋ฐœํŒŒ์•”(์—ฐ์•” ๋ฌผ์„ฑ) ๋„ค ์ธต. +- ํ™ ๋‘๊ป˜ = ์•” ์ธก์ ์€ B06 ์•” ๊ฒฝ๊ณ„ ๊นŠ์ด ยท ํ† ์‚ฌ ์ธก์ ์€ 3 m ยท ์ธก์  ์‚ฌ์ด๋Š” ๊ณ ๋ฅด๊ฒŒ ๋ณด๊ฐ„, ๋…ธ์„  ๋  ๋ฐ–์€ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ธก์  ๊ฐ’. +- ํ† ์‚ฌ ์ธก์  ์•„๋ž˜๋Š” ๋ฆฌํ•‘์•”์œผ๋กœ ๋ด„. **๋ฐœํŒŒ์•”(์—ฐ์•”)์€ ํ™œ๋™๋ฉด์ด ๋ชป ๋šซ๋Š” ์ธต** โ€” ํ™œ๋™๋ฉด์ด ๊ทธ ์œ—๋ฉด์—์„œ ์ž˜๋ฆผ. +- ๋ฌผ์„ฑ ๊ฐ’(ฮณt ยท ฮณsat ยท c ยท ฯ†)์€ E02 ์ง€๋ฐ˜ ์กฐ๊ฑด ํ‘œ์˜ ์„ฑํ†  ยท ํ’ํ™”ํ†  ยท ํ’ํ™”์•” ยท ์—ฐ์•” ์ค„์„ ๊ทธ๋Œ€๋กœ ์”€. +- ๋…ธ๋ฉด ๋ (๋‘ ๋…ธ๋ฉด ๋ ์‚ฌ์ด)์— E02 ๋…ธ๋ฉด ํ•˜์ค‘(๊ธฐ๋ณธ 10 kPa)์„ ์–น์Œ. + +## ํ™œ๋™๋ฉด ์ฐพ๊ธฐ (Scoops3D ๋ฐฉ์‹) + +- ๋…ธ์„  ์ค‘์‹ฌ์„  ์ขŒ์šฐ ํƒ์ƒ‰ ํญ(๊ธฐ๋ณธ ยฑ30 m) ์•ˆ, ๊ตฌ๊ฐ„ ์•ˆ์— ๊ตฌ ์ค‘์‹ฌ ์ž๋ฆฌ๋ฅผ ๊ฒฉ์ž๋กœ ๊น–. ์ค‘์‹ฌ ๋†’์ด = ๊ทธ ์ž๋ฆฌ ์ง€ํ‘œ + 2~30 m(2 m ๊ฐ„๊ฒฉ). +- ์ค‘์‹ฌ๋งˆ๋‹ค ๋ฐ˜์ง€๋ฆ„์„ 1 m ์”ฉ ํ‚ค์šฐ๋ฉฐ ๋•…์„ ์ž๋ฅด๋Š” ๊ตฌ๋ฅผ ๋งŒ๋“ค๊ณ , ๊ทธ ์•„๋ž˜ ํ™ ๊ธฐ๋‘ฅ๋“ค๋กœ ์•ˆ์ „์œจ์„ ์…ˆ. +- ๋ถ€ํ”ผ ์ œ์•ฝ: ํ™œ๋™์ฒด ๋ถ€ํ”ผ๊ฐ€ 10 mยณ ๋ฏธ๋งŒ์ด๋ฉด ๋นผ๊ณ  10,000 mยณ ๋ฅผ ๋„˜์œผ๋ฉด ๊ทธ ์ค‘์‹ฌ์—์„œ ๋” ํ‚ค์šฐ์ง€ ์•Š์Œ. +- ๊ฑฐ์นœ ๊ฒฉ์ž(์ค‘์‹ฌ ๊ฐ„๊ฒฉ์˜ 4 ๋ฐฐ)๋กœ ๋จผ์ € ๋ณด๊ณ , ์•ˆ์ „์œจ์ด ๋‚ฎ๊ฒŒ ๋‚˜์˜จ ์ค‘์‹ฌ ๋‘˜๋ ˆ๋งŒ ๊ฐ„๊ฒฉ์„ ์ ˆ๋ฐ˜์”ฉ ์ขํ˜€ ๋‹ค์‹œ ๋ด„. +- ์…€๋งˆ๋‹ค ์ง€๋‚˜๊ฐ„ ํ™œ๋™๋ฉด ์ค‘ ๊ฐ€์žฅ ๋‚ฎ์€ ์•ˆ์ „์œจ๊ณผ ๊ทธ ๋ถ€ํ”ผ๋ฅผ ๋‚จ๊น€ โ†’ ์…€๋ณ„ ์ตœ์†Œ ์•ˆ์ „์œจ ์ง€๋„ ยท ์ž„๊ณ„ ์ฒด์  ์ง€๋„. +- ๊ตฌ + ํƒ€์›์ฒด๋ฅผ ๊ณ ๋ฅด๋ฉด, ์œ„ํ—˜ํ•˜๊ฒŒ ๋‚˜์˜จ ์ค‘์‹ฌ์—์„œ ํ™œ๋™ ๋ฐฉํ–ฅ์— ์ง๊ฐ์œผ๋กœ ๋Š˜๋ฆฐ ํƒ€์›์ฒด(๊ธฐ๋ณธ 2 ๋ฐฐ)๋ฅผ ๋” ๋ด„. +- ํ•œ ๊ตฌ๊ฐ€ ๋•…์„ ์—ฌ๋Ÿฌ ๋ฉ์ด๋กœ ์ž๋ฅด๋ฉด ๋ฉ์ด๋งˆ๋‹ค ๋”ฐ๋กœ ์…ˆ. ๊ฒฉ์ž ๋์ด๋‚˜ ์ง€ํ˜• ๋นˆ์นธ์— ๋‹ฟ์€ ๋ฉ์ด๋Š” ๋ฒ„๋ฆผ. +- Scoops3D ์™€ ๋‹ค๋ฅธ ์ : ์ค‘์‹ฌ ๋†’์ด๋ฅผ ใ€Œ๊ทธ ์ž๋ฆฌ ์ง€ํ‘œ ์œ„ใ€ ๋กœ ์žก์Œ(์‚ฐ์ง€ ๊ณ ์ €์ฐจ ๋Œ€์‘) ยท ๋…ธ์„  ๋  ์•ˆ์—๋งŒ ์ค‘์‹ฌ์„ ๋‘ . + +## ์•ˆ์ „์œจ ์…ˆ + +- ์•ˆ์ „์œจ = ํ™œ๋™๋ฉด์„ ๋”ฐ๋ผ ๋ฒ„ํ‹ฐ๋Š” ํž˜(์ ์ฐฉ๋ ฅ + ๋งˆ์ฐฐ) รท ๋ฏธ๋„๋Ÿฌ๋œจ๋ฆฌ๋Š” ํž˜. 1 ๋ณด๋‹ค ์ž‘์œผ๋ฉด ๋ฌด๋„ˆ์ง€๋Š” ์ชฝ. +- **Hungr 3D Bishop**: ๊ธฐ๋‘ฅ๋งˆ๋‹ค ์œ„์•„๋ž˜ ํž˜์ด ๋งž๋„๋ก ๋ฐ”๋‹ฅ ํž˜์„ ๊ตฌํ•˜๊ณ , ํšŒ์ „์ถ• ๋‘˜๋ ˆ ํž˜์˜ ๋ชจ๋ฉ˜ํŠธ๊ฐ€ ๋งž๋„๋ก ์•ˆ์ „์œจ์„ ๋˜ํ’€์ด ์…ˆ. ๊ธฐ๋ณธ ๋ฐฉ๋ฒ•. +- **3D Janbu ๋ณด์ •**: ํšŒ์ „ ๋Œ€์‹  ํ™œ๋™ ๋ฐฉํ–ฅ ์ˆ˜ํ‰ ํž˜์ด ๋งž๋„๋ก ์…ˆํ•œ ๋’ค ๋ณด์ • ๊ณ„์ˆ˜ f0 = 1 + b1 ร— (d/L โˆ’ 1.4 ร— (d/L)ยฒ) ๋ฅผ ๊ณฑํ•จ(b1 = ์ ์ฐฉ ยท ๋งˆ์ฐฐ ๋‘˜ ๋‹ค 0.50 ยท ๋งˆ์ฐฐ๋งŒ 0.69 ยท ์ ์ฐฉ๋งŒ 0.31 ยท d/L = ํ™œ๋™๋ฉด ์–‘๋์„ ์ด์€ ์ง์„ ์—์„œ ๋ฐ”๋‹ฅ๊นŒ์ง€ ๊ฐ€์žฅ ๋จผ ๊ฑฐ๋ฆฌ รท ํ™œ๋™ ๋ฐฉํ–ฅ ๊ธธ์ด). +- **Hovland**: ๊ธฐ๋‘ฅ ์‚ฌ์ด ํž˜์„ ๋ชจ๋‘ ๋ฌด์‹œํ•œ ๊ฐ€์žฅ ๋‹จ์ˆœํ•œ ์…ˆ. ํŒ์ •์—๋Š” ์•ˆ ์“ฐ๊ณ  ๋น„๊ต๋กœ๋งŒ ๋ณด์—ฌ ์คŒ. +- ๋‹ค์Šค๋ฆฌ๋Š” ๊ฐ’: ๋ฐ”๋‹ฅ์ด ๋ชป ๋šซ๋Š” ์•”๋ฐ˜์— ์ž˜๋ฆฐ ๋ฉด์ ์ด 10 % ์ด์ƒ์ด๋ฉด Bishop ๊ณผ Janbu ๋ณด์ • ์ค‘ **์ž‘์€ ๊ฐ’**, ๊ทธ ๋ฐ–์€ Bishop. Bishop ์ด ์ˆ˜๋ ดํ•˜์ง€ ์•Š์€ ํ›„๋ณด๋Š” ๋ฒ„๋ฆผ. ๋„ˆ๋ฌด ํฐ ์•ˆ์ „์œจ์€ 100 ์œผ๋กœ ์ž๋ฆ„. +- ์šฐ๊ธฐ = ์ง€ํ•˜์ˆ˜์œ„๋ฉด์„ ์ง€ํ‘œ์— ๋‘” ๊ฐ„๊ทน์ˆ˜์••(KDS ์šฐ๊ธฐ ๊ทœ์น™์˜ 3D ๊ผด). ์ง€์ง„์€ ๋„ฃ์ง€ ์•Š์Œ. +- ํŒ์ • = ๋‹ค์Šค๋ฆฌ๋Š” ๊ฐ’ โ‰ฅ ๊ธฐ์ค€ ์•ˆ์ „์œจ(E02 ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ์ค‘ ํฐ ๊ฐ’ ยท ๊ธฐ๋ณธ ๊ฑด๊ธฐ 1.5 ยท ์šฐ๊ธฐ 1.3, KDS 11 70 05). + +## ์‹œํ—˜์œผ๋กœ ๋งž์ถ˜ ๊ธฐ์ค€ ๋ฌธ์ œ + +- Baligh & Azzouz ๊ตฌ ํ•ด์„ํ•ด 1.402 โ†’ 1.402. +- Zhang(1988) ๊ฒฝ์šฐ 1 = 2.158(๋ฌธํ—Œ Hungr 2.167) ยท ๊ฒฝ์šฐ 2 ์•ฝ์ธต = 1.617(1.620). +- Scoops3D ์ œ๋ฐฉ 30ยฐ = 1.325(1.32) ยท ์›๋ฟ” r_u 0.4 = 1.637(1.626). + +## 2D ์™€ ๋‹ค๋ฅธ ์  + +- 2D(E03)๋Š” ์ธก์  ํšก๋‹จ๋งˆ๋‹ค ํญ์ด ๋์—†์ด ๊ธด ์‚ฌ๋ฉด์œผ๋กœ ๋ณด๊ณ  ์…ˆ โ€” ์–‘์˜† ํ™์ด ์žก์•„ ์ฃผ๋Š” ํž˜(๋๋‹จ ํšจ๊ณผ)์ด ์—†์Œ. +- 3D ๋Š” ํ™œ๋™์ฒด ์–‘์˜† ์ €ํ•ญ๊ณผ ์ง€ํ˜• ๊ตด๊ณก(์˜ค๋ชฉ ยท ๋ณผ๋ก)์„ ๋„ฃ์Œ. ๊ฐ™์€ ๋•…์ด๋ฉด ๋Œ€๊ฐœ 3D ์•ˆ์ „์œจ์ด 2D ๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ ๊ฐ™์Œ(Cavounidis 1987). +- ๋ฌธํ—Œ ๊ฒฝํ–ฅ(์ง€์‹DB 05 8์žฅ): ํญ รท ๋†’์ด 1~1.5 = 3D/2D ์•ฝ 1.25~1.50 ยท 2~3 = ์•ฝ 1.10~1.25 ยท 6 ์ด์ƒ = ์•ฝ 1.01~1.05. +- 3D/2D ๋น„๊ฐ€ 1 ๋ณด๋‹ค ํฌ๋ฉด 2D ํŒ์ •์ด ์•ˆ์ „ ์ชฝ. 1 ๋ณด๋‹ค ์ž‘์œผ๋ฉด ์ธก์  ์‚ฌ์ด(20 m ๊ฐ„๊ฒฉ ํšก๋‹จ์ด ๋†“์นœ ๊ณณ)๋‚˜ ๋น„์Šค๋“ฌํ•œ ์ง€ํ˜•์— ์œ„ํ—˜์ด ์žˆ์„ ์ˆ˜ ์žˆ์–ด ๊ทธ ์ž๋ฆฌ๋ฅผ ์‚ดํŽด๋ณผ ๊ฒƒ. +- 3D ๋Š” ๋ฌด๋„ˆ์งˆ ํ™ ์–‘(mยณ)์„ ์•Œ๋ ค ์คŒ โ€” 2D ๋Š” ๋ชป ์คŒ. + +## ํ•œ๊ณ„ + +- **3D ๊ฐ•๋„๊ฐ์†Œ FEM(์œ ํ•œ์š”์†Œ) ์•ˆ ํ•จ** โ€” ํ™œ๋™๋ฉด ๋ชจ์–‘์„ ๋ฏธ๋ฆฌ ๊ฐ€์ •ํ•˜์ง€ ์•Š๋Š” ์ •๋ฐ€ ๋ฐฉ๋ฒ•์ด์ง€๋งŒ ๋…ธ์„  ์ „์ฒด์— ์“ฐ๊ธฐ์—” ๋ฌด๊ฑฐ์›Œ ๋ณด๋ฅ˜. +- ํ™œ๋™๋ฉด ๋ชจ์–‘์€ ๊ตฌ ยท ํƒ€์›์ฒด(์•”๋ฐ˜์— ์ž˜๋ฆฐ ๋ฉด ํฌํ•จ)๋งŒ. ๋น„๋Œ€์นญ ์—„๋ฐ€ํ•ด(Cheng & Yip ยท 3D Spencer ๋“ฑ)๋Š” ์•ˆ ์”€. +- ์ง€์ธต์€ ๋„ค ์ธต ๊ณ ์ •. E02 ์—์„œ ๋”ํ•œ ์ง€์ธต ยท ๊ตฌ๊ฐ„๋ณ„ ๋ฎ์–ด์“ฐ๊ธฐ ยท ์ง€๋ฐ˜ ๋ผ๋ฒจ ๋ฐ”๊ฟˆ ยท ๋ถ•์ ํ†  ยท ๊ฒฝ์•”์€ 3D ์— ์•ˆ ๋“ค์–ด๊ฐ. ํ™ ๋‘๊ป˜๋Š” B06 ์•” ๊ฒฝ๊ณ„(์—†์œผ๋ฉด 3 m)๋ฅผ ์”€. +- ์šฐ๊ธฐ๋Š” ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™” ํ•œ ๊ฐ€์ง€. ์ง€์ง„ ยท ์˜น๋ฒฝ ยท ์„์ถ• ยท ๋ฐฐ์ˆ˜ ์‹œ์„ค์€ ๋„ฃ์ง€ ์•Š์Œ. +- ๋ฌผ์„ฑ์€ ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ. ์‹œ์ถ” ยท ์‹œํ—˜ ์„ฑ๊ณผ๊ฐ€ ์žˆ์œผ๋ฉด E02 ์—์„œ ๋ฐ”๊ฟ€ ๊ฒƒ. diff --git a/resources/help/E04/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md b/resources/help/E04/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md new file mode 100644 index 000000000..837924bb6 --- /dev/null +++ b/resources/help/E04/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md @@ -0,0 +1,30 @@ +# 3D ํ•ด์„ ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ + +- ์•„๋ž˜ ๊ฐ’์€ ๊ทผ๊ฑฐ ๋ฌธํ—Œ ยท ๊ธฐ์ˆ ์ •๋ณด๋ฅผ ๋ณด๊ณ  ์ •ํ•œ ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’์ž„. ์‚ฌ์šฉ์ž ํ˜‘์˜๋กœ ํ™•์ •๋˜๊ธฐ ์ „์ด๋ฉฐ ๋ฐ”๋€” ์ˆ˜ ์žˆ์Œ. +- ใ€Œํ™”๋ฉดใ€ = ํƒ์ƒ‰ ์„ค์ • ยท ๊ณ ๊ธ‰ ์„ค์ • ์นธ์—์„œ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ ยท ใ€Œ๊ณ ์ •ใ€ = ํ™”๋ฉด์—์„œ ๋ชป ๋ฐ”๊ฟˆ. + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ ยท ๊นŒ๋‹ญ | ๋ฐ”๊พธ๋Š” ๊ณณ | ์ƒํƒœ | +| --- | --- | --- | --- | --- | +| 3D ๊ธฐ๋ณธ ๋ฐฉ๋ฒ• | Hungr 3D Bishop ยท ์•”๋ฐ˜์— ์ž˜๋ฆฐ ๋ฉด์€ 3D Janbu ๋ณด์ •๊ณผ ์ž‘์€ ๊ฐ’ ยท Hovland ๋น„๊ต | ์ง€์‹DB 05 1์žฅ ยท ์š”๊ตฌ์„œ 345 4-2 | ๊ณ ์ • | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| Janbu ๋ณด์ • ์ „ํ™˜ ๋ฌธํ„ฑ | ์ž˜๋ฆฐ ๋ฐ”๋‹ฅ ๋ฉด์  10 % | ๊ธฐ๋‘ฅ ๋ช‡ ๊ฐœ๋งŒ ๋‹ฟ์•„ ์ง€๋„์— ์ด์Œ๋งค๊ฐ€ ์ƒ๊ธฐ๋Š” ๊ฒƒ์„ ๋ง‰๋Š” ๊ฐ’ ยท Zhang(1988) ์•ฝ์ธต ๊ฒฝ์šฐ ์ž˜๋ฆผ ๋น„ 33 % | ๊ณ ์ • | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ํ™œ๋™๋ฉด ๋ชจ์–‘ | ๊ตฌ + ํƒ€์›์ฒด | Scoops3D ยท Hungr(1989) ํƒ€์›์ฒด ๊ผด | ํ™”๋ฉด | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ํƒ€์›์ฒด ๊ธธ์ด๋น„ | 2.0 | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ | ํ™”๋ฉด | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ํƒ์ƒ‰ ํญ | ์ค‘์‹ฌ์„  ยฑ30 m | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ ยท ์ง€์‹DB 05 6.1์ ˆ(๋…ธ์„  ๋”ฐ๋ผ ํƒ์ƒ‰ ์ƒ์ž ยท ๋ฌธํ—Œ ์˜ˆ ยฑ50 m) | ํ™”๋ฉด | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๊ธฐ๋‘ฅ ํฌ๊ธฐ | 1.0 m | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ | ํ™”๋ฉด | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๊ตฌ ์ค‘์‹ฌ ๊ฐ„๊ฒฉ ยท ๋†’์ด ๋ฒ”์œ„ ยท ๊ฐ„๊ฒฉ | 2 m ยท ์ง€ํ‘œ ์œ„ 2~30 m ยท 2 m | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ ยท Scoops3D ํƒ์ƒ‰ ๊ฒฉ์ž ๋ฐฉ์‹ | ํ™”๋ฉด | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๋ฐ˜์ง€๋ฆ„ ์ฆ๋ถ„ ยท ์ตœ๋Œ€ ๊นŠ์ด | 1 m ยท 15 m | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ ยท Scoops3D ๋ฐฉ์‹ | ํ™”๋ฉด | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๋ถ€ํ”ผ ๋ฒ”์œ„ | 10~10,000 mยณ | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ ยท ์ง€์‹DB 05 6.1์ ˆ(๋ฌธํ—Œ ์˜ˆ 50~50,000 mยณ) | ํ™”๋ฉด | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ํ† ์‚ฌ ์ธก์  ํ™ ๋‘๊ป˜ | 3 m | ๊ตญ๋‚ด ์‚ฐ์ง€ ํ’ํ™”ํ†  ยท ๋ถ•์ ํ†  1~3 m ์˜ ํฐ ์ชฝ | ๊ณ ์ • | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๋ชป ๋šซ๋Š” ์ธต | ๋ฐœํŒŒ์•”(์—ฐ์•” ๋ฌผ์„ฑ) | ํ† ์‚ฌ ์ธก์  ์•„๋ž˜ = ๋ฆฌํ•‘์•”(ํ’ํ™”์•” ๋ฌผ์„ฑ)์œผ๋กœ ๋ด„ | ๊ณ ์ • | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ์šฐ๊ธฐ ์ง€ํ•˜์ˆ˜ | ์ง€ํ‘œ๊นŒ์ง€ ํฌํ™” | KDS ์šฐ๊ธฐ ๊ทœ์น™(ํ† ์‚ฌ)์˜ 3D ๊ผด | ๊ณ ์ • | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| 3D ๊ธฐ์ค€ ์•ˆ์ „์œจ | E02 ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ์ค‘ ํฐ ๊ฐ’(๊ฑด๊ธฐ 1.5 ยท ์šฐ๊ธฐ 1.3) | KDS 11 70 05 ยท ํ•œ ๊ตฌ๊ฐ„์— ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ๊ฐ€ ์„ž์—ฌ ์—„ํ•œ ์ชฝ | E02 ๊ธฐ์ค€ ์•ˆ์ „์œจ | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ์ž๋™ ๊ตฌ๊ฐ„ ๊ธธ์ด | 100 m(๊ฒฝ๊ณ„ = ์ธก์ ) ยท ๊ผฌ๋ฆฌ 50 m ๋ฏธ๋งŒ์€ ์•ž์— ๋ถ™์ž„ | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ | ๊ณ ์ •(์‹œ์ž‘ ยท ๋ ์นธ์œผ๋กœ ์ง์ ‘ ์ง€์ • ๊ฐ€๋Šฅ) | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ํ•œ ๋ฒˆ์— ํ•ด์„ํ•  ๊ตฌ๊ฐ„ ์ƒํ•œ | 500 m | ๋ธŒ๋ ˆ์ธ ์ •ํ•จ(๊ณ„์‚ฐ ์‹œ๊ฐ„ ยท ๋ฉ”๋ชจ๋ฆฌ) | ๊ณ ์ • | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ์•ˆ์ „์œจ ์ƒ‰ ๋‹จ๊ณ„ | 1.0 ยท 1.2 ยท 1.3 ยท 1.5 ยท 2.0 ยท 3.0 | 1.3(์šฐ๊ธฐ) ยท 1.5(๊ฑด๊ธฐ) ๊ธฐ์ค€ ๊ฒฝ๊ณ„ | ๊ณ ์ • | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | + +## ๋‹ค๋ฅธ ํ™”๋ฉด์—์„œ ์˜ค๋Š” ๊ฐ’ + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| ์ง€๋ฐ˜ ๋ฌผ์„ฑ์น˜(์„ฑํ†  ยท ํ’ํ™”ํ†  ยท ํ’ํ™”์•” ยท ์—ฐ์•”) | E02 ํ‘œ ๊ฐ’ | ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ(์ง€์‹DB 04 ยท 07) | ์‚ฌ์šฉ์ž๊ฐ€ E02 ์—์„œ ๋ฐ”๊ฟˆ | +| ๋…ธ๋ฉด ํ•˜์ค‘ | 10 kPa | ์˜น๋ฒฝ ๋ฐฐ๋ฉด ์ƒ์žฌํ•˜์ค‘ ์ค€์šฉ | ์‚ฌ์šฉ์ž๊ฐ€ E02 ์—์„œ ๋ฐ”๊ฟˆ | diff --git a/resources/help/E05/00_์„ค๋ช…์„œ.md b/resources/help/E05/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..5342c10a1 --- /dev/null +++ b/resources/help/E05/00_์„ค๋ช…์„œ.md @@ -0,0 +1,55 @@ +# ๋ณด๊ณ ์„œ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +- E02~E04 ยท E07 ยท E03 ๋ณด๊ฐ• ๊ฒ€ํ†  ๊ฒฐ๊ณผ๋ฅผ ๋ชจ์•„ ๋น„ํƒˆ๋ฉด ์•ˆ์ •์„ฑ ๊ฒ€ํ†  ๋ณด๊ณ ์„œ๋ฅผ ์—‘์…€ ๊ผด๋กœ ๋งŒ๋“ฆ. +- ๊ฐ€์šด๋ฐ = ๋‚ด์žฅ ์—‘์…€(์‹œํŠธ ํƒญ ยท ๊ทธ๋ฆผ ยท ์ชฝ ๋‚˜๋ˆ” ์ ์„ )๋กœ ๋ณด๊ณ ์„œ๋ฅผ ๋ฐ”๋กœ ๋ณด๊ณ  ๊ณ ์นจ. +- ์™ผ์ชฝ = ์•ˆ๋‚ด ๊ธ€ ยท ์ƒํƒœ ์ค„ ยท ๋‚ด๋ณด๋‚ด๊ธฐ ๋‹จ์ถ” ยท ์ €์žฅ ๋‹จ์ถ”. +- ๋‚ด๋ณด๋‚ด๊ธฐ = ์—‘์…€(xlsx) ยท PDF ํŒŒ์ผ. +- ์ฒ˜์Œ ๋“ค์–ด์˜ค๋ฉด ์„œ๋ฒ„๊ฐ€ ๋ณด๊ณ ์„œ๋ฅผ ํ•œ ๋ฒˆ ๋งŒ๋“ฆ(2D ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ). ๊ทธ ์ฒซ ํŒ์ด [์ดˆ๊ธฐํ™”] ์˜ ๋˜๋Œ๋ฆผ ๊ธฐ์ค€์ด ๋จ. + +## ์ž‘์—… ์ˆœ์„œ + +1. E03 ์ธก์  ํ•ด์„์—์„œ **[ํ•ด์„]** ์„ ๋จผ์ € ๋๋ƒ„(์—†์œผ๋ฉด ๊ฐ€์šด๋ฐ์— ใ€Œ2D ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค โ€” E03 ์—์„œ [ํ•ด์„] ๋’ค ๋งŒ๋“œ์„ธ์š”.ใ€). +2. ํ•„์š”ํ•˜๋ฉด E04 3D ํ•ด์„ ยท E07 ๊ด‘์—ญ ํ•ด์„ ยท E03 ๋ณด๊ฐ• ๊ฒ€ํ† ๋„ ๋๋ƒ„(ํ•˜์ง€ ์•Š์€ ๋ถ€๋ถ„์€ ์‹œํŠธ์— ใ€Œโ€ฆ ์ „ใ€ ์œผ๋กœ ๋‚จ์Œ). +3. E05 ๋ฅผ ์—ด๋ฉด ๋ณด๊ณ ์„œ๊ฐ€ ๋œธ. ์•„๋ž˜ ์‹œํŠธ ํƒญ(ํ‘œ์ง€ ยท ๊ฐœ์š” ยท ์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D์š”์•ฝ ยท ๊ด‘์—ญ์š”์•ฝ ยท ๋ณด๊ฐ•๊ฒ€ํ† )์„ ๋ˆŒ๋Ÿฌ ์ฐจ๋ก€๋กœ ํ™•์ธํ•จ. +4. **ํ‘œ์ง€** ์˜ ๋ฐœ์ฃผ์ฒ˜ ยท ์ž‘์„ฑ์ž ์นธ, **๊ฐœ์š”** ์˜ ใ€Œ4. ์˜๊ฒฌใ€ ์นธ์— ๊ธ€์„ ์”€. +5. ์™ผ์ชฝ **[์ €์žฅ]** ์„ ๋ˆ„๋ฆ„(ใ€Œ์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.ใ€). +6. ํ•ด์„์„ ๋‹ค์‹œ ํ–ˆ์œผ๋ฉด ๊ฐ€์šด๋ฐ ์œ„ ์ฃผ์˜ ์ƒ‰ ๋  ใ€Œํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ”๋€œ โ€”ใ€ ์˜† **[๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]** ์„ ๋ˆ„๋ฆ„. +7. **[PDF ๋ฏธ๋ฆฌ๋ณด๊ธฐ]** ๋กœ ์ธ์‡„ ๋ชจ์–‘์„ ๋ณด๊ณ , **[์—‘์…€]** ๋˜๋Š” **[PDF]** ๋กœ ๋‚ด๋ ค๋ฐ›์Œ. + +## ๋‹จ์ถ” + +| ์ž๋ฆฌ | ๋‹จ์ถ” | ํ•˜๋Š” ์ผ | +| --- | --- | --- | +| ์™ผ์ชฝ ์œ„ ์ค„ | **[์—‘์…€]** | ์ง€๊ธˆ ํ™”๋ฉด์˜ ๋ณด๊ณ ์„œ๋ฅผ `์•ˆ์ •์„ฑํ‰๊ฐ€_๋ณด๊ณ ์„œ.xlsx` ๋กœ ๋‚ด๋ ค๋ฐ›์Œ ยท ์ €์žฅ์€ ์•ˆ ํ•จ | +| ์™ผ์ชฝ ์œ„ ์ค„ | **[PDF]** | ์ง€๊ธˆ ํ™”๋ฉด์˜ ๋ณด๊ณ ์„œ๋ฅผ `์•ˆ์ •์„ฑํ‰๊ฐ€_๋ณด๊ณ ์„œ.pdf` ๋กœ ๋‚ด๋ ค๋ฐ›์Œ ยท ์ €์žฅ์€ ์•ˆ ํ•จ | +| ์™ผ์ชฝ ์œ„ ์ค„ | **[PDF ๋ฏธ๋ฆฌ๋ณด๊ธฐ]** | PDF ๋ฅผ ํ™”๋ฉด ์•ˆ ์ฐฝ์œผ๋กœ ๋„์›€ ยท **[๋‹ซ๊ธฐ]** ๋กœ ๋‹ซ์Œ | +| ์™ผ์ชฝ ์•„๋ž˜ ์ค„ | **[๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]** | ํ”„๋กœ๊ทธ๋žจ ๋ชซ ์‹œํŠธ ยท ๊ทธ๋ฆผ๋งŒ ์ง€๊ธˆ ํ•ด์„ ๊ฒฐ๊ณผ๋กœ ๋‹ค์‹œ ์”€ ยท ํ‘œ์ง€ ยท ๊ฐœ์š” ยท ์‚ฌ์šฉ์ž๊ฐ€ ๋”ํ•œ ์‹œํŠธ๋Š” ๊ทธ๋Œ€๋กœ | +| ์™ผ์ชฝ ์•„๋ž˜ ์ค„ | **[์ดˆ๊ธฐํ™”]** | ์ฒ˜์Œ ๋งŒ๋“  ๋ณด๊ณ ์„œ๋กœ ๋˜๋Œ๋ฆผ ยท ํ™•์ธ ์ฐฝ ใ€Œ์ฒ˜์Œ ๋งŒ๋“  ๋ณด๊ณ ์„œ๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค. ๊ณ ์นœ ๋‚ด์šฉ ยท ๊ฐฑ์‹ ์ด ๋ชจ๋‘ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.ใ€ | +| ์™ผ์ชฝ ์•„๋ž˜ ์ค„ | **[์ €์žฅ]** | ๊ณ ์นœ ๋ณด๊ณ ์„œ๋ฅผ ์„œ๋ฒ„์— ์ €์žฅ ยท ๊ณ ์นœ ๊ฒƒ์ด ์žˆ์„ ๋•Œ๋งŒ ์ผœ์ง ยท ์ˆ˜์‹์€ ์„œ๋ฒ„๊ฐ€ ๋‹ค์‹œ ํ’‚ | +| ๊ฐ€์šด๋ฐ ์œ„ ๋  | **[๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]** | ์™ผ์ชฝ ๊ฐ™์€ ์ด๋ฆ„ ๋‹จ์ถ”์™€ ๊ฐ™์Œ ยท ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ๋ฐ”๋€ ๋•Œ๋งŒ ๋ ๊ฐ€ ๋œธ | + +- ์ €์žฅ ์•ˆ ํ•œ ๊ณ ์นจ์ด ์žˆ๋Š”๋ฐ [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ์„ ๋ˆ„๋ฅด๋ฉด ใ€Œ์ €์žฅ ์•ˆ ํ•œ ๊ณ ์นจ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ €์žฅํ•œ ๋’ค ๊ฐฑ์‹ ํ• ๊นŒ์š”?ใ€ โ†’ **[์ €์žฅ]** ์„ ๊ณ ๋ฅด๋ฉด ์ €์žฅ ๋’ค ๊ฐฑ์‹ ํ•จ. +- ์‹œํŠธ ํƒญ ์ค„ ๋ **+** = ๋นˆ ์‹œํŠธ ์ถ”๊ฐ€ ยท ํƒญ ์˜ค๋ฅธ์ชฝ ๋ˆ„๋ฆ„ ๋ฉ”๋‰ด = ์ด๋ฆ„ ๋ฐ”๊พธ๊ธฐ ยท ๋ณต์‚ฌ ยท ์ˆจ๊ธฐ๊ธฐ ยท ์‚ญ์ œ. + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +- ์ƒํƒœ ์ค„(์™ผ์ชฝ): ใ€Œ์ €์žฅ ์•ˆ ๋œ ๋ณ€๊ฒฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.ใ€ ยท ใ€Œ๋งŒ๋“œ๋Š” ์ค‘โ€ฆใ€ ยท ใ€Œ๋ณด๊ณ ์„œ ๋งŒ๋“  ๋•Œ ๋‚ ์งœ ์‹œ๊ฐใ€. +- ํŒ์ • ์นธ `O.K` = ์ดˆ๋ก ์ฑ„์›€ ยท `N.G` = ๋นจ๊ฐ• ์ฑ„์›€. +- ๊ฐœ์š”์˜ ์ธก์  ์ˆ˜ ยท ์ตœ์†Œ ์•ˆ์ „์œจ ยท ์ข…ํ•ฉ ํŒ์ • ยท ํŒ์ • ๊ธ€์€ ์ธก์ ํ‘œ ยท ์ž…๋ ฅ์กฐ๊ฑด ์‹œํŠธ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ˆ˜์‹์ด๋ผ ๊ฐฑ์‹ ํ•˜๋ฉด ๊ฐ™์ด ๋ฐ”๋€œ. +- ์‹œํŠธ๋งˆ๋‹ค ์ƒˆ ์ชฝ์—์„œ ์‹œ์ž‘. ๊ธด ํ‘œ(์ธก์ ํ‘œ ๋“ฑ)๋Š” ์ž๋™์œผ๋กœ ์ชฝ์ด ๋‚˜๋‰˜๊ณ  ๋จธ๋ฆฌ ์ค„์ด ์ชฝ๋งˆ๋‹ค ๋˜ํ’€์ด๋จ. ์ชฝ ๋‚˜๋ˆ”์€ ํ™”๋ฉด์— ์ ์„ ์œผ๋กœ ๋ณด์ž„. +- ์‹œํŠธ๋ณ„ ๋‚ด์šฉ์€ ใ€Œ์‹œํŠธ ๊ตฌ์„ฑใ€ ๋ฌธ์„œ ์ฐธ๊ณ . + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- **๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ** โ€” ํ‘œ์ง€ ยท ์ž…๋ ฅ์กฐ๊ฑด ์‹œํŠธ์— ๊ณ ์ • ๊ธ€๋กœ ๋“ค์–ด ์žˆ์Œ. ์ง€๋ฐ˜ ๋ฌผ์„ฑ์น˜๋Š” ๋ฌธํ—Œ ยท ๊ธฐ์ค€๊ฐ’์„ ์ ์šฉํ•œ ์ถ”์ •์น˜์ด๋ฉฐ ํ˜„์žฅ ์กฐ์‚ฌ ยท ์‹œํ—˜ ์„ฑ๊ณผ๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ์•Š์•˜์Œ. ์ž…๋ ฅ์กฐ๊ฑด ๋ฌผ์„ฑ ํ‘œ ใ€Œ์ถœ์ฒ˜ใ€ ์—ด = ๊ธฐ๋ณธ๊ฐ’ ๊ทธ๋Œ€๋กœ๋ฉด ใ€Œ๋ฌธํ—Œ ์ถ”์ •์น˜(๊ธฐ๋ณธ๊ฐ’)ใ€ ยท ๋ฐ”๊ฟจ์œผ๋ฉด ใ€Œ์‚ฌ์šฉ์ž ์ž…๋ ฅใ€. +- **์‚ฌ์šฉ์ž ๋ชซ** = ํ‘œ์ง€ ยท ๊ฐœ์š” โ€” ์ฒ˜์Œ ๋งŒ๋“ค ๋•Œ๋งŒ ์ฑ„์šฐ๊ณ  [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ๋•Œ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์Œ. **ํ”„๋กœ๊ทธ๋žจ ๋ชซ** = ์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D์š”์•ฝ ยท ๊ด‘์—ญ์š”์•ฝ ยท ๋ณด๊ฐ•๊ฒ€ํ†  โ€” ๊ฐฑ์‹  ๋•Œ ํ†ต์งธ๋กœ ๋‹ค์‹œ ์”€. +- ํ”„๋กœ๊ทธ๋žจ ๋ชซ ์‹œํŠธ๋ฅผ ์†์œผ๋กœ ๊ณ ์ณค๋‹ค๋ฉด ๊ฐฑ์‹  ๋•Œ ๋ฎ์ž„. ์ด๋•Œ ใ€Œ๊ณ ์นœ ๋‚ด์šฉ์ด ๋ฎ์ธ ์‹œํŠธ โ€” ์˜› ํŒ์€ _์ง€๋‚œ/ ์— ๋ณด๊ด€: ์‹œํŠธ ์ด๋ฆ„ใ€ ์•Œ๋ฆผ์ด ๋œจ๊ณ  ์˜› ํŒ์€ ์„œ๋ฒ„์— ๋ณด๊ด€๋จ. ์˜ค๋ž˜ ๋‚จ๊ธธ ๊ธ€์€ ๊ฐœ์š” ์˜๊ฒฌ ์นธ์ด๋‚˜ ์ƒˆ ์‹œํŠธ์— ์“ธ ๊ฒƒ. +- ํ”„๋กœ๊ทธ๋žจ ๋ชซ ์‹œํŠธ๋ฅผ ์ง€์› ์–ด๋„ ๊ฐฑ์‹ ํ•˜๋ฉด ๋์— ๋‹ค์‹œ ์ƒ๊น€. ๋ณด๊ฐ•๊ฒ€ํ†  ์‹œํŠธ๋Š” E03 ๋ณด๊ฐ• ๊ฒ€ํ†  ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ ์ƒ๊น€. +- ๊ณ ์นœ ๋‚ด์šฉ์€ [์ €์žฅ] ์ „๊นŒ์ง€ ์ด ๋ธŒ๋ผ์šฐ์ € ํƒญ์—๋งŒ ๋‚จ์Œ(์ž๋™ ์ €์žฅ ์—†์Œ). ํƒญ์„ ๋‹ซ๊ธฐ ์ „ [์ €์žฅ] ํ•  ๊ฒƒ. +- [์—‘์…€] ยท [PDF] ๋Š” ํ™”๋ฉด์— ์ง€๊ธˆ ๋ณด์ด๋Š” ํŒ(์ €์žฅ ์•ˆ ํ•œ ๊ณ ์นจ ํฌํ•จ)์œผ๋กœ ๋งŒ๋“ฆ. ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ๋Š” ์„œ๋ฒ„์— ์ €์žฅ๋œ ํŒ์œผ๋กœ ๋งŒ๋“ฆ. +- ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋จผ์ € ์ €์žฅํ–ˆ์œผ๋ฉด ใ€Œ๋‹ค๋ฅธ ๊ณณ์—์„œ ๋ณด๊ณ ์„œ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค โ€” ๋‹ค์‹œ ์—ฌ์„ธ์š”.ใ€ โ†’ ํ™”๋ฉด์„ ๋‹ค์‹œ ์—ด ๊ฒƒ. +- ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ใ€Œ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.ใ€ ๊ฐ€ ๋œจ๊ณ  [์ €์žฅ] ยท [์ดˆ๊ธฐํ™”] ยท [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ์ด ๊บผ์ง(๋‚ด๋ณด๋‚ด๊ธฐ๋Š” ๋จ). +- ์™ผ์ชฝ ์•ˆ๋‚ด ๊ธ€์€ ์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D์š”์•ฝ๋งŒ ์ ๊ณ  ์žˆ์œผ๋‚˜, ๊ด‘์—ญ์š”์•ฝ ยท ๋ณด๊ฐ•๊ฒ€ํ† ๋„ ๊ฐฑ์‹  ๋•Œ ๋‹ค์‹œ ์”€. +- **ใ€Œ์‚ฌ์šฉ์ž ํ™•์ • ์ „ใ€ ํ‘œ๊ธฐ** โ€” E07 ๊ด‘์—ญ ํ™”๋ฉด์—๋Š” ๋ฟŒ๋ฆฌ ์ ์ฐฉ 1 kPa ยท ์ธก์  ๋“ฑ๊ธ‰ ๋ฌธํ„ฑ 5 % ยท 20 % ๊ฐ€ ใ€Œ์‚ฌ์šฉ์ž ํ™•์ • ์ „ใ€ ์œผ๋กœ ์ ํ˜€ ์žˆ์œผ๋‚˜, ๋ณด๊ณ ์„œ ๊ด‘์—ญ์š”์•ฝ ์‹œํŠธ์—๋Š” ๊ทธ ์ค„์ด ์•„์ง ์—†์Œ(์ค€๋น„ ์ค‘). ๊ฐ’ ์ž์ฒด๋Š” ๊ด‘์—ญ์š”์•ฝ ใ€Œํ† ์ธต ๋ฌผ์„ฑใ€ ์ค„ ยท ใ€Œ์œ„ํ—˜ ์ธก์ ใ€ ์ œ๋ชฉ์— ๋“ค์–ด๊ฐ. diff --git a/resources/help/E05/10_์‹œํŠธ_๊ตฌ์„ฑ.md b/resources/help/E05/10_์‹œํŠธ_๊ตฌ์„ฑ.md new file mode 100644 index 000000000..e49665421 --- /dev/null +++ b/resources/help/E05/10_์‹œํŠธ_๊ตฌ์„ฑ.md @@ -0,0 +1,66 @@ +# ๋ณด๊ณ ์„œ ์‹œํŠธ ๊ตฌ์„ฑ + +| ์ฐจ๋ก€ | ์‹œํŠธ | ๋ชซ | ์šฉ์ง€ | ๋‚ด์šฉ ์ถœ์ฒ˜ | +| --- | --- | --- | --- | --- | +| 1 | ํ‘œ์ง€ | ์‚ฌ์šฉ์ž | A4 ์„ธ๋กœ | ์ฒ˜์Œ ๋งŒ๋“ค ๋•Œ ์‚ฌ์—…๋ช… ยท ๋…ธ์„ ๋ช… ยท ์ž‘์„ฑ์ผ๋งŒ ์ฑ„์›€ | +| 2 | ๊ฐœ์š” | ์‚ฌ์šฉ์ž | A4 ์„ธ๋กœ | ์ˆ˜์‹์ด ์ธก์ ํ‘œ ยท ์ž…๋ ฅ์กฐ๊ฑด์„ ๋”ฐ๋ผ๊ฐ | +| 3 | ์ž…๋ ฅ์กฐ๊ฑด | ํ”„๋กœ๊ทธ๋žจ | A4 ์„ธ๋กœ | E02 ์ง€๋ฐ˜ ์กฐ๊ฑด ยท ํ•ด์„ ๋ฐฉ๋ฒ• | +| 4 | ์ธก์ ํ‘œ | ํ”„๋กœ๊ทธ๋žจ | A4 ๊ฐ€๋กœ | E03 ์ธก์  ํ•ด์„ | +| 5 | ๋Œ€ํ‘œ๋‹จ๋ฉด | ํ”„๋กœ๊ทธ๋žจ | A4 ์„ธ๋กœ | E03 ๋Œ€ํ‘œ ๋‹จ๋ฉด ยท ๊ทธ๋ฆผ | +| 6 | 3D์š”์•ฝ | ํ”„๋กœ๊ทธ๋žจ | A4 ์„ธ๋กœ | E04 3D ํ•ด์„ ยท ํ‰๋ฉด ์œ„ํ—˜ ๋  ๊ทธ๋ฆผ | +| 7 | ๊ด‘์—ญ์š”์•ฝ | ํ”„๋กœ๊ทธ๋žจ | A4 ์„ธ๋กœ | E07 ๊ด‘์—ญ ํ•ด์„ | +| 8 | ๋ณด๊ฐ•๊ฒ€ํ†  | ํ”„๋กœ๊ทธ๋žจ | A4 ์„ธ๋กœ | E03 ๋ณด๊ฐ• ๊ฒ€ํ† (์žˆ์„ ๋•Œ๋งŒ) | + +## 1. ํ‘œ์ง€ + +- ์ œ๋ชฉ ใ€Œ๋น„ํƒˆ๋ฉด ์•ˆ์ •์„ฑ ๊ฒ€ํ†  ๋ณด๊ณ ์„œใ€ ยท ๋ถ€์ œ ใ€Œ์ž„๋„ ๋…ธ์„  ๋น„ํƒˆ๋ฉด ์•ˆ์ •ํ•ด์„ ๊ฒฐ๊ณผใ€. +- ์‚ฌ์—…๋ช… ยท ๋…ธ์„ ๋ช…(์ง€์—ญ ยท ์ž„๋„ ์ข…๋ฅ˜) ยท ์ž‘์„ฑ์ผ = ์ž๋™ ยท ๋ฐœ์ฃผ์ฒ˜ ยท ์ž‘์„ฑ์ž = ์‚ฌ์šฉ์ž๊ฐ€ ์”€. +- ใ€Œ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œใ€ ๊ณผ ์ฃผ์˜ ๊ธ€ ใ€Œโ€ป ์ง€๋ฐ˜ ๋ฌผ์„ฑ์น˜๋Š” ๋ฌธํ—Œ ยท ๊ธฐ์ค€๊ฐ’์„ ์ ์šฉํ•œ ์ถ”์ •์น˜์ด๋ฉฐ ํ˜„์žฅ ์กฐ์‚ฌ ยท ์‹œํ—˜ ์„ฑ๊ณผ๋ฅผ ๋ฐ˜์˜ํ•˜์ง€ ์•Š์•˜์Œ.ใ€ ๊ณ ์ •. + +## 2. ๊ฐœ์š” + +- 1. ํ•ด์„ ๋Œ€์ƒ โ€” ๋Œ€์ƒ ์ธก์  ์ˆ˜(๊ฐœ์†Œ) ยท ํ•ด์„ ๊ตฌ๊ฐ„ ์—ฐ์žฅ(m). +- 2. ์กฐ๊ฑด๋ณ„ ์ตœ์†Œ ์•ˆ์ „์œจ โ€” ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„๋งˆ๋‹ค ์ตœ์†Œ ์•ˆ์ „์œจ ยท ๊ธฐ์ค€ ์•ˆ์ „์œจ ยท ํŒ์ • ยท ์ตœ์†Œ ์•ˆ์ „์œจ ์ธก์ . ์ง€์ง„์„ ์•ˆ ์“ฐ๋ฉด ใ€Œ-ใ€. +- 3. ์ข…ํ•ฉ ํŒ์ • โ€” ๊ธฐ์ค€ ๋ฏธ๋‹ฌ(N.G) ์ธก์  ์ˆ˜ ยท ์ข…ํ•ฉ ํŒ์ • ยท ํŒ์ • ๊ธ€(์˜ˆ: ใ€Œ๋Œ€์ƒ ์ธก์  57๊ฐœ์†Œ ์ค‘ 3๊ฐœ์†Œ๊ฐ€ ๊ธฐ์ค€ ์•ˆ์ „์œจ์— ๋ฏธ๋‹ฌํ•จ. ๋ฏธ๋‹ฌ ๊ตฌ๊ฐ„์€ ๋ณด๊ฐ• ๋Œ€์ฑ… ๊ฒ€ํ† ๊ฐ€ ํ•„์š”ํ•จ.ใ€). +- 4. ์˜๊ฒฌ โ€” ์‚ฌ์šฉ์ž ๊ธ€ ์นธ. + +## 3. ์ž…๋ ฅ์กฐ๊ฑด + +- 1. ํ•ด์„ ๋ฐฉ๋ฒ• โ€” ํ•ด์„ ๊ธฐ๋ฒ•(Bishop ๊ฐ„ํŽธ๋ฒ• ๊ธฐ๋ณธ ยท Janbu ๋ณด์ • ยท Fellenius ๋น„๊ต) ยท ํ™œ๋™๋ฉด ํƒ์ƒ‰(์›ํ˜ธ + ๋น„์›ํ˜ธ ยท ์ขŒ์šฐ ๋น„ํƒˆ ๋”ฐ๋กœ) ยท 3D ํ•ด์„(Hungr 3D Bishop(+Janbu ๋ณด์ •) ยท ๊ตฌ ยท ํƒ€์›์ฒด ํƒ์ƒ‰, 3D ๋ฅผ ์•ˆ ํ–ˆ์œผ๋ฉด ใ€Œ3D ํ•ด์„ ์ „ใ€) ยท ์ ์šฉ ๊ธฐ์ค€(KDS 11 70 05). +- 2. ๊ธฐ์ค€ ์•ˆ์ „์œจ โ€” ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ๊ฐ’ = ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ์ค‘ ์—„ํ•œ ์ชฝ ยท ๋น„๊ณ ์— ๋‘˜ ๋‹ค ์ ์Œ. +- 3. ์ง€ํ•˜์ˆ˜ ๊ทœ์น™ โ€” ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ(ํ† ์‚ฌ ยท ํ’ํ™”์•” / ์•”๋ฐ˜) ยท ์ง€์ง„ ์‹œ. +- 4. ๋…ธ๋ฉด ํ•˜์ค‘(kPa). +- 5. ์ง€๋ฐ˜ ๋ฌผ์„ฑ์น˜ โ€” ์ง€์ธต ยท ์ ์šฉ ๊ตฌ๊ฐ„(์ „์ฒด ยท ๊ตฌ๊ฐ„ ๊ธ€) ยท ฮณt ยท ฮณsat ยท c ยท ฯ† ยท ์ถœ์ฒ˜(ใ€Œ๋ฌธํ—Œ ์ถ”์ •์น˜(๊ธฐ๋ณธ๊ฐ’)ใ€ ๋˜๋Š” ใ€Œ์‚ฌ์šฉ์ž ์ž…๋ ฅใ€). E02 ๊ตฌ๊ฐ„๋ณ„ ๋ฎ์–ด์“ฐ๊ธฐ๋„ ์ค„๋กœ ๋”ํ•ด์ง. + +## 4. ์ธก์ ํ‘œ + +- ์ธก์  ํ•˜๋‚˜ = ํ•œ ์ค„ ยท ์ธก์  ยท ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ยท ๊ตฌ๋ถ„(๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ์ค‘ ๊ธฐ์ค€ ๋Œ€๋น„ ๊ฐ€์žฅ ๋‚ฎ์€ ์ชฝ) ยท ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์•ˆ์ „์œจ ยท ๊ธฐ์ค€ ์•ˆ์ „์œจ ยท ํŒ์ •. +- ํŒ์ • = ๋ชจ๋“  ์กฐ๊ฑด์ด ๊ธฐ์ค€ ์ด์ƒ์ด๋ฉด O.K ยท ํ•˜๋‚˜๋ผ๋„ ๋ฏธ๋‹ฌ์ด๋ฉด N.G. + +## 5. ๋Œ€ํ‘œ๋‹จ๋ฉด + +- ๋‹จ๋ฉด ํ•˜๋‚˜ = ํ•œ ์ชฝ ยท ์ œ๋ชฉ(๋Œ€ํ‘œ ๋‹จ๋ฉด โ‘  โ€” ์ธก์  ยท ๊นŽ๊ธฐ/์Œ“๊ธฐ ยท ๋†’์ด) ยท ์„ ์ • ์‚ฌ์œ  ยท ๋‹จ๋ฉด ๊ทธ๋ฆผ(์ง€๋ฐ˜์„  ยท ์„ค๊ณ„๋ฉด ยท ์•”์„  ยท ๋…ธ๋ฉด ยท ๋น„ํƒˆ ๋†’์ด ยท ์กฐ๊ฑด๋ณ„ ์ž„๊ณ„ ํ™œ๋™๋ฉด). +- ์„ ์ • ์‚ฌ์œ  = ใ€Œ์Œ“๊ธฐ ๋†’์ด 10 m ์ด์ƒใ€ ยท ใ€Œ๊นŽ๊ธฐ ๋†’์ด 15 m ์ด์ƒใ€ ยท ใ€Œ์ตœ์†Œ ์•ˆ์ „์œจ ํ•˜์œ„ 5ใ€ ยท ใ€Œ์‚ฌ์šฉ์ž ์ง€์ •ใ€(E03 ๋Œ€ํ‘œ ๋‹จ๋ฉด ์ผฌ). +- ์กฐ๊ฑด๋ณ„ ํ•ด์„ ๋ฐฉ๋ฒ• ยท ์•ˆ์ „์œจ ยท ๊ธฐ์ค€ ยท ํŒ์ • ยท ํ™œ๋™๋ฉด ๋ฐ˜๊ฒฝ R ยท ์ค‘์‹ฌ x ยท y ยท ๋น„๊ณ (์›ํ˜ธ ยท ๋น„์›ํ˜ธ ยท ์ง€๋ฐ˜ ยท ํ† ์ธต ๋‘๊ป˜). +- ๋Œ€ํ‘œ ๋‹จ๋ฉด์ด ์—†์œผ๋ฉด ใ€Œ๋Œ€ํ‘œ ๋‹จ๋ฉด โ€” ํ•ด๋‹น ์—†์Œใ€. + +## 6. 3D์š”์•ฝ + +- 1. ํ‰๋ฉด ์œ„ํ—˜ ๋  ๊ทธ๋ฆผ โ€” ๋…ธ์„  ยท ์ธก์  2D ํŒ์ • ์ƒ‰ ยท 3D ๊ตฌ๊ฐ„ ๋ (O.K ยท N.G). 3D ๋ฅผ ์•ˆ ํ–ˆ์œผ๋ฉด ใ€Œ3D ํ•ด์„ ์ „ใ€. +- 2. ๊ตฌ๊ฐ„๋ณ„ 3D ์•ˆ์ „์œจ โ€” ๊ตฌ๊ฐ„ ยท ์‹œ์ž‘ ์ธก์  ยท ๋ ์ธก์  ยท 3D ์ตœ์†Œ Fs ยท 2D ์ตœ์†Œ Fs ยท 3D/2D ๋น„(์ˆ˜์‹) ยท ์ž„๊ณ„ ์ฒด์ (mยณ) ยท ํŒ์ •. +- 2D ์ตœ์†Œ Fs = ๊ทธ ๊ตฌ๊ฐ„ ์ธก์ ๋“ค์˜ **๊ฐ™์€ ์กฐ๊ฑด**(3D ๋ฅผ ์…ˆํ•œ ๊ฑด๊ธฐ ๋˜๋Š” ์šฐ๊ธฐ) ์ตœ์†Œ๊ฐ’. ํŒ์ • ยท ๊ธฐ์ค€์€ E04 ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ์”€. +- 3D/2D ๋น„๊ฐ€ 1 ๋ณด๋‹ค ํฌ๋ฉด 2D ๊ฐ€ ์•ˆ์ „ ์ชฝ. + +## 7. ๊ด‘์—ญ์š”์•ฝ + +- 1. ํ•ด์„ ์กฐ๊ฑด โ€” ๋…ธ์„  ๋ (์ค‘์‹ฌ์„  ยฑ m ยท ๊ฒฉ์ž) ยท ํ† ์ธต ๋ฌผ์„ฑ(ฮณt ยท ฮณsat ยท c ยท ฯ† ยท ๋ฟŒ๋ฆฌ ์ ์ฐฉ ยท ํ† ์ธต ๋‘๊ป˜) ยท ์‹œ๋‚˜๋ฆฌ์˜ค(ํ‘œ์ค€ SINMAP ์Šต์œค์ง€์ˆ˜ ๋˜๋Š” ์ตœ์•… ์™„์ „ ํฌํ™”) ยท ํ•ด์„ ๋•Œ. +- 2. ๋“ฑ๊ธ‰๋ณ„ ๋ฉด์  โ€” ์•ˆ์ • ยท ๋ณดํ†ต ์•ˆ์ • ยท ์ค€์•ˆ์ • ยท ์œ„ํ—˜ ํ•˜ํ•œ ยท ์œ„ํ—˜ ์ƒํ•œ ยท ๋ถˆ์•ˆ์ •์˜ SI ๋ฒ”์œ„ ยท ๋ฉด์ (mยฒ) ยท ๋น„์œจ(%) ยท ํ•ฉ๊ณ„, ์˜†์— ์ธก์  ๋“ฑ๊ธ‰(๋‚ฎ์Œ ยท ๋ณดํ†ต ยท ๋†’์Œ)๋ณ„ ์ธก์  ์ˆ˜. +- 3. ์œ„ํ—˜ ์ธก์  โ€” ๋ณดํ†ต ยท ๋†’์Œ ์ธก์ ๋งŒ ํ•œ ์ค„์”ฉ ยท ์ตœ์†Œ Fs ยท ์ตœ์†Œ SI ยท ๋“ฑ๊ธ‰(SI<1 ๋น„์œจ) ยท ์œ„ํ—˜ ๋ฉด์  ยท ์ตœ๋Œ€ ๊ฒฝ์‚ฌ. ์—†์œผ๋ฉด ใ€Œ์œ„ํ—˜ ์ธก์  ์—†์Œใ€. +- E07 ํ•ด์„ ์ „์ด๋ฉด ใ€Œ๊ด‘์—ญ ํ•ด์„ ์ „ โ€” E07 ๊ด‘์—ญ์—์„œ [ํ•ด์„] ๋’ค [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]ใ€. + +## 8. ๋ณด๊ฐ•๊ฒ€ํ† (๊ฐ€์ •) + +- ๋จธ๋ฆฌ ๊ธ€ ใ€ŒE ์•ˆ ๊ฐ€์ • ๊ฒ€ํ†  โ€” ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ์€ ํ˜•์ƒ๋งŒ ๋ฐ”๊ฟ” ๋‹ค์‹œ ํ•ด์„ ยท ์„ค๊ณ„(์ž„๋„)์— ์•ˆ ๋Œ๋ ค ์”€ ยท ๋ฌธํ—Œ ์ถ”์ •์น˜ใ€. +- 1. ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ • โ€” ์ธก์  ยท ๋น„ํƒˆ ยท ๊ฐ€์ •(1:n ์ „ โ†’ ํ›„ ยท ์†Œ๋‹จ ยท ์ตœ์†Œ ์™„ํ™”) ยท ์กฐ๊ฑด ยท ์ „ Fs ยท ํ›„ Fs ยท ๊ธฐ์ค€ ยท ์ „ ํŒ์ • ยท ํ›„ ํŒ์ •. +- 2. ๋ฒฝ ์™ธ์  ์•ˆ์ • โ€” ์ธก์  ยท ๋ฒฝ ยท ํ‰์ƒ์‹œ/์ง€์ง„ ์‹œ ยท ํ† ์••๋ฒ• ยท ๋ฐฐ๋ฉด ์ˆ˜์œ„ ยท ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ Fs์™€ ๊ธฐ์ค€(KDS 11 80 05) ยท ํŒ์ •. +- E03 ๋ณด๊ฐ• ๊ฒ€ํ†  ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ์‹œํŠธ ์ž์ฒด๊ฐ€ ๋น ์ง. diff --git a/resources/help/E06/00_์„ค๋ช…์„œ.md b/resources/help/E06/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..89e38d24a --- /dev/null +++ b/resources/help/E06/00_์„ค๋ช…์„œ.md @@ -0,0 +1,72 @@ +# ์•”๋ฐ˜ ํ•ด์„ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +์•” ์ธก์ (๋ฆฌํ•‘์•” ยท ๋ฐœํŒŒ์•”)์˜ ๊นŽ๊ธฐ ๋น„ํƒˆ์ด ์ ˆ๋ฆฌ(์•”๋ฐ˜ ํ‹ˆ) ๋•Œ๋ฌธ์— ๋ฌด๋„ˆ์งˆ ์ˆ˜ ์žˆ๋Š”์ง€ ๋ด„. +ํ˜„์žฅ์—์„œ ์กฐ์‚ฌํ•œ ์ ˆ๋ฆฌ ๋ฐฉํ–ฅ์„ ๋„ฃ์œผ๋ฉด ์ธก์ ๋งˆ๋‹ค ๋‘ ๊ฐ€์ง€๋ฅผ ์…ˆ. + +- **Markland ํŒ์ •** โ€” ์ ˆ๋ฆฌ ๋ฐฉํ–ฅ๋งŒ ๋ณด๊ณ  ํ‰๋ฉด ยท ์๊ธฐ ยท ์ „๋„ ๊ฐ€์šด๋ฐ ์–ด๋–ค ๋ฌด๋„ˆ์ง์ด ์ƒ๊ธธ ์ˆ˜ ์žˆ๋Š”์ง€ ๊ฐ€๋ฆผ. +- **Hoek-Bray ์•ˆ์ „์œจ** โ€” ํ‰๋ฉด ยท ์๊ธฐ๋กœ ํŒ์ •๋œ ๊ฒƒ๋งŒ ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ(ยท ์ง€์ง„) ์•ˆ์ „์œจ์„ ์…ˆ. ์ „๋„๋Š” ํŒ์ •๋งŒ ํ•จ. + +์‚ฌ๋ฉด ๋ฐฉํ–ฅ ยท ๊ฒฝ์‚ฌ ยท ๋†’์ด๋Š” ๋ณต์‚ฌ๋ณธ ํšก๋‹จ์—์„œ, ๋ฌผ์„ฑ ยท ์ง€์ง„ ยท ๊ธฐ์ค€ ์•ˆ์ „์œจ์€ ์ง€๋ฐ˜ ์กฐ๊ฑด(E02)์—์„œ ๊ฐ€์ ธ์˜ด. + +## ์ž‘์—… ์ˆœ์„œ + +1. ์ขŒ์ธก **์ ˆ๋ฆฌ๊ตฐ** ์นด๋“œ์—์„œ [+ ์ ˆ๋ฆฌ๊ตฐ] ์„ ๋ˆŒ๋Ÿฌ ์ค„์„ ๋Š˜๋ฆผ. +2. ์ค„๋งˆ๋‹ค ์ด๋ฆ„ ยท ๊ตฌ๊ฐ„(์‹œ์ž‘ ยท ๋, m) ยท ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ยท ๊ฒฝ์‚ฌ ยท ฯ†j ยท cj ยท ์ง€์†์„ฑ์„ ๋„ฃ์Œ. ์นธ ๋œป์€ `10_์นธ_๋œป` ์ฐธ๊ณ . +3. ํ•„์š”ํ•˜๋ฉด **ํ•ด์„ ์„ค์ •** ์นด๋“œ(์ธก๋ฐฉ ํ•œ๊ณ„ ยท ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ ยท ์šฐ๊ธฐ ์ˆ˜์œ„ ๋น„ ยท ์ธ์žฅ๊ท ์—ด ยท ๋ณผํŠธ)๋ฅผ ๊ณ ์นจ. +4. [์ €์žฅ] ์„ ๋ˆ„๋ฆ„. ๋นจ๊ฐ„ ์นธ์ด ์žˆ์œผ๋ฉด ์ €์žฅ๋˜์ง€ ์•Š์Œ. +5. [ํ•ด์„] ์„ ๋ˆ„๋ฆ„. ์ €์žฅ ์•ˆ ํ•œ ๊ณ ์นจ์ด ์žˆ์œผ๋ฉด ๋จผ์ € ์ €์žฅํ•œ ๋’ค ์…ˆ. +6. ๊ฐ€์šด๋ฐ **์œ„ํ—˜ ๋ ** ๋‚˜ ์šฐ์ธก **์ธก์  ๊ฒฐ๊ณผ** ํ‘œ์—์„œ ์ธก์ ์„ ๋ˆŒ๋Ÿฌ ์Šคํ…Œ๋ ˆ์˜ค๋„ท ยท ์•ˆ์ „์œจ ํ‘œ๋ฅผ ๋ด„. + +## ๋‹จ์ถ” + +| ๋‹จ์ถ” | ์ž๋ฆฌ | ํ•˜๋Š” ์ผ | +| --- | --- | --- | +| [+ ์ ˆ๋ฆฌ๊ตฐ] | ์ขŒ ยท ์ ˆ๋ฆฌ๊ตฐ ์นด๋“œ | ์ƒˆ ์ค„ ์ถ”๊ฐ€(๊ตฌ๊ฐ„ = 0 m ~ ๋งˆ์ง€๋ง‰ ์•” ์ธก์  ยท ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ 0ยฐ ยท ๊ฒฝ์‚ฌ 45ยฐ ยท ฯ†j 30ยฐ ยท cj 0 kPa) | +| [ร—] | ์ขŒ ยท ์ ˆ๋ฆฌ๊ตฐ ์ค„ ๋ | ๊ทธ ์ค„ ์ง€์›€ | +| [์ดˆ๊ธฐํ™”] | ์ขŒ ์•„๋ž˜ | ํ™•์ธ ์ฐฝ ๋’ค ์ ˆ๋ฆฌ๊ตฐ ยท ์„ค์ •์„ ์ฒ˜์Œ ์ƒํƒœ(์ ˆ๋ฆฌ ์—†์Œ ยท ๊ธฐ๋ณธ ์„ค์ •)๋กœ ๋˜๋Œ๋ฆผ | +| [์ €์žฅ] | ์ขŒ ์•„๋ž˜ | ์ ˆ๋ฆฌ๊ตฐ ยท ์„ค์ •์„ ์ž‘์—…๋ณธ์— ์”€ | +| [ํ•ด์„] | ์ขŒ ์•„๋ž˜ | ์•” ์ธก์  ร— ๊นŽ๊ธฐ ์ชฝ์„ ๋ชจ๋‘ ์…ˆ ยท ์ž…๋ ฅ์ด ์ง€๋‚œ๋ฒˆ๊ณผ ๊ฐ™์œผ๋ฉด ์ง€๋‚œ ๊ฒฐ๊ณผ๋ฅผ ์”€ | +| [๋“ฑ๊ฐ] [๋“ฑ๋ฉด์ ] | ๊ฐ€์šด๋ฐ ์œ„ | ์Šคํ…Œ๋ ˆ์˜ค๋„ท ํˆฌ์˜ ๋ฐ”๊ฟˆ(๋ณด๊ธฐ๋งŒ ยท ์ €์žฅ ์•ˆ ๋จ) | +| [์ „์ฒด] [ํ‰๋ฉด] [์๊ธฐ] [์ „๋„] | ๊ฐ€์šด๋ฐ ์œ„ | ์Šคํ…Œ๋ ˆ์˜ค๋„ท์— ๊ทธ ๋ชจ๋“œ ์œ„ํ—˜ ์˜์—ญ๋งŒ ๋ณด์ž„ | + +- ์นธ์„ ๊ณ ์น˜๊ธฐ๋งŒ ํ•˜๋ฉด ์ด ํƒญ ์•ˆ์—๋งŒ ๋‚จ์Œ(์ž๋™ ์ €์žฅ ์—†์Œ). ใ€Œ์ €์žฅ ์•ˆ ๋œ ๋ณ€๊ฒฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.ใ€๊ฐ€ ๋œจ๋ฉด [์ €์žฅ] ํ•  ๊ฒƒ. +- ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ๋Š” ์นธ ยท ๋‹จ์ถ”๊ฐ€ ์ž ๊น€. + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +**๊ฐ€์šด๋ฐ ์œ„ ยท ์œ„ํ—˜ ๋ ** โ€” ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ์ถ•์— ์ธก์ ๋งˆ๋‹ค ๊ฐ€์žฅ ๋‚˜์œ ํŒ์ • ์ƒ‰. ๋  ์กฐ๊ฐ์„ ๋ˆ„๋ฅด๋ฉด ๊ทธ ์ธก์ ์ด ์—ด๋ฆผ. ์™ผ์ชฝ์— ํŒ์ •๋ณ„ ์ธก์  ์ˆ˜. + +**๊ฐ€์šด๋ฐ ยท ์Šคํ…Œ๋ ˆ์˜ค๋„ท** โ€” ํ•˜๋ฐ˜๊ตฌ ํˆฌ์˜. ๊ทธ๋ฆผ ๋ณด๋Š” ๋ฒ•์€ `20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ` ์˜ ์Šคํ…Œ๋ ˆ์˜ค๋„ท ์ ˆ ์ฐธ๊ณ . + +- ๊ตต์€ ์„  ยท ๋„ค๋ชจ = ์‚ฌ๋ฉด ๋Œ€์› ยท ์‚ฌ๋ฉด ๊ทน์  +- ์  + ์ด๋ฆ„ = ์ ˆ๋ฆฌ ๊ทน์ (ํ‰๋ฉด ยท ์ „๋„ ๋ณด๊ธฐ) ยท ์„ธ๋ชจ + ์ด๋ฆ„ = ๋‘ ์ ˆ๋ฆฌ ๊ต์„ (์๊ธฐ ๋ณด๊ธฐ) +- ํ…Œ๋‘๋ฆฌ๊ฐ€ ๊ฐ•์กฐ๋œ ์  ยท ์„ธ๋ชจ = ์ด ์ธก์ ์—์„œ ์œ„ํ—˜์œผ๋กœ ํŒ์ •๋œ ๊ฒƒ +- ์› = ๋งˆ์ฐฐ์› ฯ† ยท ์ƒ‰ ์˜์—ญ = ํ‰๋ฉด ยท ์๊ธฐ ยท ์ „๋„ ์œ„ํ—˜ ์˜์—ญ ยท ์„  = ์ผ๊ด‘ ์—”๋ฒจ๋กœํ”„(ํ‰๋ฉด) + +**์šฐ์ธก ยท ์ธก์  ๊ฒฐ๊ณผ ํ‘œ** โ€” ์ธก์  ยท ์ชฝ(์ขŒ ยท ์šฐ) ยท ํ‰ ยท ์ ยท ์ „(์œ„ํ—˜ ๋ชจ๋“œ ์ˆ˜) ยท ์ตœ์†Œ Fs ยท ํŒ์ •. + +| ํŒ์ • | ๋œป | +| --- | --- | +| N.G | ์•ˆ์ „์œจ์ด ๊ธฐ์ค€๋ณด๋‹ค ๋‚ฎ์€ ๊ฒฝ์šฐ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์žˆ์Œ | +| ๊ฒ€ํ†  | ์ „๋„ ์œ„ํ—˜์ด ์žˆ์Œ, ๋˜๋Š” ์œ„ํ—˜ ํŒ์ •์€ ๋‚ฌ์œผ๋‚˜ ๋ธ”๋ก์ด ์•ˆ ์ƒ๊ฒจ ์•ˆ์ „์œจ์„ ๋ชป ์…ˆ | +| O.K | ์œ„ํ—˜ ๋ชจ๋“œ๋Š” ์žˆ์œผ๋‚˜ ๋ชจ๋“  ์•ˆ์ „์œจ์ด ๊ธฐ์ค€ ์ด์ƒ | +| ์œ„ํ—˜ ์—†์Œ | Markland ํŒ์ •์—์„œ ์œ„ํ—˜ ๋ชจ๋“œ ์—†์Œ | + +**์šฐ์ธก ์•„๋ž˜ ยท ๊ณ ๋ฅธ ์ธก์ ** โ€” ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ / ๊ฒฝ์‚ฌ ยท ๋†’์ด ยท ์œ—๋ฉด ๊ฒฝ์‚ฌ ยท ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ ํ•œ ์ค„, ๋ชจ๋“œ๋ณ„ ์œ„ํ—˜ ์ ˆ๋ฆฌ, ์•ˆ์ „์œจ ํ‘œ(๋ชจ๋“œ ยท ์ ˆ๋ฆฌ ยท ์กฐ๊ฑด ยท Fs ยท ๊ธฐ์ค€ ยท ํŒ์ •). + +- Fs ์˜† ใ€Œ(๋ธ”๋ก ์•ˆ ์ƒ๊น€)ใ€ = ๊ทธ ์กฐ๊ฑด์—์„œ ๋ฉ์ด๊ฐ€ ๋งŒ๋“ค์–ด์ง€์ง€ ์•Š์Œ. +- Fs ์˜† ใ€Œ(์ง€์†์„ฑ < ํ™œ๋™๋ฉด)ใ€ = ์ ˆ๋ฆฌ ๊ธธ์ด๊ฐ€ ํ™œ๋™๋ฉด๋ณด๋‹ค ์งง์Œ ยท ์‹ค์ œ๋กœ๋Š” ๋ฉ์ด๊ฐ€ ๋œ ์ƒ๊ธธ ์ˆ˜ ์žˆ์Œ. +- ๊ธฐ์ค€ = ์ง€๋ฐ˜ ์กฐ๊ฑด(E02)์˜ ๊นŽ๊ธฐ ๊ธฐ์ค€ ์•ˆ์ „์œจ(๊ธฐ๋ณธ ๊ฑด๊ธฐ 1.5 ยท ์šฐ๊ธฐ 1.2 ยท ์ง€์ง„ 1.1). + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- **๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ.** ์ ˆ๋ฆฌ๋ฉด ฯ†j ยท cj ยท ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ ๊ธฐ๋ณธ๊ฐ’์€ ๋ฌธํ—Œ ๊ฐ’์ž„. ํ˜„์žฅ ์กฐ์‚ฌ ยท ์‹œํ—˜ ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ€ ๊ฒƒ. +- ์ ˆ๋ฆฌ ๋ฐฉํ–ฅ์€ ๋ฐ˜๋“œ์‹œ ํ˜„์žฅ ์กฐ์‚ฌ๊ฐ’์„ ๋„ฃ์„ ๊ฒƒ. ์ ˆ๋ฆฌ๊ฐ€ ์—†์œผ๋ฉด ํ•ด์„ํ•  ๊ฒƒ์ด ์—†์Œ. +- ์ฃผํ–ฅ์œผ๋กœ ์ ์€ ์•ผ์žฅ์€ ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ = ์ฃผํ–ฅ + 90ยฐ(์˜ค๋ฅธ์† ๋ฒ•์น™)๋กœ ๋ฐ”๊ฟ” ๋„ฃ์„ ๊ฒƒ. ์ฃผํ–ฅ ์ž…๋ ฅ ์นธ์€ ์—†์Œ. +- ๋Œ€์ƒ = ์•” ์ธก์ ์˜ ๊นŽ๊ธฐ ์ชฝ ์ค‘ ๋†’์ด 1 m ์ด์ƒ๋งŒ. ํ† ์‚ฌ ์ธก์  ยท ์Œ“๊ธฐ ์ชฝ์€ 2D ํ•ด์„(E03)์—์„œ ๋ด„. +- ์ง€์ง„ ์กฐ๊ฑด์€ ์ง€๋ฐ˜ ์กฐ๊ฑด(E02)์—์„œ ์ง€์ง„์„ ์ผ  ๋•Œ๋งŒ ์…ˆ. +- ์ ˆ๋ฆฌ ยท ์ง€๋ฐ˜ ์กฐ๊ฑด์ด ํ•ด์„ ๋’ค ๋ฐ”๋€Œ๋ฉด ๊ฐ€์šด๋ฐ ์œ„์— ใ€Œ[ํ•ด์„] ์„ ๋‹ค์‹œ ๋ˆ„๋ฅด์„ธ์š”ใ€ ๋ ๊ฐ€ ๋œธ. +- ์ „๋„๋Š” ๊ธฐํ•˜ ํŒ์ •๋งŒ ํ•จ. ๋ธ”๋ก ์ „๋„ ์•ˆ์ „์œจ์€ ๋ณ„๋„ ์ •๋ฐ€ ๊ฒ€ํ†  ๋ชซ. +- ๋ณด๊ณ ์„œ(E05)์— ์•”๋ฐ˜ ๊ฒฐ๊ณผ ์‹œํŠธ๋Š” **์ค€๋น„ ์ค‘**. diff --git a/resources/help/E06/10_์นธ_๋œป.md b/resources/help/E06/10_์นธ_๋œป.md new file mode 100644 index 000000000..24ec84671 --- /dev/null +++ b/resources/help/E06/10_์นธ_๋œป.md @@ -0,0 +1,51 @@ +# ์นธ ๋œป + +๋ฒ”์œ„ ๋ฐ– ยท ๋นˆ ์นธ(ํ•„์ˆ˜)์€ ์นธ์ด ๋นจ๊ฐ›๊ฒŒ ๋˜๊ณ  ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ใ€Œ๋ฒ”์œ„ ๋ฐ– (์•„๋ž˜ ~ ์œ„)ใ€๊ฐ€ ๋ณด์ž„. ๋นจ๊ฐ„ ์นธ์ด ์žˆ์œผ๋ฉด [์ €์žฅ] ยท [ํ•ด์„] ์ด ์•ˆ ๋จ. + +## ์ ˆ๋ฆฌ๊ตฐ ํ‘œ (์ขŒ ยท ์ ˆ๋ฆฌ๊ตฐ ์นด๋“œ) + +ํ•œ ์ค„ = ๋ฐฉํ–ฅ์ด ๋น„์Šทํ•œ ์ ˆ๋ฆฌ ๋ฌด๋ฆฌ ํ•˜๋‚˜. ํ˜„์žฅ ์กฐ์‚ฌ๊ฐ’์„ ๋„ฃ์Œ. + +| ์นธ | ๋œป | ๋‹จ์œ„ | ๋ฒ”์œ„ | ์ƒˆ ์ค„ ๊ธฐ๋ณธ | +| --- | --- | --- | --- | --- | +| ์ด๋ฆ„ | ์ ˆ๋ฆฌ๊ตฐ ์ด๋ฆ„(๊ทธ๋ฆผ ยท ํ‘œ์— ์“ฐ์ž„) | - | ๋น„์šฐ๋ฉด ์•ˆ ๋จ | J1, J2 โ€ฆ | +| ์‹œ์ž‘ ยท ๋ | ์ด ์ ˆ๋ฆฌ๊ตฐ์„ ์“ฐ๋Š” ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ๊ตฌ๊ฐ„ ยท ๊ตฌ๊ฐ„ ์•ˆ ์ธก์ ์—๋งŒ ์”€ | m | 0 ~ 100,000 ยท ๋ โ‰ฅ ์‹œ์ž‘ | 0 ~ ๋งˆ์ง€๋ง‰ ์•” ์ธก์  | +| ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ | ์ ˆ๋ฆฌ๋ฉด์ด ๊ธฐ์šธ์–ด ๋‚ด๋ ค๊ฐ€๋Š” ๋ฐฉ์œ„(๋ถ 0ยฐ ยท ์‹œ๊ณ„ ๋ฐฉํ–ฅ) | ยฐ | 0 ~ 360 | 0 | +| ๊ฒฝ์‚ฌ | ์ ˆ๋ฆฌ๋ฉด์ด ์ˆ˜ํ‰์—์„œ ๊ธฐ์šด ๊ฐ | ยฐ | 0 ~ 90 | 45 | +| ฯ†j | ์ ˆ๋ฆฌ๋ฉด ๋งˆ์ฐฐ๊ฐ | ยฐ | 0 ~ 60 | 30 | +| cj | ์ ˆ๋ฆฌ๋ฉด ์ ์ฐฉ๋ ฅ | kPa | 0 ~ 1,000 | 0 | +| ์ง€์†์„ฑ | ์ ˆ๋ฆฌ๊ฐ€ ์ด์–ด์ง€๋Š” ๊ธธ์ด ยท ๋น„์šฐ๋ฉด ๋์—†์ด ์ด์–ด์ง„๋‹ค๊ณ  ๋ด„ | m | 0 ~ 1,000 | ๋นˆ ์นธ | + +- ํ•œ ์ธก์ ์— ๊ตฌ๊ฐ„์ด ๊ฒน์น˜๋Š” ์ ˆ๋ฆฌ๊ตฐ์ด ์—ฌ๋Ÿฟ์ด๋ฉด ๋ชจ๋‘ ํ•จ๊ป˜ ๋ด„(์๊ธฐ = ๋‘ ์ ˆ๋ฆฌ๊ตฐ ์ง๋งˆ๋‹ค). +- ์ƒˆ ์ค„ ฯ†j 30ยฐ ยท cj 0 kPa = ์ง€์‹DB 07 ์ ˆ๋ฆฌ๋ฉด ๋ฒ”์œ„(ฯ†j 25~35ยฐ ยท cj 0~30 kPa)์˜ ๋ณด์ˆ˜ ์ชฝ. +- ์ง€์†์„ฑ์€ ์•ˆ์ „์œจ์„ ๋ฐ”๊พธ์ง€ ์•Š์Œ. ํ™œ๋™๋ฉด๋ณด๋‹ค ์งง์œผ๋ฉด ๊ฒฐ๊ณผ ํ‘œ์— ใ€Œ(์ง€์†์„ฑ < ํ™œ๋™๋ฉด)ใ€ ํ‘œ์‹œ๋งŒ ๋ถ™์Œ. + +## ํ•ด์„ ์„ค์ • (์ขŒ ยท ํ•ด์„ ์„ค์ • ์นด๋“œ) + +| ์นธ | ๋œป | ๋ฒ”์œ„ | ๊ธฐ๋ณธ | +| --- | --- | --- | --- | +| ํ‰๋ฉด ์ธก๋ฐฉ ํ•œ๊ณ„ (ยฐ) | ์‚ฌ๋ฉด๊ณผ ์ ˆ๋ฆฌ ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ์ฐจ์ด๊ฐ€ ์ด ๊ฐ ์•ˆ์ด๋ฉด ํ‰๋ฉด ๋ฌด๋„ˆ์ง ํ›„๋ณด | 5 ~ 45 | 20 | +| ์ „๋„ ์ธก๋ฐฉ ํ•œ๊ณ„ (ยฐ) | ์ ˆ๋ฆฌ๊ฐ€ ์‚ฌ๋ฉด ๋ฐ˜๋Œ€๋กœ ๊ธฐ์šด ๋ฐฉํ–ฅ ์ฐจ์ด๊ฐ€ ์ด ๊ฐ ์•ˆ์ด๋ฉด ์ „๋„ ํ›„๋ณด | 5 ~ 45 | 20 | +| ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ (kN/mยณ ยท ๋น„์šฐ๋ฉด E02) | ๋ฉ์ด ๋ฌด๊ฒŒ ๊ณ„์‚ฐ ๊ฐ’ ยท ๋น„์šฐ๋ฉด ์ง€๋ฐ˜ ์กฐ๊ฑด(E02)์˜ ๊ทธ ์ธก์  ์•” ๋ฌผ์„ฑ ฮณt ยท ๊ทธ๊ฒƒ๋„ ์—†์œผ๋ฉด 25 | 15 ~ 35 | ๋นˆ ์นธ | +| ์šฐ๊ธฐ ์ˆ˜์œ„ ๋น„ (0~1) | ์šฐ๊ธฐ ์กฐ๊ฑด ๋ฌผ ์ฐจ๋Š” ์ •๋„ ยท 0 = ๋งˆ๋ฆ„ ยท 1 = ๊ฐ€๋“ ์ฐธ | 0 ~ 1 | 0.5 | +| ์ธ์žฅ๊ท ์—ด | ์‚ฌ๋ฉด ์œ—๋ฉด์—์„œ ์•„๋ž˜๋กœ ๊ฐˆ๋ผ์ง„ ํ‹ˆ์„ ๋ณผ์ง€ ยท ์—†์Œ ยท ์ž„๊ณ„(์ตœ์†Œ ์•ˆ์ „์œจ ์ž๋ฆฌ) ยท ๊นŠ์ด ์ง€์ • | - | ์—†์Œ | +| ๊ท ์—ด ๊นŠ์ด (m) | ใ€Œ๊นŠ์ด ์ง€์ •ใ€ ๋•Œ๋งŒ ๋ณด์ž„ ยท ์œ—๋ฉด์—์„œ ์žฐ ๊ท ์—ด ๊นŠ์ด | 0.1 ~ 100 | 1 | +| ๋ณผํŠธ ํž˜ (ํ‰๋ฉด kN/m ยท ์๊ธฐ kN) | ๋ก๋ณผํŠธ ยท ์•ต์ปค๊ฐ€ ์žก์•„ ์ฃผ๋Š” ํž˜ ยท ํ‰๋ฉด์€ ํญ 1 m ๋‹น ยท ์๊ธฐ๋Š” ๋ฉ์ด ํ•˜๋‚˜๋‹น | 0 ~ 5,000 | 0 | +| ๋ณผํŠธ ๊ฒฝ์‚ฌ (์ˆ˜ํ‰ ์•„๋ž˜ ยฐ) | ๋ณผํŠธ๊ฐ€ ์‚ฐ ์ชฝ์œผ๋กœ ๋ฐ•ํžˆ๋Š” ๊ฐ(์ˆ˜ํ‰ ์•„๋ž˜๊ฐ€ +) | -30 ~ 60 | 15 | + +- ์ธก๋ฐฉ ํ•œ๊ณ„ ๊ธฐ๋ณธ 20ยฐ = ์ƒ์šฉ Dips ๊ธฐ๋ณธ ยท ์ง€์‹DB 03 ์€ ์ „๋„์— 30ยฐ ๋ฅผ ์”€ โ€” ํ™”๋ฉด์—์„œ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ. +- ์šฐ๊ธฐ ์ˆ˜์œ„ ๋น„ ๊ธฐ๋ณธ 0.5 = ๋น„ํƒˆ ๋†’์ด ยฝ ์ˆ˜์œ„(KDS 11 70 05 ์•”๋ฐ˜). +- ์ธ์žฅ๊ท ์—ด ใ€Œ์ž„๊ณ„ใ€ = ๊ท ์—ด ์ž๋ฆฌ๋ฅผ ๋งˆ๋ฃจ ๋’ค๋กœ ์˜ฎ๊ฒจ ๊ฐ€๋ฉฐ ์•ˆ์ „์œจ์ด ๊ฐ€์žฅ ๋‚ฎ์€ ์ž๋ฆฌ๋ฅผ ์ฐพ์Œ. ๊ท ์—ด ์—†๋Š” ์ชฝ์ด ๋” ๋‚ฎ์œผ๋ฉด ๊ท ์—ด ์—†์Œ์œผ๋กœ ์…ˆ. ํ‰๋ฉด ๋ฌด๋„ˆ์ง์—๋งŒ ์”€. +- ๋ณผํŠธ = ์ˆ˜๋™ ์ง€์ง€๋ ฅ(๋ฉ์ด๊ฐ€ ์›€์ง์ด๋ฉด ๋ฒ„ํŒ€). 0 ์ด๋ฉด ๋ณด๊ฐ• ์—†์Œ. + +## ๊ฒฐ๊ณผ ์นธ (์šฐ์ธก) + +| ์นธ | ๋œป | +| --- | --- | +| ์ชฝ | ๋…ธ์„  ์ขŒ ยท ์šฐ ๊นŽ๊ธฐ ๋น„ํƒˆ | +| ํ‰ ยท ์ ยท ์ „ | ์œ„ํ—˜์œผ๋กœ ํŒ์ •๋œ ํ‰๋ฉด ์ ˆ๋ฆฌ ์ˆ˜ ยท ์๊ธฐ ์ง ์ˆ˜ ยท ์ „๋„ ์ ˆ๋ฆฌ ์ˆ˜ | +| ์ตœ์†Œ Fs | ๊ทธ ๋น„ํƒˆ ๋ชจ๋“  ๊ฒฝ์šฐ ์ค‘ ๊ฐ€์žฅ ๋‚ฎ์€ ์•ˆ์ „์œจ | +| ์‚ฌ๋ฉด ์ค„ | ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ / ๊ฒฝ์‚ฌ ยท ๋†’์ด ยท ์œ—๋ฉด(๋น„ํƒˆ ๋ ๋ฐ”๊นฅ ์›์ง€๋ฐ˜) ๊ฒฝ์‚ฌ ยท ์“ด ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ | +| ์๊ธฐ (์ถ”ํ–ฅ/์„ ์ฃผ๊ฐ) | ๋‘ ์ ˆ๋ฆฌ ๊ต์„ ์ด ํ–ฅํ•˜๋Š” ๋ฐฉ์œ„ / ์ˆ˜ํ‰ ์•„๋ž˜ ๊ธฐ์šด ๊ฐ | +| ์กฐ๊ฑด | ๊ฑด๊ธฐ(๋ฌผ ์—†์Œ) ยท ์šฐ๊ธฐ(์šฐ๊ธฐ ์ˆ˜์œ„ ๋น„) ยท ์ง€์ง„(E02 kh) | +| ๊ธฐ์ค€ | ์ง€๋ฐ˜ ์กฐ๊ฑด(E02) ๊นŽ๊ธฐ ๊ธฐ์ค€ ์•ˆ์ „์œจ | diff --git a/resources/help/E06/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md b/resources/help/E06/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md new file mode 100644 index 000000000..d9763ab61 --- /dev/null +++ b/resources/help/E06/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md @@ -0,0 +1,72 @@ +# ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐ + +## ๊ทผ๊ฑฐ ๋ฌธ์„œ + +- ์ง€์‹DB `์•ˆ์ •์„ฑํ‰๊ฐ€/03` ์•”๋ฐ˜ ๋น„ํƒˆ๋ฉด ํ‰์‚ฌํˆฌ์˜ ๋ฐ ๋ธ”๋ก ํ•ด์„ ์ˆ˜์‹ ๋ช…์„ธ โ€” Markland ํŒ์ • ยท Hoek-Bray ํ‰๋ฉด ยท ์๊ธฐ ์•ˆ์ „์œจ. +- Hoek & Bray ใ€ŒRock Slope Engineeringใ€ ยท Rocscience Dips ยท RocPlane ยท SWedge ์ด๋ก ์„œ(์‹๋งŒ ๋”ฐ๋ฆ„ ยท ํ”„๋กœ๊ทธ๋žจ์€ ์“ฐ์ง€ ์•Š์Œ). +- KDS 11 70 05 โ€” ์šฐ๊ธฐ ์ˆ˜์œ„(์•”๋ฐ˜ = ๋น„ํƒˆ ๋†’์ด ยฝ). +- ์ง€์‹DB `์•ˆ์ •์„ฑํ‰๊ฐ€/07` โ€” ์•” ๋‹จ์œ„์ค‘๋Ÿ‰(25~27 kN/mยณ) ยท ์ ˆ๋ฆฌ๋ฉด ๊ฐ•๋„ ๋ฒ”์œ„. + +## ๋ฐฉํ–ฅ ์ ๋Š” ๋ฒ• + +- ๋ฉด = ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ / ๊ฒฝ์‚ฌ. ์˜ˆ: 135/60 = ๋‚จ๋™์ชฝ์œผ๋กœ 60ยฐ ๊ธฐ์šด ๋ฉด. +- ์„  = ์ถ”ํ–ฅ / ์„ ์ฃผ๊ฐ. ์ถ”ํ–ฅ = ์„ ์ด ๋‚ด๋ ค๊ฐ€๋Š” ๋ฐฉ์œ„ ยท ์„ ์ฃผ๊ฐ = ์ˆ˜ํ‰ ์•„๋ž˜๋กœ ๊ธฐ์šด ๊ฐ. +- ๊ทน์  = ๋ฉด์— ์ˆ˜์ง์ธ ์„ . ๋ฉด์ด ์™„๋งŒํ• ์ˆ˜๋ก ๊ทน์ ์ด ๊ทธ๋ฆผ ๊ฐ€์šด๋ฐ๋กœ ๊ฐ. +- ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ = ๊ทธ ์ธก์  ๋…ธ์„  ๋ฐฉํ–ฅ์— ์ง๊ฐ์œผ๋กœ, ๋น„ํƒˆ์ด ๋…ธ๋ฉด ์ชฝ์œผ๋กœ ๋‚ด๋ ค๊ฐ€๋Š” ๋ฐฉ์œ„(ํ”„๋กœ๊ทธ๋žจ์ด ์ž๋™์œผ๋กœ ์…ˆ). + +## Markland ํŒ์ • (๋ฌด๋„ˆ์งˆ ์ˆ˜ ์žˆ๋Š” ๋ชจ์–‘์ธ์ง€) + +์ ˆ๋ฆฌ ๋ฐฉํ–ฅ ยท ์‚ฌ๋ฉด ๋ฐฉํ–ฅ ยท ๋งˆ์ฐฐ๊ฐ๋งŒ ๋ด„. ํž˜ ๊ณ„์‚ฐ ์—†์Œ. + +| ๋ชจ๋“œ | ์กฐ๊ฑด(๋ชจ๋‘ ๋งž์œผ๋ฉด ์œ„ํ—˜) | +| --- | --- | +| ํ‰๋ฉด | ์ ˆ๋ฆฌ ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ์ด ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ยฑ ํ‰๋ฉด ์ธก๋ฐฉ ํ•œ๊ณ„ ์•ˆ ยท ฯ†j < ์ ˆ๋ฆฌ ๊ฒฝ์‚ฌ < ๊ทธ ๋ฐฉํ–ฅ์—์„œ ๋ณธ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ | +| ์๊ธฐ | ๋‘ ์ ˆ๋ฆฌ ๊ต์„ ์ด ์‚ฌ๋ฉด ์ชฝ(๋ฐฉ์œ„ ์ฐจ 90ยฐ ์•ˆ)์œผ๋กœ ํ–ฅํ•จ ยท ฯ† < ๊ต์„  ์„ ์ฃผ๊ฐ < ๊ทธ ๋ฐฉํ–ฅ์—์„œ ๋ณธ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ ยท ฯ† = ๋‘ ์ ˆ๋ฆฌ ์ค‘ ์ž‘์€ ฯ†j | +| ์ „๋„ | ์ ˆ๋ฆฌ๊ฐ€ ์‚ฌ๋ฉด ๋ฐ˜๋Œ€์ชฝ์œผ๋กœ ๊ธฐ์›€(๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ์ฐจ๊ฐ€ 180ยฐ ยฑ ์ „๋„ ์ธก๋ฐฉ ํ•œ๊ณ„ ์•ˆ) ยท ์ ˆ๋ฆฌ ๊ฒฝ์‚ฌ > 90ยฐ โˆ’ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ + ฯ†j | + +์‰ฌ์šด ๋ง ํ’€์ด +- ํ‰๋ฉด = ์‚ฌ๋ฉด๊ณผ ๊ฐ™์€ ์ชฝ์œผ๋กœ ๊ธฐ์šด ์ ˆ๋ฆฌ ํ•˜๋‚˜๋ฅผ ๋”ฐ๋ผ ๋ฉ์ด๊ฐ€ ๋ฏธ๋„๋Ÿฌ์ง. ์ ˆ๋ฆฌ๊ฐ€ ์‚ฌ๋ฉด๋ณด๋‹ค ์™„๋งŒํ•ด์•ผ ๋น„ํƒˆ ๋ฉด์— ๋“œ๋Ÿฌ๋‚จ(์ผ๊ด‘). ๋งˆ์ฐฐ๊ฐ๋ณด๋‹ค ๊ฐ€ํŒ”๋ผ์•ผ ๋ฏธ๋„๋Ÿฌ์ง. +- ์๊ธฐ = ๋‘ ์ ˆ๋ฆฌ๊ฐ€ ๋งŒ๋‚˜ ์ƒ๊ธด V ์ž ํ™ˆ์„ ๋”ฐ๋ผ ๋ฉ์ด๊ฐ€ ๋ฏธ๋„๋Ÿฌ์ง. ํŒ์ •์€ ๊ต์„  ํ•˜๋‚˜๋กœ ๋ด„. +- ์ „๋„ = ์‚ฌ๋ฉด ๋ฐ˜๋Œ€๋กœ ๊ฐ€ํŒŒ๋ฅด๊ฒŒ ์„  ์ ˆ๋ฆฌ ์‚ฌ์ด ๊ธฐ๋‘ฅ์ด ์•ž์œผ๋กœ ๋„˜์–ด์ง. +- ใ€Œ๊ทธ ๋ฐฉํ–ฅ์—์„œ ๋ณธ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌใ€(๊ฒ‰๋ณด๊ธฐ ๊ฒฝ์‚ฌ) = ์‚ฌ๋ฉด์„ ๋น„์Šค๋“ฌํžˆ ๋ณผ ๋•Œ ๋œ ๊ฐ€ํŒ”๋ผ ๋ณด์ด๋Š” ๊ฒฝ์‚ฌ. ์ง€์‹DB 03 ์˜ ํ‰๋ฉด ์กฐ๊ฑด์€ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ ๊ทธ๋Œ€๋กœ๋ฅผ ์“ฐ๋‚˜, ์ด ํ™”๋ฉด์€ ๊ฒ‰๋ณด๊ธฐ ๊ฒฝ์‚ฌ๋ฅผ ์”€(์ธก๋ฐฉ ํ•œ๊ณ„ ์•ˆ์—์„œ ์ฐจ์ด๊ฐ€ ์ž‘์Œ). + +## Hoek-Bray ํ‰๋ฉด ์•ˆ์ „์œจ + +์•ˆ์ „์œจ = ๋ฒ„ํ‹ฐ๋Š” ํž˜ รท ๋ฏธ๋Š” ํž˜. +- ๋ฒ„ํ‹ฐ๋Š” ํž˜ = cj ร— ํ™œ๋™๋ฉด ๊ธธ์ด + (๋ฉด์„ ๋ˆ„๋ฅด๋Š” ํž˜) ร— tanฯ†j + ๋ณผํŠธ๊ฐ€ ๋ฒ„ํ‹ฐ๋Š” ๋ชซ +- ๋ฏธ๋Š” ํž˜ = ๋ฉ์ด ๋ฌด๊ฒŒ๊ฐ€ ๋ฉด์„ ๋”ฐ๋ผ ๋ฏธ๋Š” ๋ชซ + ์ˆ˜์•• ยท ์ง€์ง„ ํž˜์ด ๋ฏธ๋Š” ๋ชซ +- ๋ฉ์ด = ๋‹จ์œ„ ํญ(1 m) 2D ๋‹จ๋ฉด ยท ์œ—๋ฉด์€ ๋น„ํƒˆ ๋ ๋ฐ”๊นฅ ์›์ง€๋ฐ˜ ๊ฒฝ์‚ฌ๋กœ ์˜ค๋ฆ„. +- ๋ฌผ ยท ์ธ์žฅ๊ท ์—ด ์—†์Œ = ํ™œ๋™๋ฉด ๊ฐ€์šด๋ฐ ์ˆ˜๋‘๊ฐ€ (ํ™œ๋™๋ฉด ์˜ค๋ฆ„ ๋†’์ด ร— ์ˆ˜์œ„ ๋น„ รท 2)์ธ ์‚ผ๊ฐํ˜• ์–‘์••๋ ฅ. +- ๋ฌผ ยท ์ธ์žฅ๊ท ์—ด ์žˆ์Œ = ๊ท ์—ด ์•ˆ ์ˆ˜์‹ฌ = ๊ท ์—ด ๊นŠ์ด ร— ์ˆ˜์œ„ ๋น„ ยท ๊ท ์—ด ์ˆ˜ํ‰ ์ˆ˜์•• + ํ™œ๋™๋ฉด ์–‘์••๋ ฅ. +- ์ง€์ง„ = ๋ฐ”๊นฅ ์ชฝ ์ˆ˜ํ‰ ํž˜(๋ฌด๊ฒŒ ร— kh). +- ํ™œ๋™๋ฉด์ด ์œ—๋ฉด๊ณผ ์•ˆ ๋งŒ๋‚˜ ๋ฉ์ด๊ฐ€ ์•ˆ ์ƒ๊ธฐ๋ฉด ใ€Œ(๋ธ”๋ก ์•ˆ ์ƒ๊น€)ใ€. + +## Hoek-Bray ์๊ธฐ ์•ˆ์ „์œจ + +- ๋ฉ์ด = ๋‘ ์ ˆ๋ฆฌ ยท ์‚ฌ๋ฉด ยท ์œ—๋ฉด ๋„ค ๋ฉด์ด ๋งŒ๋“œ๋Š” ์‚ผ๊ฐ๋ฟ”. ๋ถ€ํ”ผ ร— ์•” ๋‹จ์œ„์ค‘๋Ÿ‰ = ๋ฌด๊ฒŒ. +- ๋ฌด๊ฒŒ ยท ์ˆ˜์•• ยท ์ง€์ง„ ยท ๋ณผํŠธ ํž˜์„ 3์ฐจ์› ๋ฒกํ„ฐ๋กœ ๋”ํ•œ ๋’ค ๋ฏธ๋„๋Ÿฌ์ง€๋Š” ๊ธธ์„ ๊ณ ๋ฆ„ โ€” ๋‘ ๋ฉด์„ ํ•จ๊ป˜ ํƒ€๊ณ  ๋ฏธ๋„๋Ÿฌ์ง ยท ํ•œ ๋ฉด๋งŒ ํƒ€๊ณ  ๋ฏธ๋„๋Ÿฌ์ง ยท ๋“ค๋ ค ๋–จ์–ด์ง. +- ๊ฐ ๋ฉด ์ˆ˜์ง๋ ฅ์œผ๋กœ ๋งˆ์ฐฐ ์ €ํ•ญ์„ ๊ตฌํ•ด ์•ˆ์ „์œจ = ๋ฒ„ํ‹ฐ๋Š” ํž˜ รท ๋ฏธ๋Š” ํž˜. ๋“ค๋ฆฐ ๋ฉด์€ ์ €ํ•ญ ์—†์Œ. +- ๋ฌผ = ์ˆ˜์œ„ ๋น„๊ฐ€ 1 ์ด๋ฉด ์ง€์‹DB 03 ํ‰๊ท  ์ˆ˜๋‘(ฮณw ร— Hw รท 6) ยท ์ˆ˜์œ„ ๋น„ p ์ด๋ฉด ๊ทธ pยณ ๋ฐฐ. Hw = ๊ต์„ ์ด ์œ—๋ฉด์— ๋‹ฟ๋Š” ๋†’์ด. +- ์ง€์ง„ = ๊ต์„  ๋ฐฉํ–ฅ ์ˆ˜ํ‰ ํž˜. ๋ณผํŠธ = ์‚ฐ ์ชฝ(์‚ฌ๋ฉด ๋ฐ˜๋Œ€ ๋ฐฉ์œ„)์œผ๋กœ ๋ฐ•ํžŒ ์ˆ˜๋™๋ ฅ. +- ์ง€์‹DB 03 ์˜ ๋‹ซํžŒ ๊ผด ์๊ธฐ ์‹ ๋Œ€์‹  ๊ฐ™์€ ์ด๋ก ์˜ ๋ฒกํ„ฐ ํ’€์ด(SWedge ์ด๋ก ์„œ ๊ผด)๋ฅผ ์”€ โ€” ํ•œ ๋ฉด ๋ฏธ๋„๋Ÿฌ์ง ยท ๋“ค๋ฆผ๊นŒ์ง€ ๊ฐ€๋ฆผ. + +## ์Šคํ…Œ๋ ˆ์˜ค๋„ท ์ฝ๋Š” ๋ฒ• + +- ๊ตฌ๋ฅผ ์•„๋ž˜ ๋ฐ˜์ชฝ๋งŒ ๋ณด๊ณ (ํ•˜๋ฐ˜๊ตฌ) ํ‰๋ฉด์— ํŽผ์นœ ๊ทธ๋ฆผ. ์œ„ = ๋ถ ยท ๊ฐ€์žฅ์ž๋ฆฌ = ์ˆ˜ํ‰ ยท ๊ฐ€์šด๋ฐ = ์—ฐ์ง. +- ๋“ฑ๊ฐ = ๊ฐ๋„ ๋ชจ์–‘์ด ๋ฐ”๋ฅด๊ฒŒ ๋ณด์ž„ ยท ๋“ฑ๋ฉด์  = ๋„“์ด ๋น„์œจ์ด ๋ฐ”๋ฅด๊ฒŒ ๋ณด์ž„. ํŒ์ • ๊ฒฐ๊ณผ๋Š” ๊ฐ™์Œ. +- **๋งˆ์ฐฐ์›** = ๋งˆ์ฐฐ๊ฐ ฯ† ์›. ๊ทธ๋ฆผ์˜ ฯ† = ๊ทธ ์ธก์  ์ ˆ๋ฆฌ ์ค‘ ๊ฐ€์žฅ ์ž‘์€ ฯ†j(์ œ๋ชฉ ์ค„์— ํ‘œ์‹œ) ยท ํ‘œ ํŒ์ •์€ ์ ˆ๋ฆฌ๋งˆ๋‹ค ์ œ ฯ†j ๋ฅผ ์”€ โ€” ฯ†j ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅด๋ฉด ๊ทธ๋ฆผ๊ณผ ํ‘œ๊ฐ€ ์กฐ๊ธˆ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Œ. + - ๊ทน์  ๋ณด๊ธฐ(ํ‰๋ฉด ยท ์ „๋„) โ€” ๊ทน์ ์ด ๋งˆ์ฐฐ์› ์•ˆ์ด๋ฉด ์ ˆ๋ฆฌ๊ฐ€ ฯ† ๋ณด๋‹ค ์™„๋งŒํ•ด ๋ฏธ๋„๋Ÿฌ์ง€์ง€ ์•Š์Œ. + - ๊ต์„  ๋ณด๊ธฐ(์๊ธฐ) โ€” ๊ต์„ ์ด ๋งˆ์ฐฐ์› ์•ˆ์ด๋ฉด ฯ† ๋ณด๋‹ค ๊ฐ€ํŒ”๋ผ ๋ฏธ๋„๋Ÿฌ์งˆ ์ˆ˜ ์žˆ์Œ. +- **ํ‰๋ฉด ์œ„ํ—˜ ์˜์—ญ** = ๋งˆ์ฐฐ์› ๋ฐ– ยท ์ผ๊ด‘ ์—”๋ฒจ๋กœํ”„ ์•ˆ ยท ์‚ฌ๋ฉด ๋ฐ˜๋Œ€ํŽธ ์ธก๋ฐฉ ํ•œ๊ณ„ ๋ถ€์ฑ„๊ผด. ๊ทน์ ์ด ์ด ์•ˆ์ด๋ฉด ํ‰๋ฉด ์œ„ํ—˜. +- **์๊ธฐ ์œ„ํ—˜ ์˜์—ญ** = ์‚ฌ๋ฉด ๋Œ€์› ๋ฐ– ยท ๊ต์„  ๋งˆ์ฐฐ์› ์•ˆ ยท ์‚ฌ๋ฉด ์ชฝ ๋ฐ˜์›. ๊ต์„  ์„ธ๋ชจ๊ฐ€ ์ด ์•ˆ์ด๋ฉด ์๊ธฐ ์œ„ํ—˜. +- **์ „๋„ ์œ„ํ—˜ ์˜์—ญ** = ๊ฐ€์žฅ์ž๋ฆฌ ์ชฝ ยท ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ์ธก๋ฐฉ ํ•œ๊ณ„ ๋ถ€์ฑ„๊ผด. ๊ทน์ ์ด ์ด ์•ˆ์ด๋ฉด ์ „๋„ ์œ„ํ—˜. +- ์˜์—ญ ๊ฒฝ๊ณ„๋Š” Markland ์กฐ๊ฑด๊ณผ ๊ฐ™์€ ์‹์œผ๋กœ ๊ทธ๋ฆผ. + +## ํ•œ๊ณ„ + +- ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ. ๊ฒฐ๊ณผ๋Š” ๋„ฃ์€ ์ ˆ๋ฆฌ ๊ฐ’๋งŒํผ๋งŒ ๋งž์Œ. +- 2D ํ‰๋ฉด ยท ๋‹จ์ˆœ ์‚ผ๊ฐ๋ฟ” ์๊ธฐ๋งŒ ๋ด„. ์—ฌ๋Ÿฌ ๋ฉ์ด๊ฐ€ ์ด์–ด์ง„ ๋ฌด๋„ˆ์ง ยท ๋‹จ๊ณ„ ๊นŽ๊ธฐ ์†Œ๋‹จ ยท ์ง€ํ˜• ๊ตด๊ณก์€ ์•ˆ ๋ด„. +- ์‚ฌ๋ฉด ๋†’์ด 1 m ๋ฏธ๋งŒ ๊นŽ๊ธฐ๋Š” ๋บŒ. ์œ—๋ฉด = ๋น„ํƒˆ ๋ ๋ฐ”๊นฅ max(๋†’์ด, 5 m) ์›์ง€๋ฐ˜ ํ‰๊ท  ์˜ค๋ฅด๋ง‰(์‚ฌ๋ฉด ๊ฒฝ์‚ฌ โˆ’ 1ยฐ ๊นŒ์ง€). +- ์ง€์†์„ฑ์€ ํ‘œ์‹œ๋งŒ ํ•˜๊ณ  ์•ˆ์ „์œจ ยท ํŒ์ •์— ์•ˆ ๋„ฃ์Œ. +- ์ „๋„๋Š” ๊ธฐํ•˜ ํŒ์ •๋งŒ ํ•จ(Goodman-Bray ๋ธ”๋ก ์ „๋„ ์•ˆ์ „์œจ ์—†์Œ). +- ํ‰๋ฉด ์šฐ๊ธฐ ๋ฌผ: ์ง€์‹DB 03 ์˜ ใ€Œ๊ท ์—ด ์—†์Œ ยท ยฝ ์ˆ˜์œ„ใ€ ์‹(U = ยผ ฮณw H A)์€ ์ด ํ™”๋ฉด ์ˆ˜์œ„ ๋น„ 1 ๊ณผ ๊ฐ™์€ ๊ผด์ž„. ๊ธฐ๋ณธ 0.5 ๋Š” ๊ทธ ๋ฐ˜ ์ˆ˜๋‘ โ€” ์„ค๊ณ„ ๋ชฉ์ ์— ๋งž๊ฒŒ ์ˆ˜์œ„ ๋น„๋ฅผ ์ •ํ•  ๊ฒƒ. diff --git a/resources/help/E07/00_์„ค๋ช…์„œ.md b/resources/help/E07/00_์„ค๋ช…์„œ.md new file mode 100644 index 000000000..ed45bdf69 --- /dev/null +++ b/resources/help/E07/00_์„ค๋ช…์„œ.md @@ -0,0 +1,76 @@ +# ๊ด‘์—ญ ์œ„ํ—˜ ์„ค๋ช…์„œ + +## ์ด ํ™”๋ฉด์—์„œ ํ•˜๋Š” ์ผ + +๋…ธ์„  ๋ (์ค‘์‹ฌ์„  ์ขŒ์šฐ ์ผ์ • ํญ) ์•ˆ ์ž์—ฐ ๋น„ํƒˆ์˜ ์–•์€ ์‚ฐ์‚ฌํƒœ(ํ‘œ์ธต ๋ถ•๊ดด) ์œ„ํ—˜์„ ์ง€๋„๋กœ ๊ทธ๋ฆผ. +์ง€ํ˜• ๊ฒฉ์ž ์นธ๋งˆ๋‹ค **๋ฌดํ•œ์‚ฌ๋ฉด ์•ˆ์ „์œจ Fs** ์™€ **SINMAP ์•ˆ์ • ์ง€์ˆ˜ SI** ๋ฅผ ์…ˆ โ†’ SI ๋กœ ์—ฌ์„ฏ ๋“ฑ๊ธ‰ ์ƒ‰์น  โ†’ ์ธก์ ๋งˆ๋‹ค ์œ„ํ—˜ ์ •๋„(๋‚ฎ์Œ ยท ๋ณดํ†ต ยท ๋†’์Œ)๋ฅผ ๋งค๊น€. + +- ์ง€ํ˜• = ๋ณต์‚ฌํ•œ ์›์ง€ํ˜•(๋…ธ์„  ๊ณต์‚ฌ ์ „ ๋•…). ๋…ธ์„  ์œ„ ยท ์•„๋ž˜ ์ž์—ฐ ๋น„ํƒˆ์„ ๋ด„. +- ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ ๋น„ํƒˆ ์ž์ฒด๋Š” 2D ํ•ด์„(E03) ยท ์•”๋ฐ˜ ํ•ด์„(E06) ๋ชซ. + +## ์ž‘์—… ์ˆœ์„œ + +1. ์ขŒ์ธก **ํ† ์ธต ๋ฌผ์„ฑ** ์นด๋“œ์—์„œ ๋ฌผ์„ฑ(E02 ์ง€๋ฐ˜ ์กฐ๊ฑด)์„ ๊ณ ๋ฆ„. ๊ณ ๋ฅด๋ฉด ฮณt ยท ฮณsat ยท c ยท ฯ† ์นธ์ด ์ฑ„์›Œ์ง(๊ธฐ๋ณธ = ๋ถ•์ ํ† ). +2. ํ•„์š”ํ•˜๋ฉด ๋ฟŒ๋ฆฌ ์ ์ฐฉ ยท ํ† ์ธต ๋‘๊ป˜๋ฅผ ๊ณ ์นจ. +3. **์‹œ๋‚˜๋ฆฌ์˜ค ยท ํฌํ™”** ์นด๋“œ์—์„œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ๊ณ ๋ฆ„ โ€” ํ‘œ์ค€(SINMAP ์Šต์œค์ง€์ˆ˜) ๋˜๋Š” ์ตœ์•…(์™„์ „ ํฌํ™”). +4. **๋…ธ์„  ๋  ยท ๊ฒฉ์ž ยท ์ธก์  ๋“ฑ๊ธ‰** ์นด๋“œ์—์„œ ๋  ํญ ยท ๊ฒฉ์ž ยท ์ธก์  ๋ฌธํ„ฑ ๋‘˜์„ ํ™•์ธํ•จ. +5. [ํ•ด์„] ์„ ๋ˆ„๋ฆ„. ์ง„ํ–‰ ๋ง‰๋Œ€๊ฐ€ ๋๋‚˜๋ฉด ์ง€๋„ ยท ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์˜ด. +6. ์šฐ์ธก **์ธก์ ๋ณ„ ์œ„ํ—˜** ํ‘œ์—์„œ ์ธก์ ์„ ๋ˆ„๋ฅด๋ฉด ์ง€๋„๊ฐ€ ๊ทธ ์ธก์ ์œผ๋กœ ์˜ฎ๊ฒจ ๊ฐ. + +## ๋‹จ์ถ” + +| ๋‹จ์ถ” | ์ž๋ฆฌ | ํ•˜๋Š” ์ผ | +| --- | --- | --- | +| [๊ธฐ๋ณธ๊ฐ’] | ์ขŒ ์•„๋ž˜ | ๋ชจ๋“  ์นธ์„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋˜๋Œ๋ฆผ(ํ•ด์„์€ ์•ˆ ํ•จ) | +| [๋‹ค์‹œ ํ•ด์„] | ์ขŒ ์•„๋ž˜ | ๊ฐ™์€ ์กฐ๊ฑด์ด๋ผ๋„ ์ฒ˜์Œ๋ถ€ํ„ฐ ๋‹ค์‹œ ์…ˆ | +| [ํ•ด์„] | ์ขŒ ์•„๋ž˜ | ์กฐ๊ฑด์ด ์ง€๋‚œ ๊ฒฐ๊ณผ์™€ ๊ฐ™์œผ๋ฉด ใ€Œ๊ฐ™์€ ์กฐ๊ฑด์˜ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.ใ€ ๋’ค ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์ž„ ยท ๋‹ค๋ฅด๋ฉด ์ƒˆ๋กœ ์…ˆ | +| [๋“ฑ๊ธ‰ (SI)] [์•ˆ์ „์œจ Fs] | ๊ฐ€์šด๋ฐ ์œ„ | ์ง€๋„ ์ƒ‰์„ SI ๋“ฑ๊ธ‰ ยท ์•ˆ์ „์œจ๋กœ ๋ฐ”๊ฟˆ | + +- ์นธ ๊ฐ’์€ ์ด ํƒญ ์•ˆ์—๋งŒ ๊ธฐ์–ต๋จ. ์ €์žฅ๋˜๋Š” ๊ฒƒ์€ [ํ•ด์„] ํ•œ ๊ฒฐ๊ณผ(์กฐ๊ฑด ํฌํ•จ) ํ•˜๋‚˜๋ฟ โ€” ์ƒˆ๋กœ ํ•ด์„ํ•˜๋ฉด ์ง€๋‚œ ๊ฒฐ๊ณผ๋ฅผ ๋ฎ์Œ. +- ๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ๋Š” [ํ•ด์„] ์ด ์ž ๊น€. + +## ๊ฒฐ๊ณผ ์ฝ๋Š” ๋ฒ• + +**๊ฐ€์šด๋ฐ ยท ์ง€๋„** โ€” ํœ  = ํ™•๋Œ€ ยท ๋Œ๊ธฐ = ์˜ฎ๊น€ ยท ๋‘ ๋ฒˆ ๋ˆ„๋ฆ„ = ์ „์ฒด ๋งž์ถค. ์˜…์€ ๋  = ๋…ธ์„  ๋  ยท ๊ตต์€ ์„  = ์ค‘์‹ฌ์„  ยท ์  = ์ธก์  ยท ์˜ค๋ฅธ์ชฝ ์œ„ = ๋ถ ยท ์˜ค๋ฅธ์ชฝ ์•„๋ž˜ = ์ถ•์ฒ™. +์นธ ์œ„์— ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ฆฌ๋ฉด ๋“ฑ๊ธ‰ ยท SI ยท Fs ยท ๊ฒฝ์‚ฌ ยท ํฌํ™”๋น„ m ยท ๊ฐ€๊นŒ์šด ์ธก์ ์ด ๋ณด์ž„. + +**๋ฒ”๋ก€ (๋“ฑ๊ธ‰ SI)** โ€” ๋ฐ‘์— ํ•ด์„ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค ์ด๋ฆ„์ด ๋ถ™์Œ. + +| ์ƒ‰ | ๋“ฑ๊ธ‰ | SI | +| --- | --- | --- | +| ์ง„์ดˆ๋ก | ์•ˆ์ • | 1.5 ์ด์ƒ | +| ์—ฐ๋‘ | ๋ณดํ†ต ์•ˆ์ • | 1.25 ~ 1.5 | +| ์—ฐ๋…ธ๋ž‘ | ์ค€์•ˆ์ • | 1.0 ~ 1.25 | +| ๋…ธ๋ž‘ | ์œ„ํ—˜ ํ•˜ํ•œ | 0.5 ~ 1.0 | +| ์ฃผํ™ฉ | ์œ„ํ—˜ ์ƒํ•œ | 0 ์ดˆ๊ณผ ~ 0.5 | +| ๋นจ๊ฐ• | ๋ถˆ์•ˆ์ • | 0 | + +- ์œ„ํ—˜ = ์œ„ํ—˜ ํ•˜ํ•œ ยท ์œ„ํ—˜ ์ƒํ•œ ยท ๋ถˆ์•ˆ์ •(SI < 1). + +**๋ฒ”๋ก€ (์•ˆ์ „์œจ Fs)** โ€” ๋นจ๊ฐ• 1.0 ๋ฏธ๋งŒ ยท ์ฃผํ™ฉ 1.0 ~ 1.2 ยท ๋…ธ๋ž‘ 1.2 ~ 1.5 ยท ์—ฐ๋‘ 1.5 ~ 2.0 ยท ์ดˆ๋ก 2.0 ์ด์ƒ. ๊ฐ€์žฅ ๋‚˜์œ ์ชฝ(c ยท ฯ† ์•„๋ž˜ ยท ๊ฐ€์žฅ ์ –์Œ) ๊ฐ’์ž„. + +**์šฐ์ธก ยท ๊ด‘์—ญ ๊ฒฐ๊ณผ** + +- ์š”์•ฝ โ€” ์‹œ๋‚˜๋ฆฌ์˜ค ยท ๋  ์•ˆ ์ตœ์†Œ Fs ยท ์œ„ํ—˜ ๋ฉด์ (SI < 1) ยท ์ธก์  ๋“ฑ๊ธ‰(๋‚ฎ์Œ ยท ๋ณดํ†ต ยท ๋†’์Œ ์ˆ˜ / ์ „์ฒด ์ธก์ ) ยท ์ง€ํ˜• ์žˆ๋Š” ๋  / ๋  ๋ฉด์  ยท ํ•ด์„ ๊ฑธ๋ฆฐ ์‹œ๊ฐ„๊ณผ ๋•Œ. +- ๋“ฑ๊ธ‰๋ณ„ ๋ฉด์  โ€” ์—ฌ์„ฏ ๋“ฑ๊ธ‰ ๋ฉด์ (mยฒ) ยท ๋น„์œจ(%). +- ์ธก์ ๋ณ„ ์œ„ํ—˜ โ€” ์ธก์  ยท ์ตœ์†Œ Fs ยท SI<1 ๋น„์œจ(%) ยท ์ธก์  ๋“ฑ๊ธ‰ ยท ์œ„ํ—˜ mยฒ. ๋†’์Œ ์ค„์€ ๊ฐ•์กฐ๋จ. + +**์ธก์  ๋“ฑ๊ธ‰** โ€” ์ธก์  ์•ž๋’ค ยฝ ๊ฐ„๊ฒฉ ร— ๋  ํญ(์˜ํ–ฅ ๊ตฌ์—ญ) ์•ˆ ์นธ ์ค‘ SI < 1 ์นธ ๋น„์œจ. + +| ์ธก์  ๋“ฑ๊ธ‰ | ๊ธฐ๋ณธ ๋ฌธํ„ฑ | +| --- | --- | +| ๋‚ฎ์Œ | 5 % ๋ฏธ๋งŒ | +| ๋ณดํ†ต | 5 % ์ด์ƒ 20 % ๋ฏธ๋งŒ | +| ๋†’์Œ | 20 % ์ด์ƒ | + +๋ฌธํ„ฑ ๋‘˜์€ ์ขŒ์ธก ใ€Œ์ธก์  ๋ณดํ†ต ๋ฌธํ„ฑใ€ ยท ใ€Œ์ธก์  ๋†’์Œ ๋ฌธํ„ฑใ€ ์นธ์—์„œ ๋ฐ”๊ฟˆ. + +**๋ณด๊ณ ์„œ(E05) ๊ด‘์—ญ์š”์•ฝ ์‹œํŠธ** โ€” E05 [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ๋•Œ ์ด ํ™”๋ฉด ์ €์žฅ ๊ฒฐ๊ณผ๋กœ ์ฑ„์›€: ํ•ด์„ ์กฐ๊ฑด(๋  ยท ๋ฌผ์„ฑ ยท ์‹œ๋‚˜๋ฆฌ์˜ค ยท ํ•ด์„ ๋•Œ) ยท ๋“ฑ๊ธ‰๋ณ„ ๋ฉด์  ยท ์ธก์  ๋“ฑ๊ธ‰ ํ‘œ ยท ๋ณดํ†ต ยท ๋†’์Œ ์ธก์  ์ค„(์ธก์  ยท ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ยท ์ตœ์†Œ Fs ยท ์ตœ์†Œ SI ยท ๋“ฑ๊ธ‰๊ณผ ๋น„์œจ ยท ์œ„ํ—˜ ๋ฉด์  ยท ์ตœ๋Œ€ ๊ฒฝ์‚ฌ). ๊ฒฐ๊ณผ๊ฐ€ ์—†์œผ๋ฉด ใ€Œ๊ด‘์—ญ ํ•ด์„ ์ „ใ€. + +## ์•Œ์•„ ๋‘˜ ๊ฒƒ + +- **๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ.** ๋ฌผ์„ฑ ยท T/R ยท ๋ฟŒ๋ฆฌ ์ ์ฐฉ ยท ํ† ์ธต ๋‘๊ป˜ ยท ์ธก์  ๋ฌธํ„ฑ์€ ๊ทผ๊ฑฐ ํ›„๋ณด ๊ฐ’์ž„. ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’์€ `30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’` ์ฐธ๊ณ . +- ์œ„ํ—˜ ์ง€๋„๋Š” ๋„“์€ ๋ฒ”์œ„์—์„œ ใ€Œ์–ด๋””๋ฅผ ๋จผ์ € ๋ณผ์ง€ใ€ ๊ณ ๋ฅด๋Š” ์šฉ๋„์ž„. ์ธก์  ๋‹จ๋ฉด ์•ˆ์ „์œจ(E03)์„ ๋Œ€์‹ ํ•˜์ง€ ์•Š์Œ. +- ์ง€ํ˜• ๊ฒฉ์ž ๋ ๋ฐ– ์ƒ๋ฅ˜๋Š” ์ง‘์ˆ˜๋ฉด์ ์— ์•ˆ ๋“ค์–ด๊ฐ โ€” ๋  ๊ฐ€์žฅ์ž๋ฆฌ๋Š” ์‹ค์ œ๋ณด๋‹ค ๋งˆ๋ฅด๊ฒŒ(์•ˆ์ „ํ•˜๊ฒŒ) ๋‚˜์˜ฌ ์ˆ˜ ์žˆ์Œ. +- ใ€Œ์—”์ง„์ด ๋ฐ”๋€Œ์–ด ๊ฒฐ๊ณผ๊ฐ€ ์˜›๊ฒƒ์ž…๋‹ˆ๋‹คใ€๊ฐ€ ๋œจ๋ฉด [๋‹ค์‹œ ํ•ด์„] ํ•  ๊ฒƒ. +- ์ตœ์•…(์™„์ „ ํฌํ™”)์€ ๋ชจ๋“  ์นธ์ด ๋ฌผ์— ์ฐฌ ๊ทนํ•œ ๊ฐ•์šฐ ๊ฐ€์ • โ€” ๋Œ€๋ถ€๋ถ„ ์ธก์ ์ด ๋†’์Œ์œผ๋กœ ๋‚˜์˜ด. ๋น„๊ต์šฉ์œผ๋กœ ์“ธ ๊ฒƒ. diff --git a/resources/help/E07/10_์นธ_๋œป.md b/resources/help/E07/10_์นธ_๋œป.md new file mode 100644 index 000000000..4f1a8a36a --- /dev/null +++ b/resources/help/E07/10_์นธ_๋œป.md @@ -0,0 +1,56 @@ +# ์นธ ๋œป + +๋ฒ”์œ„ ๋ฐ– ๊ฐ’์œผ๋กœ [ํ•ด์„] ์„ ๋ˆ„๋ฅด๋ฉด ๊นŒ๋‹ญ์ด ์•Œ๋ฆผ์œผ๋กœ ๋œจ๊ณ  ํ•ด์„ํ•˜์ง€ ์•Š์Œ. ใ€Œ์œ„ใ€ ์นธ์€ ใ€Œ์•„๋ž˜ใ€ ์นธ๋ณด๋‹ค ์ž‘์œผ๋ฉด ์•ˆ ๋จ(c ยท ฯ† ยท T/R) ยท ๋†’์Œ ๋ฌธํ„ฑ์€ ๋ณดํ†ต ๋ฌธํ„ฑ๋ณด๋‹ค ์ž‘์œผ๋ฉด ์•ˆ ๋จ. + +## ํ† ์ธต ๋ฌผ์„ฑ (์ขŒ ยท ์ฒซ ์นด๋“œ) + +ํ† ์ธต = ๊ธฐ๋ฐ˜์•” ์œ„์— ๋ฎ์ธ ํ™. ๋ฌดํ•œ์‚ฌ๋ฉด ์‹์ด ์“ฐ๋Š” ๊ฐ’. + +| ์นธ | ๋œป | ๋ฒ”์œ„ | ๊ธฐ๋ณธ | +| --- | --- | --- | --- | +| ๋ฌผ์„ฑ(E02 ์ง€๋ฐ˜ ์กฐ๊ฑด) | ์ง€๋ฐ˜ ์กฐ๊ฑด(E02) ๋ฌผ์„ฑ ๋ชฉ๋ก์—์„œ ๊ณ ๋ฆ„ ยท ๊ณ ๋ฅด๋ฉด ์•„๋ž˜ ์—ฌ์„ฏ ์นธ์„ ์ฑ„์›€(์ด๋ฆ„ํ‘œ์ผ ๋ฟ ยท ๊ฐ’์€ ์•„๋ž˜ ์นธ) | - | ๋ถ•์ ํ†  | +| ฮณt (kN/mยณ) | ์ –์ง€ ์•Š์€ ํ™ ๋‹จ์œ„์ค‘๋Ÿ‰ | 0 ์ดˆ๊ณผ ~ 30 | 17.5 | +| ฮณsat (kN/mยณ) | ๋ฌผ์— ์ฐฌ ํ™ ๋‹จ์œ„์ค‘๋Ÿ‰ | 9.81 ์ดˆ๊ณผ ~ 30 | 18.5 | +| c ์•„๋ž˜ (kPa) | ์ ์ฐฉ๋ ฅ ๋ฒ”์œ„ ์•„๋ž˜ ๋ | 0 ~ 500 | 5 | +| c ์œ„ (kPa) | ์ ์ฐฉ๋ ฅ ๋ฒ”์œ„ ์œ„ ๋(๋ฌผ์„ฑ์„ ๊ณ ๋ฅด๋ฉด ์•„๋ž˜ + 5) | 0 ~ 500 | 10 | +| ฯ† ์•„๋ž˜ (ยฐ) | ๋‚ด๋ถ€ ๋งˆ์ฐฐ๊ฐ ๋ฒ”์œ„ ์•„๋ž˜ ๋ | 0 ~ 60 | 27 | +| ฯ† ์œ„ (ยฐ) | ๋‚ด๋ถ€ ๋งˆ์ฐฐ๊ฐ ๋ฒ”์œ„ ์œ„ ๋(๋ฌผ์„ฑ์„ ๊ณ ๋ฅด๋ฉด ์•„๋ž˜ + 3) | 0 ~ 60 | 30 | +| ๋ฟŒ๋ฆฌ ์ ์ฐฉ (kPa) | ๋‚˜๋ฌด๋ฟŒ๋ฆฌ๊ฐ€ ํ™์„ ๋ถ™์žก๋Š” ํž˜ ยท c ์— ๋”ํ•จ ยท ๋‚˜์ง€ ยท ์ ˆ์„ฑํ† ๋ฉด = 0 | 0 ~ 50 | 1 | +| ํ† ์ธต ๋‘๊ป˜ (m) | ๋ฏธ๋„๋Ÿฌ์งˆ ์ˆ˜ ์žˆ๋Š” ํ™ ๋‘๊ป˜(์ง€ํ‘œ์—์„œ ์ˆ˜์ง) | 0.1 ~ 10 | 1.5 | + +- ๋ฒ”์œ„(์•„๋ž˜ ~ ์œ„)๋Š” SI ๊ณ„์‚ฐ์— ์”€. Fs ๋Š” ์•„๋ž˜ ๋ ๊ฐ’(๋ณด์ˆ˜ ์ชฝ)์œผ๋กœ ์…ˆ. + +## ์‹œ๋‚˜๋ฆฌ์˜ค ยท ํฌํ™” (์ขŒ ยท ๋‘˜์งธ ์นด๋“œ) + +| ์นธ | ๋œป | ๋ฒ”์œ„ | ๊ธฐ๋ณธ | +| --- | --- | --- | --- | +| ์‹œ๋‚˜๋ฆฌ์˜ค | ํ‘œ์ค€ โ€” SINMAP ์Šต์œค์ง€์ˆ˜ (T/R ๋ฒ”์œ„) ยท ์ตœ์•… โ€” ์™„์ „ ํฌํ™” (m = 1 ยท ๊ฐ•์šฐ ๊ทนํ•œ) | - | ํ‘œ์ค€ | +| T/R ์•„๋ž˜ (m ยท ์ –์€ ์ชฝ) | ํˆฌ์ˆ˜๋Ÿ‰๊ณ„์ˆ˜ T รท ์œ ํšจ ๊ฐ•์šฐ R ยท ์ž‘์„์ˆ˜๋ก ๋ฌผ์ด ์ž˜ ๊ณ ์ž„ | 1 ~ 100,000 | 2,000 | +| T/R ์œ„ (m ยท ๋งˆ๋ฅธ ์ชฝ) | ๊ฐ™์€ ๊ฐ’์˜ ๋ฒ”์œ„ ์œ„ ๋ | 1 ~ 100,000 | 3,000 | + +- T/R ๋‘ ์นธ์€ ํ‘œ์ค€์ผ ๋•Œ๋งŒ ๋ณด์ž„. +- ํฌํ™”๋น„ m = ํ† ์ธต ๋‘๊ป˜ ์ค‘ ๋ฌผ์— ์ฐฌ ๋น„์œจ(0 = ๋งˆ๋ฆ„ ยท 1 = ์ง€ํ‘œ๊นŒ์ง€ ์ฐธ). + +## ๋…ธ์„  ๋  ยท ๊ฒฉ์ž ยท ์ธก์  ๋“ฑ๊ธ‰ (์ขŒ ยท ์…‹์งธ ์นด๋“œ) + +| ์นธ | ๋œป | ๋ฒ”์œ„ | ๊ธฐ๋ณธ | +| --- | --- | --- | --- | +| ๋  ํญ ยฑ (m) | ์ค‘์‹ฌ์„ ์—์„œ ์ขŒ์šฐ๋กœ ๋ณผ ๊ฑฐ๋ฆฌ ยท ์ง€๋„ ์˜…์€ ๋ ๊ฐ€ ๋ฐ”๋กœ ๋ฐ”๋€œ | 10 ~ 500 | 100 | +| ๊ฒฉ์ž (m) | ๊ณ„์‚ฐ ์นธ ํ•œ ๋ณ€ ยท ์ž‘์„์ˆ˜๋ก ์ด˜์ด˜ํ•˜๊ณ  ์˜ค๋ž˜ ๊ฑธ๋ฆผ | 1 ~ 10 | 2 | +| ์ธก์  ๋ณดํ†ต ๋ฌธํ„ฑ (SI<1 ๋น„์œจ %) | ์˜ํ–ฅ ๊ตฌ์—ญ ์•ˆ SI < 1 ์นธ ๋น„์œจ์ด ์ด ๊ฐ’ ์ด์ƒ์ด๋ฉด ๋ณดํ†ต | 0 ์ดˆ๊ณผ ~ 100 | 5 | +| ์ธก์  ๋†’์Œ ๋ฌธํ„ฑ (SI<1 ๋น„์œจ %) | ์ด ๊ฐ’ ์ด์ƒ์ด๋ฉด ๋†’์Œ | 0 ์ดˆ๊ณผ ~ 100 | 20 | + +## ๊ฒฐ๊ณผ ์นธ (์šฐ์ธก) + +| ์นธ | ๋œป | +| --- | --- | +| ๋  ์•ˆ ์ตœ์†Œ Fs | ๋  ์•ˆ ๋ชจ๋“  ์นธ ์ค‘ ๊ฐ€์žฅ ๋‚ฎ์€ ์•ˆ์ „์œจ | +| ์œ„ํ—˜ ๋ฉด์  (SI < 1) | ์œ„ํ—˜ ํ•˜ํ•œ ยท ์œ„ํ—˜ ์ƒํ•œ ยท ๋ถˆ์•ˆ์ • ๋“ฑ๊ธ‰ ๋ฉด์  ํ•ฉ | +| ์ง€ํ˜• ์žˆ๋Š” ๋  / ๋  | ์ง€ํ˜• ์ž๋ฃŒ๊ฐ€ ์žˆ๋Š” ๋  ๋ฉด์  / ๋  ์ „์ฒด ๋ฉด์  ยท ์ฐจ์ด = ์ง€ํ˜• ๋ฐ–์ด๋ผ ๋ชป ๋ณธ ๊ณณ | +| ์ตœ์†Œ Fs (์ธก์  ํ‘œ) | ๊ทธ ์ธก์  ์˜ํ–ฅ ๊ตฌ์—ญ ์•ˆ ๊ฐ€์žฅ ๋‚ฎ์€ ์•ˆ์ „์œจ | +| SI<1 ๋น„์œจ (%) | ์˜ํ–ฅ ๊ตฌ์—ญ ์นธ ์ค‘ SI < 1 ์นธ ๋น„์œจ | +| ์œ„ํ—˜ mยฒ | ์˜ํ–ฅ ๊ตฌ์—ญ ์•ˆ SI < 1 ๋ฉด์  | +| ํˆดํŒ m | ๊ทธ ์นธ ํฌํ™”๋น„(๊ฐ€์žฅ ์ –์€ ์ชฝ) ยท ์ตœ์•… ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” 1 | + +- ์˜ํ–ฅ ๊ตฌ์—ญ = ๋  ์•ˆ ์นธ์„ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ค‘์‹ฌ์„  ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ๋กœ ๋‚˜๋ˆˆ ๋’ค, ์ด์›ƒ ์ธก์ ๊ณผ ๊ฐ€์šด๋ฐ์—์„œ ๋Š์€ ๋ชซ(์ธก์  ์•ž๋’ค ยฝ ๊ฐ„๊ฒฉ ร— ๋  ํญ). +- ์˜ํ–ฅ ๊ตฌ์—ญ์— ์ง€ํ˜•์ด ์—†๋Š” ์ธก์ ์€ ๊ฐ’์ด ใ€Œ-ใ€. diff --git a/resources/help/E07/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md b/resources/help/E07/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md new file mode 100644 index 000000000..a3af805f3 --- /dev/null +++ b/resources/help/E07/20_์“ฐ์ธ_๋ฐฉ๋ฒ•๊ณผ_๊ทผ๊ฑฐ.md @@ -0,0 +1,69 @@ +# ์“ฐ์ธ ๋ฐฉ๋ฒ•๊ณผ ๊ทผ๊ฑฐ + +## ๊ทผ๊ฑฐ ๋ฌธ์„œ + +- ์ง€์‹DB `์˜ˆ์ •๋…ธ์„ /02` ยง1.1 ๋ฌดํ•œ์‚ฌ๋ฉด ์•ˆ์ „์œจ ์‹(ํฌํ™”๋น„ ยท ๋ฟŒ๋ฆฌ ์ ์ฐฉ ํฌํ•จ) ยท ยง2 โ‘  ์ง€ํ˜•์Šต์œค์ง€์ˆ˜ + ์ •์ƒ๋ฅ˜ ๋ฌดํ•œ์‚ฌ๋ฉด(SINMAP ยท SHALSTAB). +- Pack ์™ธ(1998) SINMAP ์•ˆ์ • ์ง€์ˆ˜ ๋ฐฉ๋ฒ• ยท SINMAP 2 ์„ค๋ช…์„œ ๊ธฐ๋ณธ ๋ณด์ •(T/R 2,000 ~ 3,000 m) โ€” ์‹๋งŒ ๋”ฐ๋ฆ„ ยท ์› ํ”„๋กœ๊ทธ๋žจ์€ ์“ฐ์ง€ ์•Š์Œ. +- ์ง€์‹DB `์•ˆ์ •์„ฑํ‰๊ฐ€/04` ยท `07` โ€” ๋ถ•์ ํ†  ๋ฌผ์„ฑ ๋ฒ”์œ„(์ง€๋ฐ˜ ์กฐ๊ฑด E02 ๊ธฐ๋ณธ๊ฐ’์˜ ์ถœ์ฒ˜). +- ๊ตญ๋‚ด ์ ์šฉ ์—ฐ๊ตฌ โ€” ๋ฟŒ๋ฆฌ ์ ์ฐฉ(์‚ฐ๋ฆผ 0 ~ 5 kPa ยท ์šฐ๋ฉด์‚ฐ 1 kPa). + +## ๊ณ„์‚ฐ ์ฐจ๋ก€ + +1. ์›์ง€ํ˜• ๊ฒฉ์ž๋ฅผ ใ€Œ๊ฒฉ์žใ€ ๊ฐ„๊ฒฉ์œผ๋กœ ๋‹ค์‹œ ๊ณ ๋ฆ„. +2. ์นธ๋งˆ๋‹ค ๊ฒฝ์‚ฌ ฮฒ โ€” ์ด์›ƒ 3 ร— 3 ์นธ์œผ๋กœ ๊ตฌํ•จ(Horn ๋ฐฉ๋ฒ•). +3. ์นธ๋งˆ๋‹ค ๋‹จ์œ„ ์ง‘์ˆ˜๋ฉด์  a โ€” ์›…๋ฉ์ด๋ฅผ ์ฑ„์šด ๋’ค ๋ฌผ์ด ๊ฐ€์žฅ ๊ฐ€ํŒŒ๋ฅธ ์ด์›ƒ์œผ๋กœ ํ๋ฅธ๋‹ค๊ณ  ๋ณด๊ณ (D8) ์œ„์ชฝ์—์„œ ๋ชจ์—ฌ๋“œ๋Š” ์นธ ์ˆ˜ ร— ์นธ ํฌ๊ธฐ. ๋ฐฐ์ˆ˜์œ ์—ญ(B04)๊ณผ ๊ฐ™์€ ํ๋ฆ„ ๊ณ„์‚ฐ. +4. ์นธ๋งˆ๋‹ค ํฌํ™”๋น„ m(์‹œ๋‚˜๋ฆฌ์˜ค๋ณ„) โ†’ ์•ˆ์ „์œจ Fs ยท ์•ˆ์ • ์ง€์ˆ˜ SI โ†’ ๋“ฑ๊ธ‰. +5. ๋…ธ์„  ๋  ์•ˆ ์นธ๋งŒ ๋‚จ๊ฒจ ๋“ฑ๊ธ‰๋ณ„ ๋ฉด์  ยท ์ธก์  ํ‘œ๋ฅผ ๋งŒ๋“ฆ. + +## ํฌํ™”๋น„ m (์‹œ๋‚˜๋ฆฌ์˜ค) + +- **ํ‘œ์ค€ โ€” SINMAP ์Šต์œค์ง€์ˆ˜**: m = (R รท T) ร— a รท sinฮฒ, ๋‹จ 1 ์„ ๋„˜์œผ๋ฉด 1. + - ์œ„์ชฝ ๋ชจ์ด๋Š” ๋„“์ด a ๊ฐ€ ํฌ๊ณ  ๊ฒฝ์‚ฌ๊ฐ€ ์™„๋งŒํ• ์ˆ˜๋ก ์ž˜ ์ –์Œ(๊ณจ์งœ๊ธฐ ยท ๋น„ํƒˆ ์•„๋ž˜). + - T/R ๋ฒ”์œ„ ์•ˆ์—์„œ ๋ฐ”๋€œ ยท Fs ์ง€๋„๋Š” ๊ฐ€์žฅ ์ –์€ ์ชฝ(T/R ์•„๋ž˜)์œผ๋กœ ์…ˆ. +- **์ตœ์•… โ€” ์™„์ „ ํฌํ™”**: ๋ชจ๋“  ์นธ m = 1(์ง€ํ‘œ๊นŒ์ง€ ๋ฌผ์— ์ฐธ). ๊ทนํ•œ ๊ฐ•์šฐ ๋น„๊ต์šฉ. + +## ๋ฌดํ•œ์‚ฌ๋ฉด ์•ˆ์ „์œจ Fs + +๋น„ํƒˆ๊ณผ ๋‚˜๋ž€ํ•œ ์–‡์€ ํ™์ธต์ด ํ†ต์งธ๋กœ ๋ฏธ๋„๋Ÿฌ์ง„๋‹ค๊ณ  ๋ณด๋Š” ์‹. + +์•ˆ์ „์œจ = ๋ฒ„ํ‹ฐ๋Š” ํž˜ รท ๋ฏธ๋Š” ํž˜ +- ๋ฒ„ํ‹ฐ๋Š” ํž˜ = (c + ๋ฟŒ๋ฆฌ ์ ์ฐฉ) + [ฮณt ร— z ร— (1 โˆ’ m) + (ฮณsat โˆ’ ฮณw) ร— m ร— z] ร— cosยฒฮฒ ร— tanฯ† +- ๋ฏธ๋Š” ํž˜ = [ฮณt ร— z ร— (1 โˆ’ m) + ฮณsat ร— m ร— z] ร— sinฮฒ ร— cosฮฒ +- z = ํ† ์ธต ๋‘๊ป˜ ยท ฮณw = ๋ฌผ ๋‹จ์œ„์ค‘๋Ÿ‰ 9.81 kN/mยณ. +- ๋ฌผ์ด ์ฐจ๋ฉด(m ์ด ์ปค์ง€๋ฉด) ํ™์„ ๋ˆ„๋ฅด๋Š” ํž˜์ด ์ค„์–ด ๋งˆ์ฐฐ์ด ์•ฝํ•ด์ง โ†’ Fs ๊ฐ€ ๋‚ฎ์•„์ง. +- ๊ฑฐ์˜ ํ‰ํ‰ํ•œ ์นธ์€ ๋ฏธ๋„๋Ÿฌ์ง€์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋ด„(Fs ยท SI ๋ฌดํ•œ๋Œ€ = ์•ˆ์ •). + +## ์•ˆ์ • ์ง€์ˆ˜ SI + +c ยท ฯ† ยท T/R ์„ ์ •ํ™•ํžˆ ๋ชจ๋ฅด๋‹ˆ ๋ฒ”์œ„(์•„๋ž˜ ~ ์œ„) ์•ˆ์—์„œ ๊ณ ๋ฅด๊ฒŒ ํผ์ ธ ์žˆ๋‹ค๊ณ  ๋ณด๊ณ  ์…ˆ. + +- ๊ฐ€์žฅ ๋‚˜์œ ์กฐํ•ฉ(c ยท ฯ† ์•„๋ž˜ ยท ๊ฐ€์žฅ ์ –์Œ)์—์„œ๋„ Fs โ‰ฅ 1 โ†’ SI = ๊ทธ Fs(1 ์ด์ƒ ยท ํด์ˆ˜๋ก ์•ˆ์ •). +- ๊ฐ€์žฅ ์ข‹์€ ์กฐํ•ฉ(c ยท ฯ† ์œ„ ยท ๊ฐ€์žฅ ๋งˆ๋ฆ„)์—์„œ๋„ Fs < 1 โ†’ SI = 0(๋ถˆ์•ˆ์ •). +- ๊ทธ ์‚ฌ์ด โ†’ SI = Fs > 1 ์ผ ํ™•๋ฅ (0 ~ 1). ์˜ˆ: SI 0.7 = ๋ฒ”์œ„ ์•ˆ ์กฐํ•ฉ์˜ 70 % ๊ฐ€ ๋ฒ„ํŒ€. +- ์ตœ์•… ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” m = 1 ๊ณ ์ •์ด๋ผ c ยท ฯ† ๋ฒ”์œ„๋งŒ ๋ด„. + +| ๋“ฑ๊ธ‰ | SI | ์‰ฌ์šด ๋ง | +| --- | --- | --- | +| ์•ˆ์ • | 1.5 ์ด์ƒ | ๋‚˜์œ ์กฐํ•ฉ์—์„œ๋„ ๋„‰๋„‰ํžˆ ๋ฒ„ํŒ€ | +| ๋ณดํ†ต ์•ˆ์ • | 1.25 ~ 1.5 | ๋‚˜์œ ์กฐํ•ฉ์—์„œ๋„ ๋ฒ„ํŒ€ | +| ์ค€์•ˆ์ • | 1.0 ~ 1.25 | ๋‚˜์œ ์กฐํ•ฉ์—์„œ ๊ฒจ์šฐ ๋ฒ„ํŒ€ | +| ์œ„ํ—˜ ํ•˜ํ•œ | 0.5 ~ 1.0 | ์กฐํ•ฉ ์ ˆ๋ฐ˜ ์ด์ƒ์€ ๋ฒ„ํŒ€ | +| ์œ„ํ—˜ ์ƒํ•œ | 0 ์ดˆ๊ณผ ~ 0.5 | ์กฐํ•ฉ ์ ˆ๋ฐ˜ ๋„˜๊ฒŒ ๋ฌด๋„ˆ์ง | +| ๋ถˆ์•ˆ์ • | 0 | ์ข‹์€ ์กฐํ•ฉ์—์„œ๋„ ๋ฌด๋„ˆ์ง | + +๋“ฑ๊ธ‰ ๊ฒฝ๊ณ„ = SINMAP ์› ๋ฐฉ๋ฒ• ๊ทธ๋Œ€๋กœ. + +## ์ธก์  ๋“ฑ๊ธ‰ + +- ์˜ํ–ฅ ๊ตฌ์—ญ = ์ธก์  ์•ž๋’ค ยฝ ๊ฐ„๊ฒฉ ร— ๋  ํญ(๋  ์นธ์„ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ค‘์‹ฌ์„  ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ๋กœ ๋‚˜๋ˆ”). +- ์˜ํ–ฅ ๊ตฌ์—ญ ์นธ ์ค‘ SI < 1(์œ„ํ—˜ ํ•˜ํ•œ ์ด์ƒ) ์นธ ๋น„์œจ๋กœ ๋‚ฎ์Œ ยท ๋ณดํ†ต ยท ๋†’์Œ(๋ฌธํ„ฑ ๊ธฐ๋ณธ 5 % ยท 20 %). +- ํ•œ ์นธ์ด๋ผ๋„ ์œ„ํ—˜์ด๋ฉด ์œ„ํ—˜์œผ๋กœ ์žก๋˜ ๋ฐฉ์‹์€ ๊ฑฐ์˜ ๋ชจ๋“  ์ธก์ ์ด ์œ„ํ—˜์œผ๋กœ ๋‚˜์™€ ๋ณ€๋ณ„๋ ฅ์ด ์—†์–ด ๋น„์œจ ๋ฐฉ์‹์„ ์”€. + +## ํ•œ๊ณ„ + +- ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ. ํ† ์ธต ๋‘๊ป˜ ยท ๋ฌผ์„ฑ ยท T/R ์ด ๋  ์ „์ฒด์— ํ•œ ๊ฐ’์ž„(์นธ๋งˆ๋‹ค ๋‹ค๋ฅด์ง€ ์•Š์Œ). +- ์–•์€ ํ‰ํ–‰ ๋ฏธ๋„๋Ÿฌ์ง๋งŒ ๋ด„. ๊นŠ์€ ์›ํ˜ธ ํ™œ๋™ ยท ์•”๋ฐ˜ ๋ฌด๋„ˆ์ง ยท ํ† ์„๋ฅ˜ ํ˜๋Ÿฌ๊ฐ์€ ์•ˆ ๋ด„. +- ์ง€ํ˜• = ์›์ง€ํ˜•. ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ๋กœ ๋ฐ”๋€ ๋•…์€ ๋ฐ˜์˜ ์•ˆ ๋จ. +- ์ง€ํ˜• ๊ฒฉ์ž ๋ ๋ฐ– ์ƒ๋ฅ˜๋Š” ๋ชจ๋ฆ„ โ€” ๋  ๊ฐ€์žฅ์ž๋ฆฌ ์นธ์€ ์ง‘์ˆ˜๋ฉด์ ์ด ์ž‘๊ฒŒ ๋‚˜์™€ ์‹ค์ œ๋ณด๋‹ค ๋งˆ๋ฅด๊ฒŒ ์…ˆ. +- ๊ฐ•์šฐ๋Ÿ‰(mm) ์ง์ ‘ ์ž…๋ ฅ ์—†์Œ โ€” ์ –๋Š” ์ •๋„๋Š” T/R ๋ฒ”์œ„๋กœ๋งŒ ๋„ฃ์Œ. +- ์‚ฐ๋ฆผ์ฒญ ์œ„ํ—˜์ง€๋„ ๋“ฑ๊ธ‰ ๋น„์œจ ๊ณต๊ฐœ ์ž๋ฃŒ๊ฐ€ ์—†์–ด ๊ทธ์™€ ๋Œ€์กฐํ•˜์ง€ ๋ชปํ•จ. ์‹œํ—˜ ํ”„๋กœ์ ํŠธ ์œ„ํ—˜(SI < 1) ๋น„์œจ 11.1 % ๊ฐ€ ๊ตญ๋‚ด SINMAP ์ ์šฉ ์‚ฌ๋ก€(์•ฝ 7.5 ~ 16 %) ๋ฒ”์œ„ ์•ˆ์ž„๋งŒ ํ™•์ธํ•จ. diff --git a/resources/help/E07/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md b/resources/help/E07/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md new file mode 100644 index 000000000..df1a785b9 --- /dev/null +++ b/resources/help/E07/30_์‚ฌ์šฉ์ž_ํ™•์ •_์ „_๊ฐ’.md @@ -0,0 +1,20 @@ +# ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ๊ฐ’ + +์•„๋ž˜ ๊ฐ’์€ ๋ฌธํ—Œ ยท ์—ฐ๊ตฌ์—์„œ ๊ณ ๋ฅธ ๊ทผ๊ฑฐ ํ›„๋ณด์ž„. ํ™”๋ฉด์—์„œ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ๊ณ , ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๊ตณํž์ง€๋Š” ์‚ฌ์šฉ์ž ํ˜‘์˜๋กœ ์ •ํ•จ. ๋ฐ”๊พธ๋ฉด ๊ทธ ๊ฐ’์œผ๋กœ [ํ•ด์„] ํ•˜๋ฉด ๋จ โ€” ๊ธฐ๋ณธ๊ฐ’ ์ž์ฒด๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์Œ. + +| ๊ฐ’ | ์ง€๊ธˆ ๊ธฐ๋ณธ๊ฐ’ | ์ถœ์ฒ˜ | ์ƒํƒœ | +| --- | --- | --- | --- | +| ๋ฟŒ๋ฆฌ ์ ์ฐฉ | 1 kPa | ๊ตญ๋‚ด ์ ์šฉ ์—ฐ๊ตฌ โ€” ์‚ฐ๋ฆผ 0 ~ 5 kPa ยท ์šฐ๋ฉด์‚ฐ 1 kPa ยท ๋‚˜์ง€ ยท ์ ˆ์„ฑํ† ๋ฉด 0 | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ์ธก์  ๋ณดํ†ต ๋ฌธํ„ฑ | SI<1 ๋น„์œจ 5 % | ๊ฐœ๋ฐœ ์ชฝ์—์„œ ์ •ํ•จ ยท ์‹œํ—˜ ํ”„๋กœ์ ํŠธ ์ธก์  SI<1 ๋น„์œจ ๋ถ„ํฌ(์ค‘์•™ ์•ฝ 10 %)๋ฅผ ๋ณด๊ณ  ๊ณ ๋ฆ„ | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ์ธก์  ๋†’์Œ ๋ฌธํ„ฑ | SI<1 ๋น„์œจ 20 % | ์œ„์™€ ๊ฐ™์Œ | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ํ† ์ธต ๋ฌผ์„ฑ | E02 ๋ถ•์ ํ†  โ€” ฮณt 17.5 ยท ฮณsat 18.5 kN/mยณ ยท c 5 kPa ยท ฯ† 27ยฐ | ์ง€์‹DB `์•ˆ์ •์„ฑํ‰๊ฐ€/07` ๋ถ•์ ํ† (ฮณt 17 ~ 18 ยท c 0 ~ 10 ยท ฯ† 25 ~ 28) ยท E02 ๊ธฐ๋ณธ๊ฐ’ | ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| c ยท ฯ† ๋ฒ”์œ„ ํญ | c + 5 kPa ยท ฯ† + 3ยฐ (c 5 ~ 10 ยท ฯ† 27 ~ 30) | ์ง€์‹DB `์•ˆ์ •์„ฑํ‰๊ฐ€/04` ๋ถ•์ ํ†  ๋ฒ”์œ„ ํญ์˜ ๋ฐ˜ | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| T/R ๋ฒ”์œ„ | 2,000 ~ 3,000 m | SINMAP 2 ์„ค๋ช…์„œ ๊ธฐ๋ณธ ๋ณด์ • | ๋ฌธํ—Œ ๊ธฐ๋ณธ ยท ํ˜„์žฅ ๋ณด์ • ์—†์Œ | +| ํ† ์ธต ๋‘๊ป˜ | 1.5 m | ์ง€์‹DB `์˜ˆ์ •๋…ธ์„ /02` ยง1 ํ‘œ์ธต ๋ถ•๊ดด ํ† ์ธต 1 ~ 3 m | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๋…ธ์„  ๋  ํญ | ์ค‘์‹ฌ์„  ยฑ 100 m | ์š”๊ตฌ ๋ถ„์„(๋…ธ์„  ์ขŒ์šฐ 100 m ์˜ˆ) | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๊ฒฉ์ž | 2 m | ํ”„๋กœ๊ทธ๋žจ ๊ธฐ๋ณธ๊ฐ’ ยท ๊ทผ๊ฑฐ ๋ฌธ์„œ ์—†์Œ | ์‚ฌ์šฉ์ž ํ™•์ • ์ „ | +| ๊ธฐ๋ณธ ์‹œ๋‚˜๋ฆฌ์˜ค | ํ‘œ์ค€(SINMAP ์Šต์œค์ง€์ˆ˜) | SINMAP ํ‘œ์ค€ ๋ฐฉ๋ฒ• ยท ์ตœ์•…(์™„์ „ ํฌํ™”)์€ ์„ ํƒ์ง€ | ์ •ํ•จ | + +- ๋ฌผ์„ฑ ๊ธฐ๋ณธ์€ ๊ทธ ํ”„๋กœ์ ํŠธ ์ง€๋ฐ˜ ์กฐ๊ฑด(E02)์˜ ๋ถ•์ ํ†  ๊ฐ’์„ ๋”ฐ๋ฆ„ โ€” E02 ์—์„œ ๋ถ•์ ํ† ๋ฅผ ๊ณ ์น˜๋ฉด ์ด ํ™”๋ฉด ๊ธฐ๋ณธ๊ฐ’๋„ ๋ฐ”๋€œ. +- ํ™”๋ฉด ์ขŒ์ธก ์นด๋“œ ์•„๋ž˜ ์•ˆ๋‚ด ๊ธ€(๊ธฐ๋ณธ๊ฐ’ = ๊ทผ๊ฑฐ ํ›„๋ณด โ€ฆ)์— ๊ฐ™์€ ์ถœ์ฒ˜๊ฐ€ ํ•œ ์ค„๋กœ ์ ํ˜€ ์žˆ์Œ. +- ๋“ฑ๊ธ‰ ๊ฒฝ๊ณ„(SI 1.5 ยท 1.25 ยท 1.0 ยท 0.5 ยท 0)๋Š” SINMAP ์› ๋ฐฉ๋ฒ• ๊ฐ’์ด๋ผ ์ด ํ‘œ์— ๋„ฃ์ง€ ์•Š์Œ. diff --git a/resources/master_template/report/stab_report.json b/resources/master_template/report/stab_report.json index 9ded03dad..8ee581788 100644 --- a/resources/master_template/report/stab_report.json +++ b/resources/master_template/report/stab_report.json @@ -293,6 +293,156 @@ "์„ ": "thin" } } + }, + { + "ํฌ๊ธฐ": 8, + "๊ตต๊ฒŒ": true, + "์ฑ„์›€": "#DDEBF7", + "๊ฐ€๋กœ": "center", + "์„ธ๋กœ": "center", + "์ค„๋ฐ”๊ฟˆ": true, + "ํ…Œ๋‘๋ฆฌ": { + "์œ„": { + "์„ ": "thin" + }, + "์•„๋ž˜": { + "์„ ": "thin" + }, + "์™ผ": { + "์„ ": "thin" + }, + "์˜ค๋ฅธ": { + "์„ ": "thin" + } + } + }, + { + "ํฌ๊ธฐ": 8, + "๊ฐ€๋กœ": "center", + "์„ธ๋กœ": "center", + "ํ˜•์‹": "0.00", + "ํ…Œ๋‘๋ฆฌ": { + "์œ„": { + "์„ ": "thin" + }, + "์•„๋ž˜": { + "์„ ": "thin" + }, + "์™ผ": { + "์„ ": "thin" + }, + "์˜ค๋ฅธ": { + "์„ ": "thin" + } + } + }, + { + "ํฌ๊ธฐ": 8, + "๊ฐ€๋กœ": "center", + "์„ธ๋กœ": "center", + "ํ˜•์‹": "0.0", + "ํ…Œ๋‘๋ฆฌ": { + "์œ„": { + "์„ ": "thin" + }, + "์•„๋ž˜": { + "์„ ": "thin" + }, + "์™ผ": { + "์„ ": "thin" + }, + "์˜ค๋ฅธ": { + "์„ ": "thin" + } + } + }, + { + "ํฌ๊ธฐ": 8, + "๊ฐ€๋กœ": "center", + "์„ธ๋กœ": "center", + "ํ˜•์‹": "0", + "ํ…Œ๋‘๋ฆฌ": { + "์œ„": { + "์„ ": "thin" + }, + "์•„๋ž˜": { + "์„ ": "thin" + }, + "์™ผ": { + "์„ ": "thin" + }, + "์˜ค๋ฅธ": { + "์„ ": "thin" + } + } + }, + { + "ํฌ๊ธฐ": 11, + "๊ฐ€๋กœ": "left", + "์„ธ๋กœ": "center", + "ํ…Œ๋‘๋ฆฌ": { + "์•„๋ž˜": { + "์„ ": "hair" + } + } + }, + { + "ํฌ๊ธฐ": 11, + "๊ฐ€๋กœ": "right", + "์„ธ๋กœ": "center", + "ํ˜•์‹": "0", + "ํ…Œ๋‘๋ฆฌ": { + "์•„๋ž˜": { + "์„ ": "hair" + } + } + }, + { + "ํฌ๊ธฐ": 11, + "๊ตต๊ฒŒ": true, + "๊ฐ€๋กœ": "left", + "์„ธ๋กœ": "center", + "ํ…Œ๋‘๋ฆฌ": { + "์•„๋ž˜": { + "์„ ": "hair" + } + } + }, + { + "ํฌ๊ธฐ": 8, + "๊ตต๊ฒŒ": true, + "์ฑ„์›€": "#F2F2F2", + "๊ฐ€๋กœ": "center", + "์„ธ๋กœ": "center", + "ํ˜•์‹": "0.00", + "ํ…Œ๋‘๋ฆฌ": { + "์œ„": { + "์„ ": "thin" + }, + "์•„๋ž˜": { + "์„ ": "thin" + }, + "์™ผ": { + "์„ ": "thin" + }, + "์˜ค๋ฅธ": { + "์„ ": "thin" + } + } + }, + { + "ํฌ๊ธฐ": 22, + "๊ตต๊ฒŒ": true, + "๊ฐ€๋กœ": "center", + "์„ธ๋กœ": "center", + "ํ…Œ๋‘๋ฆฌ": { + "์œ„": { + "์„ ": "thick" + }, + "์•„๋ž˜": { + "์„ ": "thick" + } + } } ], "์‹œํŠธ": [ @@ -302,7 +452,7 @@ "์นธ": { "B4": { "๊ฐ’": "๋น„ํƒˆ๋ฉด ์•ˆ์ •์„ฑ ๊ฒ€ํ†  ๋ณด๊ณ ์„œ", - "์„œ์‹": 1 + "์„œ์‹": 26 }, "C4": { "์„œ์‹": 1 @@ -416,6 +566,10 @@ }, "E15": { "์„œ์‹": 2 + }, + "B18": { + "๊ฐ’": "๊ฒ€ํ†  ๊ธฐ์ค€ โ€” ๊ฑด์„ค๊ณต์‚ฌ ๋น„ํƒˆ๋ฉด ์„ค๊ณ„๊ธฐ์ค€(KDS 11 70 05) ยท ํ•œ๊ณ„ํ‰ํ˜•ํ•ด์„(Bishop ๊ฐ„ํŽธ๋ฒ•)", + "์„œ์‹": 2 } }, "๋ณ‘ํ•ฉ": [ @@ -427,7 +581,8 @@ "C11:E11", "C12:E12", "B14:E14", - "B15:E15" + "B15:E15", + "B18:E18" ], "์—ด": { "A": { @@ -451,7 +606,7 @@ }, "ํ–‰": { "4": { - "๋†’์ด": 60 + "๋†’์ด": 72 }, "5": { "๋†’์ด": 24 @@ -476,6 +631,9 @@ }, "12": { "๋†’์ด": 30 + }, + "18": { + "๋†’์ด": 24 } }, "๋ณด๊ธฐ": { @@ -496,6 +654,82 @@ } } }, + { + "id": "s9", + "์ด๋ฆ„": "๋ชฉ์ฐจ", + "์นธ": { + "A1": { + "๊ฐ’": "๋ชฉ ์ฐจ", + "์„œ์‹": 1 + }, + "A3": { + "๊ฐ’": "์žฅ", + "์„œ์‹": 6 + }, + "B3": { + "๊ฐ’": "๋‚ด์šฉ", + "์„œ์‹": 6 + }, + "C3": { + "๊ฐ’": "์ชฝ", + "์„œ์‹": 6 + }, + "A4": { + "์„œ์‹": 24 + }, + "B4": { + "์„œ์‹": 22 + }, + "C4": { + "์„œ์‹": 23 + } + }, + "๋ณ‘ํ•ฉ": [ + "A1:C1" + ], + "์—ด": { + "A": { + "ํญ": 10 + }, + "B": { + "ํญ": 64 + }, + "C": { + "ํญ": 10 + } + }, + "ํ–‰": { + "1": { + "๋†’์ด": 48 + }, + "2": { + "๋†’์ด": 12 + }, + "3": { + "๋†’์ด": 24 + }, + "4": { + "๋†’์ด": 26 + } + }, + "๋ณด๊ธฐ": { + "๋ˆˆ๊ธˆ์„ ": false + }, + "์ธ์‡„": { + "์šฉ์ง€": "A4", + "๋ฐฉํ–ฅ": "portrait", + "๋งž์ถค": { + "ํญ": 1, + "๋†’์ด": 0 + }, + "์—ฌ๋ฐฑ": { + "์œ„": 30, + "์•„๋ž˜": 15, + "์™ผ": 20, + "์˜ค๋ฅธ": 20 + } + } + }, { "id": "s2", "์ด๋ฆ„": "๊ฐœ์š”", @@ -1566,6 +1800,9 @@ }, "G26": { "์„œ์‹": 5 + }, + "D22": { + "์„œ์‹": 5 } }, "๋ณ‘ํ•ฉ": [ @@ -1586,7 +1823,8 @@ "B18:G18", "B19:G19", "A21:G21", - "A24:G24" + "A24:G24", + "D22:G22" ], "์—ด": { "A": { @@ -1679,7 +1917,7 @@ "์ด๋ฆ„": "์ธก์ ํ‘œ", "์นธ": { "A1": { - "๊ฐ’": "์ธก์ ๋ณ„ ์•ˆ์ „์œจ", + "๊ฐ’": "์ธก์ ๋ณ„ ์•ˆ์ •์„ฑ ํŒ์ • ์ผ๋žŒํ‘œ", "์„œ์‹": 1 }, "B1": { @@ -1710,7 +1948,7 @@ "์„œ์‹": 1 }, "A2": { - "๊ฐ’": "ํŒ์ • โ€” ๋ชจ๋“  ์กฐ๊ฑด์˜ ์•ˆ์ „์œจ์ด ๊ธฐ์ค€ ์ด์ƒ์ด๋ฉด O.K ยท ํ•˜๋‚˜๋ผ๋„ ๋ฏธ๋‹ฌ์ด๋ฉด N.G", + "๊ฐ’": "ํŒ์ • โ€” ๋ชจ๋“  ์กฐ๊ฑด์˜ ์•ˆ์ „์œจ์ด ๊ธฐ์ค€ ์ด์ƒ์ด๋ฉด O.K ยท ํ•˜๋‚˜๋ผ๋„ ๋ฏธ๋‹ฌ์ด๋ฉด N.G ยท N.G ์ธก์  = ๋Œ€์ฑ… ๊ฒ€ํ†  ํ•„์š”", "์„œ์‹": 2 }, "B2": { @@ -1841,21 +2079,32 @@ }, "J6": { "์„œ์‹": 7 + }, + "K4": { + "๊ฐ’": "๋Œ€์ฑ… ํ•„์š”", + "์„œ์‹": 6 + }, + "K5": { + "์„œ์‹": 6 + }, + "K6": { + "์„œ์‹": 7 } }, "๋ณ‘ํ•ฉ": [ - "A1:J1", - "A2:J2", + "A1:K1", + "A2:K2", "A4:A5", "B4:B5", "C4:C5", "D4:F4", "G4:I4", - "J4:J5" + "J4:J5", + "K4:K5" ], "์—ด": { "A": { - "ํญ": 16 + "ํญ": 20 }, "B": { "ํญ": 13 @@ -1883,6 +2132,9 @@ }, "J": { "ํญ": 9 + }, + "K": { + "ํญ": 12 } }, "ํ–‰": { @@ -2175,64 +2427,64 @@ "๋†’์ด": 22 }, "3": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "4": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "5": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "6": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "7": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "8": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "9": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "10": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "11": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "12": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "13": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "14": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "15": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "16": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "17": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "18": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "19": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "20": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "21": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "22": { - "๋†’์ด": 20 + "๋†’์ด": 24 }, "23": { "๋†’์ด": 32 @@ -2600,6 +2852,1067 @@ } } ] + }, + { + "id": "s7", + "์ด๋ฆ„": "๊ด‘์—ญ์š”์•ฝ", + "์นธ": { + "A1": { + "๊ฐ’": "๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ ์š”์•ฝ", + "์„œ์‹": 1 + }, + "B1": { + "์„œ์‹": 1 + }, + "C1": { + "์„œ์‹": 1 + }, + "D1": { + "์„œ์‹": 1 + }, + "E1": { + "์„œ์‹": 1 + }, + "F1": { + "์„œ์‹": 1 + }, + "G1": { + "์„œ์‹": 1 + }, + "H1": { + "์„œ์‹": 1 + }, + "A2": { + "๊ฐ’": "๋…ธ์„  ๋  ๋ฌดํ•œ์‚ฌ๋ฉด ยท SINMAP ์•ˆ์ • ์ง€์ˆ˜ โ€” ๋…ธ์„  ์œ„ ยท ์•„๋ž˜ ์ž์—ฐ์‚ฌ๋ฉด ํ‘œ์ธต ๋ถ•๊ดด ์œ„ํ—˜ ยท ๋ฌธํ—Œ ์ถ”์ •์น˜", + "์„œ์‹": 2 + }, + "B2": { + "์„œ์‹": 2 + }, + "C2": { + "์„œ์‹": 2 + }, + "D2": { + "์„œ์‹": 2 + }, + "E2": { + "์„œ์‹": 2 + }, + "F2": { + "์„œ์‹": 2 + }, + "G2": { + "์„œ์‹": 2 + }, + "H2": { + "์„œ์‹": 2 + }, + "A4": { + "๊ฐ’": "1. ํ•ด์„ ์กฐ๊ฑด", + "์„œ์‹": 3 + }, + "B4": { + "์„œ์‹": 3 + }, + "C4": { + "์„œ์‹": 3 + }, + "D4": { + "์„œ์‹": 3 + }, + "E4": { + "์„œ์‹": 3 + }, + "F4": { + "์„œ์‹": 3 + }, + "G4": { + "์„œ์‹": 3 + }, + "H4": { + "์„œ์‹": 3 + }, + "A5": { + "๊ฐ’": "๋…ธ์„  ๋ ", + "์„œ์‹": 4 + }, + "B5": { + "์„œ์‹": 5 + }, + "C5": { + "์„œ์‹": 5 + }, + "D5": { + "์„œ์‹": 5 + }, + "E5": { + "์„œ์‹": 5 + }, + "F5": { + "์„œ์‹": 5 + }, + "G5": { + "์„œ์‹": 5 + }, + "H5": { + "์„œ์‹": 5 + }, + "A6": { + "๊ฐ’": "ํ† ์ธต ๋ฌผ์„ฑ", + "์„œ์‹": 4 + }, + "B6": { + "์„œ์‹": 5 + }, + "C6": { + "์„œ์‹": 5 + }, + "D6": { + "์„œ์‹": 5 + }, + "E6": { + "์„œ์‹": 5 + }, + "F6": { + "์„œ์‹": 5 + }, + "G6": { + "์„œ์‹": 5 + }, + "H6": { + "์„œ์‹": 5 + }, + "A7": { + "๊ฐ’": "์‹œ๋‚˜๋ฆฌ์˜ค ยท ํฌํ™”", + "์„œ์‹": 4 + }, + "B7": { + "์„œ์‹": 5 + }, + "C7": { + "์„œ์‹": 5 + }, + "D7": { + "์„œ์‹": 5 + }, + "E7": { + "์„œ์‹": 5 + }, + "F7": { + "์„œ์‹": 5 + }, + "G7": { + "์„œ์‹": 5 + }, + "H7": { + "์„œ์‹": 5 + }, + "A8": { + "๊ฐ’": "ํ•ด์„ ๋•Œ", + "์„œ์‹": 4 + }, + "B8": { + "์„œ์‹": 5 + }, + "C8": { + "์„œ์‹": 5 + }, + "D8": { + "์„œ์‹": 5 + }, + "E8": { + "์„œ์‹": 5 + }, + "F8": { + "์„œ์‹": 5 + }, + "G8": { + "์„œ์‹": 5 + }, + "H8": { + "์„œ์‹": 5 + }, + "A10": { + "๊ฐ’": "2. ๋“ฑ๊ธ‰๋ณ„ ๋ฉด์ ", + "์„œ์‹": 3 + }, + "B10": { + "์„œ์‹": 3 + }, + "C10": { + "์„œ์‹": 3 + }, + "D10": { + "์„œ์‹": 3 + }, + "E10": { + "์„œ์‹": 3 + }, + "F10": { + "์„œ์‹": 3 + }, + "G10": { + "์„œ์‹": 3 + }, + "H10": { + "์„œ์‹": 3 + }, + "A11": { + "๊ฐ’": "๋“ฑ๊ธ‰", + "์„œ์‹": 6 + }, + "B11": { + "๊ฐ’": "์•ˆ์ • ์ง€์ˆ˜ SI", + "์„œ์‹": 6 + }, + "C11": { + "๊ฐ’": "๋ฉด์  (mยฒ)", + "์„œ์‹": 6 + }, + "D11": { + "๊ฐ’": "๋น„์œจ (%)", + "์„œ์‹": 6 + }, + "A12": { + "๊ฐ’": "์•ˆ์ •", + "์„œ์‹": 7 + }, + "B12": { + "๊ฐ’": "SI โ‰ฅ 1.5", + "์„œ์‹": 7 + }, + "C12": { + "์„œ์‹": 14 + }, + "D12": { + "์‹": "IF(SUM(C$12:C$17)>0,C12/SUM(C$12:C$17)*100,\"\")", + "์„œ์‹": 16 + }, + "A13": { + "๊ฐ’": "๋ณดํ†ต ์•ˆ์ •", + "์„œ์‹": 7 + }, + "B13": { + "๊ฐ’": "1.25 โ‰ค SI < 1.5", + "์„œ์‹": 7 + }, + "C13": { + "์„œ์‹": 14 + }, + "D13": { + "์‹": "IF(SUM(C$12:C$17)>0,C13/SUM(C$12:C$17)*100,\"\")", + "์„œ์‹": 16 + }, + "A14": { + "๊ฐ’": "์ค€์•ˆ์ •", + "์„œ์‹": 7 + }, + "B14": { + "๊ฐ’": "1.0 โ‰ค SI < 1.25", + "์„œ์‹": 7 + }, + "C14": { + "์„œ์‹": 14 + }, + "D14": { + "์‹": "IF(SUM(C$12:C$17)>0,C14/SUM(C$12:C$17)*100,\"\")", + "์„œ์‹": 16 + }, + "A15": { + "๊ฐ’": "์œ„ํ—˜ ํ•˜ํ•œ", + "์„œ์‹": 7 + }, + "B15": { + "๊ฐ’": "0.5 โ‰ค SI < 1.0", + "์„œ์‹": 7 + }, + "C15": { + "์„œ์‹": 14 + }, + "D15": { + "์‹": "IF(SUM(C$12:C$17)>0,C15/SUM(C$12:C$17)*100,\"\")", + "์„œ์‹": 16 + }, + "A16": { + "๊ฐ’": "์œ„ํ—˜ ์ƒํ•œ", + "์„œ์‹": 7 + }, + "B16": { + "๊ฐ’": "0 < SI < 0.5", + "์„œ์‹": 7 + }, + "C16": { + "์„œ์‹": 14 + }, + "D16": { + "์‹": "IF(SUM(C$12:C$17)>0,C16/SUM(C$12:C$17)*100,\"\")", + "์„œ์‹": 16 + }, + "A17": { + "๊ฐ’": "๋ถˆ์•ˆ์ •", + "์„œ์‹": 7 + }, + "B17": { + "๊ฐ’": "SI = 0", + "์„œ์‹": 7 + }, + "C17": { + "์„œ์‹": 14 + }, + "D17": { + "์‹": "IF(SUM(C$12:C$17)>0,C17/SUM(C$12:C$17)*100,\"\")", + "์„œ์‹": 16 + }, + "A18": { + "๊ฐ’": "ํ•ฉ๊ณ„", + "์„œ์‹": 4 + }, + "B18": { + "์„œ์‹": 4 + }, + "C18": { + "์‹": "SUM(C12:C17)", + "์„œ์‹": 14 + }, + "D18": { + "์„œ์‹": 16 + }, + "A20": { + "๊ฐ’": "3. ์œ„ํ—˜ ์ธก์  โ€” ๋ณดํ†ต ยท ๋†’์Œ (์˜ํ–ฅ ๊ตฌ์—ญ ์•ˆ SI < 1 ์…€ ๋น„์œจ 5 % ์ด์ƒ)", + "์„œ์‹": 3 + }, + "B20": { + "์„œ์‹": 3 + }, + "C20": { + "์„œ์‹": 3 + }, + "D20": { + "์„œ์‹": 3 + }, + "E20": { + "์„œ์‹": 3 + }, + "F20": { + "์„œ์‹": 3 + }, + "G20": { + "์„œ์‹": 3 + }, + "H20": { + "์„œ์‹": 3 + }, + "A21": { + "๊ฐ’": "์ธก์ ", + "์„œ์‹": 6 + }, + "B21": { + "๊ฐ’": "๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ (m)", + "์„œ์‹": 6 + }, + "C21": { + "๊ฐ’": "์ตœ์†Œ Fs", + "์„œ์‹": 6 + }, + "D21": { + "๊ฐ’": "์ตœ์†Œ SI", + "์„œ์‹": 6 + }, + "E21": { + "๊ฐ’": "์ธก์  ๋“ฑ๊ธ‰ (SI<1 ๋น„์œจ)", + "์„œ์‹": 6 + }, + "F21": { + "๊ฐ’": "์œ„ํ—˜ ๋ฉด์  (mยฒ)", + "์„œ์‹": 6 + }, + "G21": { + "๊ฐ’": "์ตœ๋Œ€ ๊ฒฝ์‚ฌ (ยฐ)", + "์„œ์‹": 6 + }, + "A22": { + "์„œ์‹": 7 + }, + "B22": { + "์„œ์‹": 17 + }, + "C22": { + "์„œ์‹": 8 + }, + "D22": { + "์„œ์‹": 8 + }, + "E22": { + "์„œ์‹": 7 + }, + "F22": { + "์„œ์‹": 14 + }, + "G22": { + "์„œ์‹": 16 + }, + "A9": { + "๊ฐ’": "๊ธฐ๋ณธ๊ฐ’", + "์„œ์‹": 4 + }, + "B9": { + "์„œ์‹": 5 + } + }, + "๋ณ‘ํ•ฉ": [ + "A1:H1", + "A2:H2", + "A4:H4", + "B5:H5", + "B6:H6", + "B7:H7", + "B8:H8", + "B9:H9", + "A10:H10", + "A20:H20" + ], + "์—ด": { + "A": { + "ํญ": 14 + }, + "B": { + "ํญ": 15 + }, + "C": { + "ํญ": 11 + }, + "D": { + "ํญ": 11 + }, + "E": { + "ํญ": 13 + }, + "F": { + "ํญ": 13 + }, + "G": { + "ํญ": 12 + }, + "H": { + "ํญ": 6 + } + }, + "ํ–‰": { + "1": { + "๋†’์ด": 36 + }, + "2": { + "๋†’์ด": 20 + }, + "4": { + "๋†’์ด": 22 + }, + "10": { + "๋†’์ด": 22 + }, + "20": { + "๋†’์ด": 22 + }, + "21": { + "๋†’์ด": 32 + }, + "9": { + "๋†’์ด": 32 + } + }, + "๋ณด๊ธฐ": { + "๋ˆˆ๊ธˆ์„ ": false + }, + "์ธ์‡„": { + "์šฉ์ง€": "A4", + "๋ฐฉํ–ฅ": "portrait", + "๋งž์ถค": { + "ํญ": 1, + "๋†’์ด": 0 + }, + "์—ฌ๋ฐฑ": { + "์œ„": 15, + "์•„๋ž˜": 15, + "์™ผ": 10, + "์˜ค๋ฅธ": 10 + }, + "๋จธ๋ฆฌํ–‰": [ + 21, + 21 + ] + } + }, + { + "id": "s8", + "์ด๋ฆ„": "๋ณด๊ฐ•๊ฒ€ํ† ", + "์นธ": { + "A1": { + "๊ฐ’": "๋ณด๊ฐ• ๊ฒ€ํ†  (๊ฐ€์ •)", + "์„œ์‹": 1 + }, + "B1": { + "์„œ์‹": 1 + }, + "C1": { + "์„œ์‹": 1 + }, + "D1": { + "์„œ์‹": 1 + }, + "E1": { + "์„œ์‹": 1 + }, + "F1": { + "์„œ์‹": 1 + }, + "G1": { + "์„œ์‹": 1 + }, + "H1": { + "์„œ์‹": 1 + }, + "I1": { + "์„œ์‹": 1 + }, + "A2": { + "๊ฐ’": "E ์•ˆ ๊ฐ€์ • ๊ฒ€ํ†  โ€” ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ์€ ํ˜•์ƒ๋งŒ ๋ฐ”๊ฟ” ๋‹ค์‹œ ํ•ด์„ ยท ์„ค๊ณ„(์ž„๋„)์— ์•ˆ ๋Œ๋ ค ์”€ ยท ๋ฌธํ—Œ ์ถ”์ •์น˜", + "์„œ์‹": 2 + }, + "B2": { + "์„œ์‹": 2 + }, + "C2": { + "์„œ์‹": 2 + }, + "D2": { + "์„œ์‹": 2 + }, + "E2": { + "์„œ์‹": 2 + }, + "F2": { + "์„œ์‹": 2 + }, + "G2": { + "์„œ์‹": 2 + }, + "H2": { + "์„œ์‹": 2 + }, + "I2": { + "์„œ์‹": 2 + }, + "A4": { + "๊ฐ’": "1. ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ • โ€” ์ „ํ›„ ์•ˆ์ „์œจ (Bishop)", + "์„œ์‹": 3 + }, + "B4": { + "์„œ์‹": 3 + }, + "C4": { + "์„œ์‹": 3 + }, + "D4": { + "์„œ์‹": 3 + }, + "E4": { + "์„œ์‹": 3 + }, + "F4": { + "์„œ์‹": 3 + }, + "G4": { + "์„œ์‹": 3 + }, + "H4": { + "์„œ์‹": 3 + }, + "I4": { + "์„œ์‹": 3 + }, + "A5": { + "๊ฐ’": "์ธก์ ", + "์„œ์‹": 6 + }, + "B5": { + "๊ฐ’": "๋น„ํƒˆ", + "์„œ์‹": 6 + }, + "C5": { + "๊ฐ’": "๊ฐ€์ •", + "์„œ์‹": 6 + }, + "D5": { + "๊ฐ’": "์กฐ๊ฑด", + "์„œ์‹": 6 + }, + "E5": { + "๊ฐ’": "์ „ Fs", + "์„œ์‹": 6 + }, + "F5": { + "๊ฐ’": "ํ›„ Fs", + "์„œ์‹": 6 + }, + "G5": { + "๊ฐ’": "๊ธฐ์ค€", + "์„œ์‹": 6 + }, + "H5": { + "๊ฐ’": "์ „ ํŒ์ •", + "์„œ์‹": 6 + }, + "I5": { + "๊ฐ’": "ํ›„ ํŒ์ •", + "์„œ์‹": 6 + }, + "A6": { + "์„œ์‹": 7 + }, + "B6": { + "์„œ์‹": 7 + }, + "C6": { + "์„œ์‹": 5 + }, + "D6": { + "์„œ์‹": 7 + }, + "E6": { + "์„œ์‹": 8 + }, + "F6": { + "์„œ์‹": 8 + }, + "G6": { + "์„œ์‹": 8 + }, + "H6": { + "์„œ์‹": 7 + }, + "I6": { + "์„œ์‹": 7 + }, + "A8": { + "๊ฐ’": "2. ๋ฒฝ ์™ธ์  ์•ˆ์ • โ€” ํ™œ๋™ ยท ์ „๋„ ยท ์ง€์ง€๋ ฅ (KDS 11 80 05)", + "์„œ์‹": 3 + }, + "B8": { + "์„œ์‹": 3 + }, + "C8": { + "์„œ์‹": 3 + }, + "D8": { + "์„œ์‹": 3 + }, + "E8": { + "์„œ์‹": 3 + }, + "F8": { + "์„œ์‹": 3 + }, + "G8": { + "์„œ์‹": 3 + }, + "H8": { + "์„œ์‹": 3 + }, + "I8": { + "์„œ์‹": 3 + }, + "A9": { + "๊ฐ’": "์ธก์ ", + "์„œ์‹": 6 + }, + "B9": { + "๊ฐ’": "๋ฒฝ ยท ๊ฒฝ์šฐ", + "์„œ์‹": 6 + }, + "C9": { + "๊ฐ’": "ํ™œ๋™ Fs", + "์„œ์‹": 6 + }, + "D9": { + "๊ฐ’": "๊ธฐ์ค€", + "์„œ์‹": 6 + }, + "E9": { + "๊ฐ’": "์ „๋„ Fs", + "์„œ์‹": 6 + }, + "F9": { + "๊ฐ’": "๊ธฐ์ค€", + "์„œ์‹": 6 + }, + "G9": { + "๊ฐ’": "์ง€์ง€๋ ฅ Fs", + "์„œ์‹": 6 + }, + "H9": { + "๊ฐ’": "๊ธฐ์ค€", + "์„œ์‹": 6 + }, + "I9": { + "๊ฐ’": "ํŒ์ •", + "์„œ์‹": 6 + }, + "A10": { + "์„œ์‹": 7 + }, + "B10": { + "์„œ์‹": 5 + }, + "C10": { + "์„œ์‹": 8 + }, + "D10": { + "์„œ์‹": 8 + }, + "E10": { + "์„œ์‹": 8 + }, + "F10": { + "์„œ์‹": 8 + }, + "G10": { + "์„œ์‹": 8 + }, + "H10": { + "์„œ์‹": 8 + }, + "I10": { + "์„œ์‹": 7 + }, + "A12": { + "๊ฐ’": "๊ฐ€์ • ยท ๋ฒฝ ๊ฒ€ํ†  ์—†์Œ โ€” E03 [๋ณด๊ฐ• ๊ฒ€ํ† ] ๋’ค [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ]", + "์„œ์‹": 5 + }, + "B12": { + "์„œ์‹": 5 + }, + "C12": { + "์„œ์‹": 5 + }, + "D12": { + "์„œ์‹": 5 + }, + "E12": { + "์„œ์‹": 5 + }, + "F12": { + "์„œ์‹": 5 + }, + "G12": { + "์„œ์‹": 5 + }, + "H12": { + "์„œ์‹": 5 + }, + "I12": { + "์„œ์‹": 5 + } + }, + "๋ณ‘ํ•ฉ": [ + "A1:I1", + "A2:I2", + "A4:I4", + "A8:I8", + "A12:I12" + ], + "์—ด": { + "A": { + "ํญ": 15 + }, + "B": { + "ํญ": 28 + }, + "C": { + "ํญ": 30 + }, + "D": { + "ํญ": 7 + }, + "E": { + "ํญ": 8 + }, + "F": { + "ํญ": 8 + }, + "G": { + "ํญ": 8 + }, + "H": { + "ํญ": 8 + }, + "I": { + "ํญ": 8 + } + }, + "ํ–‰": { + "1": { + "๋†’์ด": 36 + }, + "2": { + "๋†’์ด": 30 + }, + "4": { + "๋†’์ด": 22 + }, + "5": { + "๋†’์ด": 22 + }, + "8": { + "๋†’์ด": 22 + }, + "9": { + "๋†’์ด": 22 + } + }, + "๋ณด๊ธฐ": { + "๋ˆˆ๊ธˆ์„ ": false + }, + "์ธ์‡„": { + "์šฉ์ง€": "A4", + "๋ฐฉํ–ฅ": "portrait", + "๋งž์ถค": { + "ํญ": 1, + "๋†’์ด": 0 + }, + "์—ฌ๋ฐฑ": { + "์œ„": 15, + "์•„๋ž˜": 15, + "์™ผ": 10, + "์˜ค๋ฅธ": 10 + } + } + }, + { + "id": "s10", + "์ด๋ฆ„": "๋ถ€๋ก_์ ˆํŽธ", + "์นธ": { + "A1": { + "๊ฐ’": "๋ถ€๋ก โ‘  ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ", + "์„œ์‹": 3 + }, + "A2": { + "๊ฐ’": "๋‹จ์œ„ โ€” ๊ธธ์ด m ยท ํž˜ kN/m(ํญ 1 m) ยท ์••๋ ฅ ยท ์ ์ฐฉ๋ ฅ kPa ยท ๊ฐ ยฐ", + "์„œ์‹": 2 + }, + "A3": { + "๊ฐ’": "๋ฒˆํ˜ธ", + "์„œ์‹": 18 + }, + "A4": { + "์„œ์‹": 21 + }, + "A5": { + "๊ฐ’": "ํ•ฉ๊ณ„", + "์„œ์‹": 18 + }, + "B3": { + "๊ฐ’": "ํญ b (m)", + "์„œ์‹": 18 + }, + "B4": { + "์„œ์‹": 19 + }, + "B5": { + "์„œ์‹": 25 + }, + "C3": { + "๊ฐ’": "๋†’์ด h (m)", + "์„œ์‹": 18 + }, + "C4": { + "์„œ์‹": 19 + }, + "C5": { + "์„œ์‹": 25 + }, + "D3": { + "๊ฐ’": "์ €๋ฉด ๊ฒฝ์‚ฌ ฮฑ (ยฐ)", + "์„œ์‹": 18 + }, + "D4": { + "์„œ์‹": 20 + }, + "D5": { + "์„œ์‹": 25 + }, + "E3": { + "๊ฐ’": "๋ฌด๊ฒŒ W (kN/m)", + "์„œ์‹": 18 + }, + "E4": { + "์„œ์‹": 19 + }, + "E5": { + "์„œ์‹": 25 + }, + "F3": { + "๊ฐ’": "์ƒ์žฌ Q (kN/m)", + "์„œ์‹": 18 + }, + "F4": { + "์„œ์‹": 19 + }, + "F5": { + "์„œ์‹": 25 + }, + "G3": { + "๊ฐ’": "๊ฐ„๊ทน์ˆ˜์•• u (kPa)", + "์„œ์‹": 18 + }, + "G4": { + "์„œ์‹": 19 + }, + "G5": { + "์„œ์‹": 25 + }, + "H3": { + "๊ฐ’": "์ €๋ฉด ๊ธธ์ด l (m)", + "์„œ์‹": 18 + }, + "H4": { + "์„œ์‹": 19 + }, + "H5": { + "์„œ์‹": 25 + }, + "I3": { + "๊ฐ’": "์ ์ฐฉ๋ ฅ c (kPa)", + "์„œ์‹": 18 + }, + "I4": { + "์„œ์‹": 19 + }, + "I5": { + "์„œ์‹": 25 + }, + "J3": { + "๊ฐ’": "๋งˆ์ฐฐ๊ฐ ฯ† (ยฐ)", + "์„œ์‹": 18 + }, + "J4": { + "์„œ์‹": 20 + }, + "J5": { + "์„œ์‹": 25 + }, + "K3": { + "๊ฐ’": "์œ ํšจ ์ˆ˜์ง๋ ฅ N' (kN/m)", + "์„œ์‹": 18 + }, + "K4": { + "์„œ์‹": 19 + }, + "K5": { + "์„œ์‹": 25 + }, + "L3": { + "๊ฐ’": "๊ตฌ๋™๋ ฅ (W+Q)ยทsinฮฑ (kN/m)", + "์„œ์‹": 18 + }, + "L4": { + "์„œ์‹": 19 + }, + "L5": { + "์„œ์‹": 25 + }, + "M3": { + "๊ฐ’": "์ €ํ•ญ๋ ฅ S (kN/m)", + "์„œ์‹": 18 + }, + "M4": { + "์„œ์‹": 19 + }, + "M5": { + "์„œ์‹": 25 + } + }, + "๋ณ‘ํ•ฉ": [ + "A1:M1", + "A2:M2" + ], + "์—ด": { + "A": { + "ํญ": 5 + }, + "B": { + "ํญ": 7.6 + }, + "C": { + "ํญ": 7.6 + }, + "D": { + "ํญ": 7.6 + }, + "E": { + "ํญ": 7.6 + }, + "F": { + "ํญ": 7.6 + }, + "G": { + "ํญ": 7.6 + }, + "H": { + "ํญ": 7.6 + }, + "I": { + "ํญ": 7.6 + }, + "J": { + "ํญ": 7.6 + }, + "K": { + "ํญ": 7.6 + }, + "L": { + "ํญ": 7.6 + }, + "M": { + "ํญ": 7.6 + } + }, + "ํ–‰": { + "1": { + "๋†’์ด": 24 + }, + "2": { + "๋†’์ด": 16 + }, + "3": { + "๋†’์ด": 34 + }, + "4": { + "๋†’์ด": 12 + }, + "5": { + "๋†’์ด": 14 + } + }, + "๋ณด๊ธฐ": { + "๋ˆˆ๊ธˆ์„ ": false + }, + "์ธ์‡„": { + "์šฉ์ง€": "A4", + "๋ฐฉํ–ฅ": "portrait", + "๋งž์ถค": { + "ํญ": 1, + "๋†’์ด": 0 + }, + "์—ฌ๋ฐฑ": { + "์œ„": 15, + "์•„๋ž˜": 15, + "์™ผ": 10, + "์˜ค๋ฅธ": 10 + }, + "์ชฝ๋‚˜๋ˆ”": [ + 5 + ] + } } ], "ํ™œ์„ฑ": "s1" diff --git a/resources/master_template/report/stab_report_slots.md b/resources/master_template/report/stab_report_slots.md index 3fb11f4c7..9a9aadc3c 100644 --- a/resources/master_template/report/stab_report_slots.md +++ b/resources/master_template/report/stab_report_slots.md @@ -4,8 +4,11 @@ ## ๊ณตํ†ต -- ๋ชซ: ์‚ฌ์šฉ์ž ๋ชซ(ํ‘œ์ง€ ยท ๊ฐœ์š”) = ์ฒ˜์Œ ๋งŒ๋“ค ๋•Œ๋งŒ ์ฑ„์›€ ยท ๋‹ค์‹œ ๋งŒ๋“ค ๋•Œ(๋ณด๊ณ ์„œ ๊ฐฑ์‹ ) ์•ˆ ๊ฑด๋“œ๋ฆผ. ํ”„๋กœ๊ทธ๋žจ ๋ชซ(์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D์š”์•ฝ) = ๊ฐฑ์‹  ๋•Œ ํ†ต์งธ ๋‹ค์‹œ ์”€. -- ์„œ์‹ ๋ฒˆํ˜ธ(`์„œ์‹` ํ‘œ): 1 title ยท 2 sub ยท 3 section ยท 4 label ยท 5 text ยท 6 head ยท 7 body(๊ฐ€์šด๋ฐ ๊ธ€) ยท 8 fs(0.00) ยท 9 int(0) ยท 10 ng(N.G ๋นจ๊ฐ• ์ฑ„์›€) ยท 11 ok(O.K ์ดˆ๋ก) ยท 12 notice ยท 13 num1(#,##0.0) ยท 14 num0(#,##0) ยท 15 para(์œ„ ๋งž์ถค ๊ธด ๊ธ€) ยท 16 prop(0.0) ยท 17 chain(#,##0.00). +- ๋ชซ: ์‚ฌ์šฉ์ž ๋ชซ(ํ‘œ์ง€ ยท ๊ฐœ์š”) = ์ฒ˜์Œ ๋งŒ๋“ค ๋•Œ๋งŒ ์ฑ„์›€ ยท ๋‹ค์‹œ ๋งŒ๋“ค ๋•Œ(๋ณด๊ณ ์„œ ๊ฐฑ์‹ ) ์•ˆ ๊ฑด๋“œ๋ฆผ. ํ”„๋กœ๊ทธ๋žจ ๋ชซ(๋ชฉ์ฐจ ยท ์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D์š”์•ฝ ยท ๊ด‘์—ญ์š”์•ฝ ยท ๋ณด๊ฐ•๊ฒ€ํ†  ยท ๋ถ€๋ก_์ ˆํŽธ) = ๊ฐฑ์‹  ๋•Œ ํ†ต์งธ ๋‹ค์‹œ ์”€ ยท ์ž‘์—…๋ณธ์— ์—†๋Š” ํ”„๋กœ๊ทธ๋žจ ๋ชซ ์‹œํŠธ๋Š” ์–‘์‹ ์ฐจ๋ก€ ์ž๋ฆฌ์— ๋ผ์›€ ยท ์ƒˆ ์„œ์‹์€ ์ž‘์—…๋ณธ ์„œ์‹ ํ‘œ ๋์— ๋ถ™์—ฌ ๋ฒˆํ˜ธ๋ฅผ ๋ฐ”๊ฟˆ. +- ์‹œํŠธ ์ฐจ๋ก€: ํ‘œ์ง€ ยท ๋ชฉ์ฐจ ยท ๊ฐœ์š” ยท ์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D์š”์•ฝ ยท ๊ด‘์—ญ์š”์•ฝ ยท (๋ณด๊ฐ•๊ฒ€ํ† ) ยท ๋ถ€๋ก_์ ˆํŽธ. +- PDF: ํ‘œ์ง€ = ์ชฝ ๋ฒˆํ˜ธ ยท ๋จธ๋ฆฌ๋ง ์—†์Œ ยท ๋ณธ๋ฌธ = ๋จธ๋ฆฌ๋ง(์™ผ ์‚ฌ์—…๋ช… = ํ‘œ์ง€ `C8` ยท ์˜ค๋ฅธ ๋ณด๊ณ ์„œ ์ด๋ฆ„) ยท ๊ผฌ๋ฆฌ๋ง ใ€Œ- n / N -ใ€(๋ชฉ์ฐจ = 1 ์ชฝ). +- ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ํ‘œ๊ธฐ ํ•œ ๊ทœ์น™ โ€” ๊ธฐ๋ณธ๊ฐ’ ๊ทธ๋Œ€๋กœ = ใ€Œ(๊ธฐ๋ณธ๊ฐ’ ยท ์‚ฌ์šฉ์ž ํ™•์ • ์ „)ใ€ ยท ๋ฐ”๊ฟˆ = ใ€Œ(์‚ฌ์šฉ์ž ์ž…๋ ฅ)ใ€ ยท ๋ฌผ์„ฑ = ใ€Œ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ์‚ฌ์šฉ์ž ํ™•์ • ์ „ใ€. +- ์„œ์‹ ๋ฒˆํ˜ธ(`์„œ์‹` ํ‘œ): 1 title ยท 2 sub ยท 3 section ยท 4 label ยท 5 text ยท 6 head ยท 7 body(๊ฐ€์šด๋ฐ ๊ธ€) ยท 8 fs(0.00) ยท 9 int(0) ยท 10 ng(N.G ๋นจ๊ฐ• ์ฑ„์›€) ยท 11 ok(O.K ์ดˆ๋ก) ยท 12 notice ยท 13 num1(#,##0.0) ยท 14 num0(#,##0) ยท 15 para(์œ„ ๋งž์ถค ๊ธด ๊ธ€) ยท 16 prop(0.0) ยท 17 chain(#,##0.00) ยท 18 head8 ยท 19 num8(0.00) ยท 20 num8(0.0) ยท 21 int8 ยท 22 ๋ชฉ์ฐจ ๋‚ด์šฉ ยท 23 ๋ชฉ์ฐจ ์ชฝ ยท 24 ๋ชฉ์ฐจ ์žฅ ยท 25 sum8(0.00 ยท ํšŒ์ƒ‰) ยท 26 ํ‘œ์ง€ ์ œ๋ชฉ. - ํŒ์ • ๊ธ€(`O.K` ยท `N.G`) ์นธ = ์—”์ง„ ํŒ์ •์„ ๊ฐ’์œผ๋กœ ์”€ ยท N.G ์นธ์€ ์„œ์‹ 10 ยท O.K ์นธ์€ ์„œ์‹ 11 ์„ ์ง์ ‘ ๋ถ™์ž„ โ€” ์ธ์‡„ HTML ยท xlsx ๋Š” `์กฐ๊ฑด๋ถ€์„œ์‹` ์„ ์•ˆ ๋จน์ด๋ฏ€๋กœ(ํ™”๋ฉด๋งŒ). ์–‘์‹์˜ `์กฐ๊ฑด๋ถ€์„œ์‹`(๊ธ€ํฌํ•จ `N.G` โ†’ ๋นจ๊ฐ•)์€ ์‚ฌ์šฉ์ž๊ฐ€ ํ™”๋ฉด์—์„œ ๊ณ ์นœ ํŒ์ • ๊ธ€์šฉ. - ์ˆ˜์‹์ด ๋ณด๋Š” ๋ ํ–‰ = 20000(์ธก์ ํ‘œ ยท 3D์š”์•ฝ). ์ธก์ ์ด ์ด๋ณด๋‹ค ๋งŽ์œผ๋ฉด ๊ฐœ์š” ์ˆ˜์‹์˜ `20000` ์„ ๊ฐ™์ด ๋Š˜๋ฆผ. - ๋นˆ ๊ฐ’์€ ์นธ์„ ์•ˆ ์ ๊ฑฐ๋‚˜ `๊ฐ’` ์„ ์•ˆ ๋‘ (๋นˆ ๊ธ€ `""` ๊ธˆ์ง€). @@ -19,6 +22,16 @@ | C8:E8 ยท C9:E9 ยท C10:E10 ยท C11:E11 ยท C12:E12 | ์‚ฌ์—…๋ช… ยท ๋…ธ์„ ๋ช… ยท ๋ฐœ์ฃผ์ฒ˜ ยท ์ž‘์„ฑ์ผ ยท ์ž‘์„ฑ์ž ๊ฐ’(๋ณ‘ํ•ฉ ยท ์„œ์‹ 5) โ€” ์™ผ์œ„ ์นธ `C8` โ€ฆ `C12` ์— ๊ฐ’ | | B14:E14 | ใ€Œ๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œใ€(๊ณ ์ • ๊ธ€ ยท ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์Œ) | +## s9 ๋ชฉ์ฐจ (ํ”„๋กœ๊ทธ๋žจ ๋ชซ) + +| ์นธ | ๋‚ด์šฉ | +| --- | --- | +| `A1:C1` | ์ œ๋ชฉ(๊ณ ์ • ๊ธ€) | +| 3 ํ–‰ | ๋จธ๋ฆฌ โ€” ์žฅ ยท ๋‚ด์šฉ ยท ์ชฝ | +| 4 ํ–‰๋ถ€ํ„ฐ ๋ณด์ด๋Š” ๋ณธ๋ฌธ ์‹œํŠธ ํ•˜๋‚˜ = ํ•œ ์ค„ | A ์žฅ(`1.` โ€ฆ ยท ๋ถ€๋ก = `๋ถ€๋ก`)(24) ยท B ๋‚ด์šฉ(22) ยท C ์‹œ์ž‘ ์ชฝ(23 ยท ํ‘œ์ง€ ๋บ€ ๋ณธ๋ฌธ ์ฐจ๋ก€ ยท ๋ชฉ์ฐจ = 1) | + +- ์ชฝ ๋ฒˆํ˜ธ = ์‹œํŠธ ํ•˜๋‚˜์”ฉ ์ธ์‡„ โ†’ PDF ์ชฝ ์ˆ˜๋กœ ์…ˆ(๋ณด๊ณ ์„œ ๋งŒ๋“ค ๋•Œ ยท PDF ๋‚ด๋ณด๋‚ด๊ธฐ ๋•Œ ๋‹ค์‹œ) ยท ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์—†์œผ๋ฉด ใ€Œ-ใ€. + ## s2 ๊ฐœ์š” (์‚ฌ์šฉ์ž ๋ชซ ยท ์ˆ˜์‹์€ ์–‘์‹์— ๋“ค์–ด ์žˆ์Œ) - ๋นŒ๋”๊ฐ€ ์“ธ ๊ฒƒ: ์—†์Œ(์ˆ˜์‹์ด `์ธก์ ํ‘œ` ยท `์ž…๋ ฅ์กฐ๊ฑด` ์„ ๊ฐ€๋ฆฌํ‚ด). ์˜๊ฒฌ ์นธ `A22:F30`(๋ณ‘ํ•ฉ ยท ์„œ์‹ 15) = ์‚ฌ์šฉ์ž ๊ธ€. @@ -34,11 +47,12 @@ | | `C12` `C13` `C14` (C:G ๋ณ‘ํ•ฉ) | ๋น„๊ณ  ๊ธ€ | | ์ง€ํ•˜์ˆ˜ ๊ทœ์น™ | `B17` `B18` `B19` (B:G ๋ณ‘ํ•ฉ) | ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์‹œ ๊ทœ์น™ ๊ธ€ | | ๋…ธ๋ฉด ํ•˜์ค‘ | `B22` | ์ˆ˜(kPa ยท ์„œ์‹ 16) | +| | `D22` (D:G ๋ณ‘ํ•ฉ) | ๋น„๊ณ  โ€” ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ํ‘œ๊ธฐ | | ์ง€๋ฐ˜ ๋ฌผ์„ฑ์น˜ | **26 ํ–‰๋ถ€ํ„ฐ ํ•œ ์ค„์”ฉ** ยท ๋จธ๋ฆฌ 25 ํ–‰(๋จธ๋ฆฌ ์ค„ ๋ฐ˜๋ณต) | A ์ง€์ธต ยท B ์ ์šฉ ๊ตฌ๊ฐ„(์ „์ฒด ๋˜๋Š” ๊ตฌ๊ฐ„ ๊ธ€) ยท C ฮณt ยท D ฮณsat ยท E c ยท F ฯ†(์„œ์‹ 16) ยท G ์ถœ์ฒ˜(์„œ์‹ 5) ยท AยทB = ์„œ์‹ 7 | - ์ง€๋ฐ˜ ๋ฌผ์„ฑ์น˜๊ฐ€ ํ‘œ์˜ ๋งจ ๋์ด๋ผ ์ค„ ์ˆ˜๊ฐ€ ๋Š˜์–ด๋„ ๋‹ค๋ฅธ ์นธ์ด ์•ˆ ๋ฐ€๋ฆผ. ๊ตฌ๊ฐ„๋ณ„ ๋ฎ์–ด์“ฐ๊ธฐ ์ค„ = ๊ฐ™์€ ํ‘œ์— ์ค„ ์ถ”๊ฐ€(B ์—ด์— ๊ตฌ๊ฐ„). -## s4 ์ธก์ ํ‘œ (ํ”„๋กœ๊ทธ๋žจ ๋ชซ ยท ๋จธ๋ฆฌ ์ค„ 4~5 ํ–‰ ์ชฝ๋งˆ๋‹ค ๋ฐ˜๋ณต ยท ๊ฐ€๋กœ ยท ํญ 1์ชฝ) +## s4 ์ธก์ ํ‘œ (ํ”„๋กœ๊ทธ๋žจ ๋ชซ ยท ํŒ์ • ์ผ๋žŒํ‘œ ยท ๋จธ๋ฆฌ ์ค„ 4~5 ํ–‰ ์ชฝ๋งˆ๋‹ค ๋ฐ˜๋ณต ยท ๊ฐ€๋กœ ยท ํญ 1์ชฝ) - ์ž๋ฃŒ๋Š” **6 ํ–‰๋ถ€ํ„ฐ ์ธก์  ํ•˜๋‚˜ = ํ•œ ์ค„** ยท ์„œ์‹์€ 6 ํ–‰ ์„œ์‹์„ ์ค„๋งˆ๋‹ค ๊ทธ๋Œ€๋กœ. @@ -50,9 +64,10 @@ | D ยท E ยท F | ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ์•ˆ์ „์œจ Fs(์ง€์ง„ ๋” = ์นธ ์—†์Œ) | 8 | | G ยท H ยท I | ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„ ๊ธฐ์ค€ ์•ˆ์ „์œจ | 8 | | J | ํŒ์ • `O.K` ยท `N.G`(์—”์ง„ ํŒ์ • ๊ฐ’) | N.G = 10 ยท O.K = 11 | +| K | ๋Œ€์ฑ… ํ•„์š”(`๋Œ€์ฑ… ๊ฒ€ํ† ` ยท O.K = `-`) | 7 | - ๊ฐœ์š” ยท 3D ์ˆ˜์‹์ด `A6:A20000` ยท `B` ยท `D` ยท `E` ยท `F` ยท `J` ์—ด์„ ์ฝ์Œ โ†’ ์ด ์—ด ๋ฐฐ์น˜ ๋ฐ”๊พธ์ง€ ์•Š์Œ. -- ์ค„ ์ˆ˜ = ์ธก์  ์ˆ˜(์‹œํ—˜: ์ธก์  ์ˆ˜ = ์ค„ ์ˆ˜). ์ชฝ ๋‚˜๋ˆ”์€ ์ž๋™(`์ธ์‡„.์ชฝ๋‚˜๋ˆ”` ์•ˆ ์”€). +- ์ค„ ์ˆ˜ = ์ธก์  ์ˆ˜(์‹œํ—˜: ์ธก์  ์ˆ˜ = ์ค„ ์ˆ˜). ์ชฝ ๋‚˜๋ˆ” = ๋นŒ๋”๊ฐ€ ์ชฝ๋งˆ๋‹ค ๊ณ ๋ฅด๊ฒŒ(`์ธ์‡„.์ชฝ๋‚˜๋ˆ”` ยท ๋งˆ์ง€๋ง‰ ์ชฝ์— ํ•œ๋‘ ์ค„๋งŒ ๋‚จ์ง€ ์•Š๊ฒŒ). ## s5 ๋Œ€ํ‘œ๋‹จ๋ฉด (ํ”„๋กœ๊ทธ๋žจ ๋ชซ ยท ๋‹จ๋ฉด ํ•˜๋‚˜ = 28 ํ–‰ ๋ฉ์ด = ํ•œ ์ชฝ) @@ -62,14 +77,14 @@ | --- | --- | --- | | 1 | `A1:H1` ๋ณ‘ํ•ฉ | ๋‹จ๋ฉด ์ œ๋ชฉ(`๋Œ€ํ‘œ ๋‹จ๋ฉด โ‘  โ€” ์ธก์  ยท ๊ตฌ๋ถ„ ยท ๋†’์ด`) | | 2 | `B2:H2` ๋ณ‘ํ•ฉ | ์„ ์ • ์‚ฌ์œ  ๊ธ€ | -| 3~22 | ๊ทธ๋ฆผ `A3:H22` | ๊ทธ๋ฆผ ํ•œ ์žฅ โ€” ๊ทธ๋ฆผ ์นธ ํฌ๊ธฐ ์•ฝ 712 ร— 533 px(๊ฐ€๋กœ : ์„ธ๋กœ = 4 : 3) ยท ์—ด์‡  `fig/<์ธก์  ํ‚ค>.png` ยท ๋‹ป์€ `dr` ๋งŒํผ ๋‚ด๋ฆผ | +| 3~22 | ๊ทธ๋ฆผ `A3:H22` | ๊ทธ๋ฆผ ํ•œ ์žฅ โ€” ํ–‰ 24 pt ยท ๊ทธ๋ฆผ ์นธ 712 ร— 640 px ยท ์•ˆ์ „์œจ ์ƒ์ž ยท ์ง€์ธต ๋ฒ”๋ก€(๋ฌด๋Šฌ) ยท ์ถ•์ฒ™ ๋ง‰๋Œ€ ยท ์—ด์‡  `fig/<์ธก์  ํ‚ค>.png` ยท ๋‹ป์€ `dr` ๋งŒํผ ๋‚ด๋ฆผ | | 23 | ๋จธ๋ฆฌ(๊ณ ์ • ๊ธ€) | ์กฐ๊ฑด ยท ํ•ด์„ ๋ฐฉ๋ฒ• ยท ์•ˆ์ „์œจ ยท ๊ธฐ์ค€ ยท ํŒ์ • ยท ํ™œ๋™๋ฉด ๋ฐ˜๊ฒฝ R ยท ์ค‘์‹ฌ x ยท ์ค‘์‹ฌ y | | 24 ยท 25 ยท 26 | A = ๊ฑด๊ธฐ ยท ์šฐ๊ธฐ ยท ์ง€์ง„(๊ณ ์ • ๊ธ€) | B ํ•ด์„ ๋ฐฉ๋ฒ• ๊ธ€(์„œ์‹ 7) ยท C Fs(8) ยท D ๊ธฐ์ค€(8) ยท E ํŒ์ •(7โ†’ N.G 10 ยท O.K 11) ยท F ๋ฐ˜๊ฒฝ ยท G ์ค‘์‹ฌ x ยท H ์ค‘์‹ฌ y(13) | | 27 | `B27:H27` ๋ณ‘ํ•ฉ | ๋น„๊ณ  ๊ธ€ | | 28 | ๋นˆ ์ค„ | ์ชฝ ๋ | - `์ธ์‡„.์ชฝ๋‚˜๋ˆ”` = `[28, 56, โ€ฆ, 28 ร— ๋‹จ๋ฉด ์ˆ˜]`(๋งˆ์ง€๋ง‰ ๋ฉ์ด ๋ ์ชฝ ๋‚˜๋ˆ”์€ ์žˆ์–ด๋„ ๋นˆ ์ชฝ์ด ์•ˆ ์ƒ๊น€). -- ์ง€์ง„์„ ์•ˆ ์“ฐ๋ฉด 26 ํ–‰(๋ฉ์ด ์•ˆ) ์นธ์„ ๋น„์›€. +- ์ง€์ง„์„ ์•ˆ ์“ฐ๋ฉด 26 ํ–‰(๋ฉ์ด ์•ˆ) B = ใ€Œ๊ฒ€ํ†  ์•ˆ ํ•จใ€. ๋น„์›ํ˜ธ ๋ฉด = F ยท G ยท H ใ€Œ-ใ€. ๋น„๊ณ  ๋ = ใ€Œ๋ถ€๋ก โ“ ์ ˆํŽธ ๊ณ„์‚ฐํ‘œใ€. ## s6 3D์š”์•ฝ (ํ”„๋กœ๊ทธ๋žจ ๋ชซ ยท ๋จธ๋ฆฌ ์ค„ 19 ํ–‰ ๋ฐ˜๋ณต) @@ -79,3 +94,39 @@ | 20 ํ–‰๋ถ€ํ„ฐ ๊ตฌ๊ฐ„ ํ•˜๋‚˜ = ํ•œ ์ค„ | A ๊ตฌ๊ฐ„ ยท B ์‹œ์ž‘ ์ธก์  ยท C ๋ ์ธก์ (์„œ์‹ 7) ยท D 3D ์ตœ์†Œ Fs ยท E 2D ์ตœ์†Œ Fs(์„œ์‹ 8) ยท **F 3D/2D ๋น„ = ์ˆ˜์‹ `IF(E{r}>0,D{r}/E{r},"")`**(์„œ์‹ 8) ยท G ์ž„๊ณ„ ์ฒด์  mยณ(์„œ์‹ 14) ยท H ํŒ์ •(์„œ์‹ 7 โ†’ N.G 10 ยท O.K 11) | - 20 ํ–‰ ์„œ์‹์„ ์ค„๋งˆ๋‹ค ๋ณต์‚ฌ. + +## s7 ๊ด‘์—ญ์š”์•ฝ (ํ”„๋กœ๊ทธ๋žจ ๋ชซ ยท ๋จธ๋ฆฌ ์ค„ 21 ํ–‰ ๋ฐ˜๋ณต ยท E07 ๊ด‘์—ญ ์ €์žฅ๋ณธ ยท ์—†์œผ๋ฉด `B5` ์— ใ€Œ๊ด‘์—ญ ํ•ด์„ ์ „ใ€) + +| ์นธ | ๋‚ด์šฉ | +| --- | --- | +| `B5` `B6` `B7` `B8` (๊ฐ B:H ๋ณ‘ํ•ฉ ยท ์„œ์‹ 5) | ๋…ธ์„  ๋  ยท ํ† ์ธต ๋ฌผ์„ฑ ยท ์‹œ๋‚˜๋ฆฌ์˜ค(ํ‘œ์ค€ SINMAP ์Šต์œค์ง€์ˆ˜ ยท ์ตœ์•… ์™„์ „ ํฌํ™”) ยท ํ•ด์„ ๋•Œ ๊ธ€ | +| `B9` (B:H ๋ณ‘ํ•ฉ) | ๊ธฐ๋ณธ๊ฐ’ ๊ทธ๋Œ€๋กœ์ธ ์„ค์ • ยท ๋ฌผ์„ฑ โ€” ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ํ•œ ์ค„ | +| `C12`~`C17` | ๋“ฑ๊ธ‰ 1~6(์•ˆ์ • ยท ๋ณดํ†ต ์•ˆ์ • ยท ์ค€์•ˆ์ • ยท ์œ„ํ—˜ ํ•˜ํ•œ ยท ์œ„ํ—˜ ์ƒํ•œ ยท ๋ถˆ์•ˆ์ •) ๋ฉด์  mยฒ(์„œ์‹ 14) ยท `D` ๋น„์œจ = ์–‘์‹ ์ˆ˜์‹ ยท `C18` ํ•ฉ๊ณ„ = ์–‘์‹ ์ˆ˜์‹ | +| `E11`~`G14` | ์ธก์  ๋“ฑ๊ธ‰ ํ‘œ โ€” ๋‚ฎ์Œ ยท ๋ณดํ†ต ยท ๋†’์Œ๋ณ„ SI<1 ์…€ ๋น„์œจ ๊ธฐ์ค€ ๊ธ€(7) ยท ์ธก์  ์ˆ˜(14) | +| 22 ํ–‰๋ถ€ํ„ฐ ๋ณดํ†ต ยท ๋†’์Œ ์ธก์  ํ•˜๋‚˜ = ํ•œ ์ค„ | A ์ธก์ (์„œ์‹ 7) ยท B ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ(17) ยท C ์ตœ์†Œ Fs ยท D ์ตœ์†Œ SI(8) ยท E ์ธก์  ๋“ฑ๊ธ‰ + SI<1 ๋น„์œจ ๊ธ€(7) ยท F ์œ„ํ—˜ ๋ฉด์  mยฒ(14) ยท G ์ตœ๋Œ€ ๊ฒฝ์‚ฌ ยฐ(16) ยท ์—†์œผ๋ฉด `A22` ใ€Œ์œ„ํ—˜ ์ธก์  ์—†์Œใ€ | + +- 22 ํ–‰ ์„œ์‹์„ ์ค„๋งˆ๋‹ค ๋ณต์‚ฌ. ์ธก์  ๋“ฑ๊ธ‰ = ์˜ํ–ฅ ๊ตฌ์—ญ(์ธก์  ์•ž๋’ค ยฝ ๊ฐ„๊ฒฉ ร— ๋  ํญ) ์•ˆ SI < 1(๋“ฑ๊ธ‰ 4 ์ด์ƒ) ์…€ ๋น„์œจ โ€” ๋‚ฎ์Œ < 5 % ยท ๋ณดํ†ต 5~20 % ยท ๋†’์Œ โ‰ฅ 20 %(๋ฌธํ„ฑ = E07 ์„ค์ •). + +## s8 ๋ณด๊ฐ•๊ฒ€ํ†  (ํ”„๋กœ๊ทธ๋žจ ๋ชซ ยท E03 ๋ณด๊ฐ• ๊ฒ€ํ†  ์ €์žฅ๋ณธ ยท ์—†์œผ๋ฉด ์‹œํŠธ๋ฅผ ๋บŒ) + +| ์นธ | ๋‚ด์šฉ | +| --- | --- | +| 6 ํ–‰๋ถ€ํ„ฐ ๊ฐ€์ • ํ•˜๋‚˜ ร— ์กฐ๊ฑด ํ•˜๋‚˜ = ํ•œ ์ค„ | A ์ธก์  ยท B ๋น„ํƒˆ(์ขŒ ยท ์šฐ ยท ๊นŽ๊ธฐ ยท ์Œ“๊ธฐ)(์„œ์‹ 7) ยท C ๊ฐ€์ • ๊ธ€(1:n ์ „ โ†’ ํ›„ ยท ์†Œ๋‹จ ํญ / ๋†’์ด ยท ์ตœ์†Œ ์™„ํ™”)(5) ยท D ์กฐ๊ฑด(7) ยท E ์ „ Fs ยท F ํ›„ Fs ยท G ๊ธฐ์ค€(8) ยท H ์ „ ํŒ์ • ยท I ํ›„ ํŒ์ •(7 โ†’ N.G 10 ยท O.K 11) ยท ์—†์œผ๋ฉด ใ€Œ๊ฐ€์ • ์—†์Œใ€ | +| ๊ฐ€์ • ์ค„ ๋‹ค์Œ ํ•œ ์ค„ ๋„๊ณ  `8` ยท `9` ํ–‰ ๊ผด(์ œ๋ชฉ 3 ยท ๋จธ๋ฆฌ 6) | 2. ๋ฒฝ ์™ธ์  ์•ˆ์ • | +| ๊ทธ ์•„๋ž˜ ๋ฒฝ ํ•˜๋‚˜ ร— ๊ฒฝ์šฐ ํ•˜๋‚˜ = ํ•œ ์ค„ | A ์ธก์ (7) ยท B ๋ฒฝ ์ด๋ฆ„ ยท ํ‰์ƒ์‹œ / ์ง€์ง„ ์‹œ ยท ํ† ์••๋ฒ• ยท ๋ฐฐ๋ฉด ์ˆ˜์œ„(5) ยท C ํ™œ๋™ ยท E ์ „๋„ ยท G ์ง€์ง€๋ ฅ Fs(8) ยท D ยท F ยท H ๊ธฐ์ค€(8 ยท KDS 11 80 05) ยท I ํŒ์ •(7 โ†’ 10 ยท 11) ยท ์—†์œผ๋ฉด ใ€Œ๋ฒฝ ๊ฒ€ํ†  ์—†์Œใ€ | + +- 6 ํ–‰ ยท 10 ํ–‰ ์„œ์‹์„ ์ค„๋งˆ๋‹ค ๋ณต์‚ฌ ยท ์ €์žฅ๋ณธ = (์ธก์  ยท ์ข…๋ฅ˜ ยท ๋น„ํƒˆ)๋งˆ๋‹ค ๊ฐ€์žฅ ์ตœ๊ทผ ํ•˜๋‚˜. + +## s10 ๋ถ€๋ก_์ ˆํŽธ (ํ”„๋กœ๊ทธ๋žจ ๋ชซ ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด๋งˆ๋‹ค ํ•œ ์ชฝ) + +์–‘์‹ 1~5 ํ–‰ = ๋ฉ์ด ๊ผด(1 ์ œ๋ชฉ ยท 2 ๋‹จ์œ„ ยท 3 ๋จธ๋ฆฌ ยท 4 ์ ˆํŽธ ์ค„ ์„œ์‹ ยท 5 ํ•ฉ๊ณ„ ์„œ์‹). ๋Œ€ํ‘œ ๋‹จ๋ฉด๋งˆ๋‹ค ์ง€๋ฐฐ ์กฐ๊ฑด(Fs / ๊ธฐ์ค€ ๊ฐ€์žฅ ๋‚ฎ์Œ) ์ž„๊ณ„๋ฉด์˜ ์ ˆํŽธํ‘œ. + +| ๋ฉ์ด ์•ˆ ํ–‰ | ์นธ | ๋‚ด์šฉ | +| --- | --- | --- | +| 1 | `A:M` ๋ณ‘ํ•ฉ | ใ€Œ๋ถ€๋ก โ“ ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ โ€” ์ธก์  ยท ๊ตฌ๋ถ„ ยท ์กฐ๊ฑด ยท ๋ฐฉ๋ฒ• ยท Fsใ€ | +| 2 | `A:M` ๋ณ‘ํ•ฉ | ์ž„๊ณ„๋ฉด ยท ๋‹จ์œ„ ๊ธ€ | +| 3 | ๋จธ๋ฆฌ | ๋ฒˆํ˜ธ ยท ํญ b ยท ๋†’์ด h ยท ์ €๋ฉด ๊ฒฝ์‚ฌ ฮฑ ยท ๋ฌด๊ฒŒ W ยท ์ƒ์žฌ Q ยท ๊ฐ„๊ทน์ˆ˜์•• u ยท ์ €๋ฉด ๊ธธ์ด l ยท c ยท ฯ† ยท ์œ ํšจ ์ˆ˜์ง๋ ฅ N' ยท ๊ตฌ๋™๋ ฅ (W+Q)ยทsinฮฑ ยท ์ €ํ•ญ๋ ฅ S | +| 4 โ€ฆ | ์ ˆํŽธ ํ•˜๋‚˜ = ํ•œ ์ค„ | ์„œ์‹ 21(๋ฒˆํ˜ธ) ยท 20(ฮฑ ยท ฯ†) ยท 19(๊ทธ ๋ฐ–) | +| ๋ | ํ•ฉ๊ณ„ | A ใ€Œํ•ฉ๊ณ„ใ€ ยท E ฮฃW ยท F ฮฃQ ยท L ฮฃ๊ตฌ๋™๋ ฅ ยท M ฮฃ์ €ํ•ญ๋ ฅ(์„œ์‹ 25) | + +- ์ €ํ•ญ๋ ฅ S = ๋™์› ์ „๋‹จ ร— Fs = cยทl + N'ยทtanฯ† ยท ๋ฉ์ด ๋๋งˆ๋‹ค ์ชฝ ๋‚˜๋ˆ” ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด ์—†์Œ = `A1` ใ€Œ๋ถ€๋ก โ€” ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ ์—†์Œใ€. diff --git a/resources/tester/test_528_project_groups.py b/resources/tester/test_528_project_groups.py index e63553e50..62968e798 100644 --- a/resources/tester/test_528_project_groups.py +++ b/resources/tester/test_528_project_groups.py @@ -6,6 +6,7 @@ - ๊ณต์œ  ํŒ์ • โ€” ์ง€์ • ํšŒ์‚ฌ GET 200 ยท PUT 403 ยท ๋ฏธ์ง€์ • 403 ยท ๊ฐ™์€ ํšŒ์‚ฌ ๊ทธ๋Œ€๋กœ - ๊ฐ€์ž… โ€” go.kr = ํšŒ์‚ฌ ใ€Œ์‚ฐ๋ฆผ์ฒญใ€ ยท ACTIVE ยท ํ™”๋ฉด์ด ๋ณด๋‚ธ ํšŒ์‚ฌ ๊ฐ’ ๋ฌด์‹œ ยท ๋‹ค๋ฅธ ๋ฉ”์ผ = NO_COMPANY - ์ข…๋ฅ˜๋ณ„ ๋‹จ๊ณ„ ์‹œ๋“œ ยท storage ํ—ˆ์šฉ ๋ชฉ๋ก +- 528-6 D ยท E ์ €์žฅ์†Œ = ์ข…๋ฅ˜ ๋‹จ๊ณ„๋งŒ(B ์–‘์‹ ยท B0x ๋นˆ ํด๋” ์—†์Œ ยท D ๋Š” B04_PreProcess) ยท ROAD ๊ทธ๋Œ€๋กœ """ import asyncio @@ -17,6 +18,7 @@ from A07_Register import A07_Register_Router as reg_router from A07_Register.A07_Register_Schema import RegisterRequest from common_util import common_util_auth as auth from common_util import common_util_auth_repository as auth_repo +from common_util.common_util_project_kind import KIND_STAGE_DIRS, init_kind_storage from common_util.common_util_workflow_state import initialize_project_stages from config.config_system import PROJECT_STORAGE_STAGE_DIRS from D01_PlanRegister import D01_PlanRegister_Router as d01 @@ -165,7 +167,7 @@ def test_e01_requires_b06(monkeypatch): call() assert e.value.status_code == 404 seen, call = _e01(monkeypatch, [{**src, "project_year": 2026, "section_complete": 1}]) - assert call() == {"project_id": "stab-id", "source_fingerprint": "f" * 40} + assert call() == {"project_id": "stab-id", "source_fingerprint": "f" * 40, "warnings": []} assert seen["copied"] == ("stab-id", "s" * 36) assert seen["kind"] == "STABILITY" and seen["source_project_id"] == "s" * 36 assert seen["name"] == "์ž„๋„ ์•ˆ์ •์„ฑํ‰๊ฐ€" @@ -257,7 +259,7 @@ def test_register_ignores_sent_company(): assert "company_name" not in data -@pytest.mark.parametrize("kind,count", [("ROAD", 7), ("PLAN", 3), ("STABILITY", 4)]) +@pytest.mark.parametrize("kind,count", [("ROAD", 7), ("PLAN", 3), ("STABILITY", 5)]) def test_stage_seed_by_kind(kind, count): log = [] asyncio.run(initialize_project_stages(FakeCursor({}, log), "p1", kind)) @@ -269,3 +271,36 @@ def test_stage_seed_by_kind(kind, count): def test_storage_dirs_allowed(): for name in ("D02_PlanBase", "D04_PlanConfirm", "E02_StabGround", "E05_StabReport"): assert name in PROJECT_STORAGE_STAGE_DIRS + + +@pytest.mark.parametrize("kind", ["PLAN", "STABILITY"]) +def test_kind_storage_light(tmp_path, kind): + init_kind_storage(tmp_path, kind) + names = {p.name for p in tmp_path.iterdir()} + assert names == set(KIND_STAGE_DIRS[kind]) + assert ("B04_PreProcess" in names) == (kind == "PLAN") + assert not [p for p in tmp_path.rglob("*") if p.is_file()] + + +def test_road_storage_unchanged(tmp_path, monkeypatch): + from B02_ProjRegister import B02_ProjRegister_Repository as b02 + + seeded = [] + monkeypatch.setattr(b02, "seed_project", seeded.append) + b02._initialize_project_storage(tmp_path, "p1") + assert seeded == [tmp_path] + assert (tmp_path / "B09_Estimation" / "v1").is_dir() + assert (tmp_path / "project_manifest.json").is_file() + + +@pytest.mark.parametrize("kind", ["PLAN", "STABILITY", None]) +def test_resolve_layout_skips_kind(tmp_path, kind): + from common_util.common_util_storage import ensure_project_storage_layout + + if kind: + init_kind_storage(tmp_path, kind) + before = {p.name for p in tmp_path.iterdir()} + ensure_project_storage_layout(str(tmp_path)) + after = {p.name for p in tmp_path.iterdir()} + assert (after == before) == bool(kind) + assert ("B09_Estimation" in after) == (kind is None) diff --git a/resources/tester/test_532_1_source_robust.py b/resources/tester/test_532_1_source_robust.py new file mode 100644 index 000000000..1e238ba99 --- /dev/null +++ b/resources/tester/test_532_1_source_robust.py @@ -0,0 +1,121 @@ +"""532-1 ๋ง ยท ์š”๊ตฌ 351 โ€” E01 ๋ณต์‚ฌ๋ณธ ์ฝ๊ธฐ๊ฐ€ ํšก๋‹จ ๊ฒฝ๋กœ null ยท ํŒŒ์ผ ์—†์Œ ยท ์˜› ๊ผด์—์„œ๋„ ๋Ž. + +- ๊ฒฝ๋กœ null ยท ๊ฒฝ๋กœ ํŒŒ์ผ ์—†์Œ โ†’ B06 ์ด๋ฆ„ ๊ทœ์น™(`find_cross_file`)์œผ๋กœ ์ฐพ์Œ +- ์˜› ๊ผด โ€” ์ ˆ๋Œ€ ๊ฒฝ๋กœ(์›๋ณธ ์ชฝ์„ ์ฝ์ง€ ์•Š์Œ) ยท ์†Œ์ˆ˜ ์ธก์ ์˜ ์˜› ์ •์ˆ˜ m ์ด๋ฆ„ ยท data ์—†์Œ +- ์–ด๋””์—๋„ ์—†์œผ๋ฉด ๊ทธ ์ธก์ ๋งŒ ๋น ์ง(`missing_stations`) ยท cross_section ์€ KeyError +- ์ƒ์„ฑ ๋ณต์‚ฌ(`copy_stability`)๊ฐ€ ๊ฐ™์€ ๊ฒ€์‚ฌ โ†’ manifest `missing_cross_chainages` ยท `warnings` +""" + +import asyncio +import json +import sys +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parents[2])) + +from common_util import common_util_project_copy as cp # noqa: E402 +from E01_StabRegister import E01_StabRegister_Source as src # noqa: E402 + +CROSS = "B06_Section/cross_sections" + + +def _cross(root: Path, name: str, label: str) -> None: + path = root / CROSS / name + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps({"label": label, "samples": [{"offset_m": 0}]}), encoding="utf-8") + + +def _row(chainage, path, data=..., seq=0): + design = {"design": {"ground_type": "soil"}} + return { + "chainage_m": chainage, + "sequence_num": seq, + "cross_section_file_path": path, + "data": json.dumps(design) if data is ... else data, + } + + +@pytest.fixture +def root(tmp_path): + outside = tmp_path / "original" / CROSS / "cross_00040m.json" + outside.parent.mkdir(parents=True) + outside.write_text(json.dumps({"label": "์›๋ณธ"}), encoding="utf-8") + copy = tmp_path / "copy" + _cross(copy, "cross_00000m.json", "์ •์ƒ") + _cross(copy, "cross_00020m.json", "null") + _cross(copy, "cross_00030m.json", "๊ฒฝ๋กœ ํ‹€๋ฆผ") + _cross(copy, "cross_00040m.json", "์ ˆ๋Œ€") + _cross(copy, "cross_00050m.json", "์˜› ์ •์ˆ˜") # 49.8 m ์ธก์ ์˜ ์˜› ์ด๋ฆ„ + rows = [ + _row(0.0, f"{CROSS}/cross_00000m.json"), + _row(20.0, None, seq=None), + _row(30.0, f"{CROSS}/cross_gone.json"), + _row(40.0, str(outside)), + _row(49.8, None, data=None), + _row(60.0, None), # ์–ด๋””์—๋„ ์—†์Œ + _row(None, None), # ๋ˆ„๊ฐ€๊ฑฐ๋ฆฌ ์—†๋Š” ํ–‰ โ€” ์ธก์  ์•„๋‹˜ + ] + (copy / src.STABILITY_DB_NAME).write_text( + json.dumps({"cross_sections": rows}), encoding="utf-8" + ) + (copy / src.MANIFEST_NAME).write_text("{}", encoding="utf-8") + return copy + + +def test_stations_skip_only_missing(root): + got = src.stations(root) + assert [s["chainage_m"] for s in got] == [0.0, 20.0, 30.0, 40.0, 49.8] + assert [s["label"] for s in got] == ["์ •์ƒ", "null", "๊ฒฝ๋กœ ํ‹€๋ฆผ", "์ ˆ๋Œ€", "์˜› ์ •์ˆ˜"] + assert got[0]["ground_type"] == "soil" and got[4]["ground_type"] is None + assert src.missing_stations(root) == [60.0] + + +def test_cross_section(root): + assert src.cross_section(root, 20.0)["label"] == "null" + assert src.cross_section(root, 40.0)["label"] == "์ ˆ๋Œ€" # ์›๋ณธ ์ชฝ ํŒŒ์ผ ์•„๋‹˜ + assert src.cross_section(root, 49.8)["design"] == {} + with pytest.raises(KeyError, match="ํšก๋‹จ ์—†์Œ"): + src.cross_section(root, 60.0) + with pytest.raises(KeyError, match="์ธก์  ์—†์Œ"): + src.cross_section(root, 70.0) + + +def test_normal_copy_has_no_missing(root): + db = json.loads((root / src.STABILITY_DB_NAME).read_text(encoding="utf-8")) + db["cross_sections"] = db["cross_sections"][:1] + (root / src.STABILITY_DB_NAME).write_text(json.dumps(db), encoding="utf-8") + assert [s["label"] for s in src.stations(root)] == ["์ •์ƒ"] + assert src.missing_stations(root) == [] + + +def test_copy_stability_warns(tmp_path, monkeypatch): + source = tmp_path / "source" + _cross(source, "cross_00000m.json", "a") + _cross(source, "cross_00020m.json", "b") + dump = { + "project": {"storage_path": "x"}, + "surface_params": {"source_filter": "none"}, + "route": None, + "cross_sections": [ + _row(0.0, None), + _row(20.0, f"{CROSS}/cross_00020m.json"), + _row(35.5, None), + ], + "route_points": [], + "longitudinal_sections": [], + } + + async def fake_rows(connection, source_id): + return dump + + monkeypatch.setattr(cp, "_stability_rows", fake_rows) + monkeypatch.setattr(cp, "resolve_stored_project_path", lambda _: str(source)) + manifest = asyncio.run(cp.copy_stability(None, "s" * 36, "e" * 36, tmp_path / "e")) + assert manifest["missing_cross_chainages"] == [35.5] + assert manifest["warnings"] == ["ํšก๋‹จ ์—†๋Š” ์ธก์  1 ๊ฐœ โ€” ํ•ด์„์—์„œ ๋น ์ง"] + written = json.loads( + (tmp_path / "e" / cp.STABILITY_SOURCE_DIR / cp.MANIFEST_NAME).read_text("utf-8") + ) + assert written["missing_cross_chainages"] == [35.5] diff --git a/resources/tester/test_532_5_e03_seam.py b/resources/tester/test_532_5_e03_seam.py new file mode 100644 index 000000000..4dd5b86f2 --- /dev/null +++ b/resources/tester/test_532_5_e03_seam.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- +"""532-5 E03 ์ด์Œ๋งค โ€” 2D ํ•ด์„ ๊ธธ์— ๋ฒฝ(532-4) ยท ์ง€ํ•˜์ˆ˜ ๊ฐˆ๋ž˜(E02) ยท ๋น ์ง„ ํšก๋‹จ ์•Œ๋ฆผ(532-1 ๋ง). + +- ๋ฒฝ: ๊ฒ€์ฆ_sub2 ๋ณต์‚ฌ๋ณธ ๋ฒฝ ์„  ์ธก์ (351 m ๊ธฐ์Šญ๋ง‰์ด) โ€” ๊ธธ์ด ๋ฒฝ์„ ๋„˜๊น€ ยท ์ž„๊ณ„๋ฉด์ด ๋ฒฝ์„ ์•ˆ ์ง€๋‚จ ยท + ์ธก์  ๋ณด๊ธฐ์— ๋ฒฝ ํด๋ฆฌ๊ณค ยท ๋ชฉ๋ก ๋ฒฝ ์ˆ˜ ยท ๋ฒฝ ์žˆ๋Š” ์ธก์ ๋งŒ ์ง€๋ฌธ์ด ๋ฐ”๋€œ +- ์ง€ํ•˜์ˆ˜: E02 ์šฐ๊ธฐ ๊ฐˆ๋ž˜๋ฅผ ๋ฐ”๊พธ๋ฉด ์ธก์  ์ง€๋ฌธ์ด ๋ฐ”๋€œ ยท ์ธก์  ๋ณด๊ธฐ ์ˆ˜์œ„์„  = ๊ณ ๋ฅธ ๊ฐˆ๋ž˜ +- ๋น ์ง„ ํšก๋‹จ: ํšก๋‹จ ํŒŒ์ผ์ด ์—†์œผ๋ฉด result2d ยท stab/ground ์˜ missing_stations ์— ๊ทธ ์ธก์  +- E05 ์ด์Œ๋งค: E05 ๊ฐ€ E03 ์ •๋ณธ๋งŒ ์ฝ์Œ(์‚ฌ๋ณธ ์—†์Œ) ยท ์ธก์  ์ˆ˜ ยท N.G ์ˆ˜ ยท ์ง€๋ฐฐ ๋น„ํƒˆ์ด E03 ๊ณผ ๊ฐ™์Œ +- result2d ๋ง์นธ: ์ง€๋ฐฐ ๋น„ํƒˆ = fs / ๊ธฐ์ค€ ๊ฐ€์žฅ ๋‚ฎ์€ ์ชฝ(๊นŽ๊ธฐ 1.22/1.2 ยท ์Œ“๊ธฐ 1.25/1.3 โ†’ ์Œ“๊ธฐ ยท N.G) + + ./venv/Scripts/python.exe -m pytest resources/tester/test_532_5_e03_seam.py -q +""" + +from __future__ import annotations + +import asyncio +import json +import shutil +import sys +import tempfile +from pathlib import Path +from uuid import UUID + +import numpy as np +import pytest +from shapely.geometry import LineString, Polygon + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) + +from common_util import common_util_project_copy as cp # noqa: E402 +from common_util.common_util_station_key import chainage_key # noqa: E402 +from E01_StabRegister import E01_StabRegister_Source as src # noqa: E402 +from E02_StabGround import E02_StabGround_Model as ground_model # noqa: E402 +from E02_StabGround import E02_StabGround_Router as e02 # noqa: E402 +from E03_StabSection import E03_StabSection_Router as router # noqa: E402 +from E03_StabSection import E03_StabSection_View as view # noqa: E402 +from E03_StabSection import E03_StabSection_Worker as worker # noqa: E402 +from E03_StabSection.E03_StabSection_Engine_Station import analyze_station # noqa: E402 + +SOURCE_ID = "cb15892a-5788-4a26-9316-20a15421f68b" # ๊ฒ€์ฆ_sub2(์ฝ๊ธฐ๋งŒ) +PID = "e" * 8 + "-" + "e" * 4 + "-" + "e" * 4 + "-" + "e" * 4 + "-" + "e" * 12 +WALL_KEY = "351.00" # ๊ฒ€์ฆ_sub2 ๊ธฐ์Šญ๋ง‰์ด ์ธก์  +CORNER_M = 0.2 # ์ž„๊ณ„๋ฉด์ด ๋ฒฝ ๋ชจ์„œ๋ฆฌ๋ฅผ ์Šค์น˜๋Š” ๊ธธ์ด ํ•œ๋„(test_532_4 ์™€ ๊ฐ™์Œ) +USER = {"role": "USER", "company_id": 1} + + +async def _copy(target: Path) -> None: + import aiomysql + + from config.config_db import DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_USER + + connection = await aiomysql.connect( + host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, db=DB_NAME, autocommit=True + ) + try: + await cp.copy_project( + connection, SOURCE_ID, scope="stability", new_id=PID, project_root=target + ) + finally: + connection.close() + + +@pytest.fixture(scope="module") +def frozen(): + folder = Path(tempfile.mkdtemp(prefix="aislo_e03_seam_")) + try: + asyncio.run(_copy(folder / "copy")) + except Exception as error: # noqa: BLE001 + shutil.rmtree(folder, ignore_errors=True) + pytest.skip(f"๊ฒ€์ฆ ์›๋ณธ ๋ณต์‚ฌ ์‹คํŒจ: {error}") + root = folder / "copy" + if WALL_KEY not in router._walls(str(src.source_dir(root)), "seam"): + shutil.rmtree(folder, ignore_errors=True) + pytest.skip("๊ฒ€์ฆ_sub2 ์— ๋ฒฝ ์ธก์ ์ด ์—†์Œ") + yield root + shutil.rmtree(folder, ignore_errors=True) + + +def _fast(cross, design, **kw): + """์ง„์งœ ์—”์ง„ โ€” Bishop ์›ํ˜ธ๋งŒ(๋น ๋ฅด๊ฒŒ).""" + return analyze_station(cross, design, **{**kw, "methods": ("bishop",), "noncircular": False}) + + +@pytest.fixture +def project(frozen, monkeypatch): + """๋ฒฝ ์ธก์  + ๋ฒฝ ์—†๋Š” ์ธก์  ํ•˜๋‚˜ ยท ์ง„์งœ ์—”์ง„(๋น ๋ฅด๊ฒŒ) ยท ์„œ๋ฒ„ ์•ˆ์—์„œ ์ฐจ๋ก€๋กœ.""" + for name in ("_derived", "E02_StabGround", "E03_StabSection", "initial_snapshot"): + shutil.rmtree(frozen / name, ignore_errors=True) + original = src.stations + + def pick(root): + rows = original(root) + wall = [r for r in rows if chainage_key(r["chainage_m"]) == WALL_KEY] + return wall + [r for r in rows if chainage_key(r["chainage_m"]) != WALL_KEY][:1] + + monkeypatch.setattr(router.src, "stations", pick) + + async def fake_project(_pid): + return frozen, {"company_id": 1, "project_kind": "STABILITY"} + + monkeypatch.setattr(e02, "_project", fake_project) + monkeypatch.setattr(worker, "analyze_station", _fast) + monkeypatch.setattr(router, "WORKERS", 1) + router._JOBS.clear() + return frozen + + +def _analyze() -> dict: + async def go(): + first = await router.analyze2d(UUID(PID)) + while router._TASKS: + await asyncio.gather(*list(router._TASKS)) + return first + + return asyncio.run(go()) + + +def _save_ground(root: Path, ground: dict) -> None: + ground_model.ground_path(root).parent.mkdir(parents=True, exist_ok=True) + ground_model.ground_path(root).write_text(json.dumps(ground), encoding="utf-8") + + +def test_๋ฒฝ_์ธก์ _์ž„๊ณ„๋ฉด์ด_๋ฒฝ์„_์•ˆ_์ง€๋‚จ(project): + assert _analyze()["status"] == "accepted" + assert router._JOBS[PID]["error"] is None + body = asyncio.run(router.result2d(UUID(PID), USER)) + walls = {r["key"]: r["walls"] for r in body["stations"]} + assert walls[WALL_KEY] >= 1 and sorted(walls.values())[0] == 0 + sec = asyncio.run(router.section(UUID(PID), WALL_KEY)) + polys = [Polygon(r["polygon"]) for r in sec["regions"] if r["material_id"] == "wall"] + assert polys and any(m["id"] == "wall" for m in sec["materials"]) + crossed = 0 + for a in sec["analyses"]: + line = LineString(a["critical"]["points"]) + for poly in polys: + assert line.intersection(poly).length <= CORNER_M + crossed += 1 + assert crossed == len(sec["analyses"]) > 0 + + +def test_๋ฒฝ_์žˆ๋Š”_์ธก์ ๋งŒ_์ง€๋ฌธ์ด_๋ฐ”๋€œ(project, monkeypatch): + ground = router._ground(project) + with_walls = {it["key"]: it["fp"] for it in router.plan_run(project, ground)["items"]} + monkeypatch.setattr(router, "_walls", lambda source, fp: {}) + plain = {it["key"]: it["fp"] for it in router.plan_run(project, ground)["items"]} + assert with_walls[WALL_KEY] != plain[WALL_KEY] + assert [with_walls[k] == plain[k] for k in plain if k != WALL_KEY] == [True] + + +def test_์ง€ํ•˜์ˆ˜_๊ฐˆ๋ž˜_๋ฐ”๊พธ๋ฉด_์ง€๋ฌธ_๋ฐ”๋€œ(project): + ground = router._ground(project) + before = router.plan_run(project, ground) + ground["groundwater"]["wet_mode"] = "ru" + after = router.plan_run(project, ground) + assert before["fp"] != after["fp"] + assert all(a["fp"] != b["fp"] for a, b in zip(before["items"], after["items"])) + + +def test_์ˆ˜์œ„์„ _๊ณ ๋ฅธ_๊ฐˆ๋ž˜(project): + _analyze() + key = next(r["key"] for r in asyncio.run(router.result2d(UUID(PID), USER))["stations"]) + first = asyncio.run(router.section(UUID(PID), key)) + kds, surface = first["water"]["wet"], first["surface"] + ground = router._ground(project) + ground["groundwater"].update(wet_mode="ru", ru=0.3) + _save_ground(project, ground) + assert asyncio.run(router.section(UUID(PID), key))["water"]["wet"] is None # r_u = ์„  ์—†์Œ + ground["groundwater"].update(wet_mode="depth_ratio", depth_ratio=0.5) + _save_ground(project, ground) + deep = asyncio.run(router.section(UUID(PID), key))["water"]["wet"] + assert kds and deep + # ๊นŠ์ด ๋น„์œจ ์„  = ๊ณต์‚ฌ ๋’ค ์ง€ํ‘œ ์•„๋ž˜ ยท ๋น„ํƒˆ ์ชฝ์€ ํ™•์‹คํžˆ ์•„๋ž˜(์—ฐ์ง ๋ฉด x ๋Š” ๋บŒ โ€” ์ง€ํ‘œ ๊ฐ’์ด ๋‘˜) + sx, sz = zip(*surface) + steps = {x for x in sx if sx.count(x) > 1} + below = [float(np.interp(x, sx, sz)) - z for x, z in deep if x not in steps] + assert min(below) >= -1e-6 and max(below) > 0.1 + + +def test_๋น ์ง„_ํšก๋‹จ_๋ชฉ๋ก(project): + source = src.source_dir(project) + row = src._cross_rows(source)[3] + path = src.resolve_cross_file(source, row) + moved = path.with_suffix(".bak") + path.rename(moved) + try: + body = asyncio.run(router.result2d(UUID(PID), USER)) + ground = asyncio.run(e02.get_ground(UUID(PID), USER)) + finally: + moved.rename(path) + assert body["missing_stations"] == [float(row["chainage_m"])] + assert ground["missing_stations"] == [float(row["chainage_m"])] + assert asyncio.run(router.result2d(UUID(PID), USER))["missing_stations"] == [] + + +def test_E05_๊ฐ€_E03_์ •๋ณธ์œผ๋กœ_๋ณด๊ณ ์„œ(project): + """E03 [ํ•ด์„] ์ •๋ณธ(stab2d_summary ยท stab2d_station)์œผ๋กœ E05 ๊ฐ€ ๋Ž โ€” ์‚ฌ๋ณธ ์•ˆ ์”€ ยท ์ˆ˜๊ฐ€ ๊ฐ™์Œ.""" + from E05_StabReport import E05_StabReport_Build as build + + _analyze() + assert not (project / "E03_StabSection" / "_derived").exists() # ์˜› ์‚ฌ๋ณธ ๊ผด ์—†์Œ + body = asyncio.run(router.result2d(UUID(PID), USER)) + inputs = build.gather(project) + rows = inputs["result2d"]["stations"] + assert len(rows) == len(body["stations"]) == 2 + assert WALL_KEY in {r["station_key"] for r in rows} + ng = sum(1 for r in body["stations"] if r["verdict_overall"] == "ng") + assert sum(1 for r in rows if r["verdict"] == "ng") == ng + e03 = {r["station_key"]: r for r in body["stations"]} + for r in rows: + mine = e03[r["station_key"]] + assert r["side"] == mine["side"] in ("cut", "fill") + assert r["fs_by_condition"] == mine["fs_by_condition"] + assert r["verdict"] == mine["verdict_overall"] in ("ok", "ng") + reps = inputs["representatives"] + assert reps and all(rep["result"] and rep["result"]["cases"] for rep in reps) + book = build.form() + jobs = build.build_program(book, inputs) + assert len([k for k, svg in jobs if svg.startswith("= len(reps) + + # ๋Œ€ํ‘œ ๋‹จ๋ฉด ๋” โ†’ E05 ๋ชฉ๋ก์—์„œ ๋น ์ง ยท representative.json = overrides ๋งŒ + key = reps[0]["station_key"] + asyncio.run( + router.put_representative(UUID(PID), router.RepresentativeRequest(overrides={key: False})) + ) + assert key not in [r["station_key"] for r in build.gather(project)["representatives"]] + saved = json.loads((project / router.REPRESENTATIVE).read_text("utf-8")) + assert saved == {"overrides": {key: False}} + + +def _case(fs, need): + return {"fs": fs, "required": need, "ok": fs >= need} + + +def test_result2d_์ง€๋ฐฐ_๋น„ํƒˆ_๊ธฐ์ค€_๋Œ€๋น„(): + """๊นŽ๊ธฐ 1.22/1.2(O.K) ยท ์Œ“๊ธฐ 1.25/1.3(N.G) โ€” fs ๋งŒ ๋‚ฎ์€ ๊นŽ๊ธฐ๊ฐ€ ์•„๋‹ˆ๋ผ ์Œ“๊ธฐ๊ฐ€ ์ง€๋ฐฐ ยท N.G.""" + from E05_StabReport import E05_StabReport_Build as build + + row = { + "key": "10.00", + "chainage_m": 10.0, + "sides": [{"side": "left", "role": "cut"}, {"side": "right", "role": "fill"}], + "cases": { + "dry": {"left": _case(1.6, 1.5), "right": _case(1.7, 1.5)}, + "wet": {"left": _case(1.22, 1.2), "right": _case(1.25, 1.3)}, + }, + "min": {"dry": 1.6, "wet": 1.22}, + "verdict": {"dry": "ok", "wet": "ng"}, + } + out = view.governing_row(row) + assert out["side"] == "fill" and out["fs_min"] == 1.25 + assert out["fs_by_condition"] == {"dry": 1.7, "wet": 1.25} + assert out["verdict_overall"] == "ng" and out["method"] == "bishop" + e05 = build._row2d(row, "bishop") + assert (e05["side"], e05["fs_by_condition"], e05["verdict"]) == ( + out["side"], + out["fs_by_condition"], + out["verdict_overall"], + ) + row["verdict"] = {"dry": "ok", "wet": "ok"} + assert view.governing_row(row)["verdict_overall"] == "ok" + bare = {**row, "cases": {"dry": {"left": {"fs": 1.4, "required": None}}}} + assert view.governing_row(bare)["side"] is None and view.governing_row(bare)["fs_min"] == 1.22 diff --git a/resources/tester/test_533_3_e04_engine.py b/resources/tester/test_533_3_e04_engine.py new file mode 100644 index 000000000..011197fd3 --- /dev/null +++ b/resources/tester/test_533_3_e04_engine.py @@ -0,0 +1,184 @@ +"""E04 ์—”์ง„ ์ž‡๊ธฐ(PLAN 533-3) โ€” ๋ณต์‚ฌ๋ณธ ์ธก์  โ†’ 533-1 ํ•ฉ์„ฑ ์ง€ํ˜• โ†’ 533-2 ํƒ์ƒ‰ โ†’ ์ €์žฅ๋ณธ ๊ผด ยท +ํƒ์ƒ‰ ๋…ธ๋“œ = ๊ตฌ๊ฐ„ ยท ํƒ์ƒ‰ ํญ ์•ˆ ยท ์ง„ํ–‰๋ฅ  ยท ๋Š๊ธฐ ยท ๊ณ ๋ฆฌ๋ฅผ ๋‹ฌ์•„๋„ ๊ฒฐ๊ณผ ๋ถˆ๋ณ€. + +./venv/Scripts/python.exe -m pytest resources/tester/test_533_3_e04_engine.py -q +""" + +from __future__ import annotations + +import math + +import numpy as np +import pytest + +from E04_Stab3D import E04_Stab3D_Run as run +from E04_Stab3D.E04_Stab3D_Engine_Column import Strata +from E04_Stab3D.E04_Stab3D_Engine_Search import SearchParams, search + +# ๋…ธ์„  = x ์ถ•(๋™์ชฝ) ยท ์™ผ์ชฝ(๋ถ) = ์˜ค๋ฅด๋ง‰ 0.6 ยท ์ธก์  20 m +CHAINAGES = [0.0, 20.0, 40.0, 60.0, 80.0, 100.0] +SLOPE = 0.6 + + +def _ground(y): + return 100.0 + SLOPE * y + + +def _cross(ch: float) -> dict: + offsets = np.arange(-25.0, 25.01, 0.5) + samples = [{"offset_m": o, "z": _ground(o), "valid": True} for o in offsets] + # ๋…ธ๋ฉด |o| โ‰ค 3 ํ‰ํ‰(100) ยท ์™ผ์ชฝ 1:1 ๊นŽ๊ธฐ ยท ์˜ค๋ฅธ์ชฝ 1:1.5 ์Œ“๊ธฐ ยท ๋ฐ”๊นฅ = ์ง€๋ฐ˜ + line = [] + for o in offsets: + if abs(o) <= 3: + z = 100.0 + elif o > 0: + z = min(100.0 + (o - 3), _ground(o)) + else: + z = max(100.0 - (-o - 3) / 1.5, _ground(o)) + line.append({"offset_m": o, "elevation_m": z}) + return { + "chainage_m": ch, + "frame": {"left_xy": [0.0, 1.0]}, + "samples": samples, + "design": { + "design_line": line, + "ground_type": "soil", + "road_edges": {"left": {"offset_m": 3.0}, "right": {"offset_m": -3.0}}, + }, + } + + +STATIONS = [ + {"chainage_m": ch, "label": f"No.{i}", "center_x": ch, "center_y": 0.0} + for i, ch in enumerate(CHAINAGES) +] + +SETTINGS = { + "start_m": 20.0, + "end_m": 60.0, + "condition": "dry", + "shape": "both", + "ellipsoid_ratio": 2.0, + "box_half_width_m": 12.0, + "cell_m": 1.0, + "spacing_m": 3.0, + "h_min_m": 2.0, + "h_max_m": 10.0, + "h_step_m": 2.0, + "dr_m": 1.0, + "max_depth_m": 6.0, + "v_min_m3": 5.0, + "v_max_m3": 3000.0, +} + + +@pytest.fixture +def root(tmp_path, monkeypatch): + models = tmp_path / "B04_PreProcess" / "models" + models.mkdir(parents=True) + xs = np.arange(-60.0, 160.0, 1.0) + ys = np.arange(-60.0, 60.0, 1.0) + xx, yy = np.meshgrid(xs, ys) + z = _ground(yy) + np.savez(models / "dtm_classification.npz", x=xs, y=ys, z=z, valid_mask=np.ones_like(z, bool)) + monkeypatch.setattr(run.source, "cross_section", lambda r, ch: _cross(float(ch))) + return tmp_path + + +def test_engine_raster_and_best(root): + seen: list[tuple[int, str]] = [] + raw = run.run_engine( + root, STATIONS[0:5], None, SETTINGS, lambda p, m: seen.append((p, m)), lambda: False + ) + grid = raw["grid"] + f = raw["f"] + assert f.shape == (grid["ny"], grid["nx"]) == raw["z"].shape + assert grid["dx"] == 1.0 + # ๊ฒฉ์ž = ๊ตฌ๊ฐ„ ์ธก์  ยฑ (ํƒ์ƒ‰ ํญ + ๊นŠ์ด + 10) + assert grid["x0"] == pytest.approx(0.0 - 28.0, abs=1.0) + finite = np.isfinite(f) + assert finite.sum() > 50 + best = raw["best"] + iy = round((best["y"] - grid["y0"]) / grid["dx"]) + ix = round((best["x"] - grid["x0"]) / grid["dx"]) + # ์ „์ฒด ์ตœ์†Œ = ๋ž˜์Šคํ„ฐ ์ตœ์†Œ(๊ทธ ํ™œ๋™์ฒด๊ฐ€ ๋ฎ๋Š” ์…€ ์ค‘ ํ•˜๋‚˜) ยท ์ค‘์‹ฌ = ํƒ์ƒ‰ ๋  ์•ˆ(x 20~60 ยท |y| โ‰ค 12) + assert np.nanmin(f) == pytest.approx(best["f"], rel=1e-6) + assert 20.0 - 1e-6 <= best["x"] <= 60.0 + 1e-6 and abs(best["y"]) <= 12.0 + 1e-6 + assert 0 <= ix < grid["nx"] and 0 <= iy < grid["ny"] + assert best["r"] > 0 and best["ratio"] in (1.0, 2.0) and 0 <= best["azimuth_deg"] < 360 + assert best["f"] == best["bishop"] or best["method"] == 1 # ๋‹ค์Šค๋ฆผ = Bishop(์ž˜๋ฆผ ์—†์Œ) + assert raw["reference_fs"] == 1.5 # ๋งˆ์Šคํ„ฐ ๊ธฐ๋ณธ ๊ฑด๊ธฐ + # ํ•ฉ์„ฑ ์ง€ํ‘œ โ€” ๋…ธ๋ฉด ์œ„(์ธก์  40 ยท o=0) = ์„ค๊ณ„ 100 ยท ๊นŽ๊ธฐ ๋ ๋ฐ–(o=+20 ยท ์›์ง€ํ˜•) = ์ง€๋ฐ˜ + z = raw["z"] + jy0 = round((0.0 - grid["y0"]) / grid["dx"]) + jx40 = round((40.0 - grid["x0"]) / grid["dx"]) + assert z[jy0, jx40] == pytest.approx(100.0, abs=0.05) + jyw = round((20.0 - grid["y0"]) / grid["dx"]) + assert z[jyw, jx40] == pytest.approx(_ground(20.0), abs=0.05) + # ์ง„ํ–‰๋ฅ  โ€” 0~100 ยท ๋‹จ๊ณ„ ์ด๋ฆ„ ยท ๋งˆ์ง€๋ง‰์ด ํƒ์ƒ‰ ๋ ๋ฌด๋ ต + assert seen and all(0 <= p <= 100 for p, _ in seen) and seen[-1][0] >= 80 + + +def test_engine_stop(root): + calls = {"n": 0} + + def stop(): + calls["n"] += 1 + return calls["n"] > 3 + + with pytest.raises(InterruptedError): + run.run_engine(root, STATIONS[0:5], None, SETTINGS, lambda p, m: None, stop) + + +def test_wet_lower_than_dry(root): + dry = run.run_engine(root, STATIONS[0:5], None, SETTINGS, lambda p, m: None, lambda: False) + wet = run.run_engine( + root, + STATIONS[0:5], + None, + {**SETTINGS, "condition": "wet"}, + lambda p, m: None, + lambda: False, + ) + assert wet["best"]["f"] < dry["best"]["f"] + assert wet["reference_fs"] == 1.3 # ์šฐ๊ธฐ ๊นŽ๊ธฐ 1.2 ยท ์Œ“๊ธฐ 1.3 ์ค‘ ํฐ ๊ฐ’ + + +def test_hooks_do_not_change_search(): + """533-2 ํƒ์ƒ‰์— ๋‹จ ์ง„ํ–‰๋ฅ  ยท ๋Š๊ธฐ ๊ณ ๋ฆฌ โ€” ๊ฒฐ๊ณผ ๋ถˆ๋ณ€(sub5 ์‹œํ—˜ ๊ผด ์ž‘์€ ๋น„ํƒˆ).""" + x = np.arange(-15.0, 35.0, 1.0) + y = np.arange(-15.0, 15.0, 1.0) + xx, yy = np.meshgrid(x, y) + zz = np.clip(xx * 0.5, 0, 10) + mat = [{"id": "m", "name": "m", "gamma_t": 19.0, "gamma_sat": 19.0, "c": 10.0, "phi": 25.0}] + strata = Strata(x, y, zz, np.zeros((0,) + zz.shape), mat) + base = dict(box=(-5, 22, -1, 1), spacing=2.0, h_min=2, h_max=14, h_step=2, dr=1.0, + max_depth=10, v_min=20, v_max=2e4, ratios=(1.0,)) # fmt: skip + plain = search(strata, SearchParams(**base)) + ticks: list[int] = [] + hooked = search( + strata, + SearchParams(**base, on_progress=lambda p, m: ticks.append(p), should_stop=lambda: False), + ) + assert hooked.best["governing"] == plain.best["governing"] + assert np.array_equal(np.isnan(hooked.f_min), np.isnan(plain.f_min)) + assert np.nanmax(np.abs(hooked.f_min - plain.f_min)) == 0 + assert ticks and min(ticks) >= 0 and max(ticks) <= 100 + with pytest.raises(InterruptedError): + search(strata, SearchParams(**base, should_stop=lambda: True)) + assert math.isfinite(plain.best["governing"]) + + +def test_node_mask_section_and_route_ends(root): + """๋…ธ๋“œ ์ž๋ฆฌ = ๊ตฌ๊ฐ„ ๋ˆ„๊ฐ€ ์•ˆ ยท ํƒ์ƒ‰ ํญ ์•ˆ ยท ๋…ธ์„  ์‹œ์  ์•ž(x < 0) ยท ์ข…์  ๋’ค(x > 100)๋Š” ๋บŒ.""" + x = np.arange(-30.0, 131.0, 1.0) + y = np.arange(-20.0, 21.0, 1.0) + grid = {"x": x, "y": y} + mask = run._node_mask(STATIONS, grid, 0.0, 100.0, 12.0) + xx, yy = np.meshgrid(x, y) + assert not mask[xx < -0.5].any() and not mask[xx > 100.5].any() + assert not mask[np.abs(yy) > 12.0].any() + assert mask[(np.abs(yy) <= 12.0) & (xx >= 1) & (xx <= 99)].all() + part = run._node_mask(STATIONS, grid, 20.0, 60.0, 12.0) + assert not part[(xx < 19.5) | (xx > 60.5)].any() and part[(yy == 0) & (xx == 40)].all() diff --git a/resources/tester/test_533_3_e04_logic.py b/resources/tester/test_533_3_e04_logic.py index dc95b46d5..b26ff6e6a 100644 --- a/resources/tester/test_533_3_e04_logic.py +++ b/resources/tester/test_533_3_e04_logic.py @@ -60,6 +60,10 @@ writeFileSync(process.argv[3], JSON.stringify({ sphere: mesh(M.slipMesh(c.grid, c.z, c.sphere)), ellipsoid: mesh(M.slipMesh(c.grid, c.z, c.ellipsoid)), cut: Array.from(M.cutCells(c.grid, c.z, c.sphere).keys()), + center: M.footprintCenter(c.grid, M.cutCells(c.grid, c.z, c.sphere), c.sphere), + centerEmpty: M.footprintCenter(c.grid, new Map(), c.sphere), + floored: Object.fromEntries(M.cutCells(c.grid, c.z, + { ...c.sphere, floor: [[7 * 21 + 10, 53.0], [7 * 21 + 11, 60.0]] }).entries()), holed: mesh(M.terrainWithout(c.grid, c.z, M.cutCells(c.grid, c.z, c.sphere))), hCenter: M.slipHeight(c.sphere, 110, 207), hEdge: M.slipHeight(c.sphere, 116.5, 207), @@ -178,6 +182,23 @@ def test_slip_mesh_vertices(ts, name): assert all(z < Z[i] for i, z in cut.items()) +def test_footprint_center(ts): + """์ง€๋„ ์ตœ์†Œ ํ‘œ์‹œ = ์ž˜๋ฆฐ ์…€ ์ค‘์‹ฌ ํ‰๊ท  ยท ์ž˜๋ฆฐ ์…€ ์—†์œผ๋ฉด ๊ตฌ ์ค‘์‹ฌ.""" + cut = [i for i, _ in _py_cut(SPHERE)] + x = sum(GRID["x0"] + i % GRID["nx"] for i in cut) / len(cut) + y = sum(GRID["y0"] + i // GRID["nx"] for i in cut) / len(cut) + assert ts["center"] == pytest.approx({"x": x, "y": y}) + assert ts["centerEmpty"] == {"x": SPHERE["x"], "y": SPHERE["y"]} + + +def test_floor_truncates_slip(ts): + """๋ชป ๋šซ๋Š” ์ธต โ€” ํ™œ๋™๋ฉด์ด ์ธต ์œ—๋ฉด ์•„๋ž˜๋กœ ์•ˆ ๋‚ด๋ ค๊ฐ ยท ์ธต์ด ์ง€ํ‘œ๋ณด๋‹ค ๋†’์œผ๋ฉด ์ž˜๋ฆฐ ์…€ ์•„๋‹˜.""" + center = 7 * 21 + 10 + assert ts["floored"][str(center)] == pytest.approx(53.0) # ํ™œ๋™๋ฉด 52 โ†’ ์ธต 53 + assert str(center + 1) not in ts["floored"] # ์ธต 60 โ‰ฅ ์ง€ํ‘œ 53.5 + assert ts["floored"][str(center - 1)] == pytest.approx(dict(_py_cut(SPHERE))[center - 1]) + + def test_terrain_hole_matches_cut(ts): """ํ™œ๋™์ฒด๋ฅผ ๊ฑท์–ด๋‚ธ ์ง€ํ‘œ = ์ง€ํ‘œ ๊ผญ์ง“์  โˆ’ ์ž˜๋ฆฐ ์…€ ยท ๊ตฌ๋ฉ ํ…Œ๋‘๋ฆฌ๋Š” ํ™œ๋™๋ฉด ๋ฉ”์‰ฌ์™€ ๊ฐ™์€ ๊ผญ์ง“์ .""" cut = [i for i, _ in _py_cut(SPHERE)] diff --git a/resources/tester/test_533_3_e04_router.py b/resources/tester/test_533_3_e04_router.py index 31a69f097..0e7793b5d 100644 --- a/resources/tester/test_533_3_e04_router.py +++ b/resources/tester/test_533_3_e04_router.py @@ -24,6 +24,27 @@ STATIONS = [ ] +def _fake_engine(root, stations, ground, settings, on_progress, should_stop): + """์—”์ง„ ์ž๋ฆฌ โ€” ๊ธธ ์‹œํ—˜์€ ์…ˆ ๋‚ด์šฉ์ด ์•„๋‹ˆ๋ผ ํ๋ฆ„(์ง€๋ฌธ ยท ๋Š๊ธฐ ยท ์ €์žฅ)์„ ๋ด„(์—”์ง„ = test_533_3_e04_engine).""" + on_progress(50, "๊ฐ€์งœ") + f = np.array([[1.4, 1.2, np.nan], [1.1, 0.9, 1.6]]) + return { + "grid": {"x0": 100.0, "y0": 200.0, "dx": 1.0, "nx": 3, "ny": 2}, + "z": np.full(f.shape, 500.0), + "f": f, + "v": np.where(np.isfinite(f), 120.0, np.nan), + "m": np.where(np.isfinite(f), 0.0, np.nan), + "best": { + "x": 101.0, "y": 201.0, "z": 505.0, "r": 6.0, "ratio": 1.0, "azimuth_deg": 0.0, + "f": 0.9, "volume": 120.0, "method": 0, + "hovland": 0.85, "bishop": 0.9, "janbu_corrected": 0.95, "truncated_ratio": 0.0, + "floor": [], + }, + "trials": 7, + "reference_fs": 1.5, + } # fmt: skip + + @pytest.fixture def project(tmp_path, monkeypatch): src = tmp_path / "E01_StabRegister" / "source" @@ -36,25 +57,12 @@ def project(tmp_path, monkeypatch): z = 500.0 + 0.4 * (yy - 200.0) + 0.002 * (xx - 200.0) ** 2 # ๋…ธ์„  ์˜† ๋น„ํƒˆ np.savez(models / "dtm_classification.npz", x=xs, y=ys, z=z, valid_mask=np.ones_like(z, bool)) monkeypatch.setattr(run.source, "stations", lambda root: STATIONS) - monkeypatch.setattr( - run.source, - "db_snapshot", - lambda root: { - "route_points": [ - { - "model_x": s["center_x"], - "model_y": 200.0, - "chainage_m": s["chainage_m"], - "sequence_num": i, - } - for i, s in enumerate(STATIONS) - ] - }, - ) async def fake_root(project_id): return tmp_path + monkeypatch.setattr(run, "run_engine", _fake_engine) + monkeypatch.setattr(router_mod, "_project_root", fake_root) router_mod._JOBS.clear() return tmp_path @@ -102,7 +110,8 @@ def test_analyze_cached_force_and_result(client, project, monkeypatch): assert first == {"status": "accepted", "key": "0.00_100.00"} done = _wait(client) assert done["error"] is None and done["percent"] == 100 - assert done["summary"]["cells"] > 0 + assert done["summary"]["cells"] == 5 and done["summary"]["verdict"] == "ng" + assert done["summary"]["by_method"] == {"hovland": 0.85, "bishop": 0.9, "janbu_corrected": 0.95} # ๊ฐ™์€ ์ž…๋ ฅ = ์…ˆํ•˜์ง€ ์•Š์Œ again = client.post(f"/api/projects/{PID}/stab/analyze3d", json=BODY).json() assert again["status"] == "cached" and len(calls) == 1 @@ -174,13 +183,7 @@ def test_cancel_and_restart(client, monkeypatch): == 404 ) # ๋‹ค์‹œ ์‹œ์ž‘ - monkeypatch.setattr( - run, - "run_engine", - run._preview_engine.__wrapped__ - if hasattr(run._preview_engine, "__wrapped__") - else lambda *a: run._preview_engine(a[0], a[1], a[3], a[4], a[5]), - ) + monkeypatch.setattr(run, "run_engine", _fake_engine) assert ( client.post(f"/api/projects/{PID}/stab/analyze3d", json=BODY).json()["status"] == "accepted" ) diff --git a/resources/tester/test_534_3_e05_report.py b/resources/tester/test_534_3_e05_report.py index d46f0b3e5..41da1f402 100644 --- a/resources/tester/test_534_3_e05_report.py +++ b/resources/tester/test_534_3_e05_report.py @@ -1,7 +1,10 @@ -"""534-3(๋นŒ๋” ยท storage) โ€” E05 ์•ˆ์ •์„ฑํ‰๊ฐ€ ๋ณด๊ณ ์„œ `E05_StabReport_Build` ยท `_Store`. +"""534-3 ยท 534-5(๋นŒ๋” ยท storage ยท ๊ธธ ยท ์ธ์‡„) โ€” E05 ์•ˆ์ •์„ฑํ‰๊ฐ€ ๋ณด๊ณ ์„œ `E05_StabReport_Build` ยท +`_Section` ยท `_Print` ยท `_Store` ยท `_Router`. -๊ฐ€์งœ E ํ”„๋กœ์ ํŠธ(2D ์š”์•ฝ ยท ์ธก์ ๋ณ„ ์ €์žฅ๋ณธ ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด ยท ์ง€๋ฐ˜ ์กฐ๊ฑด ยท 3D ์ €์žฅ๋ณธ ํ•˜๋‚˜ ยท ๋ณต์‚ฌ๋ณธ ์ฝ๊ธฐ๋Š” ๋ฐ”๊ฟ” ๋ผ์›€)๋กœ -์ธก์  ์ˆ˜ = ์ค„ ์ˆ˜ ยท N.G ์ˆ˜ ยท ๊ทธ๋ฆผ ํŒŒ์ผ ์ˆ˜ ยท ๊ฐฑ์‹  ๋’ค ์‚ฌ์šฉ์ž ๋ชซ ๋ถˆ๋ณ€ ยท ๋ฎ์ž„ ๋ชฉ๋ก ยท ์ดˆ๊ธฐํ™” ยท xlsx ๋˜์ฝ๊ธฐ ยท PDF ์ชฝ ์ˆ˜. +๊ฐ€์งœ E ํ”„๋กœ์ ํŠธ(E03 ํŒŒ์ƒ ์ €์žฅ๋ณธ ๊ผด 2D ์š”์•ฝ ยท ์ธก์ ๋ณ„ ์ €์žฅ๋ณธ ยท ๋Œ€ํ‘œ ๋‹จ๋ฉด ์ผฌ/๋” ยท ์ง€๋ฐ˜ ์กฐ๊ฑด ยท +3D ์ €์žฅ๋ณธ ํ•˜๋‚˜ ยท ๋ณต์‚ฌ๋ณธ ์ฝ๊ธฐ๋Š” ๋ฐ”๊ฟ” ๋ผ์›€)๋กœ ์ธก์  ์ˆ˜ = ์ค„ ์ˆ˜ ยท N.G ์ˆ˜ ยท ๊ทธ๋ฆผ ํŒŒ์ผ ์ˆ˜ ยท +๊ฐฑ์‹  ๋’ค ์‚ฌ์šฉ์ž ๋ชซ ๋ถˆ๋ณ€ ยท ๋ฎ์ž„ ๋ชฉ๋ก ยท ์ดˆ๊ธฐํ™” ยท ํŒ 409 ยท xlsx ๋˜์ฝ๊ธฐ ยท PDF ์ชฝ ์ˆ˜ ยท +๋ชฉ์ฐจ ์ชฝ ๋ฒˆํ˜ธ = ์‹ค์ œ ยท ๋ถ€๋ก ์ ˆํŽธ ยท ์ธก์ ํ‘œ ์ชฝ ๋‚˜๋ˆ” ๊ณ ๋ฅด๊ฒŒ ยท ์˜› ์ž‘์—…๋ณธ ๊ฐฑ์‹ (๋ชฉ์ฐจ ์ž๋ฆฌ ยท ์„œ์‹ ๋ถ™์ž„). """ import io @@ -12,18 +15,24 @@ from pathlib import Path import pytest from openpyxl import load_workbook +from common_util.common_util_derived_store import derived_dir, write_derived from common_util.common_util_print_pdf import PrintError -from common_util.common_util_spreadsheet import workbook_pdf from common_util.common_util_workbook_xlsx import workbook_to_xlsx from E02_StabGround import E02_StabGround_Model as ground_model from E05_StabReport import E05_StabReport_Build as build from E05_StabReport import E05_StabReport_Figure as figure +from E05_StabReport import E05_StabReport_Print as printer from E05_StabReport import E05_StabReport_Store as store pytestmark = pytest.mark.skipif(shutil.which("node") is None, reason="node ๊ฐ€ ์—†์Œ") N = 30 # ์ธก์  ์ˆ˜ NG_AT = {3, 10, 17, 24} # N.G ์ธก์  -REPS = [("grid:3", "lowest_fs"), ("grid:12", "cut_15m"), ("grid:20", "fill_10m")] +REPS = [("grid:3", "lowest"), ("grid:12", "cut_height"), ("grid:20", "fill_height")] +# ์ž๋™ ๋Œ€ํ‘œ(๋†’์ด ยท ํ•˜์œ„ 5) ์ค‘ REPS ๋งŒ ๋‚จ๊ธฐ๋Š” ์‚ฌ์šฉ์ž ๋” +OFF = {"grid:0": False, "grid:5": False, "grid:10": False, "grid:17": False, "grid:24": False} +KIND_SUMMARY, KIND_STATION = "stab2d_summary", "stab2d_station" +SLICES = 24 # ์ž„๊ณ„๋ฉด ์ ˆํŽธ ์ˆ˜ +REQUIRED = {"dry": {"cut": 1.5, "fill": 1.5}, "wet": {"cut": 1.2, "fill": 1.3}} def _key(i: int) -> str: @@ -31,29 +40,76 @@ def _key(i: int) -> str: def _summary(i: int, bump: float = 0.0) -> dict: - ng = i in NG_AT + """E03 ์š”์•ฝ ์ค„ ๊ผด โ€” ์ง€๋ฐฐ ๋น„ํƒˆ(ํ™€์ˆ˜ = ๊นŽ๊ธฐ ์™ผ์ชฝ ยท ์ง์ˆ˜ = ์Œ“๊ธฐ ์˜ค๋ฅธ์ชฝ)๋งŒ ๋‚ฎ์€ ์•ˆ์ „์œจ.""" + ng = i in NG_AT and not bump dry = 1.31 if ng else 1.7 + i / 100 + bump - return { - "station_key": _key(i), - "chainage_m": 20.0 * i, - "side": "cut" if i % 2 else "fill", - "fs_min": dry - 0.2, - "fs_by_condition": {"dry": dry, "wet": dry - 0.2}, - "method": "bishop", - "verdict": "ng" if ng else "ok", + role = "cut" if i % 2 else "fill" + side = "left" if role == "cut" else "right" + other = "right" if side == "left" else "left" + heights = {"cut": 15.0 if i == 12 else 6.0, "fill": 10.0 if i == 20 else 6.0} + fs = {"dry": dry, "wet": dry - 0.2} + cases = { + cond: { + side: {"fs": fs[cond], "required": REQUIRED[cond][role]}, + other: {"fs": 9.0, "required": REQUIRED[cond]["fill" if role == "cut" else "cut"]}, + } + for cond in fs } + return { + "key": _key(i), + "chainage_m": 20.0 * i, + "label": f"No.{i}", + "fp": f"st{i}", + "sides": [ + {"side": "left", "role": "cut", "height_m": heights["cut"]}, + {"side": "right", "role": "fill", "height_m": heights["fill"]}, + ], + "cases": cases, + "min": {c: min(v["fs"] for v in cases[c].values()) for c in cases}, + "verdict": { + c: "ok" if all(v["fs"] >= v["required"] for v in cases[c].values()) else "ng" + for c in cases + }, + "error": None, + } + + +def _write_summary(root: Path, fp: str, rows: list[dict]) -> None: + value = { + "fingerprint": fp, + "computed_at": "2026-10-25T10:00:00", + "conditions": ["dry", "wet"], + "method": "bishop", + "stations": rows, + } + write_derived(root, KIND_SUMMARY, fp, value) + + +def _slice(i: int) -> dict: + return { + "width": 0.5, "height": 1.0 + i / 10, "alpha_deg": 10.0 + i, "weight": 9.0 + i, + "surcharge": 0.0, "pore_pressure": 2.0, "base_length": 0.51, "c": 15.0, + "phi_deg": 30.0, "normal_eff": 8.0 + i, "shear_mobilized": 6.0 + i / 10, + } # fmt: skip def _slip(F: float) -> dict: pts = [[-6.0 + k, 101.0 - 0.3 * (6 - abs(k - 6))] for k in range(13)] - return {"F": F, "kind": "circle", "points": pts, "center": [0.0, 112.0], "radius": 12.5} + return { + "F": F, + "kind": "circle", + "points": pts, + "center": [0.0, 112.0], + "radius": 12.5, + "slices": [_slice(i) for i in range(SLICES)], + } def _station_doc(i: int) -> dict: s = _summary(i) cases = [] for cond in ("dry", "wet"): - F = s["fs_by_condition"][cond] + F = s["min"][cond] for side, role in (("left", "cut"), ("right", "fill")): cases.append( { @@ -93,32 +149,13 @@ def _cross(chainage: float) -> dict: @pytest.fixture def project(tmp_path, monkeypatch) -> Path: root = tmp_path / "e_project" - d2 = root / "E03_StabSection" / "_derived" - (d2 / "results").mkdir(parents=True) - (d2 / "result2d.json").write_text( - json.dumps( - { - "input_fingerprint": "fp-1", - "computed_at": "2026-10-25T10:00:00", - "stations": [_summary(i) for i in range(N)], - } - ), - encoding="utf-8", - ) + _write_summary(root, "fp-1", [_summary(i) for i in range(N)]) for key, _ in REPS: i = int(key.split(":")[1]) - (d2 / "results" / f"grid_{i}.json").write_text( - json.dumps(_station_doc(i)), encoding="utf-8" - ) + write_derived(root, KIND_STATION, f"st{i}", {"out": _station_doc(i), "error": None}, keep=N) + (root / "E03_StabSection").mkdir() (root / "E03_StabSection" / "representative.json").write_text( - json.dumps( - { - "rule": {"fill_h_m": 10, "cut_h_m": 15, "lowest_n": 5}, - "stations": [{"station_key": k, "reason": r, "enabled": True} for k, r in REPS] - + [{"station_key": _key(5), "reason": "user", "enabled": False}], - } - ), - encoding="utf-8", + json.dumps({"overrides": OFF}), encoding="utf-8" ) ground = ground_model.pick(ground_model.load_defaults()) ground["stations"] = [ @@ -138,7 +175,15 @@ def project(tmp_path, monkeypatch) -> Path: { "fingerprint": "3d-1", "settings": {}, - "summary": {"start_m": 0.0, "end_m": 200.0, "f_min": 1.42, "volume": 830.0}, + "summary": { + "start_m": 0.0, + "end_m": 200.0, + "f_min": 1.42, + "volume": 830.0, + "condition": "wet", + "reference_fs": 1.3, + "verdict": "ok", + }, } ), encoding="utf-8", @@ -181,6 +226,29 @@ def test_์ฒ˜์Œ_๋งŒ๋“ฆ_์ธก์ ์ˆ˜_NG_๊ทธ๋ฆผ(project): j = [c for a1, c in by["์ธก์ ํ‘œ"]["์นธ"].items() if a1.startswith("J") and int(a1[1:]) >= 6] assert sum(c.get("๊ฐ’") == "N.G" and c["์„œ์‹"] == build.ST_NG for c in j) == len(NG_AT) assert sum(c.get("๊ฐ’") == "O.K" and c["์„œ์‹"] == build.ST_OK for c in j) == N - len(NG_AT) + k = [ + c.get("๊ฐ’") + for a1, c in by["์ธก์ ํ‘œ"]["์นธ"].items() + if a1.startswith("K") and int(a1[1:]) >= 6 + ] + assert k.count("๋Œ€์ฑ… ๊ฒ€ํ† ") == len(NG_AT) and k.count("-") == N - len(NG_AT) + # ์ธก์ ํ‘œ ์ชฝ ๋‚˜๋ˆ” = ๊ณ ๋ฅด๊ฒŒ(๋ฉ์ด ํฌ๊ธฐ ์ฐจ โ‰ค 1 ยท ํ•œ ์ค„ ๊ผฌ๋ฆฌ ์—†์Œ) + cuts = by["์ธก์ ํ‘œ"]["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] + sizes = [b - a for a, b in zip([5, *cuts], [*cuts, 5 + N])] + assert len(sizes) >= 2 and max(sizes) - min(sizes) <= 1 + # ๋ถ€๋ก ์ ˆํŽธ โ€” ๋Œ€ํ‘œ ๋‹จ๋ฉด๋งˆ๋‹ค ์ง€๋ฐฐ ์กฐ๊ฑด ํ•œ ๋ฉ์ด ยท ์ €ํ•ญ๋ ฅ = ๋™์› ์ „๋‹จ ร— Fs ยท ๋ฉ์ด ๋ ์ชฝ ๋‚˜๋ˆ” + s10 = by["๋ถ€๋ก_์ ˆํŽธ"]["์นธ"] + block = 3 + SLICES + 1 + assert by["๋ถ€๋ก_์ ˆํŽธ"]["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] == [block * (i + 1) for i in range(len(REPS))] + assert "๊ฑด๊ธฐ" in s10["A1"]["๊ฐ’"] and s10["A1"]["๊ฐ’"].startswith("๋ถ€๋ก โ‘  ์ ˆํŽธ ๊ณ„์‚ฐํ‘œ โ€” No.3") + assert s10["A4"]["๊ฐ’"] == 1 and s10["M4"]["๊ฐ’"] == pytest.approx(6.0 * 1.31) + assert s10[f"A{block}"]["๊ฐ’"] == "ํ•ฉ๊ณ„" + assert s10[f"E{block}"]["๊ฐ’"] == pytest.approx(sum(9.0 + i for i in range(SLICES))) + assert book["์„œ์‹"][s10["B4"]["์„œ์‹"]]["ํฌ๊ธฐ"] == 8 + # ๋ชฉ์ฐจ โ€” ๋ณธ๋ฌธ ์‹œํŠธ ์ฐจ๋ก€ ยท ์ชฝ ๋ฒˆํ˜ธ(๋ชฉ์ฐจ = 1) + toc = by["๋ชฉ์ฐจ"]["์นธ"] + assert [toc[f"B{r}"]["๊ฐ’"] for r in (4, 5)] == [printer.TITLES["s2"], printer.TITLES["s3"]] + assert toc["A10"]["๊ฐ’"] == "๋ถ€๋ก" # ๋ณด๊ฐ•๊ฒ€ํ†  ์—†์Œ โ†’ ๋ณธ๋ฌธ 6 + ๋ถ€๋ก and toc["C4"]["๊ฐ’"] == 2 figs = sorted((project / "E05_StabReport" / "fig").glob("*.png")) assert len(figs) == len(REPS) + 1 and meta["figures"][-1] == build.BAND_KEY assert [f["์—ด์‡ "] for f in by["๋Œ€ํ‘œ๋‹จ๋ฉด"]["๊ทธ๋ฆผ"]] == [build.fig_key(k) for k, _ in REPS] @@ -196,12 +264,101 @@ def test_์ฒ˜์Œ_๋งŒ๋“ฆ_์ธก์ ์ˆ˜_NG_๊ทธ๋ฆผ(project): assert s3["A26"]["๊ฐ’"] == "ํ’ํ™”ํ† " and s3["B32"]["๊ฐ’"].startswith("0 ~ 100 m ์œ„ ์ง€์ธต") assert s3["A32"]["๊ฐ’"] == "๋ถ•์ ํ† " and s3["B13"]["๊ฐ’"] == 1.3 s6 = by["3D์š”์•ฝ"]["์นธ"] - assert s6["D20"]["๊ฐ’"] == 1.42 and s6["H20"] == {"๊ฐ’": "N.G", "์„œ์‹": build.ST_NG} - assert s6["E20"]["๊ฐ’"] == pytest.approx(1.11) # 0~200 m ์•ˆ 2D ์ตœ์†Œ + assert s6["D20"]["๊ฐ’"] == 1.42 and s6["H20"] == {"๊ฐ’": "O.K", "์„œ์‹": build.ST_OK} # E04 ํŒ์ • + assert s6["E20"]["๊ฐ’"] == pytest.approx(1.11) # 0~200 m ์•ˆ 2D ์šฐ๊ธฐ ์ตœ์†Œ(3D ์™€ ๊ฐ™์€ ์กฐ๊ฑด) assert (build.initial_dir(project) / build.REPORT).is_file() assert saved["fingerprint"] == build.analysis_fingerprint(project) +def test_๊ด‘์—ญ์š”์•ฝ_E07_์ €์žฅ๋ณธ(project): + """E07 ์ €์žฅ๋ณธ ์—†์Œ = ใ€Œ๊ด‘์—ญ ํ•ด์„ ์ „ใ€ ยท ์ƒ๊น€ = ์ง€๋ฌธ ๋ฐ”๋€œ โ†’ ๊ฐฑ์‹  ๋’ค ๋“ฑ๊ธ‰ ๋ฉด์  ยท ์œ„ํ—˜ ์ธก์  ์ค„.""" + _create(project) + book, meta = store.load(project) + assert _sheets(book)["๊ด‘์—ญ์š”์•ฝ"]["์นธ"]["B5"]["๊ฐ’"].startswith("๊ด‘์—ญ ํ•ด์„ ์ „") + keys = ("stable", "moderate", "quasi", "lower", "upper", "defended") + area = { + "settings": { + "band_m": 100.0, + "cell_m": 2.0, + "gamma_t": 17.5, + "gamma_sat": 18.5, + "c": 5.0, + "c_hi": 10.0, + "phi": 27.0, + "phi_hi": 30.0, + "c_root": 0.0, + "soil_depth_m": 1.5, + "scenario": "sinmap", + "tr_lo": 2000.0, + "tr_hi": 3000.0, + "level_lo_pct": 5.0, + "level_hi_pct": 20.0, + }, + "computed_at": "2026-10-25T21:00:00", + "stations": [ + { + "chainage_m": 0.0, + "cells": 9, + "fs_min": 1.6, + "si_min": 1.6, + "worst_class": 1, + "risk_area_m2": 0.0, + "risk_ratio": 0.0, + "level": "low", + "slope_max_deg": 12.0, + "label": "No.0", + }, + { + "chainage_m": 20.0, + "cells": 9, + "fs_min": 0.8, + "si_min": 0.0, + "worst_class": 6, + "risk_area_m2": 36.0, + "risk_ratio": 1.0, + "level": "high", + "slope_max_deg": 41.5, + "label": "No.1", + }, + { + "chainage_m": 40.0, + "cells": 9, + "fs_min": 0.95, + "si_min": 0.7, + "worst_class": 4, + "risk_area_m2": 8.0, + "risk_ratio": 0.0556, + "level": "mid", + "slope_max_deg": 33.0, + "label": "No.2", + }, + ], + "summary": { + "areas_m2": dict(zip(keys, (100.0, 20.0, 12.0, 8.0, 0.0, 36.0))), + "station_levels": {"low": 1, "mid": 1, "high": 1}, + }, + } + path = project / "E07_StabArea" / "_derived" / "area.json" + path.parent.mkdir(parents=True) + path.write_text(json.dumps(area, ensure_ascii=False), encoding="utf-8") + assert build.analysis_fingerprint(project) != meta["fingerprint"] + store.refresh(project) + book, _ = store.load(project) + s7 = _sheets(book)["๊ด‘์—ญ์š”์•ฝ"]["์นธ"] + assert [s7[f"C{r}"]["๊ฐ’"] for r in range(12, 18)] == [100.0, 20.0, 12.0, 8.0, 0.0, 36.0] + assert [s7[f"A{r}"]["๊ฐ’"] for r in (22, 23)] == ["No.1", "No.2"] and "A24" not in s7 + assert ( + s7["E22"]["๊ฐ’"] == "๋†’์Œ (100.0 %)" + and s7["E23"]["๊ฐ’"] == "๋ณดํ†ต (5.6 %)" + and s7["F23"]["๊ฐ’"] == 8.0 + and [s7[f"G{r}"]["๊ฐ’"] for r in (12, 13, 14)] == [1, 1, 1] + and s7["F13"]["๊ฐ’"] == "5 ~ 20 %" + and s7["B7"]["๊ฐ’"].startswith("ํ‘œ์ค€ โ€” SINMAP") + ) + vals = book["๊ณ„์‚ฐ๊ฐ’"]["s7"] + assert vals["C18"] == {"์ˆ˜": "176"} and vals["D17"]["์ˆ˜"].startswith("20.45") + + def test_๊ฐฑ์‹ _์‚ฌ์šฉ์ž๋ชซ_๋ถˆ๋ณ€_๋ฎ์ž„(project): _create(project) book, _ = store.load(project) @@ -215,11 +372,9 @@ def test_๊ฐฑ์‹ _์‚ฌ์šฉ์ž๋ชซ_๋ถˆ๋ณ€_๋ฎ์ž„(project): by = _sheets(book) by["์ธก์ ํ‘œ"]["์นธ"]["A6"]["๊ฐ’"] = "๊ณ ์นœ ์ธก์ " store.save(project, book) - r2 = project / build.RESULT2D - doc = json.loads(r2.read_text(encoding="utf-8")) - doc["stations"][3] = _summary(4, 0.5) | {"station_key": _key(3), "chainage_m": 60.0} - doc["input_fingerprint"] = "fp-2" - r2.write_text(json.dumps(doc), encoding="utf-8") + rows = [_summary(i) for i in range(N)] + rows[3] = _summary(3, 0.5) + _write_summary(project, "fp-2", rows) assert build.analysis_fingerprint(project) != store.load(project)[1]["fingerprint"] try: out = store.refresh(project) @@ -242,6 +397,29 @@ def test_๊ฐฑ์‹ _์‚ฌ์šฉ์ž๋ชซ_๋ถˆ๋ณ€_๋ฎ์ž„(project): ) +def test_์˜›_์ž‘์—…๋ณธ_๊ฐฑ์‹ _๋ชฉ์ฐจ์ž๋ฆฌ_์„œ์‹(project): + """534-5 ์ „ ์ž‘์—…๋ณธ(๋ชฉ์ฐจ ยท ๋ถ€๋ก ์—†์Œ ยท ์„œ์‹ 18 ๊ฐœ) โ†’ ๊ฐฑ์‹  = ๋ชฉ์ฐจ๋Š” ํ‘œ์ง€ ๋‹ค์Œ ยท ๋ถ€๋ก์€ ๋ ยท + ์ƒˆ ์„œ์‹ ๋ถ™์ž„(์˜› ์„œ์‹ ๋ฒˆํ˜ธ ๊ทธ๋Œ€๋กœ) ยท ์‚ฌ์šฉ์ž ๋ชซ ๋ถˆ๋ณ€.""" + _create(project) + book, meta = store.load(project) + book["์‹œํŠธ"] = [s for s in book["์‹œํŠธ"] if s["id"] not in ("s9", "s10")] + book["์„œ์‹"] = book["์„œ์‹"][:18] + [{"ํฌ๊ธฐ": 30}] # ์‚ฌ์šฉ์ž๊ฐ€ ๋”ํ•œ ์„œ์‹ = 18 ๋ฒˆ + _sheets(book)["ํ‘œ์ง€"]["์นธ"]["C10"] = {"๊ฐ’": "์‚ฐ๋ฆผ์ฒญ", "์„œ์‹": 18} + meta["signs"] = {k: v for k, v in meta["signs"].items() if k not in ("s9", "s10")} + store.save(project, book) + (project / "E05_StabReport" / build.META).write_text(json.dumps(meta), encoding="utf-8") + store.refresh(project) + book, _ = store.load(project) + ids = [s["id"] for s in book["์‹œํŠธ"]] + assert ids[:3] == ["s1", "s9", "s2"] and ids[-1] == "s10" + by = _sheets(book) + assert by["ํ‘œ์ง€"]["์นธ"]["C10"] == {"๊ฐ’": "์‚ฐ๋ฆผ์ฒญ", "์„œ์‹": 18} and book["์„œ์‹"][18] == { + "ํฌ๊ธฐ": 30 + } + head = book["์„œ์‹"][by["๋ถ€๋ก_์ ˆํŽธ"]["์นธ"]["A3"]["์„œ์‹"]] + assert head["ํฌ๊ธฐ"] == 8 and head["๊ตต๊ฒŒ"] and len(book["์„œ์‹"]) == len(build.form()["์„œ์‹"]) + 1 + + def test_3D_์—†์Œ_2D_์—†์Œ(project): shutil.rmtree(project / "E04_Stab3D") inputs = build.gather(project) @@ -250,7 +428,7 @@ def test_3D_์—†์Œ_2D_์—†์Œ(project): by = _sheets(book) assert by["3D์š”์•ฝ"]["์นธ"]["A20"]["๊ฐ’"] == "3D ํ•ด์„ ์ „" assert by["์ž…๋ ฅ์กฐ๊ฑด"]["์นธ"]["B7"]["๊ฐ’"] == "3D ํ•ด์„ ์ „" - (project / build.RESULT2D).unlink() + shutil.rmtree(derived_dir(project, KIND_SUMMARY)) with pytest.raises(build.MissingInput): build.gather(project) @@ -261,24 +439,36 @@ def test_xlsx_๋˜์ฝ๊ธฐ_pdf_์ชฝ์ˆ˜(project): figs = store.figures_of(project, book) assert len(figs) == len(REPS) + 1 wb = load_workbook(io.BytesIO(workbook_to_xlsx(book, figs))) - assert wb.sheetnames == ["ํ‘œ์ง€", "๊ฐœ์š”", "์ž…๋ ฅ์กฐ๊ฑด", "์ธก์ ํ‘œ", "๋Œ€ํ‘œ๋‹จ๋ฉด", "3D์š”์•ฝ"] + assert wb.sheetnames == [ + "ํ‘œ์ง€", "๋ชฉ์ฐจ", "๊ฐœ์š”", "์ž…๋ ฅ์กฐ๊ฑด", "์ธก์ ํ‘œ", "๋Œ€ํ‘œ๋‹จ๋ฉด", "3D์š”์•ฝ", "๊ด‘์—ญ์š”์•ฝ", "๋ถ€๋ก_์ ˆํŽธ", + ] # fmt: skip assert len(wb["๋Œ€ํ‘œ๋‹จ๋ฉด"]._images) == len(REPS) and len(wb["3D์š”์•ฝ"]._images) == 1 assert [b.id for b in wb["๋Œ€ํ‘œ๋‹จ๋ฉด"].row_breaks.brk] == [28, 56, 84] assert wb["์ธก์ ํ‘œ"]["J9"].value == "N.G" and wb["์ธก์ ํ‘œ"]["J9"].fill.fgColor.rgb.endswith( "FFC7CE" ) - book.pop("๊ณ„์‚ฐ๊ฐ’", None) try: - pdf = workbook_pdf(book, figs) + pdf = printer.report_pdf(book, figs) except PrintError as error: pytest.skip(f"๋ธŒ๋ผ์šฐ์ € ์—†์Œ โ€” {error}") from pypdf import PdfReader texts = [p.extract_text() for p in PdfReader(io.BytesIO(pdf)).pages] stab = [i for i, t in enumerate(texts) if "์‚ฐ์ • ์•ˆ์ „์œจ" in t] - assert stab and all("์‚ฐ์ • ์•ˆ์ „์œจ" in texts[i] for i in stab) - # ํ‘œ์ง€ 1 + ๊ฐœ์š” 1 + ์ž…๋ ฅ์กฐ๊ฑด 1 + ์ธก์ ํ‘œ n + ๋Œ€ํ‘œ๋‹จ๋ฉด 3 + 3D 1 - assert len(texts) == 3 + len(stab) + len(REPS) + 1 + assert len(stab) == len(book["์‹œํŠธ"][4]["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"]) + 1 # ์ธก์ ํ‘œ = ๋‚˜๋ˆˆ ๋ฉ์ด ์ˆ˜ + # ํ‘œ์ง€ 1 + ๋ชฉ์ฐจ 1 + ๊ฐœ์š” 1 + ์ž…๋ ฅ์กฐ๊ฑด 1 + ์ธก์ ํ‘œ n + ๋Œ€ํ‘œ๋‹จ๋ฉด 3 + 3D 1 + ๊ด‘์—ญ 1 + ๋ถ€๋ก 3 + assert len(texts) == 4 + len(stab) + len(REPS) + 1 + 1 + len(REPS) + body = len(texts) - 1 + assert f"/ {body}" not in texts[0] and "๊ฒ€์ฆ ์ž„๋„" not in texts[0].split("๋ณด๊ณ ์„œ")[0] + assert all(f"- {i} / {body} -" in texts[i] and "๊ฒ€์ฆ ์ž„๋„" in texts[i] for i in range(1, body)) + # ๋ชฉ์ฐจ ์ชฝ ๋ฒˆํ˜ธ = ์‹ค์ œ ์ชฝ(๋ณธ๋ฌธ n ์ชฝ = PDF n + 1 ๋ฒˆ์งธ ์ชฝ) + toc = texts[1] + marks = {"์ธก์ ๋ณ„ ์•ˆ์ •์„ฑ ํŒ์ • ์ผ๋žŒํ‘œ": "์‚ฐ์ • ์•ˆ์ „์œจ", printer.TITLES["s5"]: "๋Œ€ํ‘œ ๋‹จ๋ฉด โ‘ "} + marks[printer.TITLES["s10"]] = "์ ˆํŽธ ๊ณ„์‚ฐํ‘œ" + for title, inside in marks.items(): + line = next(x for x in toc.splitlines() if title in x) + page = int(line.split()[-1]) + assert inside in texts[page], (title, page) def test_๋‹จ๋ฉด_๊ทธ๋ฆผ_SVG(): @@ -294,3 +484,51 @@ def test_๋‹จ๋ฉด_๊ทธ๋ฆผ_SVG(): ) assert svg.startswith("= 4 + + +def test_๊ธธ_์ €์žฅ_409_๊ฐฑ์‹ _์ดˆ๊ธฐํ™”_๋‚ด๋ณด๋‚ด๊ธฐ(project, monkeypatch): + from fastapi import FastAPI + from fastapi.testclient import TestClient + + from common_util.common_util_auth import verify_session + from E05_StabReport import E05_StabReport_Router as router_mod + + async def fake_project(_pid): + return project, 3 + + monkeypatch.setattr(router_mod, "_project", fake_project) + app = FastAPI() + app.include_router(router_mod.router) + app.dependency_overrides[verify_session] = lambda: {"role": "USER", "company_id": 3} + client = TestClient(app) + url = "/api/projects/3d3776a1-2973-4385-8dcc-fdc6e49a900e/stab/report" + try: + got = client.get(url).json() + except PrintError as error: + pytest.skip(f"๋ธŒ๋ผ์šฐ์ € ์—†์Œ โ€” {error}") + assert got["editable"] and got["stale"] is False and len(got["book"]["์‹œํŠธ"]) == 9 + book = got["book"] + _sheets(book)["ํ‘œ์ง€"]["์นธ"]["C10"] = {"๊ฐ’": "์‚ฐ๋ฆผ์ฒญ", "์„œ์‹": 5} + assert client.put(url, json={"rev": "์˜›ํŒ", "book": book}).status_code == 409 + saved = client.put(url, json={"rev": got["rev"], "book": book}).json() + assert saved["rev"] != got["rev"] + assert client.post(f"{url}/refresh", json={"rev": got["rev"]}).status_code == 409 + out = client.post(f"{url}/refresh", json={"rev": saved["rev"]}).json() + assert out["overwritten"] == [] and _sheets(out["book"])["ํ‘œ์ง€"]["์นธ"]["C10"]["๊ฐ’"] == "์‚ฐ๋ฆผ์ฒญ" + fig = client.get(f"{url}/fig", params={"key": build.fig_key("grid:3")}) + assert fig.status_code == 200 and fig.content[:4] == b"\x89PNG" + assert client.get(f"{url}/fig", params={"key": "../x"}).status_code == 400 + x = client.post(f"{url}/xlsx", json={"book": out["book"]}) + assert x.status_code == 200 and load_workbook(io.BytesIO(x.content)).sheetnames[0] == "ํ‘œ์ง€" + back = client.post(f"{url}/reset").json() + assert "๊ฐ’" not in _sheets(back["book"])["ํ‘œ์ง€"]["์นธ"].get("C10", {}) + + +def test_์„œ๋ช…_ํ™”๋ฉด_์™•๋ณต_๊ฐ™์Œ(project): + """JS ๋ฅผ ๊ฑฐ์นœ ์ฑ…(10.0 โ†’ 10)๋„ ๊ฐ™์€ ์„œ๋ช… โ€” ์•ˆ ๊ณ ์นœ ์‹œํŠธ๋ฅผ ๋ฎ์ž„์œผ๋กœ ์•Œ๋ฆฌ์ง€ ์•Š์Œ.""" + book = build.form() + build.build_program(book, build.gather(project)) + s3 = _sheets(book)["์ž…๋ ฅ์กฐ๊ฑด"] + assert s3["์นธ"]["B22"]["๊ฐ’"] == 10.0 + trip = json.loads(json.dumps(s3).replace("10.0", "10")) + assert trip["์นธ"]["B22"]["๊ฐ’"] == 10 and build.sheet_sign(trip) == build.sheet_sign(s3) diff --git a/resources/tester/test_534_3_stab_report_form.py b/resources/tester/test_534_3_stab_report_form.py index 89ac63e32..5e983c265 100644 --- a/resources/tester/test_534_3_stab_report_form.py +++ b/resources/tester/test_534_3_stab_report_form.py @@ -36,7 +36,10 @@ STYLE_KEYS = { } # fmt: skip PRINT_KEYS = {"์šฉ์ง€", "๋ฐฉํ–ฅ", "๋ฐฐ์œจ", "๋งž์ถค", "์—ฌ๋ฐฑ", "๋จธ๋ฆฌํ–‰", "์ชฝ๋‚˜๋ˆ”"} A1 = re.compile(r"^([A-Z]{1,3})(\d+)$") -NAMES = ["ํ‘œ์ง€", "๊ฐœ์š”", "์ž…๋ ฅ์กฐ๊ฑด", "์ธก์ ํ‘œ", "๋Œ€ํ‘œ๋‹จ๋ฉด", "3D์š”์•ฝ"] +NAMES = [ + "ํ‘œ์ง€", "๋ชฉ์ฐจ", "๊ฐœ์š”", "์ž…๋ ฅ์กฐ๊ฑด", "์ธก์ ํ‘œ", "๋Œ€ํ‘œ๋‹จ๋ฉด", "3D์š”์•ฝ", "๊ด‘์—ญ์š”์•ฝ", "๋ณด๊ฐ•๊ฒ€ํ† ", + "๋ถ€๋ก_์ ˆํŽธ", +] # fmt: skip needs_node = pytest.mark.skipif(shutil.which("node") is None, reason="node ๊ฐ€ ์—†์Œ") @@ -148,7 +151,8 @@ def test_์–‘์‹_๊ณ„์•ฝ(): book = _form() _contract(book) assert [s["์ด๋ฆ„"] for s in book["์‹œํŠธ"]] == NAMES - assert [s["id"] for s in book["์‹œํŠธ"]] == ["s1", "s2", "s3", "s4", "s5", "s6"] + ids = ["s1", "s9", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s10"] + assert [s["id"] for s in book["์‹œํŠธ"]] == ids def test_์–‘์‹_์ธ์‡„_์ชฝ๋‚˜๋ˆ”_๋จธ๋ฆฌํ–‰_๊ทธ๋ฆผ(): @@ -159,7 +163,18 @@ def test_์–‘์‹_์ธ์‡„_์ชฝ๋‚˜๋ˆ”_๋จธ๋ฆฌํ–‰_๊ทธ๋ฆผ(): assert by["์ธก์ ํ‘œ"]["์ธ์‡„"]["๋ฐฉํ–ฅ"] == "landscape" and by["์ธก์ ํ‘œ"]["์ธ์‡„"]["๋จธ๋ฆฌํ–‰"] == [4, 5] assert by["๋Œ€ํ‘œ๋‹จ๋ฉด"]["์ธ์‡„"]["์ชฝ๋‚˜๋ˆ”"] == [BLOCK] figs = {n: len(s.get("๊ทธ๋ฆผ", [])) for n, s in by.items()} - assert figs == {"ํ‘œ์ง€": 0, "๊ฐœ์š”": 0, "์ž…๋ ฅ์กฐ๊ฑด": 0, "์ธก์ ํ‘œ": 0, "๋Œ€ํ‘œ๋‹จ๋ฉด": 1, "3D์š”์•ฝ": 1} + assert figs == { + "ํ‘œ์ง€": 0, + "๋ชฉ์ฐจ": 0, + "๋ถ€๋ก_์ ˆํŽธ": 0, + "๊ฐœ์š”": 0, + "์ž…๋ ฅ์กฐ๊ฑด": 0, + "์ธก์ ํ‘œ": 0, + "๋Œ€ํ‘œ๋‹จ๋ฉด": 1, + "3D์š”์•ฝ": 1, + "๊ด‘์—ญ์š”์•ฝ": 0, + "๋ณด๊ฐ•๊ฒ€ํ† ": 0, + } # N.G ๋นจ๊ฐ• ์กฐ๊ฑด๋ถ€ ์„œ์‹ โ€” ์ธก์ ํ‘œ ํŒ์ • ์—ด ยท ์„œ์‹ ํ‘œ์— N.G ์นธ ์„œ์‹(๋นŒ๋”๊ฐ€ ์ธ์‡„ ยท xlsx ์šฉ์œผ๋กœ ์ง์ ‘ ์”€) assert by["์ธก์ ํ‘œ"]["์กฐ๊ฑด๋ถ€์„œ์‹"][0]["๋ฒ”์œ„"][0]["c0"] == 9 assert any(st.get("๊ธ€์ž์ƒ‰") == "#C00000" and st.get("์ฑ„์›€") for st in _form()["์„œ์‹"]) @@ -222,8 +237,9 @@ def test_๊ฒฌ๋ณธ_pdf_์ชฝ์ˆ˜_๋จธ๋ฆฌํ–‰(png): ) sections = [i for i, t in enumerate(texts) if "๋Œ€ํ‘œ ๋‹จ๋ฉด" in t and "์•ˆ์ „์œจ Fs" in t] assert len(sections) == 2 # ๋‹จ๋ฉด๋งˆ๋‹ค ํ•œ ์ชฝ - # ํ‘œ์ง€ 1 + ๊ฐœ์š” 1 + ์ž…๋ ฅ์กฐ๊ฑด 1 + ์ธก์ ํ‘œ n + ๋Œ€ํ‘œ๋‹จ๋ฉด 2 + 3D 1 - assert len(pages) == 1 + 1 + 1 + len(stab) + 2 + 1 + # ํ‘œ์ง€ 1 + ๋ชฉ์ฐจ 1 + ๊ฐœ์š” 1 + ์ž…๋ ฅ์กฐ๊ฑด 1 + ์ธก์ ํ‘œ n + ๋Œ€ํ‘œ๋‹จ๋ฉด 2 + 3D 1 + ๊ด‘์—ญ 1 + ๋ณด๊ฐ• 1 + ๋ถ€๋ก 1 + # (์–‘์‹ ๋นˆ ํŒ) + assert len(pages) == 1 + 1 + 1 + 1 + len(stab) + 2 + 1 + 1 + 1 + 1 assert all(float(pages[i].mediabox.width) > float(pages[i].mediabox.height) for i in stab) html = spreadsheet.print_htmls([{"book": book, "figures": {k: png for k in figs}}])[0] assert html.count(" dry["F"] + + +def test_wedge_not_formed(): + # ๊ต์„ ์ด ์‚ฌ๋ฉด๋ณด๋‹ค ๊ฐ€ํŒŒ๋ฆ„ โ†’ ์‚ฌ๋ฉด์œผ๋กœ ์•ˆ ๋‚˜์˜ด + assert _wedge((141, 75), (219, 75), (180, 50), 35) is None + + +# ---- Markland ๊ฒฝ๊ณ„ ------------------------------------------------------------- +SLOPE = {"dip_direction": 90.0, "dip": 70.0} + + +def _j(dd, dip, phi=30.0, jid="J"): + return {"id": jid, "dip_direction": dd, "dip": dip, "phi": phi} + + +@pytest.mark.parametrize( + "dd,dip,hit", + [ + (109.9, 45, True), + (110.1, 45, False), + (70.1, 45, True), + (90, 30.1, True), + (90, 29.9, False), + (90, 69.9, True), + (90, 70.1, False), + ], +) +def test_markland_planar(dd, dip, hit): + assert (eng.kinematic(SLOPE, [_j(dd, dip)])["planar"] == ["J"]) is hit + + +def test_markland_planar_apparent_daylight(): + """์ธก๋ฐฉ 15ยฐ โ€” ๊ฒ‰๋ณด๊ธฐ ์‚ฌ๋ฉด ๊ฒฝ์‚ฌ(tanฯˆf cos15ยฐ)๋ฅผ ๋„˜๋Š” ์ ˆ๋ฆฌ๋Š” ์•ˆ ๋‚˜์˜ด.""" + app = eng.apparent_dip(70, 90, 105) + assert eng.kinematic(SLOPE, [_j(105, app - 0.1)])["planar"] == ["J"] + assert eng.kinematic(SLOPE, [_j(105, app + 0.1)])["planar"] == [] + + +@pytest.mark.parametrize( + "dd,dip,hit", [(270, 50.1, True), (270, 49.9, False), (289.9, 80, True), (290.1, 80, False)] +) +def test_markland_toppling(dd, dip, hit): + """ฯˆp > 90 โˆ’ ฯˆf + ฯ† = 50 ยท ์—ญ๋ฐฉํ–ฅ ยฑ20.""" + assert (eng.kinematic(SLOPE, [_j(dd, dip)])["toppling"] == ["J"]) is hit + + +def test_markland_wedge_plunge_bounds(): + a, b = _j(50, 60, jid="A"), _j(130, 60, jid="B") + trend, plunge = eng.intersection(a, b) + assert trend == pytest.approx(90.0, abs=1e-6) + hit = eng.kinematic(SLOPE, [a, b])["wedge"] + assert hit and hit[0]["ids"] == ["A", "B"] + # ฯ† ๋ฅผ ์„ ์ฃผ๊ฐ ์œ„๋กœ ์˜ฌ๋ฆฌ๋ฉด ์‚ฌ๋ผ์ง + hi = [dict(a, phi=plunge + 0.1), dict(b, phi=plunge + 0.1)] + assert eng.kinematic(SLOPE, hi)["wedge"] == [] + # ์‚ฌ๋ฉด์„ ๊ต์„ ๋ณด๋‹ค ์™„๋งŒํ•˜๊ฒŒ ํ•˜๋ฉด ์•ˆ ๋‚˜์˜ด + assert eng.kinematic({"dip_direction": 90, "dip": plunge - 0.1}, [a, b])["wedge"] == [] + + +# ---- ํˆฌ์˜ ---------------------------------------------------------------------- +@pytest.mark.parametrize("kind", net.PROJECTIONS) +def test_projection_round_trip(kind): + rng = np.random.default_rng(536) + for trend, plunge in zip(rng.uniform(0, 360, 200), rng.uniform(0, 89.9, 200)): + x, y = net.project(trend, plunge, kind) + t2, p2 = net.inverse(x, y, kind) + assert p2 == pytest.approx(plunge, abs=2e-3) + # ์ขŒํ‘œ๋Š” ์†Œ์ˆ˜ ๋‹ค์„ฏ์งธ ์ž๋ฆฌ โ€” ์ค‘์‹ฌ ๊ฐ€๊นŒ์ด๋Š” ์ถ”ํ–ฅ ์˜ค์ฐจ๊ฐ€ 1e-5 / r rad ๊นŒ์ง€ + assert eng.ang_diff(t2, trend) <= math.degrees(1e-5 / math.hypot(x, y)) + 1e-6 + + +def test_projection_known_radii(): + assert net.radius(0, "equal_angle") == pytest.approx(1.0) + assert net.radius(0, "equal_area") == pytest.approx(1.0) + assert net.radius(90, "equal_angle") == pytest.approx(0.0) + assert net.radius(45, "equal_angle") == pytest.approx(math.tan(math.radians(22.5))) + assert net.radius(45, "equal_area") == pytest.approx( + math.sqrt(2) * math.sin(math.radians(22.5)) + ) + # ๋™์ชฝ ์ˆ˜ํ‰์„  = (1, 0) ยท ๋ถ์ชฝ = (0, 1) + assert net.project(90, 0, "equal_angle") == pytest.approx([1.0, 0.0], abs=1e-5) + assert net.project(0, 0, "equal_area") == pytest.approx([0.0, 1.0], abs=1e-5) + + +def _inside(pt, poly): + x, y = pt + hit = False + for (x1, y1), (x2, y2) in zip(poly, poly[1:] + poly[:1]): + if (y1 > y) != (y2 > y) and x < x1 + (y - y1) * (x2 - x1) / (y2 - y1): + hit = not hit + return hit + + +@pytest.mark.parametrize("kind", net.PROJECTIONS) +def test_zones_match_kinematic(kind): + """์œ„ํ—˜ ์˜์—ญ ๋‹ค๊ฐํ˜• ์•ˆ ๊ทน์  ยท ๊ต์„  = Markland ํŒ์ •(๊ฒฝ๊ณ„ 1ยฐ ๋  ๋ฐ– ํ‘œ๋ณธ).""" + rng = np.random.default_rng(7) + phi = 30.0 + data = net.stereonet(SLOPE, [], phi, kind) + checked = 0 + for dd, dip in zip(rng.uniform(0, 360, 1500), rng.uniform(1, 89, 1500)): + kin = eng.kinematic(SLOPE, [_j(dd, dip, phi)]) + near = ( + abs(eng.ang_diff(dd, 90) - 20) < 1 + or abs(eng.ang_diff(dd, 270) - 20) < 1 + or abs(dip - phi) < 1 + or abs(dip - eng.apparent_dip(70, 90, dd)) < 1 + or abs(dip - 50) < 1 + ) + if near: + continue + pole = net.project(*eng.pole(dd, dip), kind) + assert _inside(pole, data["zones"]["planar"]) == bool(kin["planar"]), (dd, dip) + assert _inside(pole, data["zones"]["toppling"]) == bool(kin["toppling"]), (dd, dip) + checked += 1 + assert checked > 1000 + + +# ---- ์ •๋ณธ ยท ์ธก์  ์‚ฌ๋ฉด ------------------------------------------------------------ +def _doc(): + doc = model.build_initial() + doc["sets"] = [ + { + "id": "J1", + "name": "J1", + "from_m": 0, + "to_m": 100, + "dip_direction": 90, + "dip": 45, + "phi": 30, + "c": 5, + "persistence_m": None, + } + ] + return doc + + +def test_check_and_clean(): + assert model.check_joints(_doc()) == [] + bad = _doc() + bad["sets"][0].update(dip=95, to_m=-1, name=" ") + bad["sets"].append(dict(bad["sets"][0])) + bad["settings"]["crack"] = {"mode": "depth", "z": None} + paths = {e["path"] for e in model.check_joints(bad)} + assert {"sets.0.dip", "sets.0.to_m", "sets.0.name", "sets.1.id", "settings.crack.z"} <= paths + clean = model.clean(dict(_doc(), extra=1)) + assert set(clean) == {"version", "sets", "settings"} + assert clean["sets"][0]["dip"] == 45.0 + + +def _cross(side_cut: str): + """์ขŒ(+offset) ๋˜๋Š” ์šฐ ๊นŽ๊ธฐ โ€” ๋…ธ์„  ๋ถ์ชฝ ์ง„ํ–‰(left = ์„œ์ชฝ โˆ’x) ยท ์›์ง€๋ฐ˜ ๊นŽ๊ธฐ ์ชฝ์œผ๋กœ 0.6 ์˜ค๋ฆ„ ยท + ๋…ธ๋ฉด 99 ยท ๊นŽ๊ธฐ 1:0.4 ยท ์Œ“๊ธฐ 1:1.5.""" + sign = 1.0 if side_cut == "left" else -1.0 + offs = np.arange(-20, 20.5, 0.5) + ground = [[float(o), 100 + 0.6 * sign * o] for o in offs] + design = [] + for o, z in ground: + d = sign * o + if abs(o) <= 2: + z = 99.0 + elif d > 2: + z = min(z, 99 + 2.5 * (d - 2)) + else: + z = max(z, 99 - (abs(d) - 2) / 1.5) + design.append([o, z]) + return ( + { + "frame": {"left_xy": [-1.0, 0.0]}, + "samples": [{"offset_m": o, "elevation_m": z} for o, z in ground], + }, + { + "section_mode": f"{side_cut}_cut", + "cut_slope_ratio": 0.4, + "road_edges": {"left": {"offset_m": 2.0}, "right": {"offset_m": -2.0}}, + "design_line": [{"offset_m": o, "elevation_m": z} for o, z in design], + }, + ) + + +@pytest.mark.parametrize("side,dd", [("left", 90.0), ("right", 270.0)]) +def test_station_slope_orientation(side, dd): + """์ขŒ = ์„œ์ชฝ ์‚ฐ ยท ์‚ฌ๋ฉด์ด ๋™(๋…ธ๋ฉด)์œผ๋กœ ๊ธฐ์šบ = ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ 90 ยท ์šฐ๋Š” 270 ยท ๊ฒฝ์‚ฌ = atan(1/0.4).""" + cross, design = _cross(side) + slopes = model.station_slopes(cross, design) + assert len(slopes) == 1 and slopes[0]["side"] == side + assert slopes[0]["dip_direction"] == pytest.approx(dd) + assert slopes[0]["dip"] == pytest.approx(math.degrees(math.atan(2.5)), abs=0.01) + assert slopes[0]["height_m"] > 1 + assert slopes[0]["upper_dip"] == pytest.approx(math.degrees(math.atan(0.6)), abs=0.5) + + +def test_analyze_station(monkeypatch): + cross, design = _cross("left") + monkeypatch.setattr( + model, + "rock_stations", + lambda _s: [{"chainage_m": 20.0, "label": "STA", "ground_type": "ripping_rock"}], + ) + monkeypatch.setattr(model.src, "cross_section", lambda _s, _c: {**cross, "design": design}) + ground = { + "materials": [], + "stations": [], + "safety_factors": {"dry": {"cut": 1.5}, "wet": {"cut": 1.2}}, + "seismic": {"enabled": False}, + } + out = model.analyze(None, _doc(), ground) + assert len(out) == 1 + e = out[0] + assert e["kinematic"]["planar"] == ["J1"] + assert {c["condition"] for c in e["cases"]} == {"dry", "wet"} + dry = next(c for c in e["cases"] if c["condition"] == "dry") + assert dry["required"] == 1.5 and dry["ok"] == (dry["F"] >= 1.5) + assert e["verdict"] in ("OK", "NG") + assert e["gamma_r"] == model.DEFAULT_GAMMA_R + nets = model.station_net(e, _doc()["settings"]) + assert set(nets) == {"equal_angle", "equal_area", "phi"} + assert nets["equal_area"]["zones"]["planar"] diff --git a/resources/tester/test_536_2_e07_area.py b/resources/tester/test_536_2_e07_area.py new file mode 100644 index 000000000..6bfe08965 --- /dev/null +++ b/resources/tester/test_536_2_e07_area.py @@ -0,0 +1,268 @@ +"""536-2 โ€” E07 ๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ `E07_StabArea_Engine` ยท `_Router`. + +๋ฌดํ•œ์‚ฌ๋ฉด ํ•ด์„ํ•ด(๊ฑด์กฐ ยท ํฌํ™” ยท ์ ์ฐฉ) ยท SINMAP ์•ˆ์ • ์ง€์ˆ˜(๋‹ซํžŒ ๊ผด = ํ‘œ๋ณธ ยท ๋“ฑ๊ธ‰ ๊ฒฝ๊ณ„) ยท +D8 ์ง‘์ˆ˜๋ฉด์  ๊ฒฌ๋ณธ(๊ธฐ์šด ํ‰๋ฉด ยท V ๊ณจ์งœ๊ธฐ) ยท ์Šต์œค์ง€์ˆ˜ ยท SI ํ•˜ํ•œ / ์ƒํ•œ ํ•ด์„ํ•ด ยท ์‹œ๋‚˜๋ฆฌ์˜ค ๋‘˜(ํ‘œ์ค€ ยท ์ตœ์•…) ยท +๊ฐ€์งœ ๋ณต์‚ฌ๋ณธ์œผ๋กœ ๋  ยท ์ธก์ ํ‘œ ยท ์ €์žฅ๋ณธ ยท ์ง€๋ฌธ. +""" + +import base64 +import json +import math + +import numpy as np +import pytest + +from E07_StabArea import E07_StabArea_Engine as engine +from E07_StabArea import E07_StabArea_Router as router_mod + +SOIL = dict(gamma_t=18.0, gamma_sat=20.0, c=0.0, c_hi=0.0, phi=35.0, phi_hi=35.0) +SAT = dict(scenario="saturated") + + +def _fs(beta_deg: float, m: float, **kw) -> float: + p = engine.AreaParams(**{**SOIL, **kw}) + return float(engine.infinite_slope_fs(np.array([math.radians(beta_deg)]), np.array([m]), p)[0]) + + +def test_๋ฌดํ•œ์‚ฌ๋ฉด_ํ•ด์„ํ•ด_๊ฑด์กฐ_ํฌํ™”_์ ์ฐฉ(): + t = math.tan(math.radians(35)) / math.tan(math.radians(30)) + assert _fs(30, 0.0) == pytest.approx(t, rel=1e-12) # ๊ฑด์กฐ c = 0 โ†’ tanฯ† / tanฮฒ + assert _fs(30, 1.0) == pytest.approx((20 - 9.81) / 20 * t, rel=1e-12) # ํฌํ™” โ†’ ฮณ' / ฮณsat ๋ฐฐ + # ์†๊ณ„์‚ฐ โ€” c 5 ยท ๋ฟŒ๋ฆฌ 2 ยท z 1.5 ยท ฮฒ 35ยฐ ยท m 0.5 + b, z = math.radians(35), 1.5 + w = 18 * z * 0.5 + 20 * z * 0.5 + n = (18 * z * 0.5 + (20 - 9.81) * z * 0.5) * math.cos(b) ** 2 + hand = (7 + n * math.tan(math.radians(35))) / (w * math.sin(b) * math.cos(b)) + assert _fs(35, 0.5, c=5.0, c_root=2.0) == pytest.approx(hand, rel=1e-12) + assert math.isinf(_fs(0, 1.0)) # ํ‰ํ‰ = ํ™œ๋™ ์—†์Œ + + +def test_SINMAP_์•ˆ์ •์ง€์ˆ˜_๋‹ซํžŒ๊ผด_ํ‘œ๋ณธ(): + p = engine.AreaParams( + gamma_t=17.5, gamma_sat=18.5, c=0.0, c_hi=8.0, phi=25.0, phi_hi=38.0, **SAT + ) + beta = np.radians(np.array([10.0, 30.0, 35.0, 40.0, 60.0])) + m = np.ones_like(beta) + si = engine.stability_index(beta, m, p) + a, b = engine._terms(beta, m, p) + rng = np.random.default_rng(1) + c = rng.uniform(0, 8, 400_000) + t = rng.uniform(math.tan(math.radians(25)), math.tan(math.radians(38)), 400_000) + for k in range(len(beta)): + lo = 0 * a[k] + math.tan(math.radians(25)) * b[k] + fs = c * a[k] + t * b[k] + if lo >= 1: + assert si[k] == pytest.approx(lo) # Fs_min โ‰ฅ 1 โ†’ SI = Fs_min + elif fs.max() < 1: + assert si[k] == 0.0 + else: + assert si[k] == pytest.approx((fs > 1).mean(), abs=3e-3) # P(Fs > 1) + assert si[0] >= 1 and 0 < si[1] < 1 and si[4] == 0 # ์„ธ ๊ฐˆ๋ž˜ ๋ชจ๋‘ ์ง€๋‚จ + # ํญ 0(๋ฒ”์œ„ ์—†์Œ) โ€” ๊ฒฐ์ • Fs ์™€ ๊ฐ™๊ฑฐ๋‚˜ 0 + q = engine.AreaParams(**SOIL, **SAT) + fs = engine.infinite_slope_fs(beta, m, q) + si0 = engine.stability_index(beta, m, q) + assert np.allclose(si0, np.where(fs >= 1, fs, 0.0)) + + +def test_SINMAP_๋“ฑ๊ธ‰_๊ฒฝ๊ณ„(): + si = np.array([np.inf, 1.5, 1.4999, 1.25, 1.2499, 1.0, 0.9999, 0.5, 0.4999, 1e-9, 0.0, np.nan]) + assert engine.classify(si).tolist() == [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 0] + + +def test_D8_์ง‘์ˆ˜๋ฉด์ _๊ธฐ์šดํ‰๋ฉด_V๊ณจ์งœ๊ธฐ(): + cell = 2.0 + ny, nx = 5, 7 + xx = np.arange(nx)[None, :] * cell + plane = np.repeat(100.0 - 0.5 * xx, ny, axis=0) # ๋™์ชฝ์œผ๋กœ ๊ธฐ์›€ โ†’ ํ–‰๋งˆ๋‹ค ๋™์ชฝ์œผ๋กœ ํ๋ฆ„ + a = engine.catchment_area(plane, cell) + assert np.allclose(a, (np.arange(nx)[None, :] + 1) * cell) + yy = np.arange(9)[:, None] * cell + valley = 100.0 + np.abs(yy - 8.0) * 1.0 - 0.1 * np.arange(nx)[None, :] * cell + a = engine.catchment_area(valley, cell) + assert a[4, -1] == pytest.approx(9 * nx * cell) # ์ „๋ถ€๊ฐ€ ๊ณจ ์ถœ๊ตฌ๋กœ + assert np.all(a[4, :-1] < a[4, 1:]) # ๊ณจ ๋”ฐ๋ผ ๋Š˜์–ด๋‚จ + + +def test_์Šต์œค์ง€์ˆ˜(): + beta = np.radians(np.array([30.0, 30.0, 0.0])) + a = np.array([100.0, 1e6, 50.0]) + w = engine.wetness(beta, a, 1 / 2000) + assert w[0] == pytest.approx(100 / (2000 * 0.5)) # R a / (T sinฮฒ) + assert w[1] == 1.0 and w[2] == 1.0 # ๋„˜์นจ ยท ํ‰ํ‰ = ํฌํ™” + p = engine.AreaParams(**SOIL, tr_lo=2000, tr_hi=3000) + assert engine.x_range(p) == (1 / 3000, 1 / 2000) + assert engine.wet_m(beta, a, p).tolist() == w.tolist() # ๊ธฐ๋ณธ = ๊ฐ€์žฅ ์ –์€ ์ชฝ + assert engine.wet_m(beta, a, engine.AreaParams(**SOIL, **SAT)).tolist() == [1.0] * 3 + + +SINMAP = dict(gamma_t=18.0, gamma_sat=18.0, c=2.0, c_hi=6.0, phi=30.0, phi_hi=40.0) + + +def _sinmap_fs(beta, a, c, tanphi, x, z=1.5, g=18.0): + """Pack 1998 ์‹ โ€” FS = [C + cosฮธ(1 โˆ’ W r) tanฯ†] / sinฮธ ยท C = c / (h ฯs g) ยท h = z cosฮธ.""" + w = min(x * a / math.sin(beta), 1.0) + h = z * math.cos(beta) + return (c / (h * g) + math.cos(beta) * (1 - w * 9.81 / g) * tanphi) / math.sin(beta) + + +def test_SI_ํ•˜ํ•œ_์ƒํ•œ_ํ•ด์„ํ•ด(): + """ฮณt = ฮณsat ์ด๋ฉด ์—”์ง„ = SINMAP ์›์‹ ยท ํ•˜ํ•œ = (c, ฯ† ์•„๋ž˜ ยท T/R ์•„๋ž˜) ยท ์ƒํ•œ = (์œ„ ยท T/R ์œ„).""" + p = engine.AreaParams(**SINMAP, tr_lo=1000, tr_hi=3000) + t1, t2 = math.tan(math.radians(30)), math.tan(math.radians(40)) + beta = np.radians(np.array([15.0, 33.0, 38.0, 45.0, 70.0])) + area = np.array([20.0, 400.0, 300.0, 800.0, 5000.0]) + si = engine.stability_index(beta, area, p) + rng = np.random.default_rng(7) + n = 300_000 + c, t, x = rng.uniform(2, 6, n), rng.uniform(t1, t2, n), rng.uniform(1 / 3000, 1 / 1000, n) + seen = set() + for k, (b, ak) in enumerate(zip(beta, area)): + lo = _sinmap_fs(b, ak, 2.0, t1, 1 / 1000) + hi = _sinmap_fs(b, ak, 6.0, t2, 1 / 3000) + fs = engine.infinite_slope_fs( + beta[k : k + 1], engine.wet_m(beta[k : k + 1], area[k : k + 1], p), p + ) + assert fs[0] == pytest.approx(lo, rel=1e-12) # ๊ฒฐ์ • Fs = ํ•˜ํ•œ + if lo >= 1: + assert si[k] == pytest.approx(lo, rel=1e-12) + seen.add("lo") + elif hi < 1: + assert si[k] == 0.0 + seen.add("zero") + else: + w = np.minimum(x * ak / math.sin(b), 1) + h = 1.5 * math.cos(b) + mc = (c / (h * 18) + math.cos(b) * (1 - w * 9.81 / 18) * t) / math.sin(b) + assert si[k] == pytest.approx((mc > 1).mean(), abs=3e-3) # P(Fs > 1) ยท T/R ๋„ ๋ฒ”์œ„ + seen.add("prob") + assert seen == {"lo", "zero", "prob"} + + +def test_์‹œ๋‚˜๋ฆฌ์˜ค_๋‘˜_์ตœ์•…์ด_๋”_๋‚˜์จ(): + beta = np.radians(np.linspace(5, 60, 56)) + area = np.full_like(beta, 50.0) + std = engine.stability_index(beta, area, engine.AreaParams(**SINMAP)) + worst = engine.stability_index(beta, area, engine.AreaParams(**SINMAP, **SAT)) + assert np.all(worst <= std + 1e-12) # ์™„์ „ ํฌํ™” = ์•„๋ž˜ ๊ฒฝ๊ณ„ + assert (engine.classify(worst) >= engine.RISK_CLASS).sum() > ( + engine.classify(std) >= engine.RISK_CLASS + ).sum() + # ๋‹ค ์ –๋Š” ์ง‘์ˆ˜(xยทa / sinฮฒ โ‰ฅ 1) ๋ฉด ๋‘ ์‹œ๋‚˜๋ฆฌ์˜ค๊ฐ€ ๊ฐ™์Œ + big = np.full_like(beta, 1e7) + assert np.allclose( + engine.stability_index(beta, big, engine.AreaParams(**SINMAP)), worst, rtol=1e-12 + ) + + +def _decode(text: str, dtype: str, grid: dict) -> np.ndarray: + return np.frombuffer(base64.b64decode(text), dtype=dtype).reshape(grid["ny"], grid["nx"]) + + +@pytest.fixture() +def project(tmp_path, monkeypatch): + """๊ฐ€์งœ E ๋ณต์‚ฌ๋ณธ โ€” ์„œ์ชฝ์ด ๋‚ฎ์€ 30ยฐ ๋น„ํƒˆ(๋™์„œ 200 m ยท ๋‚จ๋ถ 100 m) ยท ๋…ธ์„  = ๋‚จ๋ถ ์ค‘์‹ฌ์„ .""" + x = np.arange(0.0, 200.5, 0.5) + y = np.arange(0.0, 100.5, 0.5) + z = np.repeat((x * math.tan(math.radians(30)))[None, :], len(y), axis=0) + valid = np.ones_like(z, bool) + valid[:, x > 180] = False # ๋™์ชฝ ๋ = ์ง€ํ˜• ์—†์Œ + dem = tmp_path / "dtm.npz" + np.savez( + dem, + x=x.astype(np.float32), + y=y.astype(np.float32), + z=z.astype(np.float32), + valid_mask=valid, + ) + stations = [ + {"chainage_m": float(c), "label": f"No.{i}", "center_x": 100.0, "center_y": float(c)} + for i, c in enumerate(range(0, 101, 20)) + ] + route = [ + {"sequence_num": i, "model_x": 100.0, "model_y": float(c), "chainage_m": float(c)} + for i, c in enumerate((0, 100)) + ] + src = router_mod.source + monkeypatch.setattr(src, "terrain_grid_path", lambda _r: dem) + monkeypatch.setattr(src, "stations", lambda _r: stations) + monkeypatch.setattr(src, "db_snapshot", lambda _r: {"route_points": route}) + return tmp_path + + +def test_์ธก์ _๋“ฑ๊ธ‰_๋ฌธํ„ฑ_๊ฒฝ๊ณ„(): + """์œ„ํ—˜ ์…€ ๋น„์œจ < 5 % ๋‚ฎ์Œ ยท 5 % ์ด์ƒ 20 % ๋ฏธ๋งŒ ๋ณดํ†ต ยท 20 % ์ด์ƒ ๋†’์Œ (๊ฒฝ๊ณ„ ํฌํ•จ์€ ์œ—๋“ฑ๊ธ‰).""" + cases = [(0, 100, "low"), (4, 100, "low"), (5, 100, "mid"), (19, 100, "mid")] + cases += [(20, 100, "high"), (100, 100, "high"), (1, 20, "mid"), (1, 21, "low"), (1, 5, "high")] + for risk, cells, want in cases: + assert engine.station_level(risk, cells) == want, (risk, cells) + # ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ”๊พผ ๋ฌธํ„ฑ + assert engine.station_level(9, 100, 10, 30) == "low" + assert engine.station_level(30, 100, 10, 30) == "high" + + +def test_์ธก์ _์˜ํ–ฅ_๊ตฌ์—ญ๊ณผ_๋น„์œจ(): + """์ธก์  0 ยท 10 ยท 30 โ†’ ์˜ํ–ฅ ๊ตฌ์—ญ = ์ด์›ƒ๊ณผ ์ค‘๊ฐ„ ๋ˆ„๊ฐ€๋กœ ๋‚˜๋ˆ” ยท ๋  ์•ˆ ์…€๋งŒ ยท ์ž๋ฃŒ ์—†์Œ ์ œ์™ธ.""" + chain = np.arange(0.0, 40.0, 1.0)[None, :] # 1 ร— 40 ์…€ ยท ์…€ i ๋ˆ„๊ฐ€ = i + cls = np.ones(chain.shape, np.uint8) + cls[0, [0, 1, 2, 3, 4]] = 4 # ์ธก์  0 ์˜์—ญ(0~5 ยท ์ •ํ™•ํžˆ ์ค‘๊ฐ„ 5 ๋Š” ์•ž ์ธก์ ) 6 ์…€ ์ค‘ 5 โ†’ 83 % + cls[0, [6, 7]] = 5 # ์ธก์  10 ์˜์—ญ(6~20) ์…€ 14(19 ๋Š” ๋  ๋ฐ–) ยท ์œ„ํ—˜ 6 ยท 7 ยท 20 โ†’ 21 % + cls[0, [20, 21]] = 6 # 20 ์€ ์ธก์  10 ๋ชซ ยท ์ธก์  30 ์˜์—ญ(21~39) ์€ 21 ํ•˜๋‚˜ + cls[0, 39] = 0 # ์ž๋ฃŒ ์—†์Œ โ†’ ์…€ ์ˆ˜์—์„œ ๋น ์ง(18 ์…€) 1/18 โ†’ 5.6 % + inside = np.ones(chain.shape, bool) + inside[0, 19] = False # ๋  ๋ฐ– โ†’ ๋น ์ง + out = {k: np.ones(chain.shape) for k in ("fs", "si", "slope")} | {"cls": cls} + rows = engine.station_table([30.0, 0.0, 10.0], chain, inside, out, 2.0) + assert [r["chainage_m"] for r in rows] == [0.0, 10.0, 30.0] + assert [r["cells"] for r in rows] == [6, 14, 18] + assert [r["level"] for r in rows] == ["high", "high", "mid"] + assert rows[0]["risk_ratio"] == round(5 / 6, 4) and rows[1]["risk_ratio"] == round(3 / 14, 4) + assert rows[2]["risk_ratio"] == round(1 / 18, 4) and rows[2]["risk_area_m2"] == 4.0 + # ๋ฌธํ„ฑ์„ ์˜ฌ๋ฆฌ๋ฉด ๋“ฑ๊ธ‰์ด ๋‚ด๋ ค๊ฐ + again = engine.station_table([0.0, 10.0, 30.0], chain, inside, out, 2.0, 15.0, 50.0) + assert [r["level"] for r in again] == ["high", "mid", "low"] + + +def test_์ธก์ _๋ฌธํ„ฑ_์„ค์ •_๊ฒ€์ฆ(): + base = router_mod.AreaRequest.model_fields + assert (base["level_lo_pct"].default, base["level_hi_pct"].default) == (5.0, 20.0) + ok = dict(gamma_t=18.0, gamma_sat=20.0, c=0.0, c_hi=0.0, phi=30.0, phi_hi=30.0) + router_mod.AreaRequest(**ok, level_lo_pct=10, level_hi_pct=10) + with pytest.raises(ValueError): + router_mod.AreaRequest(**ok, level_lo_pct=30, level_hi_pct=10) + + +def test_๊ธธ_๋ _์ธก์ ํ‘œ_์ €์žฅ๋ณธ(project): + base = router_mod.defaults(project) + assert base["scenario"] == "sinmap" and (base["tr_lo"], base["tr_hi"]) == (2000.0, 3000.0) + settings = {**base, "band_m": 30.0, "cell_m": 2.0, "scenario": "saturated", "c_root": 0.0} + assert settings["material_id"] == "colluvium" and settings["c_hi"] == settings["c"] + 5 + steps = [] + summary = router_mod.run(project, project, settings, "fp", lambda p, m: steps.append(p)) + doc = router_mod.load(project) + assert steps == sorted(steps) and doc["fingerprint"] == "fp" + g = doc["grid"] + cls = _decode(doc["cls"], "u1", g) + fs = _decode(doc["fs"], " 0] + assert np.median(inner) / 1000 == pytest.approx(hand, abs=2e-3) + assert summary["fs_min"] == pytest.approx(hand, abs=5e-3) + # ๋  = ์ค‘์‹ฌ์„  ยฑ 30 m(๊ฒฉ์ž ์ƒ์ž๋„ ๋ ๋งŒํผ) ยท ๋“ฑ๊ธ‰ ๋ฉด์  ํ•ฉ = ๋ฎ์ธ ๋ฉด์  + assert g["nx"] * g["dx"] <= 62 + 1e-9 and g["ny"] * g["dx"] <= 102 + 1e-9 + assert sum(summary["areas_m2"].values()) == pytest.approx(summary["covered_area_m2"]) + assert summary["covered_area_m2"] == pytest.approx((cls > 0).sum() * 4.0) + rows = doc["stations"] + assert [r["chainage_m"] for r in rows] == [0, 20, 40, 60, 80, 100] + assert all(r["label"] == f"No.{i}" and r["worst_class"] >= 4 for i, r in enumerate(rows)) + assert all(r["level"] == "high" and r["risk_ratio"] > 0.99 for r in rows) # ์™„์ „ ํฌํ™” 30ยฐ ๋น„ํƒˆ + assert summary["station_levels"] == {"low": 0, "mid": 0, "high": 6} + assert sum(r["cells"] for r in rows) == (cls > 0).sum() + # ์ง€๋ฌธ = ์„ค์ •์— ๋”ฐ๋ผ ๋ฐ”๋€œ + assert router_mod.fingerprint("s", settings) != router_mod.fingerprint( + "s", {**settings, "c": 6} + ) + json.dumps(doc) # ๊ทธ๋Œ€๋กœ JSON diff --git a/resources/tester/test_536_3_e03_reinforce.py b/resources/tester/test_536_3_e03_reinforce.py new file mode 100644 index 000000000..a7266876a --- /dev/null +++ b/resources/tester/test_536_3_e03_reinforce.py @@ -0,0 +1,328 @@ +# -*- coding: utf-8 -*- +"""536-3 E03 ๋ณด๊ฐ• ๊ฒ€ํ†  โ€” ๋ฒฝ ์™ธ์  ์•ˆ์ • ยท ๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ • ์žฌํ•ด์„ ยท E05 ๋ณด๊ฐ•๊ฒ€ํ†  ์‹œํŠธ. + +- ๋ฒฝ ์™ธ์  ์•ˆ์ •(์ˆœ์ˆ˜): ๊ต๊ณผ์„œ ๊ณ„์ˆ˜ํ‘œ(Coulomb ยท Rankine ยท Meyerhof) ยท M-O ๋‹ซํžŒ ๊ผด ยท ์†๊ณ„์‚ฐ ์˜ˆ์ œ + (์ง์‚ฌ๊ฐ ๋ฒฝ ยท ์ƒ์žฌํ•˜์ค‘ ยท ์ˆ˜์•• ยท ๋’ท๊ตฝ ์œ„ ํ™) ยฑ2 % +- ๊ฐ€์ • ํ˜•์ƒ(์ˆœ์ˆ˜): 1:n ๋น„ํƒˆ์ด ์›์ง€๋ฐ˜๊ณผ ๋งŒ๋‚˜๋Š” ์  = ํ•ด์„ํ•ด ยท ์†Œ๋‹จ ๊ผญ์ง“์  ยท ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ(๋‹จ์กฐ ยท ์ฐฝ) +- ๊ธธ(๊ฒ€์ฆ_sub2 ๋™๊ฒฐ ๋ณต์‚ฌ ยท ๋ฒฝ ์ธก์  + ํ•˜๋‚˜): ์ง€๊ธˆ ๊ธฐ์šธ๊ธฐ ๊ทธ๋Œ€๋กœ = ์›๋ณธ ์•ˆ์ „์œจ(ยฑ3 %) ยท ์ตœ์†Œ ์™„ํ™”๊ฐ€ + ๋ชฉํ‘œ ์•ˆ์ „์œจ์„ ๋งž์ถค ยท ์›๋ณธ ์ธก์  ยท ์š”์•ฝ ์ €์žฅ๋ณธ ๊ทธ๋Œ€๋กœ ยท ๋ฒฝ ์ธก์  ์™ธ์  ์•ˆ์ • ยท ๋ฒฝ ์ชฝ ๋น„ํƒˆ ๊ฐ€์ • ๊ฑฐ์ ˆ ยท + E05 ๋ณด๊ฐ•๊ฒ€ํ†  ์‹œํŠธ(์žˆ์„ ๋•Œ๋งŒ) + + ./venv/Scripts/python.exe -m pytest resources/tester/test_536_3_e03_reinforce.py -q +""" + +from __future__ import annotations + +import asyncio +import math +import shutil +import sys +import tempfile +from pathlib import Path +from uuid import UUID + +import pytest +from fastapi import HTTPException + +ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(ROOT)) + +from common_util import common_util_project_copy as cp # noqa: E402 +from common_util.common_util_station_key import chainage_key # noqa: E402 +from E01_StabRegister import E01_StabRegister_Source as src # noqa: E402 +from E02_StabGround import E02_StabGround_Router as e02 # noqa: E402 +from E03_StabSection import E03_StabSection_Router as router # noqa: E402 +from E03_StabSection import E03_StabSection_Router_WhatIf as wf # noqa: E402 +from E03_StabSection import E03_StabSection_Worker as worker # noqa: E402 +from E03_StabSection import E03_StabSection_WallCheck as wc # noqa: E402 +from E03_StabSection import E03_StabSection_WhatIf as whatif # noqa: E402 +from E03_StabSection.E03_StabSection_Engine_Station import analyze_station # noqa: E402 + +SOURCE_ID = "cb15892a-5788-4a26-9316-20a15421f68b" # ๊ฒ€์ฆ_sub2(์ฝ๊ธฐ๋งŒ) +PID = "e" * 8 + "-" + "e" * 4 + "-" + "e" * 4 + "-" + "e" * 4 + "-" + "e" * 12 +WALL_KEY = "351.00" # ๊ฒ€์ฆ_sub2 ๊ธฐ์Šญ๋ง‰์ด ์ธก์  +SOIL = {"gamma_t": 18.0, "gamma_sat": 19.0, "c": 0.0, "phi": 30.0} + + +def close(a: float, b: float, tol: float = 0.02) -> bool: + return abs(a - b) <= tol * abs(b) + + +# โ”€โ”€ ๋ฒฝ ์™ธ์  ์•ˆ์ • โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +def test_ํ† ์••_์ง€์ง€๋ ฅ_๊ณ„์ˆ˜_๊ต๊ณผ์„œํ‘œ(): + """Das(๊ธฐ์ดˆ๊ณตํ•™) ํ‘œ โ€” Coulomb ฮด = 2/3ฯ† ยท Rankine ๊ฒฝ์‚ฌ ๋ฐฐ๋ฉด ยท Meyerhof ์ง€์ง€๋ ฅ ๊ณ„์ˆ˜.""" + assert close(wc.ka_coulomb(30, 20, 0, 0), 0.2973) + assert close(wc.ka_coulomb(30, 0, 0, 0), 1 / 3) + assert close(wc.ka_rankine(30, 0), 1 / 3) + assert close(wc.ka_rankine(30, 10), 0.3495) + nc, nq, ng = wc.bearing_factors(30) + assert close(nc, 30.14) and close(nq, 18.40) and close(ng, 15.67) + assert wc.bearing_factors(0) == (5.14, 1.0, 0.0) + + +def test_MO_๋‹ซํžŒ๊ผด(): + """ฮด = ฮธ = ฮฒ = 0 ยท kv = 0 โ€” KAE = cosยฒ(ฯ†โˆ’ฯˆ) / (cosยฒฯˆ [1 + โˆš(sinฯ† sin(ฯ†โˆ’ฯˆ) / cosฯˆ)]ยฒ) ยท ฯˆ = 0 ์ด๋ฉด Coulomb.""" + for phi, kh in ((30, 0.2), (35, 0.15)): + psi = math.atan(kh) + f = math.radians(phi) + want = math.cos(f - psi) ** 2 / ( + math.cos(psi) ** 2 + * (1 + math.sqrt(math.sin(f) * math.sin(f - psi) / math.cos(psi))) ** 2 + ) + assert close(wc.ka_coulomb(phi, 0, 0, 0, math.degrees(psi)), want, 1e-9) + assert close(wc.ka_coulomb(30, 0, 0, 0, math.degrees(math.atan(0.2))), 0.4733) + assert wc.ka_coulomb(32, 21, 5, 10, 0.0) == pytest.approx(wc.ka_coulomb(32, 21, 5, 10)) + + +RECT = [[0, 0], [2, 0], [2, 4], [0, 4]] # B 2 ยท H 4 ยท ๋ฐฐ๋ฉด = +x(๋’ท๊ตฝ x = 2) + + +def test_์ง์‚ฌ๊ฐ_๋ฒฝ_์†๊ณ„์‚ฐ(): + """Rankine ยท q 0 ยท ๋ฌผ ์—†์Œ ยท ฮผ 0.5 โ€” W 184 ยท Pa 48(H/3) ยท ํ™œ๋™ 1.917 ยท ์ „๋„ 2.875 ยท + e = 1 โˆ’ 0.6522 ยท q_max ยท Meyerhof q_ult(์œ ํšจํญ ยท ๊ฒฝ์‚ฌ ๊ณ„์ˆ˜).""" + r = wc.check_wall( + RECT, +1, backfill=SOIL, foundation=SOIL, method="rankine", + surcharge_kpa=0.0, base_friction=0.5, + ) # fmt: skip + W, Pa = 2 * 4 * 23.0, 0.5 * 18 * 16 / 3 + assert close(r["loads"]["W"], W) and close(r["loads"]["Pa"], Pa) and r["loads"]["W_soil"] == 0 + assert close(r["fs"]["sliding"], W * 0.5 / Pa) + m_res, m_over = W * 1.0, Pa * 4 / 3 + assert close(r["fs"]["overturning"], m_res / m_over) + xbar = (m_res - m_over) / W + e = 1.0 - xbar + q_max = W / 2 * (1 + 6 * e / 2) if e <= 2 / 6 else 2 * W / (3 * xbar) + alpha = math.degrees(math.atan(Pa / W)) + q_ult = 0.5 * 18 * (2 - 2 * e) * 15.668 * (1 - alpha / 30) ** 2 + assert close(r["base"]["q_max"], q_max) and close(r["fs"]["bearing"], q_ult / q_max) + assert r["case"] == "normal" and r["required"] == wc.REQUIRED["normal"] + assert r["ok"] == {k: r["fs"][k] >= r["required"][k] for k in r["fs"]} + + +def test_์ƒ์žฌํ•˜์ค‘_์ˆ˜์••_์–‘์••๋ ฅ(): + """q 10 ยท ๋ฐฐ๋ฉด ์ˆ˜์œ„ = ๋ฒฝ ๋†’์ด โ€” Pa = Ka(ยฝฮณ'Hยฒ + qH) ยท Pw = ยฝฮณwHยฒ ยท U = ยฝฮณwHB ยท ฮฃH ยท V.""" + r = wc.check_wall( + RECT, +1, backfill=SOIL, foundation=SOIL, method="rankine", + surcharge_kpa=10.0, water_ratio=1.0, base_friction=0.5, + ) # fmt: skip + g = SOIL["gamma_sat"] - wc.GAMMA_W + Pa = (0.5 * g * 16 + 10 * 4) / 3 + Pw, U = 0.5 * wc.GAMMA_W * 16, 0.5 * wc.GAMMA_W * 4 * 2 + assert close(r["loads"]["Pa"], Pa) and close(r["loads"]["Pw"], Pw) and close(r["loads"]["U"], U) + assert close(r["loads"]["H"], Pa + Pw) and close(r["loads"]["V"], 184 - U) + arm = (0.5 * g * 16 * 4 / 3 + 40 * 2) / (0.5 * g * 16 + 40) # ์‚ผ๊ฐ + ์‚ฌ๊ฐ ํ•ฉ๋ ฅ ๋†’์ด + assert close(r["loads"]["Pa_arm"], arm) + m_over = Pa * arm + Pw * 4 / 3 + U * 4 / 3 + assert close(r["loads"]["M_overturn"], m_over) + + +def test_Lํ˜•_๋ฒฝ_๋’ท๊ตฝ_์œ„_ํ™_์ง€์ง„(): + """์—ญ T ํ˜•(์•ž๊ตฝ 0 ยท ๋’ท๊ตฝ 3 ยท ๋ฒฝ์ฒด 0.6~1.0) โ€” Rankine ๊ฐ€์ƒ๋ฉด ์œ„ ํ™ = (3 โˆ’ 1.0) ร— 4.5 ร— ฮณ ยท + Coulomb ๊ฐ€์ƒ๋ฉด ฮธ = atan(2 / 5) ยท ์ง€์ง„ ์‹œ = M-O ์ฆ๋ถ„ 0.6H + ๊ด€์„ฑ ยท ๊ธฐ์ค€ = ์ง€์ง„ ์‹œ ํ‘œ.""" + L = [[0, 0], [3, 0], [3, 0.5], [1.0, 0.5], [1.0, 5], [0.6, 5], [0.6, 0.5], [0, 0.5]] + r = wc.check_wall(L, +1, backfill=SOIL, foundation=SOIL, method="rankine") + assert close(r["loads"]["W_soil"], 2.0 * 4.5 * 18) + c = wc.check_wall(L, +1, backfill=SOIL, foundation=SOIL) + assert close(c["geometry"]["theta_deg"], math.degrees(math.atan(2 / 5))) + # ๊ฐ€์ƒ๋ฉด(x = 3 โˆ’ 0.4z)๊ณผ ๋ฒฝ์ฒด(x = 1.0) ์‚ฌ์ด ยท 0.5 โ‰ค z โ‰ค 5 โ†’ โˆซ(2 โˆ’ 0.4z)dz = 4.05 mยฒ + assert close(c["loads"]["W_soil"], 4.05 * 18) + s = wc.check_wall(L, +1, backfill=SOIL, foundation=SOIL, kh=0.15) + assert s["case"] == "seismic" and s["required"] == wc.REQUIRED["seismic"] + assert s["coefficients"]["kae"] > c["coefficients"]["ka"] + assert s["loads"]["H"] > c["loads"]["H"] and s["fs"]["sliding"] < c["fs"]["sliding"] + + +def test_๋ฐฐ๋ฉด์ด_๋ฐ˜๋Œ€์ชฝ์ด๋ฉด_๊ฑฐ์šธ(): + a = wc.check_wall(RECT, +1, backfill=SOIL, foundation=SOIL) + b = wc.check_wall([[-x, z] for x, z in RECT], -1, backfill=SOIL, foundation=SOIL) + for k in a["fs"]: + assert a["fs"][k] == pytest.approx(b["fs"][k]) + + +# โ”€โ”€ ๊ฐ€์ • ํ˜•์ƒ ยท ์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +GROUND = [(-40.0, 120.0), (0.0, 100.0), (40.0, 80.0)] # 1:2 ๋น„ํƒˆ(์ขŒ = +x ๋‚ฎ์Œ) +LINE = [(-3.0, 101.5), (-2.0, 100.0), (2.0, 100.0), (3.0, 99.5)] + + +def test_๋น„ํƒˆ_๋‹ค์‹œ_๊ทธ๋ฆผ_ํ•ด์„ํ•ด(): + """์ง€๋ฐ˜ z = 100 โˆ’ x/2 ยท ์šฐ(โˆ’x) ๊นŽ๊ธฐ ์‹œ์ž‘ (โˆ’2, 100) ยท 1:n โ€” u = โˆ’x ์—์„œ (u โˆ’ 2)/n = u/2 โ†’ + u = 2 / (1 โˆ’ n/2) ยท z = 100 + u/2 ยท n โ‰ฅ 2 = ๋ชป ๋งŒ๋‚จ. ์ขŒ ์Œ“๊ธฐ (2, 100) 1:1.5 โ†’ (8, 96).""" + for n in (0.5, 1.0, 1.5): + line = whatif.reshape_side(LINE, GROUND, side="right", role="cut", start_x=-2.0, ratio=n) + u = 2.0 / (1.0 - n / 2) + x, z = min(line) + assert x == pytest.approx(-u) and z == pytest.approx(100.0 + u / 2) + assert [p for p in line if p[0] > -2.0 + 1e-9] == [list(p) for p in LINE[2:]] + gentle = dict(side="right", role="cut", start_x=-2.0, ratio=2.5) + assert whatif.reshape_side(LINE, GROUND, **gentle) is None + line = whatif.reshape_side( + LINE, GROUND, side="right", role="cut", start_x=-2.0, ratio=0.5, bench_w=1.0, bench_h=1.0 + ) + corners = [p for p in line if -3.6 < p[0] < -2.4] + assert [[round(x, 6), round(z, 6)] for x, z in corners] == [[-3.5, 101.0], [-2.5, 101.0]] + fill = whatif.reshape_side(LINE, GROUND, side="left", role="fill", start_x=2.0, ratio=1.5) + assert max(fill) == pytest.approx([8.0, 96.0]) + assert [p for p in fill if p[0] < 2.0 - 1e-9] == [list(p) for p in LINE[:2]] + assert ( + whatif.reshape_side(LINE, GROUND, side="left", role="fill", start_x=2.0, ratio=3.0) is None + ) + + +def test_์ตœ์†Œ_์™„ํ™”_์ฐพ๊ธฐ(): + found = whatif.search_ratio(lambda n: n >= 1.23, 0.4) + assert found["found"] == pytest.approx(1.25) + window = whatif.search_ratio(lambda n: 1.0 <= n <= 1.3, 0.4) # ์™„๋งŒํ• ์ˆ˜๋ก ๋Š˜ ์•ˆ์ „ํ•˜์ง€ ์•Š์Œ + assert 1.0 <= window["found"] <= 1.3 + assert whatif.search_ratio(lambda n: False, 0.4, max_ratio=1.1)["found"] is None + assert whatif.search_ratio(lambda n: True, 0.4) == {"found": 0.4, "tried": [(0.4, True)]} + assert len(whatif.search_ratio(lambda n: n >= 3.9, 0.4)["tried"]) <= 10 + + +# โ”€โ”€ ๊ธธ(๊ฒ€์ฆ_sub2 ๋™๊ฒฐ ๋ณต์‚ฌ) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +async def _copy(target: Path) -> None: + import aiomysql + + from config.config_db import DB_HOST, DB_NAME, DB_PASSWORD, DB_PORT, DB_USER + + connection = await aiomysql.connect( + host=DB_HOST, port=DB_PORT, user=DB_USER, password=DB_PASSWORD, db=DB_NAME, autocommit=True + ) + try: + await cp.copy_project( + connection, SOURCE_ID, scope="stability", new_id=PID, project_root=target + ) + finally: + connection.close() + + +@pytest.fixture(scope="module") +def frozen(): + folder = Path(tempfile.mkdtemp(prefix="aislo_e03_rf_")) + try: + asyncio.run(_copy(folder / "copy")) + except Exception as error: # noqa: BLE001 + shutil.rmtree(folder, ignore_errors=True) + pytest.skip(f"๊ฒ€์ฆ ์›๋ณธ ๋ณต์‚ฌ ์‹คํŒจ: {error}") + yield folder / "copy" + shutil.rmtree(folder, ignore_errors=True) + + +def _fast(cross, design, **kw): + """์ง„์งœ ์—”์ง„ โ€” Bishop ์›ํ˜ธ๋งŒ(๋น ๋ฅด๊ฒŒ).""" + return analyze_station(cross, design, **{**kw, "methods": ("bishop",), "noncircular": False}) + + +@pytest.fixture(scope="module") +def project(frozen): + """๋ฒฝ ์ธก์  + ๊นŽ๊ธฐ ๋น„ํƒˆ ์žˆ๋Š” ๋ฒฝ ์—†๋Š” ์ธก์  ํ•˜๋‚˜ ยท ์ง„์งœ ์—”์ง„(๋น ๋ฅด๊ฒŒ) ยท [ํ•ด์„] ํ•œ ๋ฒˆ.""" + mp = pytest.MonkeyPatch() + original = src.stations + + def pick(root): + rows = original(root) + wall = [r for r in rows if chainage_key(r["chainage_m"]) == WALL_KEY] + rest = [r for r in rows if chainage_key(r["chainage_m"]) != WALL_KEY] + return wall + rest[10:11] + + mp.setattr(router.src, "stations", pick) + + async def fake_project(_pid): + return frozen, {"company_id": 1, "project_kind": "STABILITY"} + + mp.setattr(e02, "_project", fake_project) + mp.setattr(worker, "analyze_station", _fast) + mp.setattr(wf, "analyze_station", _fast) + mp.setattr(router, "WORKERS", 1) + router._JOBS.clear() + + async def go(): + await router.analyze2d(UUID(PID)) + while router._TASKS: + await asyncio.gather(*list(router._TASKS)) + + asyncio.run(go()) + yield frozen + mp.undo() + + +def _stamps(root: Path) -> dict[str, bytes]: + out = {} + for kind in (router.KIND_STATION, router.KIND_SUMMARY): + for p in (root / "_derived" / kind).glob("*.json"): + out[str(p)] = p.read_bytes() # ์ฝ๊ธฐ๊ฐ€ ๊ณ ์นœ ๋•Œ๋ฅผ ๊ฑด๋“œ๋ฆผ(์ตœ๊ทผ ํ‘œ์‹œ) โ€” ๋‚ด์šฉ๋งŒ ๊ฒฌ์คŒ + return out + + +def _plain_station(root: Path) -> tuple[str, str, float, dict]: + summary, _ = router._latest_summary(root, "") + row = next(r for r in summary["stations"] if r["key"] != WALL_KEY) + side = next(s for s in row["sides"] if s["role"] == "cut")["side"] + return row["key"], side, row["cases"]["dry"][side]["fs"], row + + +def test_์ง€๊ธˆ_๊ธฐ์šธ๊ธฐ_๊ทธ๋Œ€๋กœ๋ฉด_์›๋ณธ_์•ˆ์ „์œจ(project): + key, side, before, _ = _plain_station(project) + rec = wf.run_whatif(project, key, wf.WhatIfRequest(side=side)) + assert rec["assume"]["ratio"] == rec["assume"]["ratio_before"] + assert close(rec["after"]["dry"]["fs"], before, 0.03) + assert rec["view"]["analyses"] and rec["before"]["dry"]["fs"] == before + + +def test_์ตœ์†Œ_์™„ํ™”๊ฐ€_๋ชฉํ‘œ๋ฅผ_๋งž์ถค_์›๋ณธ_๋ถˆ๋ณ€(project): + key, side, before, _ = _plain_station(project) + stamps = _stamps(project) + target = round(before + 0.02, 3) + rec = wf.run_whatif( + project, + key, + wf.WhatIfRequest(side=side, search=True, condition="dry", target_fs=target), + ) + search = rec["search"] + if search["found"] is None: # ์™„ํ™”๊ฐ€ ๊นŽ๊ธฐ ๋†’์ด๋ฅผ ํ‚ค์›Œ ์˜คํžˆ๋ ค ๋‚ฎ์•„์ง€๋Š” ์ง€ํ˜• โ€” ๋๊ฐ’๊นŒ์ง€ ๋ด„ + assert all(not ok for _, ok in search["tried"]) + else: + assert rec["after"]["dry"]["fs"] >= target - 1e-9 + assert rec["assume"]["ratio"] == search["found"] > rec["assume"]["ratio_before"] + steeper = [n for n, ok in search["tried"] if n < search["found"]] + assert all(not ok for n, ok in search["tried"] if n in steeper) + assert _stamps(project) == stamps # ์›๋ณธ ์ธก์  ยท ์š”์•ฝ ์ €์žฅ๋ณธ ๊ทธ๋Œ€๋กœ + again = wf.run_whatif( + project, + key, + wf.WhatIfRequest(side=side, search=True, condition="dry", target_fs=target), + ) + assert again["computed_at"] == rec["computed_at"] # ๊ฐ™์€ ๊ฐ€์ • = ์ €์žฅ๋ณธ + + +def test_๋ฒฝ_์ธก์ _์™ธ์ _์•ˆ์ •_๋ฒฝ์ชฝ_๊ฐ€์ •_๊ฑฐ์ ˆ(project): + rec = wf.run_wallcheck(project, WALL_KEY, wf.WallRequest(kh=0.15)) + assert len(rec["walls"]) == 1 + wall = rec["walls"][0] + assert [c["case"] for c in wall["cases"]] == ["normal", "seismic"] + for c in wall["cases"]: + assert c["required"] == wc.REQUIRED[c["case"]] + assert all(v > 0 for v in c["fs"].values()) + assert c["geometry"]["H"] == pytest.approx(1.5, abs=0.05) + with pytest.raises(HTTPException) as error: + wf.run_whatif(project, WALL_KEY, wf.WhatIfRequest(side=wall["side"])) + assert error.value.status_code == 422 + + +def test_E05_๋ณด๊ฐ•๊ฒ€ํ† _์‹œํŠธ(project, monkeypatch): + from E05_StabReport import E05_StabReport_Build as build + + recs = wf.records(project) + assert {r["type"] for r in recs} == {"slope", "wall"} + book = build.form() + build.build_program(book, build.gather(project)) + s8 = next(s for s in book["์‹œํŠธ"] if s["id"] == "s8") + cells = s8["์นธ"] + slopes = [r for r in recs if r["type"] == "slope"] + rows = sum(len(r["after"]) for r in slopes) + assert all(cells[f"D{6 + i}"]["๊ฐ’"] in ("๊ฑด๊ธฐ", "์šฐ๊ธฐ", "์ง€์ง„") for i in range(rows)) + wall_rows = [a1 for a1, c in cells.items() if a1[0] == "B" and "ํ‰์ƒ์‹œ" in str(c.get("๊ฐ’"))] + assert len(wall_rows) == 1 and cells[f"I{wall_rows[0][1:]}"]["๊ฐ’"] in ("O.K", "N.G") + monkeypatch.setattr(build.reinforce, "read", lambda root: []) + empty = build.form() + build.build_program(empty, build.gather(project)) + assert "s8" not in {s["id"] for s in empty["์‹œํŠธ"]} # ์—†์œผ๋ฉด ์‹œํŠธ ๋บŒ diff --git a/resources/tester/test_54_2_help_files.py b/resources/tester/test_54_2_help_files.py index 1bc48b728..b5ae4fc75 100644 --- a/resources/tester/test_54_2_help_files.py +++ b/resources/tester/test_54_2_help_files.py @@ -1,5 +1,6 @@ """54-2 [?] ์„ค๋ช…์„œ โ€” ์„œ๋ฒ„๊ฐ€ resources/help/<ํŽ˜์ด์ง€>/*.md ๋ฅผ ๋ถ€๋ฅผ ๋•Œ๋งˆ๋‹ค ์ฝ์Œ(๊ณ ์น˜๋ฉด ๋ฐ”๋กœ ๋ฐ”๋€œ).""" +import re import tempfile from pathlib import Path @@ -28,15 +29,30 @@ def test_title_order_and_live_edit(): assert "๊ณ ์นœ ๊ธ€" in read_help("B05", tmp_path)[1]["text"] -@pytest.mark.parametrize("page", ["B01", "B10", "../B05", "b05", "D05", "E06", "C01"]) +@pytest.mark.parametrize("page", ["B01", "B10", "../B05", "b05", "D05", "E08", "C01"]) def test_bad_page_refused(page): with pytest.raises(HTTPException): read_help(page) def test_529_1_D_E_pages_open_empty(tmp_path): - assert read_help("D02", tmp_path) == [] and read_help("E05", tmp_path) == [] + assert read_help("D02", tmp_path) == [] and read_help("E07", tmp_path) == [] def test_root_is_repo_resources(): assert HELP_ROOT.parts[-2:] == ("resources", "help") + + +@pytest.mark.parametrize( + "page", ["D01", "D02", "D03", "D04", "E01", "E02", "E03", "E04", "E05", "E06", "E07"] +) +def test_d_e_pages_have_manual(page): + items = read_help(page) + assert items and items[0]["key"] == "00_์„ค๋ช…์„œ" + assert len(items[0]["text"]) > 400 + for item in items: # ๋ช…์‚ฌํ˜• ์ข…๊ฒฐ โ€” ํ•ฉ์‡ผ์ฒด ยท ํ•ด์š”์ฒด ยท ํ•œ๋‹ค์ฒด ๊ธˆ์ง€ + for line in item["text"].splitlines(): + assert not re.search(r"(ํ•ฉ๋‹ˆ๋‹ค|์Šต๋‹ˆ๋‹ค|ํ•ด์š”|์„ธ์š”|ํ•œ๋‹ค|๋œ๋‹ค|์žˆ๋‹ค|์—†๋‹ค)[.!]?\s*$", line), ( + page, + line, + ) diff --git a/ui_template/ui_template_locale.ts b/ui_template/ui_template_locale.ts index a5309b16a..3d8827e53 100644 --- a/ui_template/ui_template_locale.ts +++ b/ui_template/ui_template_locale.ts @@ -16,6 +16,7 @@ * - ui_template_locale_b5.ts : B ๊ทธ๋ฃน ๋ (B08~B11) * - ui_template_locale_de.ts : D ์˜ˆ์ •๋…ธ์„  ยท E ์•ˆ์ •์„ฑํ‰๊ฐ€ ยท ๋Œ€์‹œ๋ณด๋“œ [+] ๊ณ ๋ฅด๊ธฐ * - ui_template_locale_e04.ts : E04 ์•ˆ์ •์„ฑํ‰๊ฐ€ 3D ํ•ด์„ + * - ui_template_locale_e07.ts : E07 ์•ˆ์ •์„ฑํ‰๊ฐ€ ๊ด‘์—ญ ์œ„ํ—˜ ์ง€๋„ * * ์ด ํŒŒ์ผ์˜ export ์‹œ๊ทธ๋‹ˆ์ฒ˜๋Š” ๋ถ„ํ•  ์ด์ „๊ณผ ๋™์ผํ•˜๋ฏ€๋กœ ์†Œ๋น„์ฒ˜ import ์ˆ˜์ •์€ ๋ถˆํ•„์š”ํ•˜๋‹ค. * ========================================================================== */ @@ -35,6 +36,8 @@ import { ui_locales_m2 } from "./ui_template_locale_m2"; import { ui_locales_de } from "./ui_template_locale_de"; import { ui_locales_e } from "./ui_template_locale_e"; import { ui_locales_e05 } from "./ui_template_locale_e05"; +import { ui_locales_e06 } from "./ui_template_locale_e06"; +import { ui_locales_e07 } from "./ui_template_locale_e07"; /** ์ง€์› ์–ธ์–ด ์ธ๋ฑ์Šค: 0 = ํ•œ๊ตญ์–ด, 1 = ์˜์–ด */ export const LANGUAGES = ["ko", "en"] as const; @@ -77,6 +80,8 @@ export const ui_locales = { ...ui_locales_de, ...ui_locales_e, ...ui_locales_e05, + ...ui_locales_e06, + ...ui_locales_e07, } as const; export type LocaleKey = keyof typeof ui_locales; diff --git a/ui_template/ui_template_locale_de.ts b/ui_template/ui_template_locale_de.ts index a08abd22d..9f17c0750 100644 --- a/ui_template/ui_template_locale_de.ts +++ b/ui_template/ui_template_locale_de.ts @@ -17,6 +17,8 @@ export const ui_locales_de = { DE_Step_StabSection: ["2D ํ•ด์„", "2D Analysis"], DE_Step_Stab3D: ["3D ํ•ด์„", "3D Analysis"], DE_Step_StabReport: ["๋ณด๊ณ ์„œ", "Report"], + DE_Step_StabRock: ["์•”๋ฐ˜ ํ•ด์„", "Rock Analysis"], + DE_Step_StabArea: ["๊ด‘์—ญ ์œ„ํ—˜", "Area Hazard"], DE_Step_Empty: ["์ค€๋น„ ์ค‘์ธ ๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.", "This step is not ready yet."], /* --- D04 ํ™•์ • ยท ์—…์ฒด ยท ๋‚ด๋ณด๋‚ด๊ธฐ ยท B03 ์˜ˆ์ •๋…ธ์„  ๊ฐ€์ ธ์˜ค๊ธฐ(531-3) --- */ diff --git a/ui_template/ui_template_locale_e.ts b/ui_template/ui_template_locale_e.ts index eccacb836..d262b9d94 100644 --- a/ui_template/ui_template_locale_e.ts +++ b/ui_template/ui_template_locale_e.ts @@ -140,4 +140,10 @@ export const ui_locales_e = { E02_Err_empty: ["์ง€์ธต์ด ํ•˜๋‚˜๋Š” ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค", "At least one layer is required"], E02_Err_type: ["๊ผด์ด ํ‹€๋ฆฝ๋‹ˆ๋‹ค", "Invalid shape"], E02_Err_few_points: ["์ ์ด ๋‘˜ ์ด์ƒ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค", "At least two points are required"], + + /* --- E02 ยท E03 ๋น ์ง„ ํšก๋‹จ ๋  --- */ + E_Missing_Head: [ + "ํšก๋‹จ ์—†๋Š” ์ธก์  {n} ๊ฐœ โ€” ํ•ด์„์—์„œ ๋น ์ง", + "{n} stations without a cross-section โ€” left out of the analysis", + ], } as const satisfies Record; diff --git a/ui_template/ui_template_locale_e03.ts b/ui_template/ui_template_locale_e03.ts index 40f2fae3c..3fa49b93b 100644 --- a/ui_template/ui_template_locale_e03.ts +++ b/ui_template/ui_template_locale_e03.ts @@ -50,6 +50,8 @@ export const ui_locales_e03 = { E03_Error: ["์˜ค๋ฅ˜", "Error"], E03_Cut: ["๊นŽ๊ธฐ", "Cut"], E03_Fill: ["์Œ“๊ธฐ", "Fill"], + E03_CutShort: ["๊นŽ", "C"], + E03_FillShort: ["์Œ“", "F"], E03_Left: ["์ขŒ ๋น„ํƒˆ", "Left slope"], E03_Right: ["์šฐ ๋น„ํƒˆ", "Right slope"], E03_Side: ["๋น„ํƒˆ", "Slope"], @@ -71,9 +73,14 @@ export const ui_locales_e03 = { E03_Legend_Map: ["๋“ฑ์•ˆ์ „์œจ(Fs)", "Safety factor"], E03_Legend_Layers: ["์ง€์ธต", "Layers"], E03_Offset: ["๊ฑฐ๋ฆฌ", "Offset"], - E03_NoWalls: [ - "์˜น๋ฒฝ ยท ์„์ถ•์€ ์•„์ง ํ•ด์„์— ๋ฐ˜์˜ ์ „์ž…๋‹ˆ๋‹ค(๋ฒฝ ์—†๋Š” ๋‹จ๋ฉด์œผ๋กœ ์…ˆ).", - "Retaining walls and masonry are not yet reflected (analyzed without walls).", + E03_Walls: ["๋ฒฝ ์žˆ์Œ", "Walls"], + E03_Water_Ru: [ + "์ง€ํ•˜์ˆ˜ = ๊ฐ„๊ทน์ˆ˜์••๋น„ r_u {v}(์ˆ˜์œ„์„  ์—†์Œ)", + "Groundwater = pore pressure ratio r_u {v} (no water table)", + ], + E03_Walls_Hint: [ + "์˜น๋ฒฝ ยท ์„์ถ•(ํšŒ์ƒ‰ ๋น—๊ธˆ)์€ ๊ฐ•ํ•œ ๋ฌผ์ฒด๋กœ ๋„ฃ์–ด ํ™œ๋™๋ฉด์ด ์ง€๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.", + "Retaining walls (gray hatch) are modeled as rigid bodies โ€” slip surfaces do not cross them.", ], /* --- ์šฐ์ธก ๊ฒฐ๊ณผ --- */ @@ -116,4 +123,48 @@ export const ui_locales_e03 = { E03_Graph_Sigma: ["์œ ํšจ ์ˆ˜์ง์‘๋ ฅ ฯƒ'", "Effective normal ฯƒ'"], E03_Graph_U: ["๊ฐ„๊ทน์ˆ˜์•• u", "Pore pressure u"], E03_Graph_Tau: ["๋™์› ์ „๋‹จ์‘๋ ฅ ฯ„", "Mobilized shear ฯ„"], + + /* --- ๋ณด๊ฐ• ๊ฒ€ํ†  ํƒญ --- */ + E03_Tab: ["๋ณด๊ธฐ", "View"], + E03_Tab_Result: ["ํ•ด์„ ๊ฒฐ๊ณผ", "Analysis Result"], + E03_Tab_Reinforce: ["๋ณด๊ฐ• ๊ฒ€ํ† ", "Remediation Check"], + E03_Rf_Title: ["๋น„ํƒˆ ์™„ํ™” ยท ์†Œ๋‹จ ๊ฐ€์ •", "Slope Flattening ยท Bench Assumption"], + E03_Rf_Hint: [ + "ํ˜•์ƒ๋งŒ ๋ฐ”๊ฟ” ๋‹ค์‹œ ํ•ด์„ํ•ฉ๋‹ˆ๋‹ค(๊ฐ€์ • ๊ฒ€ํ†  ยท ์„ค๊ณ„ ยท ์›๋ณธ ๊ฒฐ๊ณผ๋Š” ๊ทธ๋Œ€๋กœ). ๋น„ํƒˆ ์ „์ฒด๋ฅผ ํ•œ ๊ธฐ์šธ๊ธฐ๋กœ ๊ฐˆ์Œํ•ฉ๋‹ˆ๋‹ค.", + "Re-analyzes with the changed shape only (assumption ยท design and original results unchanged). The whole slope uses one gradient.", + ], + E03_Rf_Ratio: ["๊ธฐ์šธ๊ธฐ 1:n", "Slope 1:n"], + E03_Rf_RatioNow: ["์ง€๊ธˆ ๊ฐ’", "Current"], + E03_Rf_BenchW: ["์†Œ๋‹จ ํญ m", "Bench width m"], + E03_Rf_BenchH: ["์†Œ๋‹จ ๋†’์ด m ๋งˆ๋‹ค", "Bench every m"], + E03_Rf_Bench: ["์†Œ๋‹จ", "Bench"], + E03_Rf_Run: ["๊ฐ€์ • ํ•ด์„", "Analyze Assumption"], + E03_Rf_Search: ["์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ", "Find Minimum Flattening"], + E03_Rf_Busy: ["๊ณ„์‚ฐ ์ค‘โ€ฆ", "Computingโ€ฆ"], + E03_Rf_Goal: [ + "[์ตœ์†Œ ์™„ํ™” ์ฐพ๊ธฐ] ๋ชฉํ‘œ = ์ง€๊ธˆ ์กฐ๊ฑด({c}) ๊ธฐ์ค€์•ˆ์ „์œจ ยท ํ•œ ๋ฒˆ์— ์ˆ˜์‹ญ ์ดˆ~1๋ถ„", + "[Find] target = required FS of the current condition ({c}) ยท takes up to a minute", + ], + E03_Rf_Found: ["๊ธฐ์ค€์•ˆ์ „์œจ์„ ๋งž์ถ”๋Š” ๊ฐ€์žฅ ๊ธ‰ํ•œ ๊ธฐ์šธ๊ธฐ", "Steepest gradient meeting required FS"], + E03_Rf_NotFound: [ + "๋ชป ์ฐพ์Œ โ€” ์›์ง€๋ฐ˜์„ ๋งŒ๋‚˜๋Š” ๊ฐ€์žฅ ์™„๋งŒํ•œ 1:{n} ๊นŒ์ง€ ๋ด„", + "Not found โ€” checked up to 1:{n} (gentlest that meets the ground)", + ], + E03_Rf_Before: ["์ „ Fs", "FS before"], + E03_Rf_After: ["ํ›„ Fs", "FS after"], + E03_Rf_VerdictBefore: ["์ „ ํŒ์ •", "Before"], + E03_Rf_VerdictAfter: ["ํ›„ ํŒ์ •", "After"], + E03_Rf_AfterSection: ["๊ฐ€์ • ๋‹จ๋ฉด", "Assumed Section"], + E03_Rf_WallTitle: ["๋ฒฝ ์™ธ์  ์•ˆ์ • (KDS 11 80 05)", "Wall External Stability (KDS 11 80 05)"], + E03_Rf_Method: ["ํ† ์••", "Earth pressure"], + E03_Rf_Water: ["๋ฐฐ๋ฉด ์ˆ˜์œ„ รท ๋ฒฝ ๋†’์ด", "Water level รท wall height"], + E03_Rf_WallRun: ["์™ธ์  ์•ˆ์ • ๊ฒ€ํ† ", "Check Wall"], + E03_Rf_Wall: ["๋ฒฝ", "Wall"], + E03_Rf_Case_normal: ["ํ‰์ƒ์‹œ", "Normal"], + E03_Rf_Case_seismic: ["์ง€์ง„ ์‹œ", "Seismic"], + E03_Rf_Check: ["๊ฒ€ํ† ", "Check"], + E03_Rf_Fs: ["์•ˆ์ „์œจ", "FS"], + E03_Rf_sliding: ["ํ™œ๋™", "Sliding"], + E03_Rf_overturning: ["์ „๋„", "Overturning"], + E03_Rf_bearing: ["์ง€์ง€๋ ฅ", "Bearing"], } as const satisfies Record; diff --git a/ui_template/ui_template_locale_e04.ts b/ui_template/ui_template_locale_e04.ts index b541c2c9f..49c898651 100644 --- a/ui_template/ui_template_locale_e04.ts +++ b/ui_template/ui_template_locale_e04.ts @@ -22,7 +22,6 @@ export const ui_locales_e04 = { E04_End: ["๋ (m)", "End (m)"], E04_Shape: ["ํ™œ๋™๋ฉด ๋ชจ์–‘", "Slip shape"], E04_Shape_sphere: ["๊ตฌ", "Sphere"], - E04_Shape_ellipsoid: ["ํƒ€์›์ฒด", "Ellipsoid"], E04_Shape_both: ["๊ตฌ + ํƒ€์›์ฒด", "Sphere + ellipsoid"], E04_Ratio: ["ํƒ€์›์ฒด ๊ธธ์ด๋น„", "Ellipsoid ratio"], E04_BoxWidth: ["ํƒ์ƒ‰ ํญ (์ค‘์‹ฌ์„  ยฑm)", "Search half-width (m)"], @@ -35,9 +34,9 @@ export const ui_locales_e04 = { E04_MaxDepth: ["์ตœ๋Œ€ ๊นŠ์ด (m)", "Max depth (m)"], E04_VMin: ["์ตœ์†Œ ๋ถ€ํ”ผ (mยณ)", "Min volume (mยณ)"], E04_VMax: ["์ตœ๋Œ€ ๋ถ€ํ”ผ (mยณ)", "Max volume (mยณ)"], - E04_Method: ["ํ•ด์„ ๋ฐฉ๋ฒ•", "Method"], - E04_Method_bishop: ["3D Bishop (Hungr) + Janbu ๋ณด์ •", "3D Bishop (Hungr) + corrected Janbu"], - E04_Method_hovland: ["Hovland (๋น„๊ต)", "Hovland (comparison)"], + E04_Condition: ["์กฐ๊ฑด", "Condition"], + E04_Condition_dry: ["๊ฑด๊ธฐ", "Dry"], + E04_Condition_wet: ["์šฐ๊ธฐ (์ง€ํ‘œ๊นŒ์ง€ ํฌํ™”)", "Wet (saturated to surface)"], E04_Defaults: ["๊ธฐ๋ณธ๊ฐ’", "Defaults"], E04_Analyze: ["ํ•ด์„", "Analyze"], E04_Reanalyze: ["๋‹ค์‹œ ํ•ด์„", "Re-analyze"], @@ -78,10 +77,16 @@ export const ui_locales_e04 = { "์ž…๋ ฅ(์ง€๋ฐ˜ ์กฐ๊ฑด ยท ํƒ์ƒ‰ ์„ค์ • ยท ์—”์ง„)์ด ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค โ€” ๋‹ค์‹œ ํ•ด์„ํ•˜์„ธ์š”.", "Inputs changed โ€” re-analyze.", ], - E04_Result_Preview: [ - "๋ฏธ๋ฆฌ๋ณด๊ธฐ ์—”์ง„ ๊ฒฐ๊ณผ(3D ์—”์ง„ ์—ฐ๊ฒฐ ์ „ ยท ์ง€ํ˜• ๊ฒฝ์‚ฌ๋กœ ๋งŒ๋“  ๊ฐ’)", - "Preview engine (slope-based placeholder)", + E04_Result_Note: [ + "3D/2D ๋น„ = ๊ฐ™์€ ๊ตฌ๊ฐ„ 3D ์ตœ์†Œ รท E03 ์ธก์  ์ตœ์†Œ ยท ๋ฐฉ๋ฒ•๋ณ„ = ๊ฐ™์€ ์ž„๊ณ„๋ฉด ยท ๋ฌผ์„ฑ = ๋ฌธํ—Œ ์ถ”์ •์น˜(ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ).", + "3D/2D = 3D min รท E03 station min ยท per-method values on the same surface ยท literature properties.", ], + E04_Result_Verdict: ["ํŒ์ •", "Verdict"], + E04_Verdict_ok: ["๋งŒ์กฑ", "OK"], + E04_Verdict_ng: ["๋ถˆ๋งŒ์กฑ", "NG"], + E04_Result_Bishop: [" 3D Bishop", " 3D Bishop"], + E04_Result_Janbu: [" 3D Janbu ๋ณด์ •", " 3D corrected Janbu"], + E04_Result_Hovland: [" Hovland (๋น„๊ต)", " Hovland (comparison)"], E04_No2D: ["2D ๊ฒฐ๊ณผ ์—†์Œ", "No 2D result"], E04_MethodName_bishop: ["3D Bishop", "3D Bishop"], E04_MethodName_janbu_corrected: ["3D Janbu ๋ณด์ •", "3D corrected Janbu"], diff --git a/ui_template/ui_template_locale_e05.ts b/ui_template/ui_template_locale_e05.ts index 836b265fd..1e1b979b0 100644 --- a/ui_template/ui_template_locale_e05.ts +++ b/ui_template/ui_template_locale_e05.ts @@ -10,8 +10,8 @@ import type { LocaleEntry } from "./ui_template_locale_common"; export const ui_locales_e05 = { /* --- E05 ๋ณด๊ณ ์„œ --- */ E05_Note: [ - "ํ‘œ์ง€ ยท ๊ฐœ์š”๋Š” ์‚ฌ์šฉ์ž ๋ชซ โ€” [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ๋•Œ ๊ทธ๋Œ€๋กœ ๋‘ก๋‹ˆ๋‹ค. ์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D์š”์•ฝ์€ ํ•ด์„ ๊ฒฐ๊ณผ๋กœ ๋‹ค์‹œ ์”๋‹ˆ๋‹ค.", - "Cover and summary are yours โ€” kept on [Refresh report]. Conditions, stations, sections and 3D sheets are rewritten from results.", + "ํ‘œ์ง€ ยท ๊ฐœ์š”๋Š” ์‚ฌ์šฉ์ž ๋ชซ โ€” [๋ณด๊ณ ์„œ ๊ฐฑ์‹ ] ๋•Œ ๊ทธ๋Œ€๋กœ ๋‘ก๋‹ˆ๋‹ค. ๋ชฉ์ฐจ ยท ์ž…๋ ฅ์กฐ๊ฑด ยท ์ธก์ ํ‘œ ยท ๋Œ€ํ‘œ๋‹จ๋ฉด ยท 3D ยท ๊ด‘์—ญ ยท ๋ณด๊ฐ• ยท ๋ถ€๋ก์€ ํ•ด์„ ๊ฒฐ๊ณผ๋กœ ๋‹ค์‹œ ์”๋‹ˆ๋‹ค.", + "Cover and summary are yours โ€” kept on [Refresh report]. Contents, conditions, stations, sections, 3D, area, reinforcement and appendix sheets are rewritten from results.", ], E05_Btn_Refresh: ["๋ณด๊ณ ์„œ ๊ฐฑ์‹ ", "Refresh report"], E05_Btn_Xlsx: ["์—‘์…€", "Excel"], diff --git a/ui_template/ui_template_locale_e06.ts b/ui_template/ui_template_locale_e06.ts new file mode 100644 index 000000000..e604dca01 --- /dev/null +++ b/ui_template/ui_template_locale_e06.ts @@ -0,0 +1,122 @@ +/* ============================================================================= + * ui_template_locale_e06.ts + * ๋‹ค๊ตญ์–ด ์‚ฌ์ „ โ€” E06 ์•”๋ฐ˜ ํ‰์‚ฌํˆฌ์˜ ํ•ด์„(์š”๊ตฌ 345 ยท 351 ยท PLAN 536-1) + * + * ์‹ ๊ทœ ๋ฌธ๊ตฌ๋Š” ํ•ด๋‹น ์„น์…˜ ์ตœํ•˜๋‹จ์— ์ถ”๊ฐ€. + * ========================================================================== */ + +import type { LocaleEntry } from "./ui_template_locale_common"; + +export const ui_locales_e06 = { + /* --- E06 ์ขŒ์ธก --- */ + E06_Note: [ + "์•” ์ธก์ (๋ฆฌํ•‘์•” ยท ๋ฐœํŒŒ์•”) ๊นŽ๊ธฐ ๋น„ํƒˆ๋งŒ ๋ด…๋‹ˆ๋‹ค. ์ ˆ๋ฆฌ๊ตฐ์„ ๋„ฃ๊ณ  [์ €์žฅ] โ†’ [ํ•ด์„]. ์‚ฌ๋ฉด ๋ฐฉํ–ฅ ยท ๊ฒฝ์‚ฌ ยท ๋†’์ด๋Š” ๋ณต์‚ฌ๋ณธ ํšก๋‹จ, ๋ฌผ์„ฑ ยท ์ง€์ง„ ยท ๊ธฐ์ค€์•ˆ์ „์œจ์€ ์ง€๋ฐ˜ ์กฐ๊ฑด(E02)์„ ์”๋‹ˆ๋‹ค.", + "Only cut slopes at rock stations (rippable ยท blasting rock). Enter joint sets, [Save] โ†’ [Analyze]. Slope orientation, angle and height come from the copied sections; strength, seismic and required FS from Ground (E02).", + ], + E06_Card_Joints: ["์ ˆ๋ฆฌ๊ตฐ", "Joint sets"], + E06_JointsHint: [ + "ํ˜„์žฅ ์กฐ์‚ฌ๊ฐ’ โ€” ๊ฒฝ์‚ฌ๋ฐฉํ–ฅ ยท ๊ฒฝ์‚ฌ(ยฐ) ยท ์ ˆ๋ฆฌ๋ฉด ฯ†j(ยฐ) ยท cj(kPa) ยท ์ง€์†์„ฑ(m ยท ๋น„์šฐ๋ฉด ๋ฌดํ•œ). ๊ตฌ๊ฐ„(m) ์•ˆ ์ธก์ ์—๋งŒ ์”๋‹ˆ๋‹ค.", + "Field data โ€” dip direction ยท dip (ยฐ) ยท joint ฯ†j (ยฐ) ยท cj (kPa) ยท persistence (m, blank = unlimited). Applies to stations within the range (m).", + ], + E06_Col_Name: ["์ด๋ฆ„", "Name"], + E06_Col_From: ["์‹œ์ž‘", "From"], + E06_Col_To: ["๋", "To"], + E06_Col_DipDir: ["๊ฒฝ์‚ฌ๋ฐฉํ–ฅ", "Dip dir."], + E06_Col_Dip: ["๊ฒฝ์‚ฌ", "Dip"], + E06_Col_Phi: ["ฯ†j", "ฯ†j"], + E06_Col_C: ["cj", "cj"], + E06_Col_Persist: ["์ง€์†์„ฑ", "Persist."], + E06_Add: ["+ ์ ˆ๋ฆฌ๊ตฐ", "+ Joint set"], + E06_Remove: ["์ง€์›€", "Remove"], + E06_NoSets: [ + "์ ˆ๋ฆฌ๊ตฐ์ด ์—†์Šต๋‹ˆ๋‹ค โ€” [+ ์ ˆ๋ฆฌ๊ตฐ] ์œผ๋กœ ๋„ฃ์œผ์„ธ์š”.", + "No joint sets โ€” add one with [+ Joint set].", + ], + E06_Card_Settings: ["ํ•ด์„ ์„ค์ •", "Analysis settings"], + E06_Set_LateralPlanar: ["ํ‰๋ฉด ์ธก๋ฐฉ ํ•œ๊ณ„ (ยฐ)", "Planar lateral limit (ยฐ)"], + E06_Set_LateralTopple: ["์ „๋„ ์ธก๋ฐฉ ํ•œ๊ณ„ (ยฐ)", "Toppling lateral limit (ยฐ)"], + E06_Set_Gamma: ["์•” ๋‹จ์œ„์ค‘๋Ÿ‰ (kN/mยณ ยท ๋น„์šฐ๋ฉด E02)", "Rock unit weight (kN/mยณ, blank = E02)"], + E06_Set_WaterWet: ["์šฐ๊ธฐ ์ˆ˜์œ„ ๋น„ (0~1)", "Wet water ratio (0โ€“1)"], + E06_Set_Crack: ["์ธ์žฅ๊ท ์—ด", "Tension crack"], + E06_Crack_None: ["์—†์Œ", "None"], + E06_Crack_Critical: ["์ž„๊ณ„(์ตœ์†Œ ์•ˆ์ „์œจ ์ž๋ฆฌ)", "Critical (minimum FS)"], + E06_Crack_Depth: ["๊นŠ์ด ์ง€์ •", "Given depth"], + E06_Set_CrackZ: ["๊ท ์—ด ๊นŠ์ด (m)", "Crack depth (m)"], + E06_Set_BoltForce: ["๋ณผํŠธ ํž˜ (ํ‰๋ฉด kN/m ยท ์๊ธฐ kN)", "Bolt force (plane kN/m ยท wedge kN)"], + E06_Set_BoltPlunge: ["๋ณผํŠธ ๊ฒฝ์‚ฌ (์ˆ˜ํ‰ ์•„๋ž˜ ยฐ)", "Bolt plunge (below horizontal ยฐ)"], + E06_SettingsHint: [ + "์ธก๋ฐฉ ํ•œ๊ณ„ ๊ธฐ๋ณธ 20ยฐ (Dips ๊ธฐ๋ณธ ยท ์ง€์‹DB 03 ์ „๋„๋Š” 30ยฐ) ยท ์šฐ๊ธฐ = ๋น„ํƒˆ ๋†’์ด ยฝ ์ˆ˜์œ„(KDS 11 70 05) ยท ๋ณผํŠธ = ์ˆ˜๋™ ์ง€์ง€๋ ฅ.", + "Lateral limit default 20ยฐ (Dips default; knowledge DB uses 30ยฐ for toppling) ยท wet = water at half height (KDS 11 70 05) ยท bolts = passive support.", + ], + E06_Analyze: ["ํ•ด์„", "Analyze"], + E06_Running: ["ํ•ด์„ ์ค‘โ€ฆ", "Analyzingโ€ฆ"], + E06_Done: ["์•”๋ฐ˜ ํ•ด์„์„ ๋งˆ์ณค์Šต๋‹ˆ๋‹ค.", "Rock analysis done."], + E06_Fresh: ["์ž…๋ ฅ์ด ๊ฐ™์•„ ์ง€๋‚œ ๊ฒฐ๊ณผ๋ฅผ ์”๋‹ˆ๋‹ค.", "Inputs unchanged โ€” previous result kept."], + E06_Saved: ["์ €์žฅํ–ˆ์Šต๋‹ˆ๋‹ค.", "Saved."], + E06_Unsaved: ["์ €์žฅ ์•ˆ ๋œ ๋ณ€๊ฒฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค.", "There are unsaved changes."], + E06_ReadOnly: ["๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.", "This project is view-only."], + E06_Invalid: ["ํ‹€๋ฆฐ ์นธ", "Invalid fields"], + E06_Err_Range: ["๋ฒ”์œ„ ๋ฐ–", "Out of range"], + E06_ResetConfirm: [ + "์ ˆ๋ฆฌ๊ตฐ ยท ์„ค์ •์„ ์ดˆ๊ธฐ๊ฐ’(์ ˆ๋ฆฌ ์—†์Œ)์œผ๋กœ ๋˜๋Œ๋ฆด๊นŒ์š”?", + "Reset joint sets and settings to the initial state (no joints)?", + ], + E06_ResetDone: ["์ดˆ๊ธฐ๊ฐ’์œผ๋กœ ๋˜๋Œ๋ ธ์Šต๋‹ˆ๋‹ค.", "Reset to initial values."], + E06_NoProject: ["์—ด๋ฆฐ ํ”„๋กœ์ ํŠธ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.", "No project is open."], + + /* --- E06 ๊ฐ€์šด๋ฐ ยท ์šฐ์ธก --- */ + E06_Stale: [ + "์ ˆ๋ฆฌ ยท ์ง€๋ฐ˜ ์กฐ๊ฑด์ด ํ•ด์„ ๋’ค ๋ฐ”๋€Œ์—ˆ์Šต๋‹ˆ๋‹ค โ€” [ํ•ด์„] ์„ ๋‹ค์‹œ ๋ˆ„๋ฅด์„ธ์š”.", + "Joints or ground changed since the analysis โ€” press [Analyze] again.", + ], + E06_Band: ["์œ„ํ—˜ ๋ ", "Risk band"], + E06_View_All: ["์ „์ฒด", "All"], + E06_Mode_planar: ["ํ‰๋ฉด", "Planar"], + E06_Mode_wedge: ["์๊ธฐ", "Wedge"], + E06_Mode_toppling: ["์ „๋„", "Toppling"], + E06_Proj_Angle: ["๋“ฑ๊ฐ", "Equal angle"], + E06_Proj_Area: ["๋“ฑ๋ฉด์ ", "Equal area"], + E06_PickStation: ["์ธก์ ์„ ๊ณ ๋ฅด์„ธ์š”.", "Pick a station."], + E06_Lg_Slope: ["์‚ฌ๋ฉด ๋Œ€์› ยท ๊ทน์ ", "Slope plane ยท pole"], + E06_Lg_Friction: ["๋งˆ์ฐฐ์› ฯ†", "Friction circle ฯ†"], + E06_Lg_Planar: ["ํ‰๋ฉด ์œ„ํ—˜(๊ทน์ )", "Planar zone (poles)"], + E06_Lg_Daylight: ["์ผ๊ด‘ ์—”๋ฒจ๋กœํ”„", "Daylight envelope"], + E06_Lg_Wedge: ["์๊ธฐ ์œ„ํ—˜(๊ต์„ )", "Wedge zone (intersections)"], + E06_Lg_Line: ["๊ต์„ ", "Intersection"], + E06_Lg_Toppling: ["์ „๋„ ์œ„ํ—˜(๊ทน์ )", "Toppling zone (poles)"], + E06_Lg_Pole: ["์ ˆ๋ฆฌ ๊ทน์ ", "Joint pole"], + E06_Result_Title: ["์ธก์  ๊ฒฐ๊ณผ", "Station results"], + E06_NoResult: ["ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค โ€” [ํ•ด์„] ์„ ๋ˆ„๋ฅด์„ธ์š”.", "No results โ€” press [Analyze]."], + E06_NoRock: ["์•” ์ธก์  ๊นŽ๊ธฐ ๋น„ํƒˆ์ด ์—†์Šต๋‹ˆ๋‹ค.", "No rock cut slopes."], + E06_L_Station: ["์ธก์ ", "Station"], + E06_L_Side: ["์ชฝ", "Side"], + E06_L_Modes: ["ํ‰ ยท ์ ยท ์ „", "P ยท W ยท T"], + E06_L_MinF: ["์ตœ์†Œ Fs", "Min Fs"], + E06_L_Verdict: ["ํŒ์ •", "Verdict"], + E06_Left: ["์ขŒ", "L"], + E06_Right: ["์šฐ", "R"], + E06_V_NG: ["N.G", "N.G"], + E06_V_check: ["๊ฒ€ํ† ", "Check"], + E06_V_OK: ["O.K", "O.K"], + E06_V_none: ["์œ„ํ—˜ ์—†์Œ", "No risk"], + E06_SlopeLine: [ + "์‚ฌ๋ฉด {dd}ยฐ / {dip}ยฐ ยท ๋†’์ด {h} m ยท ์œ—๋ฉด {up}ยฐ ยท ์•” ฮณ {g} kN/mยณ", + "Slope {dd}ยฐ / {dip}ยฐ ยท height {h} m ยท upper {up}ยฐ ยท rock ฮณ {g} kN/mยณ", + ], + E06_NoRisk: ["์œ„ํ—˜ ๋ชจ๋“œ ์—†์Œ(Markland).", "No kinematic failure mode (Markland)."], + E06_NoJointsHere: ["์ด ์ธก์  ๊ตฌ๊ฐ„์— ์ ˆ๋ฆฌ๊ตฐ์ด ์—†์Šต๋‹ˆ๋‹ค.", "No joint sets cover this station."], + E06_C_Mode: ["๋ชจ๋“œ", "Mode"], + E06_C_Joints: ["์ ˆ๋ฆฌ", "Joints"], + E06_C_Cond: ["์กฐ๊ฑด", "Case"], + E06_C_F: ["Fs", "Fs"], + E06_C_Req: ["๊ธฐ์ค€", "Req."], + E06_Cond_dry: ["๊ฑด๊ธฐ", "Dry"], + E06_Cond_wet: ["์šฐ๊ธฐ", "Wet"], + E06_Cond_seismic: ["์ง€์ง„", "Seismic"], + E06_NotFormed: ["(๋ธ”๋ก ์•ˆ ์ƒ๊น€)", "(no block)"], + E06_Persist: ["(์ง€์†์„ฑ < ํ™œ๋™๋ฉด)", "(persistence < plane)"], + E06_ToppleNote: [ + "์ „๋„ = ๊ธฐํ•˜ ํŒ์ •๋งŒ(๋ธ”๋ก ์ „๋„ ์•ˆ์ „์œจ์€ Goodman-Bray ์ •๋ฐ€ ๊ฒ€ํ†  ๋ชซ).", + "Toppling = kinematic only (block toppling FS needs a Goodmanโ€“Bray check).", + ], +} as const satisfies Record; diff --git a/ui_template/ui_template_locale_e07.ts b/ui_template/ui_template_locale_e07.ts new file mode 100644 index 000000000..a405ebc12 --- /dev/null +++ b/ui_template/ui_template_locale_e07.ts @@ -0,0 +1,98 @@ +/* ============================================================================= + * ui_template_locale_e07.ts + * ๋‹ค๊ตญ์–ด ์‚ฌ์ „ โ€” E07 ์•ˆ์ •์„ฑํ‰๊ฐ€ ๊ด‘์—ญ ์ž์—ฐ์‚ฌ๋ฉด ์œ„ํ—˜ ์ง€๋„(์š”๊ตฌ 345 ยท 351 ยท PLAN 536-2) + * + * ์‹ ๊ทœ ๋ฌธ๊ตฌ๋Š” ํ•ด๋‹น ์„น์…˜ ์ตœํ•˜๋‹จ์— ์ถ”๊ฐ€. + * ========================================================================== */ + +import type { LocaleEntry } from "./ui_template_locale_common"; + +export const ui_locales_e07 = { + /* --- E07 ๊ด‘์—ญ ์œ„ํ—˜ ์ง€๋„ --- */ + E07_NoProject: ["ํ”„๋กœ์ ํŠธ๋ฅผ ๋จผ์ € ๊ณ ๋ฅด์„ธ์š”.", "Pick a project first."], + E07_Hint: [ + "๋…ธ์„  ๋  ์•ˆ ์ž์—ฐ์‚ฌ๋ฉด์˜ ํ‘œ์ธต ๋ถ•๊ดด ์œ„ํ—˜ โ€” ๋ฌดํ•œ์‚ฌ๋ฉด ์•ˆ์ „์œจ๊ณผ SINMAP ์•ˆ์ • ์ง€์ˆ˜(SI)๋กœ ๋“ฑ๊ธ‰์„ ๋งค๊น๋‹ˆ๋‹ค. ์ง€ํ˜• = ๋ณต์‚ฌํ•œ ์›์ง€ํ˜•.", + "Shallow-landslide hazard of natural slopes in the route band โ€” infinite-slope Fs and SINMAP stability index (SI). Terrain = copied original DTM.", + ], + E07_ReadOnly: ["๋ณด๊ธฐ๋งŒ ํ—ˆ์šฉ๋œ ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.", "This project is view-only."], + E07_Card_Soil: ["ํ† ์ธต ๋ฌผ์„ฑ", "Soil layer"], + E07_Card_Rain: ["์‹œ๋‚˜๋ฆฌ์˜ค ยท ํฌํ™”", "Scenario ยท saturation"], + E07_Card_Band: ["๋…ธ์„  ๋  ยท ๊ฒฉ์ž ยท ์ธก์  ๋“ฑ๊ธ‰", "Route band ยท grid ยท station level"], + E07_Material: ["๋ฌผ์„ฑ(E02 ์ง€๋ฐ˜ ์กฐ๊ฑด)", "Material (E02 ground)"], + E07_GammaT: ["ฮณt (kN/mยณ)", "ฮณt (kN/mยณ)"], + E07_GammaSat: ["ฮณsat (kN/mยณ)", "ฮณsat (kN/mยณ)"], + E07_C: ["c ์•„๋ž˜ (kPa)", "c low (kPa)"], + E07_CHi: ["c ์œ„ (kPa)", "c high (kPa)"], + E07_Phi: ["ฯ† ์•„๋ž˜ (ยฐ)", "ฯ† low (ยฐ)"], + E07_PhiHi: ["ฯ† ์œ„ (ยฐ)", "ฯ† high (ยฐ)"], + E07_CRoot: ["๋ฟŒ๋ฆฌ ์ ์ฐฉ (kPa)", "Root cohesion (kPa)"], + E07_Depth: ["ํ† ์ธต ๋‘๊ป˜ (m)", "Soil depth (m)"], + E07_Scenario: ["์‹œ๋‚˜๋ฆฌ์˜ค", "Scenario"], + E07_Scenario_sinmap: [ + "ํ‘œ์ค€ โ€” SINMAP ์Šต์œค์ง€์ˆ˜ (T/R ๋ฒ”์œ„)", + "Standard โ€” SINMAP wetness (T/R range)", + ], + E07_Scenario_saturated: [ + "์ตœ์•… โ€” ์™„์ „ ํฌํ™” (m = 1 ยท ๊ฐ•์šฐ ๊ทนํ•œ)", + "Worst โ€” fully saturated (m = 1 ยท extreme rain)", + ], + E07_TrLo: ["T/R ์•„๋ž˜ (m ยท ์ –์€ ์ชฝ)", "T/R low (m ยท wetter)"], + E07_TrHi: ["T/R ์œ„ (m ยท ๋งˆ๋ฅธ ์ชฝ)", "T/R high (m ยท drier)"], + E07_Band: ["๋  ํญ ยฑ (m)", "Band half-width (m)"], + E07_Cell: ["๊ฒฉ์ž (m)", "Cell (m)"], + E07_LevelLo: ["์ธก์  ๋ณดํ†ต ๋ฌธํ„ฑ (SI<1 ๋น„์œจ %)", "Station mid threshold (SI<1 share %)"], + E07_LevelHi: ["์ธก์  ๋†’์Œ ๋ฌธํ„ฑ (SI<1 ๋น„์œจ %)", "Station high threshold (SI<1 share %)"], + E07_Defaults: ["๊ธฐ๋ณธ๊ฐ’", "Defaults"], + E07_Analyze: ["ํ•ด์„", "Analyze"], + E07_Reanalyze: ["๋‹ค์‹œ ํ•ด์„", "Re-analyze"], + E07_Running: ["ํ•ด์„ ์ค‘", "Analyzing"], + E07_Done: ["๊ด‘์—ญ ํ•ด์„์„ ๋งˆ์ณค์Šต๋‹ˆ๋‹ค.", "Area analysis finished."], + E07_Cached: ["๊ฐ™์€ ์กฐ๊ฑด์˜ ๊ฒฐ๊ณผ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.", "A result with the same inputs exists."], + E07_Layer_Class: ["๋“ฑ๊ธ‰ (SI)", "Class (SI)"], + E07_Layer_FS: ["์•ˆ์ „์œจ Fs", "Safety factor Fs"], + E07_Legend_Class: ["SINMAP ๋“ฑ๊ธ‰", "SINMAP class"], + E07_Legend_FS: ["๋ฌดํ•œ์‚ฌ๋ฉด Fs", "Infinite-slope Fs"], + E07_Class_stable: ["์•ˆ์ • (SI โ‰ฅ 1.5)", "Stable (SI โ‰ฅ 1.5)"], + E07_Class_moderate: ["๋ณดํ†ต ์•ˆ์ • (โ‰ฅ 1.25)", "Moderately stable (โ‰ฅ 1.25)"], + E07_Class_quasi: ["์ค€์•ˆ์ • (โ‰ฅ 1.0)", "Quasi-stable (โ‰ฅ 1.0)"], + E07_Class_lower: ["์œ„ํ—˜ ํ•˜ํ•œ (โ‰ฅ 0.5)", "Lower threshold (โ‰ฅ 0.5)"], + E07_Class_upper: ["์œ„ํ—˜ ์ƒํ•œ (> 0)", "Upper threshold (> 0)"], + E07_Class_defended: ["๋ถˆ์•ˆ์ • (SI = 0)", "Defended (SI = 0)"], + E07_Map_Empty: [ + "[ํ•ด์„] ์„ ๋ˆ„๋ฅด๋ฉด ์œ„ํ—˜ ์ง€๋„๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.", + "Press [Analyze] to draw the hazard map.", + ], + E07_Tip_Slope: ["๊ฒฝ์‚ฌ", "Slope"], + E07_Tip_Station: ["๊ฐ€๊นŒ์šด ์ธก์ ", "Nearest station"], + E07_Result_Title: ["๊ด‘์—ญ ๊ฒฐ๊ณผ", "Area result"], + E07_Result_None: ["์•„์ง ํ•ด์„ ๊ฒฐ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.", "No result yet."], + E07_Result_Stale: [ + "์—”์ง„์ด ๋ฐ”๋€Œ์–ด ๊ฒฐ๊ณผ๊ฐ€ ์˜›๊ฒƒ์ž…๋‹ˆ๋‹ค โ€” [๋‹ค์‹œ ํ•ด์„].", + "The engine changed โ€” re-analyze.", + ], + E07_Result_Note: [ + "๋ฌธํ—Œ ์ถ”์ •์น˜ ยท ํ˜„์žฅ ์‹œํ—˜ ์—†์Œ ยท ๊ธฐ๋ณธ๊ฐ’์€ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ. ํ‘œ์ค€ = ํฌํ™”๋น„ W = min(Rยทa / (Tยทsinฮธ), 1). SI = c ยท ฯ† ยท T/R ๋ฒ”์œ„ ์•ˆ์—์„œ Fs > 1 ์ผ ํ™•๋ฅ (ํ•˜ํ•œ Fs โ‰ฅ 1 ์ด๋ฉด ๊ทธ ๊ฐ’ ยท ์ƒํ•œ Fs < 1 ์ด๋ฉด 0). Fs = ๊ฐ€์žฅ ๋‚˜์œ ์ชฝ(c ยท ฯ† ์•„๋ž˜ ยท ๊ฐ€์žฅ ์ –์Œ). ์ง€ํ˜• ๊ฒฉ์ž ๋ ๋ฐ– ์ƒ๋ฅ˜๋Š” ์ง‘์ˆ˜๋ฉด์ ์— ์•ˆ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค.", + "Literature values, no field tests; defaults are editable. Standard = wetness W = min(Rยทa / (Tยทsinฮธ), 1). SI = probability of Fs > 1 over the c ยท ฯ† ยท T/R ranges (lower-bound Fs when โ‰ฅ 1, 0 when upper-bound Fs < 1). Fs = worst case (low c ยท ฯ†, wettest). Upslope area beyond the terrain grid is not counted.", + ], + E07_Sum_FsMin: ["๋  ์•ˆ ์ตœ์†Œ Fs", "Min Fs in band"], + E07_Sum_Risk: ["์œ„ํ—˜ ๋ฉด์  (SI < 1)", "Hazard area (SI < 1)"], + E07_Sum_Levels: ["์ธก์  ๋“ฑ๊ธ‰ (๋‚ฎ์Œ ยท ๋ณดํ†ต ยท ๋†’์Œ)", "Station levels (low ยท mid ยท high)"], + E07_Sum_Covered: ["์ง€ํ˜• ์žˆ๋Š” ๋  / ๋ ", "Band with terrain / band"], + E07_Sum_Time: ["ํ•ด์„", "Computed"], + E07_Areas_Title: ["๋“ฑ๊ธ‰๋ณ„ ๋ฉด์ ", "Area by class"], + E07_Stations_Title: ["์ธก์ ๋ณ„ ์œ„ํ—˜ (๋ˆ„๋ฅด๋ฉด ์ง€๋„๋กœ)", "Hazard by station (click to locate)"], + E07_Level_low: ["๋‚ฎ์Œ", "Low"], + E07_Level_mid: ["๋ณดํ†ต", "Medium"], + E07_Level_high: ["๋†’์Œ", "High"], + E07_Col_Class: ["๋“ฑ๊ธ‰", "Class"], + E07_Col_Area: ["๋ฉด์  (mยฒ)", "Area (mยฒ)"], + E07_Col_Station: ["์ธก์ ", "Station"], + E07_Col_FsMin: ["์ตœ์†Œ Fs", "Min Fs"], + E07_Col_Ratio: ["SI<1 ๋น„์œจ (%)", "SI<1 share (%)"], + E07_Col_Level: ["์ธก์  ๋“ฑ๊ธ‰", "Station level"], + E07_Col_RiskArea: ["์œ„ํ—˜ mยฒ", "Hazard mยฒ"], + E07_Defaults_Source: [ + "๊ธฐ๋ณธ๊ฐ’ = ๊ทผ๊ฑฐ ํ›„๋ณด ยท ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์Œ โ€” ๋ฌผ์„ฑ = E02 ๋ถ•์ ํ†  ยท T/R 2,000~3,000 m = SINMAP ์„ค๋ช…์„œ ๊ธฐ๋ณธ ๋ณด์ • ยท ๋ฟŒ๋ฆฌ ์ ์ฐฉ 1 kPa = ๊ตญ๋‚ด ์—ฐ๊ตฌ(์‚ฐ๋ฆผ 0~5 kPa ยท ๋‚˜์ง€ ยท ์ ˆ์„ฑํ† ๋ฉด 0) โ€” ์‚ฌ์šฉ์ž ํ™•์ • ์ „ ยท ์ธก์  ๋“ฑ๊ธ‰ ๋ฌธํ„ฑ 5 % ยท 20 % = ์˜ํ–ฅ ๊ตฌ์—ญ(์ธก์  ์•ž๋’ค ยฝ ๊ฐ„๊ฒฉ ร— ๋  ํญ) ์•ˆ SI<1 ์…€ ๋น„์œจ โ€” ์‚ฌ์šฉ์ž ํ™•์ • ์ „.", + "Defaults are evidence-based candidates and editable โ€” soil = E02 colluvium ยท T/R 2,000โ€“3,000 m = SINMAP manual default ยท root cohesion 1 kPa = Korean studies (forest 0โ€“5 kPa ยท bare / cut slopes 0) โ€” not yet confirmed by the user ยท station level thresholds 5 % ยท 20 % = share of SI<1 cells in the station zone (half-spacing either side ร— band width) โ€” not yet confirmed by the user.", + ], +} as const satisfies Record;