diff --git a/B03_FileInput/B03_FileInput_Service_Chain.py b/B03_FileInput/B03_FileInput_Service_Chain.py index 00e959af..14127036 100644 --- a/B03_FileInput/B03_FileInput_Service_Chain.py +++ b/B03_FileInput/B03_FileInput_Service_Chain.py @@ -148,6 +148,10 @@ async def run_auto_design_chain( mark_designing, save_initial_snapshot, ) + from common_util.common_util_route_geometry import ( + expected_route_csv_path, + write_route_csv, + ) from common_util.common_util_storage import resolve_stored_project_path from common_util.common_util_surface_confirmation import get_surface_confirmation_params from config.config_db import get_db_pool @@ -195,6 +199,12 @@ async def run_auto_design_chain( mark_design_failed(project_root, "계획노선이 없어 초기 노선을 세울 수 없습니다.") return None + # 2.5) 예상노선(원본) 정본을 남긴다 — 초기값 스냅샷과 달리 재확정 체인이 지우지 + # 않는 자리라, 노선 초기화가 언제나 이 값으로 돌아갈 수 있다(2026-09-06 PLAN 0-7). + expected_path = expected_route_csv_path(project_root) + if not expected_path.is_file(): + write_route_csv(expected_path, points) + # 3) B05 경로 계산 request = RouteSolveRequest( filter_key=str(defaults["source_filter"]), diff --git a/B05_Profile/B05_Profile_Api_Replan.ts b/B05_Profile/B05_Profile_Api_Replan.ts new file mode 100644 index 00000000..2761b114 --- /dev/null +++ b/B05_Profile/B05_Profile_Api_Replan.ts @@ -0,0 +1,86 @@ +/* ============================================================================= + * B05_Profile_Api_Replan.ts + * 계획노선 두 벌(예상노선·계획노선) 읽기와 노선 갈아 끼우기 요청. + * + * GET /projects/{id}/route/plan → 예상노선·계획노선 정점(사업지 좌표계 m) + * POST /projects/{id}/route/replan → 고친 계획노선으로 갈아 끼우고 재계산 + * POST /projects/{id}/route/replan/reset → 계획노선을 예상노선으로 되돌리고 재계산 + * + * 재계산은 배수유역부터 전 단계를 다시 도는 무거운 작업이라(용화 67측점 기준 3분대) + * 타임아웃을 길게 잡는다 — 기본값으로 두면 중간에 끊긴다. + * ========================================================================== */ + +import { API_BASE_URL } from "@config/config_frontend"; + +/** 노선 재계산 대기 상한 — 배수유역 분석(90초대)까지 포함해 넉넉히 잡는다. */ +const REPLAN_TIMEOUT_MS = 15 * 60 * 1000; + +export interface RoutePlanResponse { + status: string; + project_id: string; + /** 예상노선(원본) 정점 [[x, y], …] — 사업지 좌표계(m). */ + expected: Array<[number, number]>; + /** 계획노선(수정본). 고친 적이 없으면 예상노선과 같은 값. */ + planned: Array<[number, number]>; + /** 사용자가 고친 계획노선이 저장돼 있으면 true. */ + edited: boolean; +} + +export interface RouteReplanResponse { + status: string; + project_id: string; + route_id: number | null; + total_length_m: number | null; + vertex_count: number; +} + +async function requestJson(path: string, init: RequestInit, timeoutMs: number): Promise { + const controller = new AbortController(); + const timer = window.setTimeout(() => controller.abort(), timeoutMs); + try { + const response = await fetch(`${API_BASE_URL}${path}`, { + ...init, + credentials: "include", + headers: { "Content-Type": "application/json", ...(init.headers ?? {}) }, + signal: controller.signal, + }); + const payload = (await response.json()) as T & { message?: string }; + if (!response.ok) throw new Error(payload.message ?? `HTTP ${response.status}`); + return payload; + } finally { + window.clearTimeout(timer); + } +} + +/** 예상노선·계획노선을 함께 읽는다(편집 모달이 점선·실선으로 그린다). */ +export async function fetchRoutePlan(projectId: string): Promise { + return requestJson( + `/projects/${projectId}/route/plan`, + { method: "GET" }, + 60000, + ); +} + +/** 고친 계획노선으로 갈아 끼우고 배수유역부터 다시 계산한다. */ +export async function replanRoute( + projectId: string, + vertices: Array<[number, number]>, +): Promise { + return requestJson( + `/projects/${projectId}/route/replan`, + { + method: "POST", + body: JSON.stringify({ vertices: vertices.map(([x, y]) => ({ x, y })) }), + }, + REPLAN_TIMEOUT_MS, + ); +} + +/** 계획노선을 예상노선으로 되돌리고 같은 재계산을 돈다(노선 초기화). */ +export async function resetRoutePlan(projectId: string): Promise { + return requestJson( + `/projects/${projectId}/route/replan/reset`, + { method: "POST" }, + REPLAN_TIMEOUT_MS, + ); +} diff --git a/B05_Profile/B05_Profile_Router_Replan.py b/B05_Profile/B05_Profile_Router_Replan.py new file mode 100644 index 00000000..6071d1e7 --- /dev/null +++ b/B05_Profile/B05_Profile_Router_Replan.py @@ -0,0 +1,300 @@ +"""계획노선 편집 — 노선 두 벌(예상노선·계획노선)과 그 뒤 재계산의 서버 몫. + +노선은 두 벌이다(2026-09-06 사용자 확정, PLAN 0-7). + + - **예상노선**(원본) — 파일 업로드 자동 체인이 낸 노선. `initial_snapshot/planned_route.csv`. + 어떤 경로로도 고치지 않는다. + - **계획노선**(수정본) — 예상노선과 **같은 값으로 시작**해 사용자가 고쳐 쓰는 노선. + `B05_Profile/route/planned_route.csv`. 설계 계통은 이것이 있으면 이것을 읽는다 + (`load_design_route`). + +노선 초기화는 수정본 파일을 지우는 것이다 — 그러면 원본을 읽으므로 「원본을 수정본으로 +복사」와 결과가 같다. + +[확인]을 눌렀을 때만 계산이 돈다. 재계산은 **초기 업로드 체인의 로직을 그대로 재사용**한다 +(2026-09-06 사용자 제안) — `run_redesign_chain`이 배수유역 다시 분석 → 기본 관 저장 → 관 +정착 계획선 재산출 → 종횡단 재생성 → 옛 측점 설계를 누가거리로 이월 → B05·B06 확정까지 한 +줄로 돈다. 여기서 하는 일은 그 앞에 **노선을 갈아 끼우는 것**뿐이다. + +시설 처리(2026-09-06 사용자 확정) — 횡단배수 지점은 노선 자리에 따라 생기고 없어지므로 +저장분을 버리고 **새로 계산한 값**을 쓴다. 그 밖의 구조물은 **옛 측점값을 그대로** 이어받는다 +(`structures.json`은 프로젝트 정본이라 손대지 않으면 그대로 남는다). + + GET /api/projects/{id}/route/plan → 예상노선·계획노선 정점(사업지 좌표계) + POST /api/projects/{id}/route/replan → 고친 계획노선으로 갈아 끼우고 재계산 + POST /api/projects/{id}/route/replan/reset → 계획노선을 예상노선으로 되돌리고 재계산 +""" + +import asyncio +import csv +import logging +from pathlib import Path +from typing import Any +from uuid import UUID + +from fastapi import APIRouter +from fastapi.responses import JSONResponse +from pydantic import BaseModel, Field + +from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path +from common_util.common_util_initial_snapshot import design_route_csv_path +from common_util.common_util_route_geometry import ( + densify_route, + expected_route_csv_path, + planned_route_working_path, + read_planned_route_csv, + write_route_csv, +) +from common_util.common_util_storage import resolve_stored_project_path +from config.config_db import get_db_pool +from config.config_system import ( + ROUTE_DIRECT_LINK_CELL_FACTOR, + ROUTE_GRID_RES_M, + ROUTE_PLANNED_DENSIFY_SAFETY, +) + +logger = logging.getLogger(__name__) +router = APIRouter(prefix="/api/projects", tags=["B05 Route Design"]) + +_PROJECT_PATH_MISSING = {"status": "error", "message": "프로젝트 저장 경로를 찾을 수 없습니다."} + + +class RouteVertexInput(BaseModel): + """계획노선 정점 하나 — 사업지 좌표계(m).""" + + x: float + y: float + + +class RouteReplanRequest(BaseModel): + """고친 계획노선. 정점은 시점 → 종점 순서(사업지 좌표계 m).""" + + vertices: list[RouteVertexInput] = Field(default_factory=list) + + +async def _project_paths(project_id: UUID) -> tuple[Path, str] | None: + """(프로젝트 실경로, 저장소 상대경로). 없으면 None.""" + pool = get_db_pool() + async with pool.acquire() as connection: + stored = await get_project_storage_relative_path(connection, project_id) + if not stored: + return None + return Path(resolve_stored_project_path(stored)), str(stored) + + +def _vertices_of(path: Path) -> list[list[float]]: + """CSV 정점을 [[x, y], …]로. 없거나 못 읽으면 빈 목록.""" + if not path.is_file(): + return [] + route = read_planned_route_csv(path) + if route is None: + return [] + return [[float(vertex.x), float(vertex.y)] for vertex in route.vertices] + + +def _ensure_expected_route(project_root: Path) -> str: + """예상노선(원본) 정본이 없으면 만들어 둔다. 어디서 씨앗을 얻었는지 돌려준다. + + 새 프로젝트는 자동 체인이 만들어 두지만(2026-09-06), 그 전에 만들어진 프로젝트는 + 초기값 스냅샷 안에만 있거나 그마저 재확정 체인이 지운 뒤일 수 있다. 노선 초기화가 + 성립하려면 이 파일이 반드시 있어야 하므로 여기서 한 번 세워 둔다. + """ + target = expected_route_csv_path(project_root) + if target.is_file(): + return "already" + for source, label in ( + (design_route_csv_path(project_root), "snapshot"), + (planned_route_working_path(project_root), "working"), + ): + vertices = _vertices_of(source) + if len(vertices) >= 2: + write_route_csv(target, [{"x": x, "y": y} for x, y in vertices]) + logger.info("예상노선 정본을 %s 에서 세웠습니다: %s", label, target) + return label + return "none" + + +def _write_working_route(path: Path, vertices: list[tuple[float, float]]) -> int: + """계획노선(수정본) CSV를 쓴다. 열 이름은 `read_planned_route_csv()`가 아는 것. + + 쓰기 전에 **조밀화**한다 — 정점이 성기면 B05 격자 탐색이 원좌표를 그대로 잇지 못하고 + 제 나름의 길을 찾아 사용자가 그린 선과 달라진다(`densify_route` 주석). 평면 형상은 + 바뀌지 않고 같은 선 위에 점만 더 찍힌다. + """ + dense = densify_route( + vertices, + ROUTE_DIRECT_LINK_CELL_FACTOR * ROUTE_GRID_RES_M * ROUTE_PLANNED_DENSIFY_SAFETY, + ) + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("w", encoding="utf-8", newline="") as file: + writer = csv.writer(file) + writer.writerow(("sequence", "x", "y")) + writer.writerows((index, round(x, 4), round(y, 4)) for index, (x, y) in enumerate(dense)) + return len(dense) + + +async def _recompute(project_id: UUID, project_root: Path, stored_path: str) -> dict[str, Any]: + """노선을 갈아 끼운 뒤의 재계산 — 초기 업로드 체인과 같은 로직을 그대로 탄다.""" + import aiomysql + + from B03_FileInput.B03_FileInput_Service_Chain import ( + _planned_route_points_in_project_crs, + run_redesign_chain, + ) + from B04_PreProcess.B04_PreProcess_Service import find_surface_model_for_selection + from B05_Profile.B05_Profile_Repository import get_latest_route + from common_util.common_util_drainage_pipes import clear_pipe_points + from common_util.common_util_surface_confirmation import get_surface_confirmation_params + from common_util.common_util_workflow_state import get_workflow_state, start_stage + + # 횡단배수 지점은 새 노선에서 새로 계산한다 — 저장분을 남기면 옛 자리가 살아난다. + await asyncio.to_thread(clear_pipe_points, stored_path) + + pool = get_db_pool() + async with pool.acquire() as connection: + selection = await get_surface_confirmation_params(connection, str(project_id)) + latest = await get_latest_route(connection, project_id) + # 지금 노선이 쓰던 지표면 모델이 1순위다 — 확정 선택값으로 다시 찾으면 프로젝트마다 + # 모델 조합이 달라 못 찾는 경우가 있다(용화: filter=classification/method=dtm 무매칭). + surface_model_id = ( + int(latest["surface_model_id"]) if latest and latest.get("surface_model_id") else None + ) + if surface_model_id is None: + try: + surface_model_id = await find_surface_model_for_selection( + connection, project_id, selection + ) + except Exception: + surface_model_id = None + async with connection.cursor() as cursor: + await cursor.execute( + "SELECT route_start_m, route_end_m FROM projects WHERE id = %s", + (str(project_id),), + ) + range_row = await cursor.fetchone() + if surface_model_id is None: + return {"error": "확정된 지표면 모델을 찾지 못해 노선을 다시 계산할 수 없습니다."} + route_range = (range_row[0], range_row[1]) if range_row else None + + # 갈아 끼운 노선을 stage 2 제어점으로 세운다 — 재확정 체인이 여기서 BP·EP·경유점을 + # 읽어 경로를 다시 푼다. 읽기는 설계 계통과 같은 한 곳(`load_design_route`)을 지난다. + points = await asyncio.to_thread( + _planned_route_points_in_project_crs, project_root, selection, route_range + ) + if not points or len(points) < 2: + return {"error": "계획노선을 읽지 못했습니다(정점이 2개 미만)."} + + # 수정본도 예상노선 정본도 없던 프로젝트 — 지금 읽은 것이 곧 예상노선이므로 여기서 세운다. + expected_path = expected_route_csv_path(project_root) + if not expected_path.is_file() and not planned_route_working_path(project_root).is_file(): + await asyncio.to_thread(write_route_csv, expected_path, points) + logger.info("예상노선 정본을 원본 재판독으로 세웠습니다: %s", expected_path) + + pool = get_db_pool() + async with pool.acquire() as connection: + async with connection.cursor(aiomysql.DictCursor) as cursor: + state = await get_workflow_state(cursor, str(project_id)) + stage2 = next( + (s for s in (state or {}).get("stages", []) if int(s.get("stage_no", -1)) == 2), None + ) + params = dict((stage2 or {}).get("params") or {}) + params["points"] = { + "bp": points[0], + "ep": points[-1], + "cp": [{**point, "order": index} for index, point in enumerate(points[1:-1], start=1)], + } + async with connection.cursor(aiomysql.DictCursor) as cursor: + await start_stage(cursor, str(project_id), 2, params) + await connection.commit() + + await run_redesign_chain(project_id, int(surface_model_id), selection) + + pool = get_db_pool() + async with pool.acquire() as connection: + made = await get_latest_route(connection, project_id) + return { + "route_id": int(made["id"]) if made else None, + "total_length_m": float(made.get("total_length_m") or 0.0) if made else None, + "vertex_count": len(points), + } + + +@router.get("/{project_id}/route/plan", response_model=None) +async def read_route_plan(project_id: UUID) -> dict[str, Any] | JSONResponse: + """예상노선(원본)과 계획노선(수정본) 정점을 함께 돌려준다 — 편집 모달이 둘 다 그린다.""" + paths = await _project_paths(project_id) + if paths is None: + return JSONResponse(status_code=404, content=_PROJECT_PATH_MISSING) + project_root, _ = paths + # 예상노선 정본이 먼저, 없으면 옛 자리(초기값 스냅샷)를 본다. + expected = await asyncio.to_thread(_vertices_of, expected_route_csv_path(project_root)) + if not expected: + expected = await asyncio.to_thread(_vertices_of, design_route_csv_path(project_root)) + working_path = planned_route_working_path(project_root) + planned = await asyncio.to_thread(_vertices_of, working_path) + return { + "status": "success", + "project_id": str(project_id), + "expected": expected, + # 고친 적이 없으면 계획노선 = 예상노선(같은 값으로 시작한다). + "planned": planned or expected, + "edited": bool(planned), + } + + +@router.post("/{project_id}/route/replan", response_model=None) +async def replan_route( + project_id: UUID, request: RouteReplanRequest +) -> dict[str, Any] | JSONResponse: + """고친 계획노선으로 갈아 끼우고 배수유역부터 다시 계산한다(모달 [확인]).""" + if len(request.vertices) < 2: + return JSONResponse( + status_code=400, + content={"status": "error", "message": "계획노선은 정점이 2개 이상이어야 합니다."}, + ) + paths = await _project_paths(project_id) + if paths is None: + return JSONResponse(status_code=404, content=_PROJECT_PATH_MISSING) + project_root, stored_path = paths + + # 고치기 전에 예상노선(원본)이 서 있는지 본다 — 초기화가 돌아갈 자리다. + await asyncio.to_thread(_ensure_expected_route, project_root) + vertices = [(vertex.x, vertex.y) for vertex in request.vertices] + written = await asyncio.to_thread( + _write_working_route, planned_route_working_path(project_root), vertices + ) + logger.info( + "계획노선 갈아 끼움: project_id=%s 정점 %d→%d(조밀화)", + project_id, + len(vertices), + written, + ) + result = await _recompute(project_id, project_root, stored_path) + if "error" in result: + return JSONResponse( + status_code=409, content={"status": "error", "message": result["error"]} + ) + return {"status": "success", "project_id": str(project_id), **result} + + +@router.post("/{project_id}/route/replan/reset", response_model=None) +async def reset_route_plan(project_id: UUID) -> dict[str, Any] | JSONResponse: + """계획노선을 예상노선으로 되돌리고 다시 계산한다(노선 초기화). + + 수정본 파일을 지우면 설계 계통이 원본을 읽으므로 「원본을 수정본으로 복사」와 같다. + """ + paths = await _project_paths(project_id) + if paths is None: + return JSONResponse(status_code=404, content=_PROJECT_PATH_MISSING) + project_root, stored_path = paths + await asyncio.to_thread(_ensure_expected_route, project_root) + working_path = planned_route_working_path(project_root) + if working_path.is_file(): + working_path.unlink() + logger.info("계획노선 초기화(예상노선으로): project_id=%s", project_id) + result = await _recompute(project_id, project_root, stored_path) + if "error" in result: + return JSONResponse( + status_code=409, content={"status": "error", "message": result["error"]} + ) + return {"status": "success", "project_id": str(project_id), **result} diff --git a/B05_Profile/B05_Profile_UI_Page.ts b/B05_Profile/B05_Profile_UI_Page.ts index e21df61d..57f200e4 100644 --- a/B05_Profile/B05_Profile_UI_Page.ts +++ b/B05_Profile/B05_Profile_UI_Page.ts @@ -26,6 +26,7 @@ import { createRoutePanel, type RoutePanelValues } from "./B05_Profile_UI_Panel" import { createRouteProfilePanel } from "./B05_Profile_UI_Profile_Panel"; import { leaveForDashboard } from "../A00_Common/b_missing_data_guard"; import { navigateTo } from "../A00_Common/router"; +import { openRouteEditModal } from "./B05_Profile_UI_RouteEdit"; import { createSelectionSync } from "./B05_Profile_UI_Selection"; import { restoreStructurePick, @@ -270,6 +271,12 @@ export async function renderB05Route(root: HTMLElement): Promise { const panel = createRoutePanel({ onSolve: () => void solveRouteAction(actionContext), + // 계획노선 편집 — 모달 [확인]에서 서버가 배수유역부터 다시 계산하므로, 끝나면 + // 옛 노선 기준 캐시를 버리고 페이지를 새로 세운다([초기화]와 같은 뒷정리). + onEditPlannedRoute: () => + void openRouteEditModal(activeProjectId, () => { + navigateTo(ROUTES.B05_PROFILE); + }), onTempSave: () => void tempSaveAction(actionContext), onGoCross: () => { // 페이지 이동 = 코리도 영구저장 시점(2026-08-23 사용자 확정) — 이동은 막지 않는다. diff --git a/B05_Profile/B05_Profile_UI_Panel.ts b/B05_Profile/B05_Profile_UI_Panel.ts index e9ec05cf..2480ac40 100644 --- a/B05_Profile/B05_Profile_UI_Panel.ts +++ b/B05_Profile/B05_Profile_UI_Panel.ts @@ -86,6 +86,8 @@ const SPEED_CHOICES: Record> = { interface PanelCallbacks { onSolve: () => void; + /** [계획노선 편집] — 큰 모달을 열어 노선을 고친다(계산은 모달 [확인]에서만 돈다). */ + onEditPlannedRoute: () => void; /** [임시저장] — 현재 편집(계획선 델타·관로·비정규 측점·상단측)을 확정 전이 없이 저장. */ onTempSave: () => void; /** [횡단 이동] — 저장 없이 B06 횡단 페이지로 이동만. */ @@ -302,6 +304,17 @@ export function createRoutePanel(callbacks: PanelCallbacks) { // 포인트 팔레트 + 임도 기준·옵션을 한 컨테이너로 병합하고 [최적 경로 계산]도 이 안에 // 둔다 — 경로 재탐색은 B04~B06 재계산을 부르는 무거운 작업이라 가끔만 쓴다 // (2026-08-08 사용자 지시). + // 계획노선 편집 — 노선을 고치는 유일한 입구(2026-09-06 사용자 확정). 자동탐색 + // (「경로 계산 설정」)은 사용 중단이라 이 자리가 노선을 바꾸는 길이다. + const plannedRoute = section("계획노선"); + const plannedRouteBtn = document.createElement("button"); + plannedRouteBtn.type = "button"; + plannedRouteBtn.className = "b05-route__btn"; + plannedRouteBtn.textContent = "계획노선 편집"; + plannedRouteBtn.title = "예상노선 위에서 계획노선을 고칩니다. [확인] 때만 다시 계산합니다."; + plannedRouteBtn.addEventListener("click", () => callbacks.onEditPlannedRoute()); + plannedRoute.body.append(plannedRouteBtn); + const routeCalc = section("경로 계산 설정"); routeCalc.root.classList.add("is-collapsed"); const paletteGrid = document.createElement("div"); @@ -603,7 +616,14 @@ export function createRoutePanel(callbacks: PanelCallbacks) { // 배치 순서: 구조물 배치 > 페이지 설정 > 경로 계산 설정(비활성) > 선택 포인트 // (평소 숨김) > 하단 고정 dock (2026-08-18 사용자 확정). - root.append(structures.root, sectionOptions.root, routeCalc.root, selected.root, actionDock); + root.append( + structures.root, + sectionOptions.root, + plannedRoute.root, + routeCalc.root, + selected.root, + actionDock, + ); // 컨테이너 제목 행 전체 클릭 시 본문을 접거나 편다(공용 collapsible). 내부 details 등 별도 // 접힘 항목은 손대지 않는다. diff --git a/B05_Profile/B05_Profile_UI_RouteEdit.ts b/B05_Profile/B05_Profile_UI_RouteEdit.ts new file mode 100644 index 00000000..6fbbae78 --- /dev/null +++ b/B05_Profile/B05_Profile_UI_RouteEdit.ts @@ -0,0 +1,375 @@ +/* ============================================================================= + * B05_Profile_UI_RouteEdit.ts + * 계획노선 편집 모달 — 예상노선(점선) 위에 계획노선(실선)을 고쳐 그린다. + * + * 왜 모달인가(2026-09-06 사용자 확정) — [확인]을 누르면 배수유역부터 종·횡단·유토곡선까지 + * 전 단계가 다시 도는 무거운 작업이다(용화 67측점 3분대). 신중히 하라는 뜻으로 큰 모달을 + * 쓰고, **편집 중에는 아무 계산도 나가지 않는다**. + * + * 노선은 두 벌이다 — 예상노선(원본, 안 바뀜)과 계획노선(수정본, 사용자가 고침). + * [예상노선으로]는 수정본을 버리고 원본으로 되돌린다(서버가 파일을 지우고 같은 재계산). + * + * 그림은 배수유역도와 같은 지도 도구(`B04_PreProcess_UI_MapRender`)를 쓴다 — 등고선 도엽은 + * 위경도, 노선은 사업지 좌표계(m)지만 두 변환기가 같은 정규화 공간을 본다. + * ========================================================================== */ + +import { + computeMapRect, + computeRouteView, + drawPreparedLayer, + createNormalizer, + metricToScreen, + prepareLayer, + type PreparedLayer, + type ViewState, +} from "../B04_PreProcess/B04_PreProcess_UI_MapRender"; +import type { VWorldMeta } from "../B04_PreProcess/B04_PreProcess_Api_Fetch"; +import { clearDrafts, clearResults } from "../A00_Common/b_page_state"; +import { showToast } from "@ui/ui_template_elements"; +import { fetchDrainageLayers } from "./B05_Profile_UI_Drainage_Parts"; +import { fetchRoutePlan, replanRoute, resetRoutePlan } from "./B05_Profile_Api_Replan"; +import "./B05_Profile_UI_Style_RouteEdit.css"; + +/** 노드를 잡았다고 볼 거리(px). 손가락·마우스 모두 무리 없는 크기. */ +const NODE_HIT_PX = 9; +/** 노드 반지름(px). */ +const NODE_R = 4; +/** 끌기로 볼 최소 이동(px) — 이보다 작으면 클릭으로 본다. */ +const DRAG_THRESHOLD_PX = 3; + +type Vertex = [number, number]; + +/** 모달을 연다. [확인]·[예상노선으로]가 끝나면 `onApplied`를 부른다(화면 다시 읽기). */ +export async function openRouteEditModal( + projectId: string, + onApplied: () => void | Promise, +): Promise { + const overlay = document.createElement("div"); + overlay.className = "b05-routeedit"; + overlay.innerHTML = ` + `; + document.body.append(overlay); + + const canvas = overlay.querySelector(".b05-routeedit__canvas")!; + const status = overlay.querySelector(".b05-routeedit__status")!; + const busy = overlay.querySelector(".b05-routeedit__busy")!; + const context = canvas.getContext("2d")!; + + let expected: Vertex[] = []; + let planned: Vertex[] = []; + let meta: VWorldMeta | null = null; + let sheets: PreparedLayer[] = []; + let view: ViewState = { + width: 0, + height: 0, + scale: 1, + offsetX: 0, + offsetY: 0, + mapRect: computeMapRect(null, 0, 0), + }; + let closed = false; + + const close = (): void => { + closed = true; + window.removeEventListener("resize", resize); + overlay.remove(); + }; + overlay.querySelector(".b05-routeedit__close")!.addEventListener("click", close); + overlay.querySelector('[data-act="cancel"]')!.addEventListener("click", close); + // 배경 클릭으로 닫지 않는다 — 고치던 노선을 실수로 날리지 않게. + + function resize(): void { + if (closed) return; + const wrap = canvas.parentElement!; + const ratio = window.devicePixelRatio || 1; + const width = wrap.clientWidth; + const height = wrap.clientHeight; + canvas.width = Math.round(width * ratio); + canvas.height = Math.round(height * ratio); + canvas.style.width = `${width}px`; + canvas.style.height = `${height}px`; + context.setTransform(ratio, 0, 0, ratio, 0, 0); + view = { ...view, width, height, mapRect: computeMapRect(meta, width, height) }; + draw(); + } + window.addEventListener("resize", resize); + + const toScreen = (vertex: Vertex): [number, number] => + meta ? metricToScreen(meta, view, vertex[0], vertex[1]) : [0, 0]; + + /** 화면 px → 사업지 좌표(m). `metricToScreen`이 선형이므로 두 기준점으로 역산한다. */ + function toMetric(px: number, py: number): Vertex { + if (!meta) return [0, 0]; + const [x0, y0] = metricToScreen(meta, view, meta.x_min, meta.y_min); + const [x1, y1] = metricToScreen( + meta, + view, + meta.x_min + meta.width_meters, + meta.y_min + meta.height_meters, + ); + const sx = (x1 - x0) / (meta.width_meters || 1); + const sy = (y1 - y0) / (meta.height_meters || 1); + return [meta.x_min + (px - x0) / (sx || 1), meta.y_min + (py - y0) / (sy || 1)]; + } + + function strokePolyline(points: Vertex[], dash: number[], color: string, width: number): void { + if (points.length < 2) return; + context.save(); + context.setLineDash(dash); + context.strokeStyle = color; + context.lineWidth = width; + context.beginPath(); + points.forEach((vertex, index) => { + const [x, y] = toScreen(vertex); + if (index === 0) context.moveTo(x, y); + else context.lineTo(x, y); + }); + context.stroke(); + context.restore(); + } + + function draw(): void { + if (closed) return; + const style = getComputedStyle(document.documentElement); + context.clearRect(0, 0, view.width, view.height); + context.fillStyle = style.getPropertyValue("--color-surface") || "#111"; + context.fillRect(0, 0, view.width, view.height); + + context.save(); + context.strokeStyle = style.getPropertyValue("--map-sheet-contour") || "#a5b4fc"; + context.lineWidth = 0.8; + for (const layer of sheets) drawPreparedLayer(context, layer, view, "dot"); + context.restore(); + + strokePolyline( + expected, + [6, 5], + style.getPropertyValue("--color-text-secondary") || "#9ca3af", + 1.6, + ); + strokePolyline(planned, [], style.getPropertyValue("--map-route") || "#f97316", 2.4); + + context.save(); + context.fillStyle = style.getPropertyValue("--map-route") || "#f97316"; + context.strokeStyle = style.getPropertyValue("--map-halo") || "rgba(255,255,255,0.9)"; + context.lineWidth = 1; + for (const vertex of planned) { + const [x, y] = toScreen(vertex); + context.beginPath(); + context.arc(x, y, NODE_R, 0, Math.PI * 2); + context.fill(); + context.stroke(); + } + context.restore(); + } + + /** 화면 좌표에 가장 가까운 노드. 문턱 밖이면 -1. */ + function nodeAt(px: number, py: number): number { + let best = -1; + let bestDistance = NODE_HIT_PX; + planned.forEach((vertex, index) => { + const [x, y] = toScreen(vertex); + const distance = Math.hypot(x - px, y - py); + if (distance <= bestDistance) { + bestDistance = distance; + best = index; + } + }); + return best; + } + + /** 두 노드 사이 선분 중 클릭에 가장 가까운 것 — 새 노드를 끼울 자리. */ + function segmentAt(px: number, py: number): number { + let best = -1; + let bestDistance = 12; + for (let index = 0; index < planned.length - 1; index += 1) { + const [ax, ay] = toScreen(planned[index]); + const [bx, by] = toScreen(planned[index + 1]); + const dx = bx - ax; + const dy = by - ay; + const lengthSquared = dx * dx + dy * dy || 1; + const t = Math.max(0, Math.min(1, ((px - ax) * dx + (py - ay) * dy) / lengthSquared)); + const distance = Math.hypot(ax + t * dx - px, ay + t * dy - py); + if (distance < bestDistance) { + bestDistance = distance; + best = index; + } + } + return best; + } + + // ── 조작 — 노드 끌기 / 배경 끌기(팬) / 휠 확대 / 두 번 클릭 삽입 / 오른쪽 클릭 삭제 ── + let dragNode = -1; + let panFrom: { x: number; y: number; offsetX: number; offsetY: number } | null = null; + + canvas.addEventListener("pointerdown", (event) => { + if (event.button !== 0) return; + const rect = canvas.getBoundingClientRect(); + const px = event.clientX - rect.left; + const py = event.clientY - rect.top; + dragNode = nodeAt(px, py); + if (dragNode < 0) panFrom = { x: px, y: py, offsetX: view.offsetX, offsetY: view.offsetY }; + canvas.setPointerCapture(event.pointerId); + }); + + canvas.addEventListener("pointermove", (event) => { + const rect = canvas.getBoundingClientRect(); + const px = event.clientX - rect.left; + const py = event.clientY - rect.top; + if (dragNode >= 0) { + planned[dragNode] = toMetric(px, py); + draw(); + return; + } + if (panFrom) { + if (Math.hypot(px - panFrom.x, py - panFrom.y) < DRAG_THRESHOLD_PX) return; + view = { + ...view, + offsetX: panFrom.offsetX + (px - panFrom.x), + offsetY: panFrom.offsetY + (py - panFrom.y), + }; + draw(); + return; + } + canvas.style.cursor = nodeAt(px, py) >= 0 ? "grab" : "default"; + }); + + const endDrag = (event: PointerEvent): void => { + if (canvas.hasPointerCapture(event.pointerId)) canvas.releasePointerCapture(event.pointerId); + dragNode = -1; + panFrom = null; + }; + canvas.addEventListener("pointerup", endDrag); + canvas.addEventListener("pointercancel", endDrag); + + canvas.addEventListener("dblclick", (event) => { + const rect = canvas.getBoundingClientRect(); + const px = event.clientX - rect.left; + const py = event.clientY - rect.top; + const segment = segmentAt(px, py); + if (segment < 0) return; + planned.splice(segment + 1, 0, toMetric(px, py)); + status.textContent = `노드 ${planned.length}개 — 새 노드를 넣었습니다.`; + draw(); + }); + + canvas.addEventListener("contextmenu", (event) => { + event.preventDefault(); + const rect = canvas.getBoundingClientRect(); + const index = nodeAt(event.clientX - rect.left, event.clientY - rect.top); + if (index < 0) return; + if (planned.length <= 2) { + showToast("노선은 노드가 2개 이상이어야 합니다.", "error"); + return; + } + planned.splice(index, 1); + status.textContent = `노드 ${planned.length}개 — 노드를 지웠습니다.`; + draw(); + }); + + canvas.addEventListener( + "wheel", + (event) => { + event.preventDefault(); + const rect = canvas.getBoundingClientRect(); + const px = event.clientX - rect.left; + const py = event.clientY - rect.top; + const factor = event.deltaY < 0 ? 1.2 : 1 / 1.2; + const nextScale = Math.max(1, Math.min(2000, view.scale * factor)); + const ratio = nextScale / view.scale; + // 커서 아래 지점이 제자리에 남도록 이동량을 함께 고친다. + view = { + ...view, + scale: nextScale, + offsetX: px - (px - view.offsetX) * ratio, + offsetY: py - (py - view.offsetY) * ratio, + }; + draw(); + }, + { passive: false }, + ); + + async function runHeavy(label: string, task: () => Promise): Promise { + busy.hidden = false; + busy.querySelector("span")!.textContent = + `${label} — 배수유역부터 다시 계산 중입니다. 몇 분 걸립니다.`; + try { + await task(); + // 노선이 바뀌면 세션 초안·조회 캐시는 옛 노선 것이라 남기지 않는다(PLAN 0-7 확정 5). + clearDrafts(projectId); + clearResults(projectId); + showToast("노선을 다시 계산했습니다.", "success"); + close(); + await onApplied(); + } catch (error) { + busy.hidden = true; + showToast(error instanceof Error ? error.message : "노선 재계산에 실패했습니다.", "error"); + } + } + + overlay.querySelector('[data-act="apply"]')!.addEventListener("click", () => { + if (planned.length < 2) { + showToast("노선은 노드가 2개 이상이어야 합니다.", "error"); + return; + } + void runHeavy("계획노선 반영", () => replanRoute(projectId, planned)); + }); + overlay.querySelector('[data-act="reset"]')!.addEventListener("click", () => { + void runHeavy("예상노선으로 되돌리기", () => resetRoutePlan(projectId)); + }); + + // ── 자료 읽기 — 노선 두 벌 + 등고선 도엽(배수유역도와 같은 것) ── + try { + const [plan, drainage] = await Promise.all([ + fetchRoutePlan(projectId), + fetchDrainageLayers(projectId, () => {}), + ]); + if (closed) return; + expected = plan.expected as Vertex[]; + planned = (plan.planned as Vertex[]).map((vertex) => [vertex[0], vertex[1]]); + meta = drainage.meta; + const normalizer = createNormalizer(drainage.meta); + sheets = drainage.layers + .map(([, collection]) => (collection ? prepareLayer(collection, normalizer) : null)) + .filter((layer): layer is PreparedLayer => layer !== null); + resize(); + const xs = planned.map((vertex) => vertex[0]); + const ys = planned.map((vertex) => vertex[1]); + const fitted = computeRouteView( + meta, + { + x_min: Math.min(...xs), + x_max: Math.max(...xs), + y_min: Math.min(...ys), + y_max: Math.max(...ys), + }, + view.width, + view.height, + ); + view = { ...view, ...fitted }; + status.textContent = `노드 ${planned.length}개 · ${plan.edited ? "고친 계획노선" : "예상노선과 같음"}`; + draw(); + } catch (error) { + status.textContent = error instanceof Error ? error.message : "노선을 읽지 못했습니다."; + } +} diff --git a/B05_Profile/B05_Profile_UI_Style_RouteEdit.css b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css new file mode 100644 index 00000000..c67c77b5 --- /dev/null +++ b/B05_Profile/B05_Profile_UI_Style_RouteEdit.css @@ -0,0 +1,132 @@ +/* 계획노선 편집 모달 — 큰 모달 하나. 계산이 오래 걸리는 조작이라 화면을 통째로 덮는다 + (2026-09-06 사용자 확정). 색은 전부 테마 토큰을 쓴다. */ + +.b05-routeedit { + position: fixed; + inset: 0; + z-index: var(--z-modal, 1000); + display: flex; + align-items: center; + justify-content: center; + background: rgb(0 0 0 / 55%); +} + +.b05-routeedit__box { + position: relative; + display: flex; + flex-direction: column; + width: min(1200px, 94vw); + height: min(820px, 92vh); + overflow: hidden; + border: 1px solid var(--color-border); + border-radius: var(--radius-16, 12px); + background: var(--color-surface-raised); + box-shadow: 0 12px 40px rgb(0 0 0 / 45%); +} + +.b05-routeedit__head { + display: flex; + flex: none; + align-items: center; + gap: var(--spacing-12); + padding: var(--spacing-12) var(--spacing-16); + border-bottom: 1px solid var(--color-border); +} + +.b05-routeedit__hint { + flex: 1 1 auto; + color: var(--color-text-secondary); + font-size: var(--text-caption); +} + +.b05-routeedit__close { + flex: none; + border: none; + background: none; + color: var(--color-text-secondary); + font-size: 16px; + cursor: pointer; +} + +.b05-routeedit__canvas-wrap { + position: relative; + flex: 1 1 auto; + min-height: 0; + background: var(--color-surface); +} + +.b05-routeedit__canvas { + display: block; + width: 100%; + height: 100%; + touch-action: none; +} + +.b05-routeedit__foot { + display: flex; + flex: none; + align-items: center; + gap: var(--spacing-8); + padding: var(--spacing-12) var(--spacing-16); + border-top: 1px solid var(--color-border); +} + +.b05-routeedit__status { + flex: 1 1 auto; + color: var(--color-text-secondary); + font-size: var(--text-caption); +} + +.b05-routeedit__legend { + display: inline-flex; + align-items: center; + gap: var(--spacing-8); + color: var(--color-text-secondary); + font-size: var(--text-caption); +} + +.b05-routeedit__legend i { + display: inline-block; + width: 22px; + height: 0; + margin-right: 2px; + vertical-align: middle; +} + +.b05-routeedit__legend i.is-expected { + border-top: 2px dashed var(--color-text-secondary, #9ca3af); +} + +.b05-routeedit__legend i.is-planned { + border-top: 2px solid var(--map-route, #f97316); +} + +.b05-routeedit__btn { + flex: none; + padding: var(--spacing-8) var(--spacing-16); + border: 1px solid var(--color-border); + border-radius: var(--radius-8, 6px); + background: var(--color-surface); + color: var(--color-text-body); + cursor: pointer; +} + +.b05-routeedit__btn.is-primary { + border-color: transparent; + background: var(--color-primary, #7c3aed); + color: #fff; +} + +/* 재계산 중에는 화면 전체를 덮는다 — 결과를 기다릴 수밖에 없는 조작(CLAUDE.md 5장). */ +.b05-routeedit__busy { + position: absolute; + inset: 0; + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + padding: var(--spacing-16); + background: rgb(0 0 0 / 55%); + color: #fff; + text-align: center; +} diff --git a/common_util/common_util_route_geometry.py b/common_util/common_util_route_geometry.py index aa1a297c..35c707cd 100644 --- a/common_util/common_util_route_geometry.py +++ b/common_util/common_util_route_geometry.py @@ -212,6 +212,40 @@ def find_planned_route_file(input_dir: Path) -> Path | None: return None +def expected_route_csv_path(project_root: Path) -> Path: + """예상노선(원본) CSV 자리 — 자동 체인이 낸 노선을 **그대로** 보관한다. + + 초기값 스냅샷(`initial_snapshot/`) 안에도 같은 CSV가 있지만 그 폴더는 재확정 체인이 + 통째로 지운다(`discard_initial_snapshot`). 노선 초기화는 언제나 예상노선으로 돌아갈 수 + 있어야 하므로 스냅샷 **밖**에 한 벌 둔다(2026-09-06). + """ + return Path(project_root) / "B05_Profile" / "route" / "expected_route.csv" + + +def write_route_csv(path: Path, points: list[dict[str, float]]) -> None: + """노선 정점을 CSV로 적는다. 열 이름은 `read_planned_route_csv()`가 아는 것.""" + path.parent.mkdir(parents=True, exist_ok=True) + with path.open("w", encoding="utf-8", newline="") as file: + writer = csv.writer(file) + writer.writerow(("sequence", "x", "y")) + writer.writerows( + (index, round(float(point["x"]), 4), round(float(point["y"]), 4)) + for index, point in enumerate(points) + ) + + +def planned_route_working_path(project_root: Path) -> Path: + """계획노선(수정본) CSV 자리 — 사용자가 노선을 고치면 여기에 쓴다. + + 노선은 두 벌이다(2026-09-06 사용자 확정): **예상노선**(원본, + `initial_snapshot/planned_route.csv`)은 자동 체인이 한 번 쓰고 안 바뀌며, + **계획노선**(수정본)은 예상노선과 같은 값으로 시작해 사용자가 고쳐 쓴다. + 설계 계통은 수정본이 있으면 그것을 읽는다 — 노선 초기화는 이 파일을 지우는 것이며, + 그것이 곧 「원본을 수정본으로 복사」와 같다. + """ + return Path(project_root) / "B05_Profile" / "route" / "planned_route.csv" + + def load_design_route( project_root: Path, surface_params: dict[str, Any] | None = None, @@ -244,9 +278,15 @@ def load_design_route( # 지표면·노선이 바뀌면 `discard_initial_snapshot()`이 폴더째 지우므로 이 경로가 저절로 # 닫히고 원본 재판독으로 되돌아간다. 트림 **전** 원본이 필요한 호출(도엽 범위 — # surface_params 없음)은 여기를 타지 않는다. + # 읽는 순서 — 계획노선(수정본) → 예상노선(원본) → 초기값 스냅샷(옛 자리). if surface_params: - master = design_route_csv_path(project_root) - if master.is_file(): + for master in ( + planned_route_working_path(project_root), + expected_route_csv_path(project_root), + design_route_csv_path(project_root), + ): + if not master.is_file(): + continue stored = read_planned_route_csv(master) if stored is not None and len(stored.vertices) >= 2: return replace_vertices( diff --git a/main.py b/main.py index f667060e..0d6673c4 100644 --- a/main.py +++ b/main.py @@ -43,6 +43,7 @@ from B04_PreProcess.B04_PreProcess_Router_Watershed import router as b04_watersh from B05_Profile.B05_Profile_Router import router as b05_route_router from B05_Profile.B05_Profile_Router_Corridor import router as b05_corridor_router from B05_Profile.B05_Profile_Router_Lifecycle import router as b05_route_lifecycle_router +from B05_Profile.B05_Profile_Router_Replan import router as b05_route_replan_router from B05_Profile.B05_Profile_Structures_Router import router as b05_structures_router from B06_Section.B06_Section_Router import router as b06_section_router from B06_Section.B06_Section_Router_Confirm import ( @@ -418,6 +419,7 @@ app.include_router(tiles_router, dependencies=protected_with_company) app.include_router(b05_route_router, dependencies=protected_with_company) app.include_router(b05_route_lifecycle_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_structures_router, dependencies=protected_with_company) app.include_router(b06_section_router, dependencies=protected_with_company) app.include_router(b06_section_confirm_router, dependencies=protected_with_company) diff --git a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/1. 총론.md b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/1. 총론.md index bd7fb4cb..0221384d 100644 --- a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/1. 총론.md +++ b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/1. 총론.md @@ -75,27 +75,25 @@ 집·운재방법은 (그림 5-2-3)과 같이 문화의 발달정도, 지형과 산림의 상태, 산림작업기술의 수준, 제품의 규격 및 판매기준 등에 따라서 많은 영향을 받게 된다. 관류{Flume, 철포이용, 뚝(언)이용 등 포함}뗏목(편류 : Rafting), 벌류(Floating)수운(Water Transportation)선박(Shipping)활로(Chute, 토수라, 목수라, 프라스틱수라, 알루미늄수라 등) 지예{Skidding, Off Road, 인력, 동물(소, 말 등), 임업용트랙터, 트랙터장치용원치류, 스키더류, 가선집재기 등}육운(Land Transportation)도로(On Road, 인력나르기, 우마차, 목마, 트럭류, 트랙터류, 포워더, 트레일러류)궤도(Rail Way, 산림철도, 산림궤도)삭도(Cable Way, 가선집재기류, 가공삭도, 철선운반 등)공운(Air Transportation)항공(Air Borne, 헬리곱터, 기구)그림 5-2-3. 집·운재방법과 종류 옛날에는 토수라, 목수라 등의 방법으로 집재하산하여 주로 관류, 뗏목, 벌류 등의 방법으로 운재하였다. 그러나 근래에는 도로교통과 자동차공업의 발달로 인하여 집재, 운재방법이 육운은 대부분 기계화 또는 자동차화 되고 있고, 수운은 대부분 선박에 의존하고 있지만 앞으로 기구(氣球)나 헬리콥터에 의한 공운의 발달이 주목된다. 육운의 방법으로 합리적인 임업경영을 하기 위한 필수적인 시설은 적정임도망이 되도록 노망 체계를 구축하는 것이다. 이는 산림작업 환경개선과 산림작업의 기계화에 의하여 노동력을 경감하고 노동의 질을 향상시켜 산림작업의 합리화와 능률화에 기여한다. 따라서 지형과 자연경관 및 산림작업성이 잘 조화될 수 있도록 노망을 계획하여야 하며 임도가 임지의 경사의 상부에 배치되느냐 경사의 하부에 배치되느냐에 따라서 집재방법과 집재비용은 달라진다. 임도가 경사의 상부에 배치되어 있다면 상향집재방법을 채택하여 임목을 집재하여야 할 것이고 경사의 하부에 배치되어 있다면 하향집재방법을 채택하여야 할 것이다. 주요집재수단에 대한 작업한계선은 (표 5-2-1)과 같다. 표 5-2-1. 집재수단별 최대집재거리 및 작업안전 한계기울기 -| 집 재 수 단 | | | | 하향집재 | | 상향집재 | | 비 고 | | -|---|---|---|---|---|---|---|---|---|---| -| | | | | 최 대 | 작업한계 | 최 대 | 작업한계 | | | -| | | | | 집재거리 | 기울기 | 집재거리 | 기울기 | | | -| | | | | (m) | (%) | (m) | (%) | | | -| 인 력 | 보조도구이용 | | | 100(200) | 45(65) | | | | | -| | 수라(LOG-LINE) | | | 150(200) | 40(60) | | | | | -| 자 동 차 류 | 트 랙 터 | 농용 트랙터 | | 300(500) | 25(60) | 100 | 10(15) | | | -| | | 개조 트랙터 | | 500(800) | 40(60) | 150 | 15 | | | -| | | 임업용트랙터 | | 800(1,000) | 45(60) | 200(250) | 20(25) | | | -| | 포 워 더 | | | 2,000 | 35(40) | 1,500 | 20(25) | 습윤지역은궤도식을이용(60%까지가능) | | -| | 프로세서, 하베스터 | | | 50 | 30(40) | 50(50) | 10(15) | | | -| 가 선 류 | 기계톱 윈치 | | | 50 | 35 | 50(100) | 45 | high-lead 방식 | | -| | 트랙터 윈치 | | | 150(200) | 100 | 150(200) | 120 | high-lead 방식 | | -| | 고정식가선집재기 | | | 1,000(2,000) | 100 | 1,000(2,000) | 120 | standing skyline 방식 | | -| | 타워형 가 선 집재기 | | 단거리집재기 | 300 | 100 | 400 | 120 | | | -| | | | 중거리집재기 | 500 | 100 | 800 | 120 | | | -| | | | 장거리집재기 | 500(800) | 100 | 1,000 | 120 | | | -| * ( ) 내의 숫자는 특수한 경우의 최대치임. | | | | | | | | | | -| ** 자료: Sedlak(1985,1987), Murphy(1979), Heinrich(1982), Trzesniowski(1985), FAO(1976), | | | | | | | | | | -| Studier and Binkley(1982), Sundberg and Silversides(1988), スリ-エ硏究會(1991), 김(1993) | | | | | | | | | | +| 구분 | 집재수단 | 하향 최대거리(m) | 하향 한계기울기(%) | 상향 최대거리(m) | 상향 한계기울기(%) | 비고 | +|---|---|---:|---:|---:|---:|---| +| 인력 | 보조도구 이용 | 100(200) | 45(65) | | | | +| 인력 | 수라(Log-line) | 150(200) | 40(60) | | | | +| 자동차류 | 농용 트랙터 | 300(500) | 25(60) | 100 | 10(15) | | +| 자동차류 | 개조 트랙터 | 500(800) | 40(60) | 150 | 15 | | +| 자동차류 | 임업용 트랙터 | 800(1,000) | 45(60) | 200(250) | 20(25) | | +| 자동차류 | 포워더 | 2,000 | 35(40) | 1,500 | 20(25) | 습윤지역은 궤도식 이용(60%까지 가능) | +| 자동차류 | 프로세서·하베스터 | 50 | 30(40) | 50(50) | 10(15) | | +| 가선류 | 기계톱 윈치 | 50 | 35 | 50(100) | 45 | High-lead 방식 | +| 가선류 | 트랙터 윈치 | 150(200) | 100 | 150(200) | 120 | High-lead 방식 | +| 가선류 | 고정식 가선집재기 | 1,000(2,000) | 100 | 1,000(2,000) | 120 | Standing skyline 방식 | +| 가선류 | 타워형 가선집재기(단거리) | 300 | 100 | 400 | 120 | | +| 가선류 | 타워형 가선집재기(중거리) | 500 | 100 | 800 | 120 | | +| 가선류 | 타워형 가선집재기(장거리) | 500(800) | 100 | 1,000 | 120 | | + +※ 괄호 안 숫자는 특수한 경우의 최대치이다. + +자료: Sedlak(1985, 1987), Murphy(1979), Heinrich(1982), Trzesniowski(1985), FAO(1976), Studier and Binkley(1982), Sundberg and Silversides(1988), スリ-エ硏究會(1991), 김(1993). #### 1) 하향집재 - ① 인력집재 : 수피가 있는 임목의 지예집재는 경사도가 80%이상, 수피가 없는 임목은 60%이상이면 가능하나 적설이나 지표가 젖어 있을 경우에는 32%에서도 가능하다. diff --git a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/2. 임도구조.md b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/2. 임도구조.md index 0175af71..1030cc59 100644 --- a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/2. 임도구조.md +++ b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/2. 임도구조.md @@ -120,7 +120,7 @@ $$\varepsilon=\frac{L^2}{2R}-0.5$$ - $L$: 차량 앞면에서 뒷차축까지 거리(m, 8m 적용) (예제) 광릉시험림 내 임도의 최소곡선반지름이 20m일 때 굴곡부의 확폭량은 $\varepsilon=8^2/(2\times20)-0.5=1.1$m이다. -#### 2) 세미트레일러(semi-trailler)연결차일 경우 +#### 2) 세미트레일러(semi-trailer) 연결차일 경우 $$\varepsilon=\varepsilon_1+\varepsilon_2=\frac{L_1^2}{2R}+\frac{L_2^2}{2R'}$$ diff --git a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/3. 임도계획.md b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/3. 임도계획.md index 6e1940d1..e3f0cff8 100644 --- a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/3. 임도계획.md +++ b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/3. 임도계획.md @@ -501,7 +501,18 @@ $$I=\frac{ASD\times FRD}{2,500}$$ #### 3) 경제성 분석 -공공투자사업의 경제성평가는 계획 또는 검토되고 있는 대안들의 비용과 편익을 분석하여 경제적 효율성과 공공투자의 당위성을 검토하는 것이다. 임도교통에 소요되는 비용은 임도비와 임도사용자비용으로 구별된다. 임도사용자비용은 임도 관리청이 임도구조·규격을 향상시키기 위하여 임도비를 많이 투자하면 할수록 임도사용자가 차량을 운행하는데 지출되는 연료비, 오일비, 타이어비, 수선비 등의 임도사용비용이 그만큼 절감될 것이다. 이때 그 절감액을 편익(Benefit)이라 한다. ※ ㉠ 비용항목 : 임도비로서 조사·계획비, 설계비, 보상비, 유지관리비 및 임도운영비로구성되며, 이들 항목은 과거의 경험치나 개략설계 결과로부터 산출한다. ㉡ 편익항목 : 임도사용자비용은 이를 다시 차량운행비(Vehicle Operating Costs)와 시간비(Time Cost) 및 작업방법개선비로 구분할 수 있다. RTC = RC + RUC 여기서, RTC : 임도운송비(Road Transportation Costs)RC : 임도비(Road Costs)RUC : 임도사용자비용(Road User Costs)경제성 분석의 궁극적인 목적은 임도운송비를 최소한으로 줄일 수 있는 대안을 설정하는 것이라고 할 수 있다. 차량 1대당의 임도비와 임도사용자비용을 (그림 5-2-24)에서 보면 임도비는 초기투자비(건설 +공공투자사업의 경제성평가는 대안들의 비용과 편익을 분석하여 경제적 효율성과 공공투자의 당위성을 검토하는 것이다. 임도교통 비용은 임도비와 임도사용자비용으로 구별한다. 임도구조·규격을 향상시키면 차량의 연료비·오일비·타이어비·수선비 등이 절감되며, 이 절감액을 편익(Benefit)이라 한다. + +- 비용항목: 조사·계획비, 설계비, 보상비, 유지관리비, 임도운영비이며 과거 경험치나 개략설계 결과로 산출한다. +- 편익항목: 차량운행비(Vehicle Operating Costs), 시간비(Time Cost), 작업방법개선비로 구분한다. + +$$RTC=RC+RUC$$ + +- $RTC$: 임도운송비(Road Transportation Costs) +- $RC$: 임도비(Road Costs) +- $RUC$: 임도사용자비용(Road User Costs) + +경제성 분석의 궁극적인 목적은 임도운송비를 최소화하는 대안을 설정하는 것이다. 그림 5-2-24에서 임도비는 초기투자비(건설 ##### 비) 가 대부분이기 때문에 시간의 경과에 따라 변동이 거의 없으므로 이를 고정비(Fixed Cost)로 diff --git a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/4. 노선측량.md b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/4. 노선측량.md index 1814fa56..b277a390 100644 --- a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/4. 노선측량.md +++ b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/4. 노선측량.md @@ -28,19 +28,18 @@ - ① 흙의 물리적 성질을 구하여 그 흙이 지니고 있는 성상을 파악하기 위한 시험 - 흙의 함수량 시험, 흙입자의 비중시험, 입도시험, 컨시스턴시(consistency)시험(액성한계시험, 소성한계시험) 등 - ② 흙의 분류방법 ㉮ 입경(粒徑)에 의한 토립자 구분 표 5-2-14. 입경에 의한 토립자의 구분(단위:mm) -| 미국재료시험협회 및일본공업규격 | colloid | | | | clay | | | | | | | | silt | | | | | | 세사(細砂) | | | | | | 조사(組砂) | | | | | | | | | 력(礫) | | | | | -|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| -| | | | 0.001 | | | | | | | | | 0.005 | | | | | | 0.05 | | | | | | 0.25 | | | | | | | 2.0 | | | | | | | | -| 미국도로관리국 | colloid | | | | clay | | | | | | | | silt | | | | | | fine sand | | | | coarse sand | | | | | | | gravel | | | | | | boullder | | | -| | 0.001 | | | | | | | | | 0.005 | | | | | | | 0.074 | | | | | 0.42 | | | | | | | 2.0 | | | | 2 | | | 0 | | | -| 미국토성국 | colloid | | | clay | | | | | silt | | | | | sand | | | | | | | | | | | | | | | | | fine gravel | | | | | gravel | | | -| | | | | | | | | | | | | | | very fine | | | | | fine | | | medium | | | | | coarse | | | | | | | | | | | | -| | | 0.001 | | | | 0.005 | | | | | | | 0.05 | | | | | 0.1 | | | 0.25 | | | | | 0.5 | | | 1.0 | | | | | | 2.0 | | | | -| 일본철도성 토질조사위원회 | colloid | | | | clay | | | | | | silt | | | | | 砂 | | | | | | | | | | | | | | | | | 礫 | | | | | | -| | | | | | | | | | | | | | | | | 微粒砂 | | | | 細粒砂 | | | 中粒砂 | | | | | 組粒砂 | | | | | 細礫 | | | 礫 | | | -| | | | 0.001 | | | | | 0.005 | | | | | 0.05 | | | | | | 0.1 | | | 0.25 | | | | 0.5 | | | | | | 1.0 | | 2.0 | | | | | +| 분류기관 | 토립자 구분과 입경 범위(mm) | +|---|---| +| 미국재료시험협회·일본공업규격 | colloid < 0.001; clay 0.001∼0.005; silt 0.005∼0.05; 세사 0.05∼0.25; 조사 0.25∼2.0; 력 > 2.0 | +| 미국도로관리국 | colloid < 0.001; clay 0.001∼0.005; silt 0.005∼0.074; fine sand 0.074∼0.42; coarse sand 0.42∼2.0; gravel 2.0∼20; boulder > 20 | +| 미국토성국 | colloid < 0.001; clay 0.001∼0.005; silt 0.005∼0.05; very fine sand 0.05∼0.1; fine sand 0.1∼0.25; medium sand 0.25∼0.5; coarse sand 0.5∼1.0; fine gravel 1.0∼2.0; gravel > 2.0 | +| 일본철도성 토질조사위원회 | colloid < 0.001; clay 0.001∼0.005; silt 0.005∼0.05; 미립사 0.05∼0.1; 세립사 0.1∼0.25; 중립사 0.25∼0.5; 조립사 0.5∼1.0; 세력 1.0∼2.0; 력 > 2.0 | -㉯ 삼각좌표에 의한 구분 : 3성분의 함유율 합계가 반드시 100%가 되어야 한다(그림 5-2-25 참조). ㉰ 통일 분류법 •통일분류법(Unified Dlassification)은 카스그란드(Casagrande)에 의하여 개발된 것으로서 콘시스턴시 시험(액성한계시험, 소성한계시험)에 의한 공학적 분류방법이다. •흙의 조합을 3개 문자로 쓸 경우가 있지만 거의 대부분 2개 문자조합으로 표시한다. (즉, 1개 문자는 흙의 형, 다른 문자는 흙의 속성)•흙의 종류는 15종(조립토 8, 세립토 6, 유기질토 1종)으로 구분하여 SM, SC, ML,OL, MH, OH 등으로 표시한다(그림 5-2-26 참조). 표 5-2-15. 통일 분류법에 사용되는 기호와 그 의미 +㉯ 삼각좌표에 의한 구분: 3성분의 함유율 합계가 반드시 100%가 되어야 한다(그림 5-2-25 참조). + +㉰ 통일분류법: 통일분류법(Unified Classification)은 Casagrande가 개발한 콘시스턴시 시험에 의한 공학적 분류방법이다. 흙은 대부분 2개 문자 조합으로 표시하며 첫 문자는 흙의 형, 둘째 문자는 속성을 뜻한다. 흙은 조립토 8종, 세립토 6종, 유기질토 1종 등 15종으로 구분하며 SM, SC, ML, OL, MH, OH 등으로 표시한다(그림 5-2-26 참조). + +표 5-2-15. 통일분류법에 사용되는 기호와 의미 | 토질의형 | | 제1문자 | 토질의속성 | 제2문자 | |---|---|---|---|---| @@ -450,7 +449,13 @@ b) 30°경거 | 4 | 0.5010 | 1.0020 | 1.5030 | 2.0040 | 2.5050 | 3.0061 | 3.5071 | 4.0081 | 4.5091 | 56 | | 5 | 0.5013 | 1.0025 | 1.5038 | 2.0050 | 2.5063 | 3.0076 | 3.5088 | 4.0101 | 4.5113 | 55 | -(풀이) (표 5-2-25)와 같은 경위거표에서 위거 30°의 위거란에서 5′(분)의 줄을 보면1은 0.8653, 2는 1.7306, 5는 4.3265, … , 9는 7.7877이다. 여기서, 25.10m = 2×10 + 5 + 1 ÷ 10 으로 풀이할 수 있으므로 측선 AB의 위거 = (1.7306×10)+4.3265+(0.8653÷10) = 21.71903 또한, 경거 30°의 경거란에서 5′(분)의 줄을 보면,1은 0.5013, 2는 1.0025, 5는 2.5063, … , 9는 4.5113 이므로 측선 AB의 경거 = (1.0025×10)+2.5063+(0.5013÷10) = 12.58143 따라서, 위거 = 21.72m, 경거 = 12.58m +(풀이) 표 5-2-25의 30° 위거란 5′ 행에서 1은 0.8653, 2는 1.7306, 5는 4.3265, 9는 7.7877이다. $25.10=2\times10+5+1/10$이므로: + +$$\text{위거}=(1.7306\times10)+4.3265+(0.8653/10)=21.71903\fallingdotseq21.72\,\mathrm{m}$$ + +같은 행의 경거값은 1이 0.5013, 2가 1.0025, 5가 2.5063, 9가 4.5113이다. + +$$\text{경거}=(1.0025\times10)+2.5063+(0.5013/10)=12.58143\fallingdotseq12.58\,\mathrm{m}$$ ### 마. 평면곡선(horizontal curve)의 설치 #### 1) 곡선의 종류 @@ -475,7 +480,7 @@ b) 30°경거 - ⑤ 곡선장(Curve Length : CL) = AHB = 0.017453 R·I°= 2π·R·I°/360° - ⑥ 장현(Long Chord : C) = AB = 2 R·sin (I/2) - ⑦ 절선장(Tangent Length : TL 혹은 T) = VA = VB = R·tan(I/2) -- ⑧ 외거(외할)(External Distanse : E) = VH = R{sec(I/2) - 1)} +- ⑧ 외거(외할)(External Distance): $E=VH=R\{\sec(I/2)-1\}$ - ⑨ 중앙종거(Middle Ordinate : M) = HF = R {1- cos(I/2)}= C2 / 8R - ⑩ 교각(편각) = (Intersection Angle : IA 또는 I) = I @@ -513,29 +518,49 @@ $$\delta=\frac{\ell}{2R}\;(\mathrm{radian})=\frac{1,718.87'\times\ell}{R}$$ - ⑤ 이와 같은 방법을 계속하여 측점말뚝을 설치한다. - ⑥ 마지막 말뚝 $P_n$과 EC가 일치하지 않을 경우 $P_nB=\ell_2=AB-AP_n$를 종단현이라 하며, $BP_n$에 대한 편각은 $\delta_2=1,718.87'\ell_2/R$이다. -(예제1) 광능시험림 임도내 I. P 16에 대한 곡선 제원이 I = 32°15', R = 200m이고, 노선기점에서 I. P 16까지의 거리는 1,200.50m이다. 기점부터 20m 간격으로 측점말뚝을 설치할 때 각 측점의 거리와 제원을 구하라. (풀이) 절선장(T. L) = 200×tan(32°15'/2) = 57.82m 외거(E) = 200×{sec(32°15'/2)-1} = 8.19m 곡선장(C. L) = 0.017453×200×32°15'= 112.57m 곡선시점(B. C) = 1200.50-57.82 = 1,142.68m 곡선종점(E. C) = 1200.50-57.82+112.57 = 1,255.25m 시단현(l1) = 20-2.68 = 17.32m 종단현(l2) = 1,255.25-(62×20) = 15.25m δ= (ℓ/R)×1,1718.87'= (20.00/200)×1,1718.87'= 2°51'53 δ1 = (ℓ1/R)×1,1718.87'= (17.32/200)×1,1718.87'= 2°28'51 δ2 = (ℓ2/R)×1,1718.87'= (15.25/200)×1,1718.87'= 2°11'04 따라서, B. C점에서 각 측점말뚝의 편각은 (표 5-2-26)과 같다. 표 5-2-26. B. C점에서 각 측점의 편각 +(예제 1) $I=32°15′$, $R=200$m이고 노선 기점에서 IP 16까지 거리가 1,200.50m일 때 20m 간격으로 측점말뚝을 설치한다. -| 기점에서부터의 거리(m) | | 편각 | 비고 | -|---|---|---|---| -| 1,142 | .68 | | B.C | -| 1,160 | | 2°28′51″ | | -| 1,180 | | 5°20′44″ | | -| 1,200 | | 8°12′37″ | | -| 1,220 | | 11°4′30″ | | -| 1,240 | | 13°56′23″ | | -| 1,255 | .25 | 16°7′27″ | E.C | +- $TL=200\tan(32°15′/2)=57.82$m +- $E=200\{\sec(32°15′/2)-1\}=8.19$m +- $CL=0.017453\times200\times32°15′=112.57$m +- $BC=1,200.50-57.82=1,142.68$m +- $EC=1,200.50-57.82+112.57=1,255.25$m +- 시단현 $\ell_1=20-2.68=17.32$m +- 종단현 $\ell_2=1,255.25-(62\times20)=15.25$m +- $\delta=(20/200)\times1,718.87′=2°51′53″$ +- $\delta_1=(17.32/200)\times1,718.87′=2°28′51″$ +- $\delta_2=(15.25/200)\times1,718.87′=2°11′04″$ -(예제2) 곡선함수표를 이용하여 I=37°30′, R=300m, IP까지거리=1,150.70m일때 20m 간격으로 단곡선의 중심말뚝을 설치하여라. (풀이) 곡선 함수표에서 I=37°30′에 대한 곡선 제원은 T. L=33.945, C. L=65.450, E= 5.604 이 고 이는 R=100m일 경우이므로 이를 R=300m로 조정하면 +표 5-2-26. BC점에서 각 측점의 편각 + +| 기점에서부터의 거리(m) | 편각 | 비고 | +|---:|---:|---| +| 1,142.68 | | BC | +| 1,160 | 2°28′51″ | | +| 1,180 | 5°20′44″ | | +| 1,200 | 8°12′37″ | | +| 1,220 | 11°04′30″ | | +| 1,240 | 13°56′23″ | | +| 1,255.25 | 16°07′27″ | EC | + +(예제 2) $I=37°30′$, $R=300$m, IP까지 거리 1,150.70m일 때 20m 간격으로 단곡선 중심말뚝을 설치한다. 곡선함수표의 $R=100$m 기준값은 $TL=33.945$, $CL=65.450$, $E=5.604$이다. - ○ T. L = 33.945×300/100 = 101.835m - ○ C. L = 65.450×300/100 = 196.350m - ○ E = 5.604×300/100 = 16.812m - ○ BC = IP-T. L = 1,150.70-101.835 = 1,048.865m -- ○ EC = IP-T. L+C. L = 1,150.70-101.835+196.350 = 1,245.215m 또한, R = 300m일 때 ℓ= 20m에 대한 편각 δ는 곡선설치표에서 1°54′35"이다. 따라서 (BC 1,048.865m에서 가장 가까운 20배수는 1,060.0m이고, EC 1,245.215m에서 가장 가까운 20의 배수는 1240.0m이므로 +- ○ $EC=IP-TL+CL=1,150.70-101.835+196.350=1,245.215$m + +$R=300$m이고 $\ell=20$m일 때 편각 $\delta$는 1°54′35″이다. BC 다음의 20m 배수는 1,060m이고 EC 이전의 20m 배수는 1,240m이다. - ○ 시단현 ℓ1 = 60-48.865 = 11.135m -- ○ 종단현 ℓ2 = 45.215-40 = 5.215m 이에 대한 편각 δ1과 δ2는 곡선 설치표에서 R = 300m 란에서 ℓ을 보면, -- ○ 시단현 10m … 0°57′18" -- ○ 종단현 5m … 0°28′39"1m … 0°5′44"0.2m … 0°1′09"0.1m … 0°0′34"+ 0.01m … 0°0′03"+ 0.03m … 0°0′10"δ2 =0°29′51"δ1 =1°3′46"각도를 초(")까지 측정하였으면 비례보간법에 의하여 계산한다. 따라서 BC에서 각 측점말뚝의 편각은 (표 5-2-27)과 같다. 표 5-2-27. BC에서 각 측점의 편각 +- ○ 종단현 $\ell_2=45.215-40=5.215$m +- ○ 시단현 10m의 편각: 0°57′18″ +- ○ 편각 구성값: 5m=0°28′39″, 1m=0°05′44″, 0.2m=0°01′09″, 0.1m=0°00′34″, 0.01m=0°00′03″, 0.03m=0°00′10″ +- ○ $\delta_1=1°03′46″$, $\delta_2=0°29′51″$ + +각도를 초 단위까지 측정하면 비례보간법으로 계산한다. + +표 5-2-27. BC에서 각 측점의 편각 | 측점말뚝(m) | 편각 | 비고 | |---:|---:|---| diff --git a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/5. 설계.md b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/5. 설계.md index b4173e6b..24837bba 100644 --- a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/5. 설계.md +++ b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/5. 설계.md @@ -156,26 +156,37 @@ $$ ##### 다) 측점과 곡선제도법 - ① 트레버어스제도법으로 중심선의 골격이 제도되고 나면 (표 5-2-31)과 같이 각 교점(IP)마다 곡선제원을 계산하고 시점(BP)에서부터 측점말뚝은 일련번호(예 No.11)로 하고 보조 말뚝은 그 측점에서 떨어진 거리(예 No.11+13)로 표기하는 보조측점을 부여한다. -| 표5-2-31. 교점제원과선형계산(예) | | | | | | | | | | | | | -|---|---|---|---|---|---|---|---|---|---|---|---|---| -| ○교점번호: BP | | | | | | | | | | | | | -| 좌 표 | | X | | 470740.500 | | 방위각 | | | | | 188°39′19.41″ | | -| | | Y | | 142679.000 | | | | | | | | | -| ○교점번호: IP1 (좌향) | | | | | | | | | | | | | -| 곡선종류 | | 원곡선 | | | IP좌표 | | | X | | 470697.8000 | | | -| | | | | | | | | Y | | 142672.5000 | | | -| 방위각 | 1 | 188°39′19.41″ | | | 원중심좌표 | | | X | | 470720.5531 | | | -| | 2 | 111°55′46.52″ | | | | | | Y | | 142712.3783 | | | -| IA | 76°43′32.90″ | | | | R | | 36.0000 | | | 거리 | | 43.1919 | -| TL | 28.4953 | | | | SL | | 9.9128 | | | CL | | 48.2083 | -| ○측점별위치및제원표 | | | | | | | | | | | | | -| 구분 | 누가거리 | | 측점(No) | | X좌표 | | | | Y좌표 | | | 방위각 | -| BP | 0.0000 | | 0+00.0000 | | 470740.50 | | | | 142679.00 | | | | -| BC | 14.6966 | | 0+14.6966 | | 470725.97 | | | | 142676.79 | | | 188°39′19.41″ | -| | 20.0000 | | 1+00.0000 | | 470720.69 | | | | 142676.38 | | | 180°12′53.06″ | -| | 40.0000 | | 2+00.0000 | | 470701.68 | | | | 142681.72 | | | 148°23′ 1.50″ | -| | 60.0000 | | 3+00.0000 | | 470688.35 | | | | 142696.29 | | | 116°33′ 9.94″ | -| EC | 62.9049 | | 3+02.9049 | | 470687.16 | | | | 142698.93 | | | 111°55′46.52″ | +표 5-2-31. 교점제원과 선형계산(예) + +| 교점 | 항목 | 값 | +|---|---|---:| +| BP | X좌표 | 470740.500 | +| BP | Y좌표 | 142679.000 | +| BP | 방위각 | 188°39′19.41″ | +| IP1(좌향) | 곡선종류 | 원곡선 | +| IP1(좌향) | X좌표 | 470697.8000 | +| IP1(좌향) | Y좌표 | 142672.5000 | +| IP1(좌향) | 제1 방위각 | 188°39′19.41″ | +| IP1(좌향) | 제2 방위각 | 111°55′46.52″ | +| IP1(좌향) | 원중심 X좌표 | 470720.5531 | +| IP1(좌향) | 원중심 Y좌표 | 142712.3783 | +| IP1(좌향) | IA | 76°43′32.90″ | +| IP1(좌향) | R | 36.0000 | +| IP1(좌향) | 거리 | 43.1919 | +| IP1(좌향) | TL | 28.4953 | +| IP1(좌향) | SL | 9.9128 | +| IP1(좌향) | CL | 48.2083 | + +측점별 위치 및 제원 + +| 구분 | 누가거리 | 측점(No.) | X좌표 | Y좌표 | 방위각 | +|---|---:|---|---:|---:|---:| +| BP | 0.0000 | 0+00.0000 | 470740.50 | 142679.00 | | +| BC | 14.6966 | 0+14.6966 | 470725.97 | 142676.79 | 188°39′19.41″ | +| | 20.0000 | 1+00.0000 | 470720.69 | 142676.38 | 180°12′53.06″ | +| | 40.0000 | 2+00.0000 | 470701.68 | 142681.72 | 148°23′01.50″ | +| | 60.0000 | 3+00.0000 | 470688.35 | 142696.29 | 116°33′09.94″ | +| EC | 62.9049 | 3+02.9049 | 470687.16 | 142698.93 | 111°55′46.52″ | - ② 교점에서 절선장(TL)을 끊어 시곡점{(BC), IP간의 거리(43.1919m) - TL(28.4953m) =BC(14.6966m)} 종곡점{(EC), BC(14.6966m) + CL(48.2083m) = EC(62.9049m)}의 측점을 부여하고 교점 내각의 2등분선 방향으로 외할장(E, SL)만큼 중곡점(MC)을 부여한다. - ③ 시곡점과 종곡점에서 각 절선에 대한 수직선을 내려 만나는 점이 곡선의 중심(0)이 되며 그 길이를 곡선 반지름으로 하여 단곡선을 그리면 중심선의 곡선부가 된다. @@ -417,15 +428,17 @@ $$H=\frac{\text{토적}}{\text{총면적}}=\frac{124.5}{12\times30}=0.346\,\math | No.5 | 10.00 | 11.30 | | | | | 11.30 | | No.2 | 2.50×40 = 500.0 | | | | 사토장 | 20.00 | | | | | | | | | | BP | 9.01×120 = 1,081.2 | | 계 | 100.00 | 282.68 | 246.32 | | 273.67 | 166.68 | 116.00 | 106.99 | | 106.99 2,867.20 = 26.8 ≒27(m) | | 9.01 1,081.2 = 120(m) | -| 주) ㉠토량환산계수: 자연 상태의 토 량과 다짐 상태의 토 량의 비율 | | | | | | | | | | | | | -| ㉡보정토량: 자연상태토량/토량환산계수 | | | | | | | | | | | | | -| ㉢평균운반거리= 총작업량/총운반성토량 | | | | | | | | | | | | | -| ㉣도쟈운반성토= 60m이하의토량(건설표준품셈참조) | | | | | | | | | | | | | -| ㉤덤프운반성토= 60m초과의토량(건설표준품셈참조) | | | | | | | | | | | | | +주) + +- ㉠ 토량환산계수: 자연상태 토량과 다짐상태 토량의 비율 +- ㉡ 보정토량: 자연상태 토량/토량환산계수 +- ㉢ 평균운반거리: 총작업량/총운반성토량 +- ㉣ 도저운반성토: 60m 이하의 토량(건설표준품셈 참조) +- ㉤ 덤프운반성토: 60m 초과의 토량(건설표준품셈 참조) - ② 유용토는 20m 미만의 총성토량 및 총절토량 중 적은 수량을 기재하고 총절토량 - 총성토량 > 0 일 경우에는 잔토란에, 총절토량 - 총성토량 < 0 일 경우에는 부족토란에 그 수량을 기재한다. ※ ㉠ BP에서 총절토량은 9.20㎥이나 20m미만 거리에 총성토량이 없으므로 잔토란에 9.20㎥가 되고, No.1의 총절토량 62.40㎥는 BC의 총성토량 57.11㎥가 20m 미만거리이므로 유용토로 57.11㎥를 우선 감하고 나머지 5.29㎥는 잔토란에 기재한다. ㉡ BC의 총절토량 36.40㎥는 다음 측점 No.2에서 총절토량 67.98㎥, 총성토량 14.17㎥이므로 그대로 BC의 잔토란에 36.40㎥를 기재하고, No.2의 유용토란에 14.17㎥, 잔토란에(67.98 - 14.17) 53.81㎥를 기재한다. ㉢ EC에서 총성토량 23.55㎥, 총절토량 59.20㎥이므로 23.55㎥는 유용토로 처리하고 나머지(59.20 - 23.55) 35.65㎥는 잔토로 처리하여야 하지만 No.3에서 총성토량이 118.84㎥이므로 EC에서 No.3 까지의 거리는 8m < 20m이므로 EC의 잔토량 35.65㎥는 No.3에서 유용이 가능하기 때문에 부족토량은(118.84 - 35.65) 83.19㎥가 된다. ㉣ No.4에서 총성토량이 60.00㎥, No.4 +10에서 총절토량이 36.20㎥이므로 유용토로 36.20 ㎥를 제하면 부족토는 (60.00 - 36.20) 23.80㎥가 된다. - ③ 이렇게 하여 유용토량(166.68㎥) + 잔토량(116.00㎥) = 총절토량(282.68㎥), 유용토량(166.68㎥) + 부족토량(106.99㎥) = 총성토량(273.67㎥)이면 계산이 맞지만 다를 경우에는 다시 검산하여 맞도록 한다. -- ④ 유용토량 166.68㎥는 불도저의 절취작업시 삽날에 의하여 횡방향으로 집성토되는 것을 의미하여 무대로 처리하고, 부족토는 불도저 또는 덤프트럭운반으로서 흙쌓기를 하여야만 노체 또는 노상의 형성이 가능한 것을 의미한다. 따라서 부족토가 있는 측점을 기준으로 가장 가까운 곳에 있는 잔토부터 운반하여 60m이내에 있는 토량은 불도저운반거리로 계산하고 그 이상은 덤프트럭운반거리로 계획하여 계산한다. ※ ㉠ 부족토의 공급을 위하여 전체적인 잔토량을 살펴보면, No.3과 No.4에서는 흙이 모자라고 BP부터 N0.2까지는 흙이 남으므로 운반의 효율성으로 볼 때 운반형태가 교차되지 않도록 하기 위하여 No.4 부터 흙을 수급함이 타당하므로 No.4에서 가장 가까운 거리에 있는 No.5에서 부터 수급하면 작업량은 11.30㎥ × 20m = 226㎥·m가 되고 나머지 부족토(23.80-11.30) 12.5㎥는 그 다음 가까운 거리에 있는 No.2에서 운반하면 작업량은 12.5㎥ × 40m = 500㎥·m가 된다. ㉡ 그 다음 No.3의 부족토를 보면 가장가까운 거리에 있는 No.2의 잔토량(53.81-12.50)41.31㎥를 우선 수급하면 작업량은 41.31㎥ × 20m = 826.20㎥·m이고 나머지 부족토량(83.19-41.31) 41.88㎥ 중 36.40㎥는 BC에서 수급하면 작업량은 36.40㎥ ×30m = 1092.00㎥·m가 되고, 나머지 부족토량(83.19-41.31-36.40) 5.48㎥는 No.1과 BP에서 수급한다. ㉢ 이에 대한 운반량을 합계하면 106.99㎥이고, 작업량을 합하면 2,867.2㎥·m가 되므로 불도저 운반성토량의 가중평균운반거리는 2,867.2㎥·m ≑ 106.99㎥ = 26.8 ≒ 27m가 된다. ㉣ 덤프트럭 운반은 60m초과되는 거리를 기준으로 한다. 본표에서는 작업이 곤란하지만 사토(흙버리기:116.0-106.99=9.01㎥) 처리의 예로서 설명하면 다음과 같다. 사토장을 No.6되는 곳에 설치한다고 가정하면, BP의 잔토량(9.20-0.19)9.01㎥에 대한 작업량은 9.01㎥ × 120m = 1,081.2㎥·m가 된다. 이외에도 잔토량이 더 있을 경우에는 이와같이 계산하고 합하여 불도저 성토량의 평균운반거리와 같이 산출한다. +- ④ 유용토량 166.68㎥는 불도저의 절취작업시 삽날에 의하여 횡방향으로 집성토되는 것을 의미하여 무대로 처리하고, 부족토는 불도저 또는 덤프트럭운반으로서 흙쌓기를 하여야만 노체 또는 노상의 형성이 가능한 것을 의미한다. 따라서 부족토가 있는 측점을 기준으로 가장 가까운 곳에 있는 잔토부터 운반하여 60m이내에 있는 토량은 불도저운반거리로 계산하고 그 이상은 덤프트럭운반거리로 계획하여 계산한다. ※ ㉠ 부족토의 공급을 위하여 전체적인 잔토량을 살펴보면, No.3과 No.4에서는 흙이 모자라고 BP부터 N0.2까지는 흙이 남으므로 운반의 효율성으로 볼 때 운반형태가 교차되지 않도록 하기 위하여 No.4 부터 흙을 수급함이 타당하므로 No.4에서 가장 가까운 거리에 있는 No.5에서 부터 수급하면 작업량은 11.30㎥ × 20m = 226㎥·m가 되고 나머지 부족토(23.80-11.30) 12.5㎥는 그 다음 가까운 거리에 있는 No.2에서 운반하면 작업량은 12.5㎥ × 40m = 500㎥·m가 된다. ㉡ 그 다음 No.3의 부족토를 보면 가장가까운 거리에 있는 No.2의 잔토량(53.81-12.50)41.31㎥를 우선 수급하면 작업량은 41.31㎥ × 20m = 826.20㎥·m이고 나머지 부족토량(83.19-41.31) 41.88㎥ 중 36.40㎥는 BC에서 수급하면 작업량은 36.40㎥ ×30m = 1092.00㎥·m가 되고, 나머지 부족토량(83.19-41.31-36.40) 5.48㎥는 No.1과 BP에서 수급한다. ㉢ 이에 대한 운반량을 합계하면 106.99㎥이고, 작업량을 합하면 2,867.2㎥·m가 되므로 불도저 운반성토량의 가중평균운반거리는 2,867.2㎥·m ÷ 106.99㎥ = 26.8 ≒ 27m가 된다. ㉣ 덤프트럭 운반은 60m초과되는 거리를 기준으로 한다. 본표에서는 작업이 곤란하지만 사토(흙버리기:116.0-106.99=9.01㎥) 처리의 예로서 설명하면 다음과 같다. 사토장을 No.6되는 곳에 설치한다고 가정하면, BP의 잔토량(9.20-0.19)9.01㎥에 대한 작업량은 9.01㎥ × 120m = 1,081.2㎥·m가 된다. 이외에도 잔토량이 더 있을 경우에는 이와같이 계산하고 합하여 불도저 성토량의 평균운반거리와 같이 산출한다. - ⑤ 이와 같이 계산하여 불도저운반 성토량(106.99) + 덤프트럭운반 성토량(0) = 부족토량(106.99㎥)이 되어야 하고, 부족토량(106.99) + 사토운반량(9.01) = 잔토량(116.0㎥)이 되어야 계산이 맞고 그렇지 않을 경우에는 다시 검산한다. - ○ 유토(토량)곡선에 의한 방법 - - 작성방법 diff --git a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/6. 시공.md b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/6. 시공.md index 6baaba67..6587a08f 100644 --- a/resources/knowledge/original/산림과임업기술(임도)/2. 임도/6. 시공.md +++ b/resources/knowledge/original/산림과임업기술(임도)/2. 임도/6. 시공.md @@ -177,25 +177,6 @@ - ① 노상위에 동상방지층, 보조기층, 기층, 중간층, 표층의 순으로 {그림 5-2-83(a)}와 같이 구성되며 - ② 노상이 연약한 경우에는 노상토가 보조기층으로 침입하는 것을 방지할 목적으로 하천사 또는 양질의 산사를 사용하여 차단층을 시공하고, 한냉지에서는 동상의 위험이 있을 경우에는 모래, 막자갈, 슬래그 등 선택된 재료로서 동상방지용 선택층을 시공한다. -| 층의호칭 | 대표적인사용재료 | 포포 ( 장장 구두 조께 ) | 구성요소의명칭 | | | | -|---|---|---|---|---|---|---| -| 표 층 | 밀립도아스팔트콘크리트 | | | | 포래두 | | -| | | | | | | 포 | -| 기 층 | 조립도아스팔트콘크리트 | | 시멘트콘크리트슬래브 | | 장브께 | | -| | | | | | | 장 | -| 상층보조기층 | 입도조정부순돌 | | | | 슬의 | | -| | | | | | | 구 | -| 하층보조기층 | 막 자 갈 | | 보 조 기 층 | | | | -| | | | | | | 조 | -| | 노 상 | | 노 상 | | | | - -| 포 | 포 ( | -|---|---| -| 장 | 장 | -| 구 | 두 | -| 조 | 께 ) | - - - ③ 표층위에 미끄럼 방지와 마모저항을 높이기 위하여 3㎝ 내외의 마모층을 시공하는 경우에도 있으나 이 경우의 마모층은 포장구조계산에는 포함시키지 않는다. - ○ 시멘트콘크리트 포장 노상위에 보조기층, 기층(별도로 두지 않을 경우도 있음), 시멘트 콘크리트 표층으로 {그림 5- @@ -257,21 +238,15 @@ | | 최대입경 | 입 도 | 소성지수 | | | | 보 조 기 층 | 50㎜이하1 층완성두께의 1/2이하로 100㎜까지허 용 | 특별한 규 정 없음. | 입도조정: 6 이하 시멘트안정처리 : 9 이하 석회안정처리 : 6∼18 이하 | •수침CBR값>30 •일축압축강도 시멘트안정처리 : 10kg/㎠ 석회안정처리 : 7kg/㎠ | •입상재료20㎝ 이하 •안정처리15-20㎝ | | 기 층 | 40㎜ 이하 1 층완성두께의 1/2이하 | 별도있음. | 입도조정: 4 이하 시멘트안정처리 : 9 이하 석회안정처리 : 6∼18 이하 역청안정처리 : 9 이하 | •수침CBR값>80 •일축압축강도 시멘트안정처리 : 30kg/㎠ 석회안정처리 : 10kg/㎠ 안정도350kg이상 | 15㎝이하 10-20㎝ 10㎝이하 | -| 주) 주요공법 | | | | | | -| ㉠입상재료공법: 막자갈, 막 부순 돌을 그대로 사용 마무리하며 보조기층에 많이 사용한다. | | | | | | -| ㉡ 입도조정공법 : 2종 이상의 재료를 혼합하여 입도를 조정 사용하며, 노상혼합방식과 중앙혼 | | | | | | -| 합방식이있다. | | | | | | -| ㉢ 시멘트 안정처리공법 : 현지재료 또는 여기에 보충재를 가한 것에 시멘트를 첨가하는 공법 | | | | | | -| 으로서 강도를 높이며 함수 량의 변화를 막고 내구성을 높일 수 있다. | | | | | | -| ㉣ 가열아스팔트 안정처리공법 : 현지재료 또는 여기에 보충재를 가한 것에 아스팔트로 가열처 | | | | | | -| 리하는 공법으로서 중 교통 도로의 기층에 많이 이용하며, 시공성, 내구성이우수하다. | | | | | | -| ㉤ 상온아스팔트 안정처리공법 : 현지재료 또는 여기에 보충재를 가한 것에 유화아스팔트나 커 | | | | | | -| 트백 아스팔트와 같이 점성이 낮은 역청재료를 첨가 혼합하는 공법으로서 경교통도로의 기 | | | | | | -| 층에주로사용한다. | | | | | | -| ㉥ 마카담공법 : 한 층 마무리 두께와 거의 같은 단일 입경이 주골재(쇄석)를 포설하고 전압한 | | | | | | -| 후 그 위에 채움 골 재를 살포하여 주 골재의 간극에 전압, 역입시켜마무리하는공법. | | | | | | -| ㉦ 침투식공법 : 포설한 골재에 역청재료를 살포 침투시켜 골재의 맞물림(Interlocking)과 역청 | | | | | | -| 재의 접착성과 점성에 의하여 골재의 이동이 방지되는 안정된 층을 이루는 공법 | | | | | | +주요 공법은 다음과 같다. + +- ㉠ 입상재료공법: 막자갈·막부순돌을 그대로 사용하여 마무리하며 보조기층에 많이 사용한다. +- ㉡ 입도조정공법: 2종 이상의 재료를 혼합해 입도를 조정하며 노상혼합방식과 중앙혼합방식이 있다. +- ㉢ 시멘트 안정처리공법: 현지재료 또는 보충재에 시멘트를 첨가하여 강도와 내구성을 높이고 함수량 변화를 막는다. +- ㉣ 가열아스팔트 안정처리공법: 현지재료 또는 보충재를 아스팔트로 가열처리하며 중교통 도로 기층에 많이 이용한다. +- ㉤ 상온아스팔트 안정처리공법: 유화아스팔트나 커트백 아스팔트 같은 저점도 역청재료를 첨가·혼합하며 경교통 도로 기층에 주로 사용한다. +- ㉥ 마카담공법: 한 층 마무리 두께와 비슷한 단일 입경의 주골재를 포설·전압한 뒤 채움골재를 살포하여 간극에 전압·역입시킨다. +- ㉦ 침투식공법: 포설 골재에 역청재료를 살포·침투시켜 골재 맞물림과 역청재의 접착성·점성으로 안정된 층을 형성한다. ##### 라) 표층 - ① 표층은 교통하중이나 기상작용의 영향을 가장 많이 받는 부분으로 좋은 입도의 조·세골재 및 필러의 아스팔트를 첨가한 가연 아스팔트 혼합물을 충분히 다져서 사용한다. diff --git a/resources/knowledge/original/산림과임업기술(임도)/3. 훼손지복구/1. 토석채취지복구.md b/resources/knowledge/original/산림과임업기술(임도)/3. 훼손지복구/1. 토석채취지복구.md index 9441d3d0..8108701a 100644 --- a/resources/knowledge/original/산림과임업기술(임도)/3. 훼손지복구/1. 토석채취지복구.md +++ b/resources/knowledge/original/산림과임업기술(임도)/3. 훼손지복구/1. 토석채취지복구.md @@ -61,7 +61,34 @@ ![복구_그림5-3-1_채석적지부위별명칭](<../pic/복구_그림5-3-1_채석적지부위별명칭.png>) -A : 벌채구역(허가구역에 포함)B : 돌림배수로 C : 절암(절취) 벽면의 (중앙부위의) 비탈어깨(法肩)D : 절암벽면의 비탈밑(法尻)D′: 본래사면의 비탈밑(法尻)E : 절암벽면의 비탈길이(사면장)E′: 절암벽면의 비탈(수직)길이(수직에 가까울 때)F : 절암벽면의 수직부위의 밑점G : 절암벽면의 수직높이 H : 절암벽사면(잔벽사면 : 채석적지 사면)의 사면적 I : 절암벽 수직면의 단면적(거의 수직으로 채석할 때) J : 절암벽 사면, 수직면, 밑면이 형성하는 채취 후 잔치된 토석체적 계산용 단면적 K : 본래 산지표면 L : 표토제거후 암반면M : 표토제거깊이N : 채취(채석장)부위 밑면길이(편평하게 된 지역)O : 토사채취량 계산용 구적도 단면적 α: 채취후 절암벽면과 밑면(수평선)에 의하여 형성되는 잔벽의 경사도 β: 본래 산지표면의 경사도 P : 절암벽면(E)과 수직벽면에 의하여 형성되는 삼각형의 밑면길이 Q : P 부분의 평면적(Pm × Sm = Qm2)R : N 부분의 평면적(Nm × Sm = Rm2)S : 비탈밑(D) 부위의 (좌에서 우까지) 전체길이 T : 산물처리장의 비탈끝 U : 산물처리장의 퇴적부위길이 V : 산물처리장의 평면적(Um × Sm = Vm2)W : 퇴적토사단면적(폐석 또는 퇴적토사)X : 산물처리장의 퇴적토사지의 사면적 그림 5-3-1. 채석적지 부위별 명칭 +- A: 벌채구역(허가구역에 포함) +- B: 돌림배수로 +- C: 절암(절취)벽면 중앙부의 비탈어깨(法肩) +- D: 절암벽면의 비탈밑(法尻) +- D′: 본래 사면의 비탈밑(法尻) +- E: 절암벽면의 비탈길이(사면장) +- E′: 절암벽면의 수직길이(수직에 가까울 때) +- F: 절암벽면 수직부위의 밑점 +- G: 절암벽면의 수직높이 +- H: 절암벽사면(잔벽사면·채석적지 사면)의 사면적 +- I: 절암벽 수직면의 단면적(거의 수직으로 채석할 때) +- J: 절암벽 사면·수직면·밑면이 형성하는 채취 후 잔치된 토석체적 계산용 단면적 +- K: 본래 산지표면 +- L: 표토 제거 후 암반면 +- M: 표토 제거깊이 +- N: 채취(채석장)부위 밑면길이(편평해진 지역) +- O: 토사채취량 계산용 구적도 단면적 +- $\alpha$: 채취 후 절암벽면과 밑면(수평선)이 형성하는 잔벽 경사도 +- $\beta$: 본래 산지표면의 경사도 +- P: 절암벽면(E)과 수직벽면이 형성하는 삼각형의 밑면길이 +- Q: P 부분의 평면적($P\,\mathrm{m}\times S\,\mathrm{m}=Q\,\mathrm{m^2}$) +- R: N 부분의 평면적($N\,\mathrm{m}\times S\,\mathrm{m}=R\,\mathrm{m^2}$) +- S: 비탈밑(D) 부위의 좌우 전체길이 +- T: 산물처리장의 비탈끝 +- U: 산물처리장의 퇴적부위 길이 +- V: 산물처리장의 평면적($U\,\mathrm{m}\times S\,\mathrm{m}=V\,\mathrm{m^2}$) +- W: 퇴적토사 단면적(폐석 또는 퇴적토사) +- X: 산물처리장 퇴적토사지의 사면적 #### 3) 채석적지 유형 @@ -266,17 +293,45 @@ A : 벌채구역(허가구역에 포함)B : 돌림배수로 C : 절암(절취) 표 5-3-5. 채석적지 표준유형에 대한 안정·녹화공사 표준공종(공법) 배치기준 -| 부 위 별 적 용 공 종 (공 법) | 단 위 | 유형 1. 쇄골재채취장 | -|---|---|---| -| - (잔벽주위 토석구역 : 보전구역 : 600㎡) ① 산비탈 돌쌓기 (길이 4m × 6개소) (높이 0.5m) ② 돌 수로내기 (소형) (0.3 × 0.9 × 0.3m) ③ 새심기 (새·솔새 등의 풀포기) ④ 녹화식수공법 (교목수종은 수고 1m 이상) - (채굴잔벽비탈 구역 : 사면적 3,500㎡, 평면적 1,700㎡) ① 비탈 다듬기 (3,500㎡ × 0.15m) ② 돌 단쌓기 (높이 1m) ③ 돌 수로내기 (소형) (0.3 × 0.9 × 0.3m) (종배수로) ④ 새집공법 (길이 3m × 높이 1m 돌쌓기 구조) (녹화식수 6본/집) ⑤ 소단상 객토식수공법 (침·활·관목 혼효식재) (소단연장 230m × 너비 1m × 두께 0.3m 객토 = 70㎥) ⑥ 객토자루새심기 (20ℓ × 150개) (객토 3㎥) ⑦ 덩굴식물녹화공법 (담쟁이, 등나무 기타) ⑧ 차폐수벽공법 (가로수 규격의 대묘) * 분사식 씨뿌리기공법 * 종비토 뿜어붙이기공법 - (산물처리장 및 퇴적장구역 : 6,800㎡) ① 비탈 다듬기 (1,000㎡ × 0.2m) ② 돌 단쌓기 (높이 1m) ③ 산비탈 돌쌓기 (높이 0.5m) (깊이 4m × 25개소) ④ 옹벽 (석축 높이 2m·대석 사용) ⑤ 돌 수로내기 (소형) (0.3 × 0.9 × 0.3m) ⑥ 돌 수로내기 (중형) (0.5 × 1.5 × 0.5m) ⑦ 돌 조공 (잡석사용) ⑧ 새심기 (새, 솔새 등의 풀포기) | m m ㎡ 본 ㎥ m m 개소 ㎥ (객토) 본 ㎥ (객토) 본 ㎥ (객토) (자루/주) 본 m ㎡ ㎡ ㎥ m m m m m m ㎡ | 24 120 100 250 520 100 100 50 (45) (300) 70 (500) 3 (600) (500) 50 200 100 100 30 400 50 300 100 | +| 구역 | 공종(공법) 및 규격 | 단위 | 수량 | +|---|---|---:|---:| +| 잔벽주위 토석구역(보전구역 600㎡) | 산비탈 돌쌓기(길이 4m×6개소, 높이 0.5m) | m | 24 | +| | 돌 수로내기 소형(0.3×0.9×0.3m) | m | 120 | +| | 새심기(새·솔새 등의 풀포기) | ㎡ | 100 | +| | 녹화식수공법(교목 수고 1m 이상) | 본 | 250 | +| 채굴잔벽비탈구역(사면적 3,500㎡, 평면적 1,700㎡) | 비탈 다듬기(3,500㎡×0.15m) | ㎥ | 520 | +| | 돌 단쌓기(높이 1m) | m | 100 | +| | 돌 수로내기 소형(0.3×0.9×0.3m, 종배수로) | m | 100 | +| | 새집공법(길이 3m×높이 1m 돌쌓기 구조) | 개소 | 50 | +| | 새집공법 녹화식수 | ㎥(객토)/본 | (45)/(300) | +| | 소단상 객토식수공법(소단연장 230m×너비 1m×두께 0.3m) | ㎥(객토)/본 | 70/(500) | +| | 객토자루새심기(20ℓ×150개) | ㎥(객토)/(자루·주) | 3/(600) | +| | 덩굴식물녹화공법(담쟁이·등나무 등) | 본 | (500) | +| | 차폐수벽공법(가로수 규격 대묘) | m | 50 | +| | * 분사식 씨뿌리기공법 | ㎡ | | +| | * 종비토 뿜어붙이기공법 | ㎡ | | +| 산물처리장·퇴적장구역(6,800㎡) | 비탈 다듬기(1,000㎡×0.2m) | ㎥ | 200 | +| | 돌 단쌓기(높이 1m) | m | 100 | +| | 산비탈 돌쌓기(높이 0.5m, 길이 4m×25개소) | m | 100 | +| | 옹벽(석축 높이 2m·대석 사용) | m | 30 | +| | 돌 수로내기 소형(0.3×0.9×0.3m) | m | 400 | +| | 돌 수로내기 중형(0.5×1.5×0.5m) | m | 50 | +| | 돌 조공(잡석 사용) | m | 300 | +| | 새심기(새·솔새 등의 풀포기) | ㎡ | 100 | +| | 줄 떼심기(반떼 사용) | m | 300 | +| | 줄 씨뿌리기(줄너비 0.2m×3,000m=600㎡) | ㎡ | 600 | +| | 녹화식수공법(교목 수고 1m 이상 대묘) | 본 | 3,490 | +| | 차폐수벽공법(가로수 규격 대묘) | m | 30 | +| | * 객토공사(6,800㎡×0.1m=680㎥/2) | ㎥ | 340 | +| 진입로·기타구역(900㎡) | 비탈 다듬기(900㎡×0.05m) | ㎥ | 50 | +| | 산비탈 돌쌓기(높이 0.5m, 길이 4m×8개소) | m | 32 | +| | 돌 수로내기 소형(0.3×0.9×0.3m) | m | 30 | +| | 새심기(새·솔새 등의 풀포기) | ㎡ | 50 | +| | 줄 씨뿌리기(줄너비 0.2m×300m) | ㎡ | 60 | +| | 녹화식수공법(교목 수고 1m 이상 대묘) | 본 | 300 | +| 합계 | 1ha당 묘목 식재본수(덩굴식물 제외) | 본 | (5,000) | -(계속) - -| 부 위 별 적 용 공 종 (공 법) | 단 위 | 유형 1. 쇄골재채취장 | -|---|---|---| -| ⑨ 줄 떼심기 (반떼 사용) ⑩ 줄 씨뿌리기 (줄너비 0.2m × 3,000m = 600㎡) ⑪ 녹화식수공법 (교목수종은 수고 1m 이상의 대묘) ⑫ 차폐수벽공법 (가로수 규격의 대묘) *객토공사 (6,800㎡ × 0.1m = 680㎥/2) - (진입로·기타 구역 : 900㎡) ① 비탈 다듬기 (900㎡ × 0.05m) ② 산비탈 돌쌓기 (높이 0.5m) (길이 4m × 8개소) ③ 돌 수로내기 (소형) (0.3 × 0.9 × 0.3m) ④ 새심기 (새, 솔새 등의 풀포기) ⑤ 줄 씨뿌리기 (줄너비 0.2m × 300m = 600㎡) ⑥ 녹화식수공법 (교목수종은 수고 1m 이상 대묘) (* : 도시지역 또는 특수한 경우에 한함) | m ㎡ 본 m ㎥ ㎥ m m ㎡ ㎡ 본 | 300 600 3,490 30 340 50 32 30 50 60 300 | -| (1ha 당묘목 식재본수 합계) (덩굴식물 제외) | (본) | (5,000) | -| 합 계 | | | +※ `*` 공종은 도시지역 또는 특수한 경우에 한한다. ※ 채석적지 면적 1ha 기준