refactor(B05,B06): B05_wf2_Route -> B05_Profile, B06_wf3_ProfileCross -> B06_Section 동시 개명

- 한몸으로 동작하는 두 페이지라 한 커밋으로 처리 (상호 참조 다수)
- B05 37파일 + B06 20파일 접두사 개명 (git mv, 이력 보존)
- 참조 치환 91파일: import 경로, 라우트 슬러그(b05-profile/b06-section),
  라우트 키(B05_PROFILE/B06_SECTION), B03 자동 체인, storage 상수, pyproject 제외 경로
- 로직 변경 없음. typecheck·백엔드 import 검증 통과

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 10:03:11 +09:00
co-authored by Claude Fable 5
parent b59aae3ea3
commit 54954a05e5
77 changed files with 223 additions and 223 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+2 -2
View File
@@ -18,8 +18,8 @@ export interface WorkflowState {
export const WORKFLOW_STEP_ROUTES: readonly RoutePath[] = [
ROUTES.B03_FILE_INPUT,
ROUTES.B04_PREPROCESS,
ROUTES.B05_WF2_ROUTE,
ROUTES.B06_WF3_PROFILE_CROSS,
ROUTES.B05_PROFILE,
ROUTES.B06_SECTION,
ROUTES.B07_WF4_DESIGN_DETAIL,
ROUTES.B08_WF5_QUANTITY,
ROUTES.B09_WF6_ESTIMATION,
+2 -2
View File
@@ -19,8 +19,8 @@ const FOOTERLESS_ROUTES: readonly RoutePath[] = [
ROUTES.B02_PROJ_REGISTER,
ROUTES.B03_FILE_INPUT,
ROUTES.B04_PREPROCESS,
ROUTES.B05_WF2_ROUTE,
ROUTES.B06_WF3_PROFILE_CROSS,
ROUTES.B05_PROFILE,
ROUTES.B06_SECTION,
ROUTES.B07_WF4_DESIGN_DETAIL,
ROUTES.B08_WF5_QUANTITY,
ROUTES.B09_WF6_ESTIMATION,
+6 -6
View File
@@ -41,10 +41,10 @@ const routeTable: Partial<Record<RoutePath, () => Promise<PageRenderer>>> = {
(await import("../B03_FileInput/B03_FileInput_UI_Page")).renderB03FileInput,
[ROUTES.B04_PREPROCESS]: async () =>
(await import("../B04_PreProcess/B04_PreProcess_UI_Page")).renderB04Surface,
[ROUTES.B05_WF2_ROUTE]: async () =>
(await import("../B05_wf2_Route/B05_wf2_Route_UI_Page")).renderB05Route,
[ROUTES.B06_WF3_PROFILE_CROSS]: async () =>
(await import("../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Page")).renderB06ProfileCross,
[ROUTES.B05_PROFILE]: async () =>
(await import("../B05_Profile/B05_Profile_UI_Page")).renderB05Route,
[ROUTES.B06_SECTION]: async () =>
(await import("../B06_Section/B06_Section_UI_Page")).renderB06ProfileCross,
[ROUTES.B07_WF4_DESIGN_DETAIL]: async () =>
(await import("../B07_wf4_DesignDetail/B07_wf4_DesignDetail_UI_Page")).renderB07DesignDetail,
// B08_WF5_QUANTITY: 재작성 예정으로 기존 구현을 0_old_260726_codex.zip에 백업 후 제거.
@@ -107,8 +107,8 @@ export async function renderCurrentRoute(outlet: HTMLElement): Promise<void> {
ROUTES.B02_PROJ_REGISTER,
ROUTES.B03_FILE_INPUT,
ROUTES.B04_PREPROCESS,
ROUTES.B05_WF2_ROUTE,
ROUTES.B06_WF3_PROFILE_CROSS,
ROUTES.B05_PROFILE,
ROUTES.B06_SECTION,
ROUTES.B07_WF4_DESIGN_DETAIL,
ROUTES.B08_WF5_QUANTITY,
ROUTES.B09_WF6_ESTIMATION,
+10 -10
View File
@@ -59,10 +59,10 @@ async def run_auto_design_chain(project_id: UUID, surface_model_id: int | None =
예외를 밖으로 던지지 않는다 — 로그와 각 단계의 workflow 상태 기록으로 남긴다.
"""
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Repository import get_latest_route
from B05_wf2_Route.B05_wf2_Route_Router import confirm_latest_route, solve_route
from B05_wf2_Route.B05_wf2_Route_Schema import RoutePoint, RouteSolveRequest
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router_Confirm import confirm_sections
from B05_Profile.B05_Profile_Repository import get_latest_route
from B05_Profile.B05_Profile_Router import confirm_latest_route, solve_route
from B05_Profile.B05_Profile_Schema import RoutePoint, RouteSolveRequest
from B06_Section.B06_Section_Router_Confirm import confirm_sections
from common_util.common_util_storage import resolve_stored_project_path
from common_util.common_util_surface_confirmation import surface_confirmation_defaults
from config.config_db import get_db_pool
@@ -167,16 +167,16 @@ async def run_redesign_chain(
경로가 아예 없으면 신규 자동 체인(계획노선 CSV 기본값)으로 되돌아간다.
"""
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Repository import get_latest_route
from B05_wf2_Route.B05_wf2_Route_Router import confirm_latest_route, solve_route
from B05_wf2_Route.B05_wf2_Route_Schema import RouteSolveRequest
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
from B05_Profile.B05_Profile_Repository import get_latest_route
from B05_Profile.B05_Profile_Router import confirm_latest_route, solve_route
from B05_Profile.B05_Profile_Schema import RouteSolveRequest
from B06_Section.B06_Section_Repository import (
get_cross_section_designs,
get_longitudinal_section,
update_cross_section_design,
)
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router_Confirm import confirm_sections
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Schema import SectionConfirmRequest
from B06_Section.B06_Section_Router_Confirm import confirm_sections
from B06_Section.B06_Section_Schema import SectionConfirmRequest
from common_util.common_util_workflow_state import get_workflow_state
from config.config_db import get_db_pool
+1 -1
View File
@@ -54,7 +54,7 @@ function L(key: keyof typeof ui_locales): string {
export async function renderB03FileInput(root: HTMLElement): Promise<void> {
const dashboardUser = await fetchDashboardMe();
const completionRoute =
dashboardUser.role === "SYSTEM_ADMIN" ? ROUTES.B04_PREPROCESS : ROUTES.B05_WF2_ROUTE;
dashboardUser.role === "SYSTEM_ADMIN" ? ROUTES.B04_PREPROCESS : ROUTES.B05_PROFILE;
const slots = initializeSlots();
const cardMap = new Map<FileSlot, HTMLElement>();
const resultList = document.createElement("ul");
@@ -24,7 +24,7 @@ from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Export import STAGES, drainage_dir
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Flow import polygonize_labels
from B04_PreProcess.B04_PreProcess_Engine_Watershed_Grid import GridSpec
from B05_wf2_Route.B05_wf2_Route_Repository import get_surface_crs_epsg
from B05_Profile.B05_Profile_Repository import get_surface_crs_epsg
from common_util.common_util_route_geometry import (
find_planned_route_file,
read_planned_route_csv,
@@ -35,7 +35,7 @@ from B04_PreProcess.B04_PreProcess_Engine_Watershed_Grid import (
AZIMUTH_STEPS,
mask_row_spans,
)
from B05_wf2_Route.B05_wf2_Route_Repository import get_surface_crs_epsg
from B05_Profile.B05_Profile_Repository import get_surface_crs_epsg
from common_util.common_util_route_geometry import (
StructureCandidate,
find_planned_route_file,
+3 -3
View File
@@ -10,7 +10,7 @@ import {
import { createWorkflowLayout } from "@ui/ui_template_workflow_layout";
import { fetchDashboardMe } from "../B01_Dashboard/B01_Dashboard_Api_Fetch";
import { clearPreloadMark, purgeOtherProjects } from "../A00_Common/b_asset_cache";
import { clearRouteLatestCache } from "../B05_wf2_Route/B05_wf2_Route_Api_Fetch";
import { clearRouteLatestCache } from "../B05_Profile/B05_Profile_Api_Fetch";
import { workflowSteps } from "../A00_Common/b_page_scaffold";
import {
fetchWorkflowState,
@@ -39,7 +39,7 @@ const SOURCE_FILTERS = ["grid_min_z", "csf", "pmf"] as const;
const MODEL_METHODS = ["tin", "dtm", "nurbs", "implicit", "meshfree"] as const;
const DEFAULT_FILTER = "csf";
const DEFAULT_METHOD = "dtm";
const ROUTE_STAGE = ROUTES.B05_WF2_ROUTE;
const ROUTE_STAGE = ROUTES.B05_PROFILE;
function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
@@ -109,7 +109,7 @@ export async function renderB04Surface(root: HTMLElement): Promise<void> {
const surfaceStage = workflowState?.stages.find((stage) => stage.stage_no === 1);
goToWorkflowStage(
guardedProjectId,
surfaceStage?.state === "COMPLETE" ? ROUTES.B05_WF2_ROUTE : ROUTES.B03_FILE_INPUT,
surfaceStage?.state === "COMPLETE" ? ROUTES.B05_PROFILE : ROUTES.B03_FILE_INPUT,
);
return;
}
@@ -1,8 +1,8 @@
/* =============================================================================
* B05_wf2_Route_Api_Fetch.ts
* B05_Profile_Api_Fetch.ts
* 2 ( ) API
*
* (B05_wf2_Route_Router.py):
* (B05_Profile_Router.py):
* POST /api/projects/{project_id}/route/solve + DB
* POST /api/projects/{project_id}/route/confirm
*
@@ -9,12 +9,12 @@ import logging
from pathlib import Path
from typing import Any
from B05_wf2_Route.B05_wf2_Route_Debug import log_b05_debug
from B05_wf2_Route.B05_wf2_Route_Engine_RidgeValley import solve_ridge_valley_route
from B05_wf2_Route.B05_wf2_Route_Engine_Solver import solve_optimal_route
from B05_Profile.B05_Profile_Debug import log_b05_debug
from B05_Profile.B05_Profile_Engine_RidgeValley import solve_ridge_valley_route
from B05_Profile.B05_Profile_Engine_Solver import solve_optimal_route
from common_util.common_util_json import atomic_write_json
_ROUTE_SUBDIR = Path("B05_wf2_Route") / "route"
_ROUTE_SUBDIR = Path("B05_Profile") / "route"
# route_points 테이블에 저장할 렌더링 샘플 최대 개수
_MAX_RENDER_POINTS = 500
logger = logging.getLogger(__name__)
@@ -17,7 +17,7 @@ from typing import Any
import numpy as np
from B05_wf2_Route.B05_wf2_Route_Engine_Grade_Solver import (
from B05_Profile.B05_Profile_Engine_Grade_Solver import (
balance_boundaries,
build_vertical_curves,
evaluate_profile,
@@ -10,8 +10,8 @@
- 종단곡선은 변화점 대칭 배치이며 좌우에 직선 구간이 반드시 남는다.
- 곡선 기본 **반경** R = 측점간격 × `curve_radius_ratio`, 길이 L = R × |A| 파생.
수치 최적화(직선 분할 DP·표고 결정) [[B05_wf2_Route_Engine_Grade_Solver]],
기준 해석과 오케스트레이션은 [[B05_wf2_Route_Engine_Grade]] 담당한다. 모듈은
수치 최적화(직선 분할 DP·표고 결정) [[B05_Profile_Engine_Grade_Solver]],
기준 해석과 오케스트레이션은 [[B05_Profile_Engine_Grade]] 담당한다. 모듈은
scipy에 의존하지 않는 순수 기하 계산만 두어 프론트엔드 구현과 1:1 대응시킨다.
"""
@@ -1,8 +1,8 @@
"""B05 종단 계획선 선형 산출 오케스트레이터 (지반 추종 직선 분할 + 편집 재구성).
[[B05_wf2_Route_Engine_Grade]] 확정한 설계 기준과
[[B05_wf2_Route_Engine_Grade_Solver]] 수치 계산,
[[B05_wf2_Route_Engine_Grade_Alignment]] 기하 파생을 묶어
[[B05_Profile_Engine_Grade]] 확정한 설계 기준과
[[B05_Profile_Engine_Grade_Solver]] 수치 계산,
[[B05_Profile_Engine_Grade_Alignment]] 기하 파생을 묶어
`design_profiles` 배열에 넣을 계획선 벌을 만든다.
진입점이 있다.
@@ -22,12 +22,12 @@ from typing import Any
import numpy as np
from B05_wf2_Route.B05_wf2_Route_Engine_Grade import (
from B05_Profile.B05_Profile_Engine_Grade import (
GradeDesignOptions,
detect_main_direction,
ground_profile,
)
from B05_wf2_Route.B05_wf2_Route_Engine_Grade_Alignment import (
from B05_Profile.B05_Profile_Engine_Grade_Alignment import (
ALIGNMENT_SCHEMA_VERSION,
AlignmentPolicy,
build_alignment,
@@ -35,7 +35,7 @@ from B05_wf2_Route.B05_wf2_Route_Engine_Grade_Alignment import (
chainage_key,
evaluate,
)
from B05_wf2_Route.B05_wf2_Route_Engine_Grade_Solver import (
from B05_Profile.B05_Profile_Engine_Grade_Solver import (
grade_limits,
integration_weights,
solve_alignment_elevations,
@@ -1,6 +1,6 @@
"""B05 종단 계획선 산출의 수치 계산부 (적분 가중치·PVI 최적화·종단곡선 기하).
[[B05_wf2_Route_Engine_Grade]] 설계 기준을 확정한 모듈의 함수를 호출한다.
[[B05_Profile_Engine_Grade]] 설계 기준을 확정한 모듈의 함수를 호출한다.
설계 기준 객체에 의존하지 않고 스칼라 인자만 받아, 기준 해석과 수치 계산을
분리한다(700 제한에 따른 분할).
"""
@@ -13,14 +13,14 @@ from typing import Any
import numpy as np
from B05_wf2_Route.B05_wf2_Route_Engine_Geometry import (
from B05_Profile.B05_Profile_Engine_Geometry import (
circle_intrusions,
circumradius_2d,
point_to_polyline_dist_2d,
resample_polyline_2d,
)
from B05_wf2_Route.B05_wf2_Route_Engine_Skeleton import load_or_build_skeleton
from B05_wf2_Route.B05_wf2_Route_Engine_Solver import (
from B05_Profile.B05_Profile_Engine_Skeleton import load_or_build_skeleton
from B05_Profile.B05_Profile_Engine_Solver import (
_MODELS_SUBDIR,
_load_or_build_cost_surface,
)
@@ -11,12 +11,12 @@ import logging
from pathlib import Path
from typing import Any
from B05_wf2_Route.B05_wf2_Route_Engine_Grade import GradeDesignOptions, design_grade_line
from B05_wf2_Route.B05_wf2_Route_Engine_Grade_Profile import (
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions, design_grade_line
from B05_Profile.B05_Profile_Engine_Grade_Profile import (
design_alignment_profile,
design_pipe_anchored_profile,
)
from B05_wf2_Route.B05_wf2_Route_Engine_Sections_Core import (
from B05_Profile.B05_Profile_Engine_Sections_Core import (
SectionGenerationOptions,
generate_sections,
)
@@ -33,7 +33,7 @@ from config.config_system import (
logger = logging.getLogger(__name__)
_STAGE_SUBDIR = Path("B06_wf3_ProfileCross")
_STAGE_SUBDIR = Path("B06_Section")
_MODELS_SUBDIR = Path("B04_PreProcess") / "models"
@@ -2,7 +2,7 @@
수문학적 정의: D8 흐름누적이 임계값 이상인 =계곡, DEM 반전 능선.
누적값 크기의 2 임계값으로 /지를 나눈다. whitebox 우선, 실패 numpy
D8 폴백. 산출 polyline은 비용면과 동일 좌표계이며 B05_wf2_Route/route에 캐시된다.
D8 폴백. 산출 polyline은 비용면과 동일 좌표계이며 B05_Profile/route에 캐시된다.
"""
import json
@@ -14,7 +14,7 @@ from typing import Any
import numpy as np
from B05_wf2_Route.B05_wf2_Route_Engine_Solver import (
from B05_Profile.B05_Profile_Engine_Solver import (
_MODELS_SUBDIR,
_ROUTE_CACHE_SUBDIR,
_cost_surface_signature,
@@ -11,7 +11,7 @@ from typing import Any
import numpy as np
from B05_wf2_Route.B05_wf2_Route_Engine_Geometry import (
from B05_Profile.B05_Profile_Engine_Geometry import (
circle_intrusions,
circumradius_2d,
compute_gradients,
@@ -39,7 +39,7 @@ from config.config_system import (
# B04 지표면 모델 폴더명 (비용면의 표고 원본)
_MODELS_SUBDIR = Path("B04_PreProcess") / "models"
# B05 비용면 캐시 폴더명
_ROUTE_CACHE_SUBDIR = Path("B05_wf2_Route") / "route"
_ROUTE_CACHE_SUBDIR = Path("B05_Profile") / "route"
def _load_dtm_grid(models_dir: Path, filter_key: str, smooth: bool):
@@ -12,7 +12,7 @@ from uuid import UUID
import aiomysql
_STAGE_ROOT = "B05_wf2_Route"
_STAGE_ROOT = "B05_Profile"
def _validate_stage_path(relative_path: str) -> str:
@@ -12,13 +12,13 @@ from fastapi import APIRouter
from fastapi.responses import JSONResponse
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Debug import log_b05_debug
from B05_wf2_Route.B05_wf2_Route_Engine import run_route_design
from B05_wf2_Route.B05_wf2_Route_Engine_Grade import GradeDesignOptions, resolve_grade_options
from B05_wf2_Route.B05_wf2_Route_Engine_Grade_Profile import rebuild_alignment_profile
from B05_wf2_Route.B05_wf2_Route_Engine_Sections import run_section_generation
from B05_wf2_Route.B05_wf2_Route_Engine_Sections_Core import SectionGenerationOptions
from B05_wf2_Route.B05_wf2_Route_Repository import (
from B05_Profile.B05_Profile_Debug import log_b05_debug
from B05_Profile.B05_Profile_Engine import run_route_design
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions, resolve_grade_options
from B05_Profile.B05_Profile_Engine_Grade_Profile import rebuild_alignment_profile
from B05_Profile.B05_Profile_Engine_Sections import run_section_generation
from B05_Profile.B05_Profile_Engine_Sections_Core import SectionGenerationOptions
from B05_Profile.B05_Profile_Repository import (
confirm_route,
create_route,
create_route_statistics,
@@ -28,12 +28,12 @@ from B05_wf2_Route.B05_wf2_Route_Repository import (
insert_route_points,
update_longitudinal_grade_summary,
)
from B05_wf2_Route.B05_wf2_Route_Router_Confirm import (
from B05_Profile.B05_Profile_Router_Confirm import (
_append_irregular_cross_sections,
_merge_uphill_overrides_into_longitudinal,
sync_uphill_overrides_into_designs,
)
from B05_wf2_Route.B05_wf2_Route_Schema import (
from B05_Profile.B05_Profile_Schema import (
GRADE_PERCENT_FIELDS,
ContourIntervalUpdateRequest,
ContourIntervalUpdateResponse,
@@ -46,7 +46,7 @@ from B05_wf2_Route.B05_wf2_Route_Schema import (
RouteSolveResponse,
normalize_grade_percent,
)
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
from B06_Section.B06_Section_Repository import (
create_longitudinal_section,
delete_sections_for_route,
get_latest_grade_options,
@@ -1,6 +1,6 @@
"""B05 경로 확정 시 종단 정본 파일에 비정규 측점·상단측 변경을 병합하는 헬퍼.
라우터(`B05_wf2_Route_Router.py`) confirm 엔드포인트가 호출한다. 파일 기반 병합이라
라우터(`B05_Profile_Router.py`) confirm 엔드포인트가 호출한다. 파일 기반 병합이라
재확정해도 중복이 생기지 않으며, 모든 호출은 비치명적(실패해도 경로 확정은 진행)이다.
"""
@@ -13,11 +13,11 @@ from uuid import UUID
import aiomysql
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Engine_Sections import generate_irregular_sections
from B05_wf2_Route.B05_wf2_Route_Engine_Sections_Core import SectionGenerationOptions
from B05_wf2_Route.B05_wf2_Route_Repository import get_surface_crs_epsg
from B05_wf2_Route.B05_wf2_Route_Schema import RouteConfirmRequest
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
from B05_Profile.B05_Profile_Engine_Sections import generate_irregular_sections
from B05_Profile.B05_Profile_Engine_Sections_Core import SectionGenerationOptions
from B05_Profile.B05_Profile_Repository import get_surface_crs_epsg
from B05_Profile.B05_Profile_Schema import RouteConfirmRequest
from B06_Section.B06_Section_Repository import (
get_latest_section_options,
get_longitudinal_section,
)
@@ -90,12 +90,12 @@ async def sync_uphill_overrides_into_designs(
양성(both_fill) 측구가 없으므로 건드리지 않는다.
"""
# 지연 import — B06 라우터 모듈 로드는 이 함수가 실제 불릴 때만 필요하다.
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Engine_Design import compute_cross_design
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
from B06_Section.B06_Section_Engine_Design import compute_cross_design
from B06_Section.B06_Section_Repository import (
get_cross_section_designs,
update_cross_section_design,
)
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router import _read_cross_design_inputs
from B06_Section.B06_Section_Router import _read_cross_design_inputs
if not overrides:
return
@@ -23,17 +23,17 @@ import {
type PreparedLayer,
type ViewState,
} from "../B04_PreProcess/B04_PreProcess_UI_MapRender";
import { type RoutePoint } from "./B05_wf2_Route_Api_Fetch";
import { type RoutePoint } from "./B05_Profile_Api_Fetch";
import type { FlowArrow } from "../B04_PreProcess/B04_PreProcess_UI_FlowArrows";
import {
buildStrengthArray,
createFlowLegend,
} from "../B04_PreProcess/B04_PreProcess_UI_FlowRamp";
import { resampleRoute } from "../B04_PreProcess/B04_PreProcess_UI_RouteSamples";
import { createPipeEditor } from "./B05_wf2_Route_UI_Drainage_Pipes";
import { createPipeEditor } from "./B05_Profile_UI_Drainage_Pipes";
import { createProgressCircle } from "@ui/ui_template_progress";
import { createMapContextMenu } from "@ui/ui_template_context_menu";
import { drawDrainageScene } from "./B05_wf2_Route_UI_Drainage_Render";
import { drawDrainageScene } from "./B05_Profile_UI_Drainage_Render";
import {
mountDrainageToggles,
DRAINAGE_LAYERS,
@@ -54,7 +54,7 @@ import {
reconcilePipes,
WIDTH_KEY,
type DrainageLayer,
} from "./B05_wf2_Route_UI_Drainage_Parts";
} from "./B05_Profile_UI_Drainage_Parts";
/** 이만큼(px) 이하로 움직였다 뗐으면 클릭으로 본다 — 손떨림으로 선택이 안 되는 일을 막는다. */
const CLICK_SLOP_PX = 4;
@@ -2,7 +2,7 @@
* (B05)
*
* ·· , DOM ( ,
* ) . (`B05_wf2_Route_UI_Drainage_Panel.ts`) 700
* ) . (`B05_Profile_UI_Drainage_Panel.ts`) 700
* , .
* ========================================================================== */
@@ -19,7 +19,7 @@ import {
import { normalizeStrength, rampColor } from "../B04_PreProcess/B04_PreProcess_UI_FlowRamp";
import type { MapContextMenu } from "@ui/ui_template_context_menu";
import type { DetailBasin } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
import type { PipeEditor, PipePoint } from "./B05_wf2_Route_UI_Drainage_Pipes";
import type { PipeEditor, PipePoint } from "./B05_Profile_UI_Drainage_Pipes";
function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
@@ -36,8 +36,8 @@ import {
DRAINAGE_LAYERS,
layerColor,
type DrainageLayer,
} from "./B05_wf2_Route_UI_Drainage_Parts";
import type { PipeEditor } from "./B05_wf2_Route_UI_Drainage_Pipes";
} from "./B05_Profile_UI_Drainage_Parts";
import type { PipeEditor } from "./B05_Profile_UI_Drainage_Pipes";
export interface DrainageScene {
meta: VWorldMeta | null;
@@ -1,5 +1,5 @@
/* =============================================================================
* B05_wf2_Route_UI_IrregularStations.ts
* B05_Profile_UI_IrregularStations.ts
* ( ) ·· .
*
* ( ) ( X + XX)
@@ -25,33 +25,33 @@ import {
type CirclePoint,
type RouteLatestResponse,
type RoutePoint,
} from "./B05_wf2_Route_Api_Fetch";
} from "./B05_Profile_Api_Fetch";
import {
type ModelBounds,
type PlacedRoutePoint,
type RouteDesignPoints,
type RoutePointKind,
} from "./B05_wf2_Route_UI_Markers";
import { createRoutePanel, type RoutePanelValues } from "./B05_wf2_Route_UI_Panel";
import { createRouteProfilePanel } from "./B05_wf2_Route_UI_Profile_Panel";
import { createSelectionSync } from "./B05_wf2_Route_UI_Selection";
import { createRouteViewer } from "./B05_wf2_Route_UI_Viewer";
} from "./B05_Profile_UI_Markers";
import { createRoutePanel, type RoutePanelValues } from "./B05_Profile_UI_Panel";
import { createRouteProfilePanel } from "./B05_Profile_UI_Profile_Panel";
import { createSelectionSync } from "./B05_Profile_UI_Selection";
import { createRouteViewer } from "./B05_Profile_UI_Viewer";
import {
irregularLabel,
irregularStationId,
isPipeStation,
type IrregularStation,
} from "./B05_wf2_Route_UI_IrregularStations";
} from "./B05_Profile_UI_IrregularStations";
import {
fetchSectionContext,
type SectionDetailResponse,
type SectionStation,
} from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Api_Fetch";
} from "../B06_Section/B06_Section_Api_Fetch";
import {
invalidateSectionDetail,
loadSectionDetail,
} from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Section_Store";
import "./B05_wf2_Route_UI_Style.css";
} from "../B06_Section/B06_Section_Section_Store";
import "./B05_Profile_UI_Style.css";
type GradeClass = RoutePanelValues["gradeClass"];
type RoadWidths = Record<GradeClass, number>;
@@ -1,9 +1,9 @@
import type { PlacedRoutePoint, RoutePointKind } from "./B05_wf2_Route_UI_Markers";
import type { PlacedRoutePoint, RoutePointKind } from "./B05_Profile_UI_Markers";
import {
createIrregularStationsSection,
type IrregularStation,
type IrregularStationsSection,
} from "./B05_wf2_Route_UI_IrregularStations";
} from "./B05_Profile_UI_IrregularStations";
import { type ButtonVariant, createButton, createSelectField } from "@ui/ui_template_elements";
import { attachCollapsible } from "@ui/ui_template_collapsible";
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
@@ -1,8 +1,8 @@
/* =============================================================================
* B05_wf2_Route_UI_Profile_Alignment.ts
* B05_Profile_UI_Profile_Alignment.ts
* ( + ) .
*
* `B05_wf2_Route_Engine_Grade_Alignment.py` ** ** .
* `B05_Profile_Engine_Grade_Alignment.py` ** ** .
* 0.1m ,
* .
*
@@ -8,8 +8,8 @@
import type {
DesignProfile,
LongitudinalSection,
} from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Api_Fetch";
import type { ProfileAlignment } from "./B05_wf2_Route_UI_Profile_Alignment";
} from "../B06_Section/B06_Section_Api_Fetch";
import type { ProfileAlignment } from "./B05_Profile_UI_Profile_Alignment";
export function readAlignment(data: LongitudinalSection): ProfileAlignment | null {
const candidate = data.profile_alignment as ProfileAlignment | undefined;
@@ -1,5 +1,5 @@
/* =============================================================================
* B05_wf2_Route_UI_Profile_Edit.ts
* B05_Profile_UI_Profile_Edit.ts
* .
*
* ,
@@ -19,8 +19,8 @@ import type {
AlignmentEdits,
AlignmentSegment,
ProfileAlignment,
} from "./B05_wf2_Route_UI_Profile_Alignment";
import { chainageKey, emptyEdits, hasEdits } from "./B05_wf2_Route_UI_Profile_Alignment";
} from "./B05_Profile_UI_Profile_Alignment";
import { chainageKey, emptyEdits, hasEdits } from "./B05_Profile_UI_Profile_Alignment";
const DRAFT_KEY_PREFIX = "b05-profile-alignment-draft";
/** 버튼 폭(21px)에 여유를 더한 값 — 이보다 가까우면 겹친 것으로 본다. */
@@ -1,5 +1,5 @@
/* =============================================================================
* B05_wf2_Route_UI_Profile_MassHaul.ts
* B05_Profile_UI_Profile_MassHaul.ts
* B05 ** **.
*
* B06과 (2026-08-03 )
@@ -50,7 +50,7 @@ import {
import { createWorkflowPanelHandle } from "@ui/ui_template_overlay";
import { createPanelResizer } from "@ui/ui_template_resizer";
import "@util/common_util_mass_haul.css";
import "./B05_wf2_Route_UI_Style_MassHaul.css";
import "./B05_Profile_UI_Style_MassHaul.css";
/** 2차 패널 펼침 여부 — 세션 동안만 유지(패널 높이·접힘과 같은 수명). */
const OPEN_KEY = "b05-route-profile-masshaul-open";
@@ -1,5 +1,5 @@
/* =============================================================================
* B05_wf2_Route_UI_Profile_Panel.ts
* B05_Profile_UI_Profile_Panel.ts
* + 2, .
*
* 60% , 12 ** **
@@ -13,35 +13,35 @@
import type {
LongitudinalSection,
SectionDetailResponse,
} from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Api_Fetch";
} from "../B06_Section/B06_Section_Api_Fetch";
import {
createLongitudinalProfile,
longitudinalMinimumWidth,
} from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Longitudinal";
import { LONG_PAD } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common";
} from "../B06_Section/B06_Section_UI_Longitudinal";
import { LONG_PAD } from "../B06_Section/B06_Section_UI_Section_Common";
import { createWorkflowPanelHandle } from "@ui/ui_template_overlay";
import { createPanelResizer } from "@ui/ui_template_resizer";
import { createDrainagePanel } from "./B05_wf2_Route_UI_Drainage_Panel";
import { mountStructureMenu } from "./B05_wf2_Route_UI_Profile_Structures";
import { createDrainagePanel } from "./B05_Profile_UI_Drainage_Panel";
import { mountStructureMenu } from "./B05_Profile_UI_Profile_Structures";
import {
createProfileTableOverlay,
TABLE_OVERLAY_MIN_HEIGHT,
} from "./B05_wf2_Route_UI_Profile_TableOverlay";
} from "./B05_Profile_UI_Profile_TableOverlay";
import {
hasLegacyAlignment,
normalizedLongitudinal,
readAlignment,
toDesignProfile,
} from "./B05_wf2_Route_UI_Profile_Data";
} from "./B05_Profile_UI_Profile_Data";
import { createProgressCircle } from "@ui/ui_template_progress";
import { showToast } from "@ui/ui_template_elements";
import { saveProfileAlignment } from "./B05_wf2_Route_Api_Fetch";
import { previewCrossDesigns } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Api_Fetch";
import { saveProfileAlignment } from "./B05_Profile_Api_Fetch";
import { previewCrossDesigns } from "../B06_Section/B06_Section_Api_Fetch";
import type {
AlignmentBase,
AlignmentEdits,
ProfileAlignment,
} from "./B05_wf2_Route_UI_Profile_Alignment";
} from "./B05_Profile_UI_Profile_Alignment";
import {
adjustStation,
buildAlignment,
@@ -50,31 +50,31 @@ import {
setCurveRadius,
shiftSegment,
toAlignmentBase,
} from "./B05_wf2_Route_UI_Profile_Alignment";
import { createEditOverlay, createProfileEditStore } from "./B05_wf2_Route_UI_Profile_Edit";
} from "./B05_Profile_UI_Profile_Alignment";
import { createEditOverlay, createProfileEditStore } from "./B05_Profile_UI_Profile_Edit";
import { configureBalloonOffsets } from "@util/common_util_mass_haul_balance_view";
import {
buildStickyYAxis,
createRouteMassHaulPanel,
MASSHAUL_MIN_HEIGHT,
type RouteMassHaulContext,
} from "./B05_wf2_Route_UI_Profile_MassHaul";
} from "./B05_Profile_UI_Profile_MassHaul";
import {
createProfileTable,
tableCellWidthFor,
TABLE_TARGET_FONT_PX,
} from "./B05_wf2_Route_UI_Profile_Table";
} from "./B05_Profile_UI_Profile_Table";
import {
irregularLabel,
irregularStationId,
type IrregularStation,
} from "./B05_wf2_Route_UI_IrregularStations";
import type { SectionStation } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_Api_Fetch";
import "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style.css";
} from "./B05_Profile_UI_IrregularStations";
import type { SectionStation } from "../B06_Section/B06_Section_Api_Fetch";
import "../B06_Section/B06_Section_UI_Style.css";
// SVG 차트 색상(.b06-chart__*)의 정의처는 _Style_Cross.css다. 이걸 빼면 B05로 바로 진입했을 때
// 배경 rect가 브라우저 기본 fill(검정)로 그려진다 — B06을 먼저 방문해야 정상으로 보이던 원인.
import "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_Cross.css";
import "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Style_Cross_Areas.css";
import "../B06_Section/B06_Section_UI_Style_Cross.css";
import "../B06_Section/B06_Section_UI_Style_Cross_Areas.css";
const COLLAPSED_KEY = "b05-route-profile-collapsed";
/** 드래그로 조절한 하단 패널 높이(px) 보관 키 — 브라우저 세션 동안만 유지한다. */
@@ -10,7 +10,7 @@
* ========================================================================== */
import { createMapContextMenu } from "@ui/ui_template_context_menu";
import { isPipeStation, type IrregularStation } from "./B05_wf2_Route_UI_IrregularStations";
import { isPipeStation, type IrregularStation } from "./B05_Profile_UI_IrregularStations";
/** 선을 잡았다고 볼 좌우 여유(px). 선 자체는 얇아 그대로는 집기 어렵다. */
const GRAB_SLACK_PX = 6;
@@ -1,5 +1,5 @@
/* =============================================================================
* B05_wf2_Route_UI_Profile_Table.ts
* B05_Profile_UI_Profile_Table.ts
* ( 3 · 7 · 2 = 12).
*
* .
@@ -16,13 +16,13 @@ import type {
AlignmentCurve,
AlignmentSegment,
ProfileAlignment,
} from "./B05_wf2_Route_UI_Profile_Alignment";
import { stationLabel } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common";
} from "./B05_Profile_UI_Profile_Alignment";
import { stationLabel } from "../B06_Section/B06_Section_UI_Section_Common";
import {
irregularStationId,
isPipeStation,
type IrregularStation,
} from "./B05_wf2_Route_UI_IrregularStations";
} from "./B05_Profile_UI_IrregularStations";
export interface ProfileTableOptions {
alignment: ProfileAlignment;
@@ -1,5 +1,5 @@
/* =============================================================================
* B05_wf2_Route_UI_Profile_TableOverlay.ts
* B05_Profile_UI_Profile_TableOverlay.ts
* 12 ** **
* (2026-08-05 ).
*
@@ -13,7 +13,7 @@ import {
irregularStationId,
isPipeStation,
type IrregularStation,
} from "./B05_wf2_Route_UI_IrregularStations";
} from "./B05_Profile_UI_IrregularStations";
export interface SelectionSyncPorts {
/** 현재 구조물 측점 목록(수동 + 배관 투영분). */
@@ -121,7 +121,7 @@
}
/* 가로 스크롤 고정 Y축(.b05-profile__yaxis*) 정의처는
B05_wf2_Route_UI_Style_MassHaul.css다 buildStickyYAxis를 쓰는 B05·B06이 함께 얻는다. */
B05_Profile_UI_Style_MassHaul.css다 buildStickyYAxis를 쓰는 B05·B06이 함께 얻는다. */
.b05-route-profile.is-collapsed .b05-route-profile__body-wrap,
.b05-route-profile.is-collapsed .b05-route-profile__body,
@@ -1,5 +1,5 @@
/* =============================================================================
* B05_wf2_Route_UI_Style_MassHaul.css
* B05_Profile_UI_Style_MassHaul.css
* B05 계획 유토곡선 2차 슬라이드 패널 전용 스타일.
*
* 곡선·범례·요약 자체의 스타일은 공용(`common_util_mass_haul.css`) 이미 정의한다.
@@ -12,7 +12,7 @@ import {
type RouteMarkers,
type RoutePointKind,
type SectionStationMarker,
} from "./B05_wf2_Route_UI_Markers";
} from "./B05_Profile_UI_Markers";
const LIGHT_VIEWER_BACKGROUND = 0xf5f7fa;
const DARK_VIEWER_BACKGROUND = 0x251f38;
@@ -1,8 +1,8 @@
/* =============================================================================
* B06_wf3_ProfileCross_Api_Fetch.ts
* B06_Section_Api_Fetch.ts
* 3 (· ) API
*
* (B06_wf3_ProfileCross_Router.py):
* (B06_Section_Router.py):
* GET /api/projects/{project_id}/sections/context +
* GET /api/projects/{project_id}/sections/{route_id}
* GET /api/projects/{project_id}/sections/{route_id}/detail
@@ -178,7 +178,7 @@ export interface LongitudinalSection {
/**
* (PVI) . B05가 `design_profiles`
* . optional이다.
* `B05_wf2_Route_UI_Profile_Alignment.ProfileAlignment`.
* `B05_Profile_UI_Profile_Alignment.ProfileAlignment`.
*/
profile_alignment?: unknown;
}
@@ -18,7 +18,7 @@
from collections.abc import Callable
from typing import Any
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Engine_Areas import (
from B06_Section.B06_Section_Engine_Areas import (
_split_cut_areas,
_trapezoid_areas,
)
@@ -13,7 +13,7 @@ from uuid import UUID
import aiomysql
_STAGE_ROOT = "B06_wf3_ProfileCross"
_STAGE_ROOT = "B06_Section"
def _validate_stage_path(relative_path: str) -> str:
@@ -12,16 +12,16 @@ from fastapi import APIRouter, Depends
from fastapi.responses import JSONResponse
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Engine_Grade import GradeDesignOptions, resolve_grade_options
from B05_wf2_Route.B05_wf2_Route_Engine_Grade_Profile import rebuild_alignment_profile
from B05_wf2_Route.B05_wf2_Route_Engine_Sections import (
from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions, resolve_grade_options
from B05_Profile.B05_Profile_Engine_Grade_Profile import rebuild_alignment_profile
from B05_Profile.B05_Profile_Engine_Sections import (
cross_filename,
prune_stale_cross_files,
run_section_generation,
)
from B05_wf2_Route.B05_wf2_Route_Engine_Sections_Core import SectionGenerationOptions
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Engine_Design import compute_cross_design
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
from B05_Profile.B05_Profile_Engine_Sections_Core import SectionGenerationOptions
from B06_Section.B06_Section_Engine_Design import compute_cross_design
from B06_Section.B06_Section_Repository import (
count_cross_sections,
create_longitudinal_section,
delete_sections_for_route,
@@ -35,7 +35,7 @@ from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
list_recent_company_projects,
update_cross_section_design,
)
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Schema import (
from B06_Section.B06_Section_Schema import (
CompanyStandardListResponse,
CompanyStandardProject,
CompanyStandardResponse,
@@ -19,8 +19,8 @@ from fastapi import APIRouter, Body
from fastapi.responses import JSONResponse
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Router_Confirm import _merge_uphill_overrides_into_longitudinal
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
from B05_Profile.B05_Profile_Router_Confirm import _merge_uphill_overrides_into_longitudinal
from B06_Section.B06_Section_Repository import (
confirm_sections_for_route,
get_cross_section_designs,
get_cross_sections_missing_design_chainages,
@@ -30,8 +30,8 @@ from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
merge_longitudinal_section_options,
update_cross_section_design,
)
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router import _compute_default_designs
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Schema import (
from B06_Section.B06_Section_Router import _compute_default_designs
from B06_Section.B06_Section_Schema import (
SectionConfirmRequest,
SectionConfirmResponse,
)
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_Section_Store.ts
* B06_Section_Section_Store.ts
* (SectionDetailResponse) ** ** B05·B06이 .
*
* (2026-08-03 )
@@ -15,8 +15,8 @@
* (· ·) / `replace`·`invalidate`.
* ========================================================================== */
import type { SectionDetailResponse } from "./B06_wf3_ProfileCross_Api_Fetch";
import { fetchSectionDetail } from "./B06_wf3_ProfileCross_Api_Fetch";
import type { SectionDetailResponse } from "./B06_Section_Api_Fetch";
import { fetchSectionDetail } from "./B06_Section_Api_Fetch";
const cache = new Map<string, SectionDetailResponse>();
const pending = new Map<string, Promise<SectionDetailResponse>>();
@@ -1,11 +1,11 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Cross_Areas.ts
* B06_Section_UI_Cross_Areas.ts
* · readout과 .
*
* ( ) =, = .
* EA/RR/BR ,
* .
* (`B06_wf3_ProfileCross_Engine_Design._split_cut_areas`), **
* (`B06_Section_Engine_Design._split_cut_areas`), **
* **( ).
*
* (2026-08-02 ):
@@ -15,8 +15,8 @@
* 700 `_UI_Cross_Design`(588) readout을 .
* ========================================================================== */
import type { CrossDesign, SectionSample } from "./B06_wf3_ProfileCross_Api_Fetch";
import { L, svgElement } from "./B06_wf3_ProfileCross_UI_Section_Common";
import type { CrossDesign, SectionSample } from "./B06_Section_Api_Fetch";
import { L, svgElement } from "./B06_Section_UI_Section_Common";
/**
* = . `cut_total` · .
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Cross_Design.ts
* B06_Section_UI_Cross_Design.ts
* (····)
* · · .
*
@@ -23,7 +23,7 @@ import type {
GroundType,
SectionMode,
SectionSample,
} from "./B06_wf3_ProfileCross_Api_Fetch";
} from "./B06_Section_Api_Fetch";
const SVG_NS = "http://www.w3.org/2000/svg";
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Cross_View.ts
* B06_Section_UI_Cross_View.ts
* SVG X:Y 1:1 .
*
* 700 . ·
@@ -7,13 +7,13 @@
* / Y스케일을 1:1 ppm으로 .
* ========================================================================== */
import type { CrossSection, SectionSample } from "./B06_wf3_ProfileCross_Api_Fetch";
import type { CrossSection, SectionSample } from "./B06_Section_Api_Fetch";
import {
appendCrossAreaBands,
type AreaHighlightSetter,
buildAreaReadout,
type CrossAreaKey,
} from "./B06_wf3_ProfileCross_UI_Cross_Areas";
} from "./B06_Section_UI_Cross_Areas";
import {
appendCrossDesignOverlay,
appendPavementOverlay,
@@ -22,7 +22,7 @@ import {
buildRockBoundaryControl,
sectionModeLabel,
type RockBoundaryControl,
} from "./B06_wf3_ProfileCross_UI_Cross_Design";
} from "./B06_Section_UI_Cross_Design";
import {
CROSS_HEIGHT,
CROSS_PAD,
@@ -34,7 +34,7 @@ import {
svgElement,
svgText,
validElevation,
} from "./B06_wf3_ProfileCross_UI_Section_Common";
} from "./B06_Section_UI_Section_Common";
/**
* . ** **
@@ -1,12 +1,12 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Longitudinal.ts
* B06_Section_UI_Longitudinal.ts
* (·· · ) SVG .
*
* 700 . ·
* `_UI_Section_Common` . / Y스케일(`YScaleOptions`) .
* ========================================================================== */
import type { DesignProfile, LongitudinalSection } from "./B06_wf3_ProfileCross_Api_Fetch";
import type { DesignProfile, LongitudinalSection } from "./B06_Section_Api_Fetch";
import {
emptyView,
inferStationInterval,
@@ -20,7 +20,7 @@ import {
svgText,
validElevation,
type YScaleOptions,
} from "./B06_wf3_ProfileCross_UI_Section_Common";
} from "./B06_Section_UI_Section_Common";
/** 측점 라벨의 측점번호에 시작 측점 오프셋을 더한다(잔여거리는 그대로). */
function offsetStationLabel(chainageM: number, interval: number, stationOffset: number): string {
@@ -29,26 +29,26 @@ import {
type SectionContextResponse,
type SectionDetailResponse,
type StandardCrossSection,
} from "./B06_wf3_ProfileCross_Api_Fetch";
import { type StationWidthControl } from "./B06_wf3_ProfileCross_UI_Cross_View";
import { readAlignmentDraft } from "../B05_wf2_Route/B05_wf2_Route_UI_Profile_Edit";
} from "./B06_Section_Api_Fetch";
import { type StationWidthControl } from "./B06_Section_UI_Cross_View";
import { readAlignmentDraft } from "../B05_Profile/B05_Profile_UI_Profile_Edit";
import {
type CrossDesignChange,
createSectionView,
type RockBoundaryControl,
} from "./B06_wf3_ProfileCross_UI_Section_View";
} from "./B06_Section_UI_Section_View";
import { computeMassHaul, massHaulPayload } from "@util/common_util_mass_haul";
import { computeHaulPlan } from "@util/common_util_mass_haul_balance";
import { balloonOffsetsPayload } from "@util/common_util_mass_haul_balance_view";
import { designElevationAt } from "./B06_wf3_ProfileCross_UI_Section_Common";
import { designElevationAt } from "./B06_Section_UI_Section_Common";
import {
createStandardPanel,
type StandardPanelController,
} from "./B06_wf3_ProfileCross_UI_Standard_Panel";
import "./B06_wf3_ProfileCross_UI_Style.css";
import "./B06_wf3_ProfileCross_UI_Style_Cross.css";
import "./B06_wf3_ProfileCross_UI_Style_Cross_Areas.css";
import { loadSectionDetail, replaceSectionDetail } from "./B06_wf3_ProfileCross_Section_Store";
} from "./B06_Section_UI_Standard_Panel";
import "./B06_Section_UI_Style.css";
import "./B06_Section_UI_Style_Cross.css";
import "./B06_Section_UI_Style_Cross_Areas.css";
import { loadSectionDetail, replaceSectionDetail } from "./B06_Section_Section_Store";
import "@util/common_util_mass_haul.css";
function L(key: keyof typeof ui_locales): string {
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Section_Common.ts
* B06_Section_UI_Section_Common.ts
* · SVG ··.
*
* 700 .
@@ -7,13 +7,13 @@
* . .
* ========================================================================== */
import type { CrossDesignChange } from "./B06_wf3_ProfileCross_UI_Cross_Design";
import type { CrossDesignChange } from "./B06_Section_UI_Cross_Design";
import type {
DesignProfile,
LongitudinalSection,
SectionDetailResponse,
SectionSample,
} from "./B06_wf3_ProfileCross_Api_Fetch";
} from "./B06_Section_Api_Fetch";
export type { CrossDesignChange };
export type DesignChangeHandler = (chainageM: number, change: CrossDesignChange) => void;
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Section_View.ts
* B06_Section_UI_Section_View.ts
* · 컨트롤러: 상단 ( + ) ,
* /, (ResizeObserver), , ,
* .
@@ -16,25 +16,25 @@
import { createPanelResizer } from "@ui/ui_template_resizer";
import { createWorkflowPanelHandle } from "@ui/ui_template_overlay";
// 가로 스크롤 고정 Y축 — B05와 같은 오버레이를 쓴다(정의처: B05 MassHaul 모듈 + 그 CSS).
import { buildStickyYAxis } from "../B05_wf2_Route/B05_wf2_Route_UI_Profile_MassHaul";
import { buildStickyYAxis } from "../B05_Profile/B05_Profile_UI_Profile_MassHaul";
import type {
CrossSection,
EarthworkConversion,
HaulEquipmentLimit,
SectionDetailResponse,
} from "./B06_wf3_ProfileCross_Api_Fetch";
import type { RockBoundaryControl } from "./B06_wf3_ProfileCross_UI_Cross_Design";
import type { CrossAreaKey } from "./B06_wf3_ProfileCross_UI_Cross_Areas";
} from "./B06_Section_Api_Fetch";
import type { RockBoundaryControl } from "./B06_Section_UI_Cross_Design";
import type { CrossAreaKey } from "./B06_Section_UI_Cross_Areas";
import {
createCrossSectionCard,
crossCardNaturalHeight,
type CrossCardElement,
type StationWidthControl,
} from "./B06_wf3_ProfileCross_UI_Cross_View";
} from "./B06_Section_UI_Cross_View";
import {
createLongitudinalProfile,
longitudinalMinimumWidth,
} from "./B06_wf3_ProfileCross_UI_Longitudinal";
} from "./B06_Section_UI_Longitudinal";
import {
applyLegendToggle,
computeMassHaulSeries,
@@ -70,7 +70,7 @@ import {
longitudinalMaxChainage,
LONG_PAD,
type YScaleOptions,
} from "./B06_wf3_ProfileCross_UI_Section_Common";
} from "./B06_Section_UI_Section_Common";
export type { CrossDesignChange, DesignChangeHandler, RockBoundaryControl };
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Standard_Diagram.ts
* B06_Section_UI_Standard_Diagram.ts
* "변수 위치 안내" .
*
* (···/ ·)
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Standard_Panel.ts
* B06_Section_UI_Standard_Panel.ts
* "표준 횡단면 설정" ( / / 3).
*
* ·· · . config
@@ -13,14 +13,14 @@
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
import { createButton, createInputField, createSelectField } from "@ui/ui_template_elements";
import { buildStandardDiagram } from "./B06_wf3_ProfileCross_UI_Standard_Diagram";
import { buildStandardDiagram } from "./B06_Section_UI_Standard_Diagram";
import {
getCompanyStandard,
listCompanyStandards,
type StandardCrossGroup,
type StandardCrossKey,
type StandardCrossSection,
} from "./B06_wf3_ProfileCross_Api_Fetch";
} from "./B06_Section_Api_Fetch";
function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Style.css
* B06_Section_UI_Style.css
* 3차 워크플로우(·횡단 생성) 페이지 전용 스타일.
*
* 원칙(frontend.md §1): 하드코딩 색상 금지. theme.css 변수(var(--...)) 참조.
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Style_Cross.css
* B06_Section_UI_Style_Cross.css
* ·횡단 도면 (섹션·카드·차트·설계 컨트롤·/·오버레이) 스타일.
* 700줄 제한 대응으로 _UI_Style.css에서 분리(패널//모식도는 원본 유지).
* ========================================================================== */
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Style_Cross_Areas.css
* B06_Section_UI_Style_Cross_Areas.css
* 횡단 카드의 **면적 오버레이 계열** 스타일 ·성토 면적 , 면적 강조 밴드,
* 설계선· 경계선·포장층, 경계선 제어 버튼.
*
@@ -12,9 +12,9 @@ from fastapi import APIRouter
from fastapi.responses import JSONResponse
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Engine_Sections import prune_stale_cross_files
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Engine_Design import compute_cross_design
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import (
from B05_Profile.B05_Profile_Engine_Sections import prune_stale_cross_files
from B06_Section.B06_Section_Engine_Design import compute_cross_design
from B06_Section.B06_Section_Repository import (
get_confirmed_route_context,
get_cross_section_design,
get_longitudinal_section,
+2 -2
View File
@@ -21,12 +21,12 @@ from uuid import UUID
from pyproj import Transformer
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
from B05_wf2_Route.B05_wf2_Route_Repository import (
from B05_Profile.B05_Profile_Repository import (
get_latest_route,
get_route_points,
get_surface_crs_epsg,
)
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Repository import get_longitudinal_section
from B06_Section.B06_Section_Repository import get_longitudinal_section
from common_util.common_util_route_geometry import (
RouteVertex,
build_route_vertices,
+1 -1
View File
@@ -1,5 +1,5 @@
/* =============================================================================
* B06_wf3_ProfileCross_UI_Style_MassHaul.css
* B06_Section_UI_Style_MassHaul.css
* 유토곡선(Mass Haul Diagram) 스타일 — 곡선·오버레이 범례·요약줄.
* 700줄 제한 대응으로 _UI_Style_Cross.css에서 분리(종·횡단 도면 본체는 원본 유지).
*
+1 -1
View File
@@ -374,7 +374,7 @@ function crossAreaSamples(sections: MassHaulSection[], minSlope?: number): AreaS
* 종단 기준 — 계획고와 지반고의 차이(절토고/성토고)에 노반폭을 곱한 개략 단면적.
*
* `difference_m = 계획고 − 지반고`이므로 음수면 절토, 양수면 성토다
* (`B05_wf2_Route_UI_Profile_Alignment.ts`가 이미 쓰는 부호 규약과 같다).
* (`B05_Profile_UI_Profile_Alignment.ts`가 이미 쓰는 부호 규약과 같다).
* 노반폭과 지반유형은 종단 샘플에 없으므로 **가장 가까운 설계 측점**에서 가져온다.
* 설계된 측점이 하나도 없으면 노반폭을 알 수 없어 곡선을 만들지 않는다.
*
+3 -3
View File
@@ -9,9 +9,9 @@ PROJECT_STORAGE_LAYOUT_V2 = (
("B03_FileInput", "input"),
("B04_PreProcess", "processed"),
("B04_PreProcess", "models"),
("B05_wf2_Route", "route"),
("B06_wf3_ProfileCross", "longitudinal"),
("B06_wf3_ProfileCross", "cross_sections"),
("B05_Profile", "route"),
("B06_Section", "longitudinal"),
("B06_Section", "cross_sections"),
("B07_wf4_DesignDetail", "structures"),
("B08_wf5_Quantity", "quantities"),
("B09_wf6_Estimation", "v1"),
+4 -4
View File
@@ -85,8 +85,8 @@ export const ROUTES = {
B02_PROJ_REGISTER: "b02-proj-register",
B03_FILE_INPUT: "b03-file-input",
B04_PREPROCESS: "b04-preprocess",
B05_WF2_ROUTE: "b05-wf2-route",
B06_WF3_PROFILE_CROSS: "b06-wf3-profile-cross",
B05_PROFILE: "b05-profile",
B06_SECTION: "b06-section",
B07_WF4_DESIGN_DETAIL: "b07-wf4-design-detail",
B08_WF5_QUANTITY: "b08-wf5-quantity",
B09_WF6_ESTIMATION: "b09-wf6-estimation",
@@ -108,8 +108,8 @@ export const PROTECTED_ROUTES: readonly RoutePath[] = [
ROUTES.B02_PROJ_REGISTER,
ROUTES.B03_FILE_INPUT,
ROUTES.B04_PREPROCESS,
ROUTES.B05_WF2_ROUTE,
ROUTES.B06_WF3_PROFILE_CROSS,
ROUTES.B05_PROFILE,
ROUTES.B06_SECTION,
ROUTES.B07_WF4_DESIGN_DETAIL,
ROUTES.B08_WF5_QUANTITY,
ROUTES.B09_WF6_ESTIMATION,
+4 -4
View File
@@ -314,7 +314,7 @@ DRAINAGE_ARROW_MIN_COVERAGE = float(os.getenv("DRAINAGE_ARROW_MIN_COVERAGE", "0.
# 방향 일치도 하한(원형 평균 결과 길이 0~1). 블록 안 방향이 제각각이면 평균이 무의미하므로 버린다.
DRAINAGE_ARROW_MIN_AGREEMENT = float(os.getenv("DRAINAGE_ARROW_MIN_AGREEMENT", "0.7"))
# 단계별 검증 산출물은 같은 폴더에 `{번호}_{단계}.geojson` + `manifest.json`으로 쌓인다.
# 파일명 규칙은 B05_wf2_Route_Engine_Watershed_Export.STAGES가 유일한 정의처다.
# 파일명 규칙은 B05_Profile_Engine_Watershed_Export.STAGES가 유일한 정의처다.
# ── 확률강우량 (map.wamis.go.kr 등우선도) ──
# 전국 공통 등우선 GeoJSON 원본을 이 폴더에 1회 내려받아 영구 캐시한다(약 20~30MB).
@@ -406,7 +406,7 @@ FOREST_ROAD_MIN_WIDTH_M = {"trunk": 3.0, "branch": 3.0, "work": 2.5}
# 단위: 길이 m, 경사비 수평:수직=ratio:1, 횡단경사 %.
#
# 이 상수가 **표준 횡단면 기하의 유일한 정의처**다. B06 설정 패널 기본값과
# 절·성토 단면적 엔진(B06_wf3_ProfileCross_Engine_Design)이 모두 여기를 읽으며,
# 절·성토 단면적 엔진(B06_Section_Engine_Design)이 모두 여기를 읽으며,
# 아래 5-4-2의 파생 상수 외에 같은 값을 별도로 정의하지 않는다.
# ─────────────────────────────────────────────────────────────────────────
STANDARD_CROSS_SECTION = {
@@ -688,8 +688,8 @@ PROJECT_STORAGE_STAGE_DIRS = frozenset(
{
"B03_FileInput",
"B04_PreProcess",
"B05_wf2_Route",
"B06_wf3_ProfileCross",
"B05_Profile",
"B06_Section",
"B07_wf4_DesignDetail",
"B08_wf5_Quantity",
"B09_wf6_Estimation",
+3 -3
View File
@@ -292,7 +292,7 @@ CREATE TABLE IF NOT EXISTS routes (
grade_percent JSON, -- [2.5, 1.8, 3.2, ...] 각 구간 경사도
constraints JSON, -- max_grade, min_radius, avoidance_zones
algorithm_params JSON, -- cost_weights, ...
route_data_path VARCHAR(500), -- storage/.../B05_wf2_Route/route/...
route_data_path VARCHAR(500), -- storage/.../B05_Profile/route/...
computed_at TIMESTAMP NULL DEFAULT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -333,7 +333,7 @@ CREATE TABLE IF NOT EXISTS longitudinal_sections (
route_id INT NOT NULL, -- FK는 later
computed_at TIMESTAMP NULL DEFAULT NULL,
data JSON, -- chainages, elevations, grades, design_elevations
longitudinal_file_path VARCHAR(500), -- storage/.../B06_wf3_ProfileCross/longitudinal/...
longitudinal_file_path VARCHAR(500), -- storage/.../B06_Section/longitudinal/...
status VARCHAR(50) DEFAULT 'DRAFT' -- DRAFT, CONFIRMED
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -345,7 +345,7 @@ CREATE TABLE IF NOT EXISTS cross_sections (
chainage_m FLOAT,
sequence_num INT,
data JSON, -- left_slope, right_slope, width_m, cut_volume_m3, fill_volume_m3, structures, notes
cross_section_file_path VARCHAR(500), -- storage/.../B06_wf3_ProfileCross/cross_sections/...
cross_section_file_path VARCHAR(500), -- storage/.../B06_Section/cross_sections/...
status VARCHAR(50) DEFAULT 'DRAFT' -- DRAFT, CONFIRMED
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+3 -3
View File
@@ -35,9 +35,9 @@ from B04_PreProcess.B04_PreProcess_Router_GIS import router as b04_surface_gis_r
from B04_PreProcess.B04_PreProcess_Router_GIS import tiles_router
from B04_PreProcess.B04_PreProcess_Router_Inflow import router as b04_inflow_router
from B04_PreProcess.B04_PreProcess_Router_Watershed import router as b04_watershed_router
from B05_wf2_Route.B05_wf2_Route_Router import router as b05_route_router
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router import router as b06_section_router
from B06_wf3_ProfileCross.B06_wf3_ProfileCross_Router_Confirm import (
from B05_Profile.B05_Profile_Router import router as b05_route_router
from B06_Section.B06_Section_Router import router as b06_section_router
from B06_Section.B06_Section_Router_Confirm import (
router as b06_section_confirm_router,
)
from B07_wf4_DesignDetail.B07_wf4_DesignDetail_Router import router as b07_design_router
+1 -1
View File
@@ -2,7 +2,7 @@
target-version = "py313"
line-length = 100
# 폐기 엔진 보관 폴더는 이동 당시 원본 그대로 두기로 했으므로 린트/포맷 대상에서 뺀다.
extend-exclude = ["B05_wf2_Route/_legacy_watershed"]
extend-exclude = ["B05_Profile/_legacy_watershed"]
[tool.ruff.lint]
select = ["E", "F", "I"]
+4 -4
View File
@@ -1,6 +1,6 @@
/* =============================================================================
* ui_template_locale_b2.ts
* B (B05_wf2_Route ~ B11_Status)
* B (B05_Profile ~ B11_Status)
*
* ui_template_locale_common.ts WF_Step_*
* ( ).
@@ -11,7 +11,7 @@
import type { LocaleEntry } from "./ui_template_locale_common";
export const ui_locales_b2 = {
/* --- B05_wf2_Route 배수유역도 패널 (하단 패널 안쪽 우측) --- */
/* --- B05_Profile 배수유역도 패널 (하단 패널 안쪽 우측) --- */
B05_Drainage_Title: ["배수유역도", "Drainage Basins"],
B05_Drainage_Layer_Satellite: ["위성사진", "Satellite"],
B05_Drainage_Layer_Satellite_Tip: [
@@ -92,7 +92,7 @@ export const ui_locales_b2 = {
/* {chainage}=측점 누가거리(m) */
B05_Drainage_Basin_Chainage: ["측점 누가거리 {chainage}m", "Station chainage {chainage}m"],
/* --- B05_wf2_Route 경로 설계 --- */
/* --- B05_Profile 경로 설계 --- */
B05_Route_Title: ["종단설계", "Profile Design"],
B05_Route_Group_Points: ["경로 제어점", "Route Control Points"],
B05_Route_Point_BP: ["시점 (BP)", "Begin (BP)"],
@@ -150,7 +150,7 @@ export const ui_locales_b2 = {
B05_Route_Field_StationLines: ["측점 가로선", "Station cross lines"],
B05_Route_Field_StationLabels: ["측점 라벨", "Station labels"],
/* --- B06_wf3_ProfileCross 종·횡단 생성 --- */
/* --- B06_Section 종·횡단 생성 --- */
B06_Profile_Title: ["횡단설계", "Cross Design"],
B06_Profile_Group_Route: ["대상 경로", "Target Route"],
B06_Profile_Field_RouteId: ["경로 ID (routes.id)", "Route ID"],