- 종단 상단 표시줄을 법정 판정 3항목으로 축소: 최대 기울기/상한(별표2 Ⅰ.2.라),
절·성토 불균형(Ⅰ.1.나.(4)(다)), 종단곡선 필요 n곳(Ⅰ.2.마 — 대수차 5% 초과인데
곡선이 빠진 변화점). 변화점·종단곡선 개수·기본 곡선길이 L·중복 경고칩은 제거.
- [초기선 복원] → [편집 되돌리기] — 실제 동작은 화면 편집 델타 삭제이지 저장 지점
복귀가 아니다. 툴팁에 동작과 [초기화]와의 차이를 적었다.
- 3D 뷰포트 안내 라벨 삭제(로딩 완료 후 남던 조작법 문구).
- 배수유역 버튼 정비: 유역 다시 나누기 / 선택한 관 삭제 / 배관 배치 초기화 +
각 툴팁에 실제 동작 명시(선택 삭제는 툴팁 자체가 없었다).
- 유토곡선 요약 12칩 → 5칩(절토·성토·잉여|부족·운반·검산). 토질 3종 내역·다짐
환산·블록 수·장거리 운반·사토는 해당 칩 툴팁으로. B05·B06 공용 함수라 동시 반영.
- 설계속도 축 도입(임도는 속도를 낼 수 없는 노선 — 기본 20km/h):
· 임도 종류는 프로젝트 등록값(projects.road_type)을 읽어 B05가 읽기 전용 표시
(main→간선임도, fire→산불진화임도, 그 외→작업임도). 화면에서 고치지 않는다.
· 설계속도 선택(간선·산불진화 20/30/40, 작업 20 고정) 신설 — 종단기울기 상한과
종단곡선 반경이 등급이 아니라 이 값으로 정해진다(지식DB 설계제원_총괄 §6·§7).
· B02 임도 종류 선택지를 현행 3종으로 정리(지선 폐지·계류보전은 사방 구분).
· 백엔드: ROUTE_GRADE_CLASSES 3종+branch 호환, selectable_design_speeds 신설,
resolve_design_speed 신설, legal_grade_limit_pct·resolve_grade_options에
설계속도 인자, sections/context가 road_type 제공(B06도 저장값 승계).
· 계획선 정책은 그릴 때마다 현재 기준으로 동기화 — 저장분의 옛 상한이 남지 않는다.
- 검증: pytest 125 통과(설계속도 12건 신규), tsc, 헤드 브라우저 — 진입 시 상한 9%,
40km/h 전환 시 7% 즉시 반영, 원복 9%, 안내문 '간선임도 · 설계속도 20km/h ·
일반지형', 유토곡선 5칩, 유역 버튼 새 이름·툴팁, 3D 라벨 빈 문자열 확인.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
508 lines
19 KiB
TypeScript
508 lines
19 KiB
TypeScript
/* =============================================================================
|
|
* B06_Section_Api_Fetch.ts
|
|
* 3차 워크플로우(종·횡단 생성) API 클라이언트
|
|
*
|
|
* 백엔드 계약 (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 → 종횡단 원시 샘플 조회
|
|
* POST /api/projects/{project_id}/sections/{route_id}/regenerate → 횡단 반폭 재생성
|
|
* POST /api/projects/{project_id}/sections/{route_id}/confirm → 종횡단 확정
|
|
*
|
|
* 규칙:
|
|
* - 모든 제어 상수는 config_frontend에서 참조 (하드코딩 금지).
|
|
* - 오류 응답 형식 {status:"error", message:"..."}을 Error로 변환.
|
|
* ========================================================================== */
|
|
|
|
import { API_BASE_URL, API_TIMEOUT_MS } from "@config/config_frontend";
|
|
import type {
|
|
BalloonOffsets,
|
|
EarthworkConversion,
|
|
GroundType,
|
|
HaulEquipmentLimit,
|
|
} from "@util/common_util_mass_haul_types";
|
|
|
|
export interface SectionOptionDefaults {
|
|
station_interval_m: number;
|
|
cross_half_width_m: number;
|
|
cross_sample_interval_m: number;
|
|
long_sample_interval_m: number;
|
|
vertical_exaggeration: number;
|
|
}
|
|
|
|
/** 측구 규격(상단폭/저폭/깊이, m). */
|
|
export interface DitchSpec {
|
|
top_width_m: number;
|
|
bottom_width_m: number;
|
|
depth_m: number;
|
|
}
|
|
|
|
/** 지반그룹 하나의 표준 횡단면 기본값 (config STANDARD_CROSS_SECTION 사본). */
|
|
export interface StandardCrossGroup {
|
|
road_width_m: number;
|
|
shoulder_left_m: number;
|
|
shoulder_right_m: number;
|
|
ditch: DitchSpec;
|
|
/** 암 그룹만 존재: L형 측구(폭/깊이, m). */
|
|
ditch_l_type?: { width_m: number; depth_m: number };
|
|
cross_slope_pct: { min: number; max: number };
|
|
fill_slope_ratio: number;
|
|
cut_slope_ratio: number;
|
|
/** 포장 그룹만 존재: 포장층 두께(m). */
|
|
pavement_thickness_m?: number;
|
|
}
|
|
|
|
/** 표준 횡단면 설정 패널 그룹 키. */
|
|
export type StandardCrossKey = "soil" | "rock" | "paved";
|
|
|
|
export type StandardCrossSection = Record<StandardCrossKey, StandardCrossGroup>;
|
|
|
|
// 지반유형·토량환산계수·운반장비 한계거리는 B05 계획 유토곡선과 공유하므로 정의처를
|
|
// `@util/common_util_mass_haul_types` 한 곳에 두고 여기서는 재수출만 한다(사본 금지).
|
|
export type {
|
|
GroundType,
|
|
EarthworkConversionFactor,
|
|
EarthworkConversion,
|
|
HaulEquipmentLimit,
|
|
BalloonOffsets,
|
|
} from "@util/common_util_mass_haul_types";
|
|
|
|
export interface SectionContextResponse {
|
|
project_id: string;
|
|
route_id: number | null;
|
|
filter_key: string | null;
|
|
method: string | null;
|
|
smooth: boolean | null;
|
|
crs_epsg: number | null;
|
|
/** 프로젝트 등록(B02)에서 정한 임도 종류 — B05 계획선 법정 기준의 출발점(2026-08-19). */
|
|
road_type: string | null;
|
|
defaults: SectionOptionDefaults;
|
|
/** 표준 횡단면 설정 패널(토사/암/포장) 기본값. */
|
|
standard_cross_section: StandardCrossSection;
|
|
/** 암 경계선 기본 오프셋(m)과 상/하 제어 스텝(m). */
|
|
rock_boundary_default_offset_m: number;
|
|
rock_boundary_step_m: number;
|
|
/** 지반유형별 토량환산계수. 유토곡선은 프론트가 이 값으로 계산한다. */
|
|
earthwork_conversion: EarthworkConversion;
|
|
/** 평균운반거리별 운반장비 경계. 유토곡선의 토량 분배가 이 값으로 장비를 고른다. */
|
|
haul_equipment_limits?: HaulEquipmentLimit[];
|
|
/**
|
|
* 자연방토 판정 경사(rise/run). 성토측 자연 지반이 이보다 가파르면 흙이 스스로 흘러내려
|
|
* 운반비를 세지 않는다. 못 받으면 프론트는 **자연방토 없음**으로 본다(보수적).
|
|
*/
|
|
natural_spoil_min_ground_slope?: number | null;
|
|
}
|
|
|
|
/** 종단 요약 조회 결과 (SectionSummaryResponse) */
|
|
export interface SectionSummaryResponse {
|
|
status: string;
|
|
project_id: string;
|
|
route_id: number;
|
|
longitudinal: Record<string, unknown> | null;
|
|
length_m: number | null;
|
|
cross_section_count: number;
|
|
}
|
|
|
|
export interface SectionSample {
|
|
chainage_m?: number;
|
|
offset_m?: number;
|
|
elevation_m?: number | null;
|
|
z?: number | null;
|
|
valid: boolean;
|
|
}
|
|
|
|
export interface SectionStation {
|
|
station_id: string;
|
|
chainage_m: number;
|
|
label: string;
|
|
/** irregular = 사용자가 구조물용으로 추가한 비정규 측점(프론트 주입, 백엔드 미영속). */
|
|
kind: "bp" | "ep" | "regular" | "irregular";
|
|
/** 비정규 측점의 구조물 설명(백엔드가 확정 시 부여). 복귀 시 사이드바 목록 복원에 쓴다. */
|
|
structure?: string;
|
|
center_z: number | null;
|
|
azimuth_deg: number | null;
|
|
center_x: number;
|
|
center_y: number;
|
|
/** 횡단 기준 등고가 높은 쪽(측구 설계 기본 방향). B05 solve 자동 판정 + 사용자 변경. */
|
|
uphill_side?: "left" | "right" | null;
|
|
frame: { left_xy: [number, number] };
|
|
}
|
|
|
|
/** 계획선 샘플 (계획고와 지반고, 그 차이). */
|
|
export interface DesignProfileSample {
|
|
chainage_m: number;
|
|
elevation_m: number;
|
|
ground_elevation_m: number;
|
|
difference_m: number;
|
|
}
|
|
|
|
/** 절·성토 균형을 판정하는 구역 단위 결과. */
|
|
export interface DesignProfileSegment {
|
|
index: number;
|
|
start_chainage_m: number;
|
|
end_chainage_m: number;
|
|
cut_area_m2: number;
|
|
fill_area_m2: number;
|
|
balance_error_m2: number;
|
|
}
|
|
|
|
export interface DesignProfileSummary {
|
|
cut_area_m2: number;
|
|
fill_area_m2: number;
|
|
balance_error_m2: number;
|
|
max_grade_pct: number;
|
|
vertical_curve_count: number;
|
|
pvi_count: number;
|
|
balance_segment_count: number;
|
|
balanced: boolean;
|
|
main_direction: string;
|
|
suggested_elevation_offset_m: number | null;
|
|
warnings: string[];
|
|
}
|
|
|
|
/**
|
|
* 종단 계획선. 횡단 설계 기반 계획선이 추가될 수 있어 배열로 전달된다.
|
|
*/
|
|
export interface DesignProfile {
|
|
id: string;
|
|
name: string;
|
|
basis: string;
|
|
samples: DesignProfileSample[];
|
|
balance_segments: DesignProfileSegment[];
|
|
summary: DesignProfileSummary;
|
|
}
|
|
|
|
export interface LongitudinalSection {
|
|
length_m: number;
|
|
samples: SectionSample[];
|
|
stations: SectionStation[];
|
|
design_profiles?: DesignProfile[];
|
|
/**
|
|
* 계획선 변화점(PVI) 구조. B05가 편집 기준선으로 사용하며 `design_profiles`는
|
|
* 여기서 파생된다. 구 데이터에는 없을 수 있어 optional이다.
|
|
* 구조는 `B05_Profile_UI_Profile_Alignment.ProfileAlignment`.
|
|
*/
|
|
profile_alignment?: unknown;
|
|
}
|
|
|
|
export interface CrossSection extends SectionStation {
|
|
samples: SectionSample[];
|
|
/** DB에 저장된 잠정 설계 지정(있을 때만). 상세 조회 시 얹혀 온다. */
|
|
design?: CrossDesign;
|
|
}
|
|
|
|
export interface SectionDetailResponse {
|
|
longitudinal: LongitudinalSection;
|
|
cross_sections: CrossSection[];
|
|
/** 확정 시 저장해 둔 유토곡선 balloon 위치. 브라우저가 바뀌어도 같은 자리에 뜬다. */
|
|
balloon_offsets?: BalloonOffsets | null;
|
|
}
|
|
|
|
/** 종횡단 확정 결과 (SectionConfirmResponse) */
|
|
export interface SectionConfirmResponse {
|
|
status: string;
|
|
project_id: string;
|
|
route_id: number;
|
|
confirmed: boolean;
|
|
}
|
|
|
|
/** 측점 표준횡단 설계 지정값 (버튼 상태). */
|
|
export type SectionMode = "left_cut" | "right_cut" | "both_cut" | "both_fill";
|
|
export type DitchSide = "left" | "right";
|
|
export type DitchType = "standard" | "l_type";
|
|
|
|
/** 측점 표준횡단 설계 계산 결과(잠정치). data.design에 저장되는 구조와 동일. */
|
|
export interface CrossDesign {
|
|
ground_type: GroundType;
|
|
geometry_preset: "soil" | "rock";
|
|
section_mode: SectionMode;
|
|
ditch_side: DitchSide;
|
|
/** 측구 형식(일반/L형). 양성(측구 없음)은 null. */
|
|
ditch_type: DitchType | null;
|
|
cut_slope_ratio: number;
|
|
/** 2단계 절토의 토사(상단) 경사비. 암 지반에서만 의미. */
|
|
soil_cut_slope_ratio?: number;
|
|
/** 암반 경계 기준 2단계 경사 적용 여부(엔진이 실제 적용했는지). */
|
|
two_stage_slope?: boolean;
|
|
fill_slope_ratio: number;
|
|
roadbed_width_m: number;
|
|
carriageway_width_m: number;
|
|
cross_slope_pct: number;
|
|
ditch:
|
|
| { type: "standard"; top_width_m: number; bottom_width_m: number; depth_m: number }
|
|
| { type: "l_type"; width_m: number; depth_m: number }
|
|
| { type: "none" };
|
|
/** 측구 생성 여부(엔진이 자동/override 반영해 실제 적용한 결과). */
|
|
ditch_enabled?: boolean;
|
|
/** 포장 중첩 여부와 포장층 두께(포장 시). */
|
|
paved: boolean;
|
|
pavement_thickness_m?: number;
|
|
/** B05 법정 경사 분석의 포장 제안 여부(사용자 토글과 무관하게 유지). */
|
|
pavement_suggested?: boolean;
|
|
/** 노면(노견 포함) 양 끝점 — 노면 렌더링 기준. */
|
|
road_edges: {
|
|
left: { offset_m: number; elevation_m: number };
|
|
right: { offset_m: number; elevation_m: number };
|
|
};
|
|
/** 차도(노견 제외) 양 끝점 — 포장 범위 기준. */
|
|
carriageway_edges?: {
|
|
left: { offset_m: number; elevation_m: number };
|
|
right: { offset_m: number; elevation_m: number };
|
|
};
|
|
design_elevation_m: number;
|
|
cut_area_m2: number;
|
|
/**
|
|
* 절토 내역(합 = `cut_area_m2`). 지표면~암반 경계선이 토사, 그 아래가 암이다.
|
|
* 경계선을 올리내리면 두 값이 함께 바뀌고 유토곡선도 따라 움직인다.
|
|
* 구 데이터에는 없으므로 optional — 없으면 `cut_area_m2` 전량을 지반유형으로 본다.
|
|
*/
|
|
cut_soil_area_m2?: number;
|
|
cut_rock_area_m2?: number;
|
|
/** 암반부에 적용할 지반유형(`ripping_rock`/`blasting_rock`). 토사 측점은 null. */
|
|
cut_rock_kind?: GroundType | null;
|
|
fill_area_m2: number;
|
|
/** 성토측 자연 지반 경사(rise/run). 자연방토 판정 입력. 성토측이 없으면 null. */
|
|
fill_ground_slope?: number | null;
|
|
ditch_area_m2: number;
|
|
design_line: Array<{ offset_m: number; elevation_m: number }>;
|
|
/** 확정 시 병합되는 암 경계선 오프셋(m). 세션 값이 우선이며 복원 폴백으로 쓴다. */
|
|
rock_boundary_offset_m?: number;
|
|
/** 측점 개별 표시 반폭(m, 2026-08-06). 확정 시 병합되며 세션 값이 우선이다. */
|
|
display_half_width_m?: number;
|
|
}
|
|
|
|
export interface CrossDesignResponse {
|
|
status: string;
|
|
chainage_m: number;
|
|
design: CrossDesign;
|
|
}
|
|
|
|
export interface CrossDesignRequest {
|
|
chainage_m: number;
|
|
ground_type: GroundType;
|
|
section_mode: SectionMode;
|
|
ditch_side?: DitchSide | null;
|
|
/** 측구 형식(일반/L형). L형은 암 지반에서만 허용된다. */
|
|
ditch_type?: DitchType;
|
|
/** 포장 중첩 여부 — 횡단경사·포장층만 포장 그룹 값으로 계산된다. */
|
|
paved?: boolean;
|
|
/** 암 경계선 오프셋(m, 지면선 기준 하향 음수). 암 지반 2단계 절토 무릎 계산용. */
|
|
rock_boundary_offset_m?: number | null;
|
|
/** 암 지반 2단계 경사 적용 여부(기본 true, 토글로 해제). */
|
|
two_stage_slope?: boolean;
|
|
/** 측구 생성 여부. null/미지정=자동 판정, true/false=수동 override. */
|
|
ditch_enabled?: boolean | null;
|
|
/** 설정 패널 편집값. 요청값 → config 기본값 순으로 우선한다. */
|
|
standard_cross_section?: StandardCrossSection;
|
|
}
|
|
|
|
/** 확정 시 측점별 data.design에 병합할 프론트 세션 보관값. */
|
|
export interface CrossSectionPatch {
|
|
chainage_m: number;
|
|
rock_boundary_offset_m?: number;
|
|
/** 측점 개별 표시 반폭(m, 2026-08-06 사용자 지시). */
|
|
display_half_width_m?: number;
|
|
}
|
|
|
|
/** 공통 fetch 헬퍼: 타임아웃 + 인증 헤더 + 오류 응답 변환. */
|
|
async function requestJson<T>(path: string, init: RequestInit): Promise<T> {
|
|
const controller = new AbortController();
|
|
const timeoutId = window.setTimeout(() => controller.abort(), API_TIMEOUT_MS);
|
|
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(timeoutId);
|
|
}
|
|
}
|
|
|
|
/** 최신 확정 경로와 B04 확정값, config 기반 생성 기본값을 조회한다. */
|
|
export async function fetchSectionContext(projectId: string): Promise<SectionContextResponse> {
|
|
return requestJson<SectionContextResponse>(`/projects/${projectId}/sections/context`, {
|
|
method: "GET",
|
|
});
|
|
}
|
|
|
|
/** 경로의 종단면 요약을 조회한다. */
|
|
export async function getSections(
|
|
projectId: string,
|
|
routeId: number,
|
|
): Promise<SectionSummaryResponse> {
|
|
return requestJson<SectionSummaryResponse>(`/projects/${projectId}/sections/${routeId}`, {
|
|
method: "GET",
|
|
});
|
|
}
|
|
|
|
/** 경로의 SVG 렌더링용 종단·횡단 원시 샘플을 조회한다. */
|
|
export async function fetchSectionDetail(
|
|
projectId: string,
|
|
routeId: number,
|
|
): Promise<SectionDetailResponse> {
|
|
return requestJson<SectionDetailResponse>(`/projects/${projectId}/sections/${routeId}/detail`, {
|
|
method: "GET",
|
|
});
|
|
}
|
|
|
|
/** 횡단 반폭을 반영해 종횡단을 재생성·저장하고 갱신된 상세를 반환한다. */
|
|
export async function regenerateSections(
|
|
projectId: string,
|
|
routeId: number,
|
|
crossHalfWidthM: number,
|
|
): Promise<SectionDetailResponse> {
|
|
return requestJson<SectionDetailResponse>(
|
|
`/projects/${projectId}/sections/${routeId}/regenerate`,
|
|
{ method: "POST", body: JSON.stringify({ cross_half_width_m: crossHalfWidthM }) },
|
|
);
|
|
}
|
|
|
|
/** 측점 표준횡단 설계(지반유형·단면유형)를 즉시 계산·저장하고 잠정 결과를 반환한다. */
|
|
export async function computeCrossDesign(
|
|
projectId: string,
|
|
routeId: number,
|
|
request: CrossDesignRequest,
|
|
): Promise<CrossDesignResponse> {
|
|
return requestJson<CrossDesignResponse>(
|
|
`/projects/${projectId}/sections/${routeId}/cross-design`,
|
|
{ method: "POST", body: JSON.stringify(request) },
|
|
);
|
|
}
|
|
|
|
/** 경로의 종·횡단면을 확정한다. 표준 횡단면 설정값·측점별 세션 보관값을 함께 저장할 수 있다. */
|
|
export async function confirmSections(
|
|
projectId: string,
|
|
routeId: number,
|
|
standardCrossSection?: StandardCrossSection,
|
|
crossPatches?: CrossSectionPatch[],
|
|
/** 프론트에서 계산한 유토곡선 결과. 확정 시점에만 영구 저장한다. */
|
|
massHaul?: Record<string, unknown>,
|
|
): Promise<SectionConfirmResponse> {
|
|
const body: Record<string, unknown> = {};
|
|
if (standardCrossSection) body.standard_cross_section = standardCrossSection;
|
|
if (crossPatches?.length) body.cross_patches = crossPatches;
|
|
if (massHaul) body.mass_haul = massHaul;
|
|
return requestJson<SectionConfirmResponse>(`/projects/${projectId}/sections/${routeId}/confirm`, {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 편집 중인 종·횡단을 **확정하지 않고** 영구저장소에만 남긴다(임시 저장).
|
|
* 저장 내용은 확정과 같지만 경로 상태·워크플로 단계를 건드리지 않아 페이지 이동도 없다.
|
|
*/
|
|
export async function saveSections(
|
|
projectId: string,
|
|
routeId: number,
|
|
standardCrossSection?: StandardCrossSection,
|
|
crossPatches?: CrossSectionPatch[],
|
|
massHaul?: Record<string, unknown>,
|
|
): Promise<SectionConfirmResponse> {
|
|
const body: Record<string, unknown> = {};
|
|
if (standardCrossSection) body.standard_cross_section = standardCrossSection;
|
|
if (crossPatches?.length) body.cross_patches = crossPatches;
|
|
if (massHaul) body.mass_haul = massHaul;
|
|
return requestJson<SectionConfirmResponse>(`/projects/${projectId}/sections/${routeId}/save`, {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
/** 같은 회사에서 표준횡단 설계값을 불러올 수 있는 프로젝트 항목. */
|
|
export interface CompanyStandardProject {
|
|
project_id: string;
|
|
name: string;
|
|
}
|
|
|
|
export interface CompanyStandardListResponse {
|
|
status: string;
|
|
projects: CompanyStandardProject[];
|
|
}
|
|
|
|
export interface CompanyStandardResponse {
|
|
status: string;
|
|
project_id: string;
|
|
standard_cross_section: StandardCrossSection;
|
|
}
|
|
|
|
/** 같은 회사에서 설계값을 불러올 수 있는 프로젝트 목록을 조회한다(회사 스코프). */
|
|
export async function listCompanyStandards(
|
|
projectId: string,
|
|
): Promise<CompanyStandardListResponse> {
|
|
return requestJson<CompanyStandardListResponse>(
|
|
`/projects/${projectId}/sections/company-standards`,
|
|
{ method: "GET" },
|
|
);
|
|
}
|
|
|
|
/** 특정 프로젝트의 표준횡단 설계값을 미리보기용으로 조회한다(적용 전, 현재 값 불변). */
|
|
export async function getCompanyStandard(
|
|
projectId: string,
|
|
sourceProjectId: string,
|
|
): Promise<CompanyStandardResponse> {
|
|
return requestJson<CompanyStandardResponse>(
|
|
`/projects/${projectId}/sections/company-standards/${sourceProjectId}`,
|
|
{ method: "GET" },
|
|
);
|
|
}
|
|
|
|
/** 계획선 편집 델타(자동 선형 대비 계획고 델타 + 종단곡선 반경). B05 편집 스토어와 같은 모양. */
|
|
export interface ProfileAlignmentEdits {
|
|
station_offsets: Record<string, number>;
|
|
curve_radii: Record<string, number>;
|
|
}
|
|
|
|
/**
|
|
* 계획선 편집 프리뷰 결과 — **바뀌는 것만** 온다.
|
|
* 횡단 지반선 원시 샘플은 계획고와 무관해 그대로이므로 싣지 않는다(응답 1MB → 수십 KB).
|
|
*/
|
|
export interface CrossDesignPreviewResponse {
|
|
status: string;
|
|
/** 유토곡선이 읽는 설계 필드만 담긴 부분 갱신값(설계선 좌표 등은 오지 않는다). */
|
|
designs: Array<{ chainage_m: number; design: Partial<CrossDesign> }>;
|
|
}
|
|
|
|
/**
|
|
* 계획선 편집을 반영해 **계획선 + 전 측점 횡단 설계**를 다시 계산해 받는다(서버 저장 없음).
|
|
*
|
|
* B05에서 계획고를 끄는 동안 횡단 단면적이 함께 움직여야 횡단 기준 유토곡선이 따라온다.
|
|
* 측점마다 따로 부르면 수십 번 왕복하므로 한 번에 계산한다. 영속화는 각 페이지의
|
|
* 임시저장·확정이 맡는다.
|
|
*/
|
|
export async function previewCrossDesigns(
|
|
projectId: string,
|
|
routeId: number,
|
|
edits: ProfileAlignmentEdits,
|
|
standardCrossSection?: StandardCrossSection,
|
|
options?: {
|
|
/** true면 설계 전체(설계선 좌표 포함)를 받는다 — B06 진입 시 stale 일괄 재계산용. */
|
|
fullDesigns?: boolean;
|
|
/** 측점별 암 경계 오프셋 세션값(chainage 키 → m). DB 저장분보다 우선한다. */
|
|
rockBoundaryOffsets?: Record<string, number>;
|
|
},
|
|
): Promise<CrossDesignPreviewResponse> {
|
|
return requestJson<CrossDesignPreviewResponse>(
|
|
`/projects/${projectId}/sections/${routeId}/cross-design/preview`,
|
|
{
|
|
method: "POST",
|
|
body: JSON.stringify({
|
|
...edits,
|
|
standard_cross_section: standardCrossSection ?? null,
|
|
full_designs: options?.fullDesigns ?? false,
|
|
rock_boundary_offsets: options?.rockBoundaryOffsets ?? null,
|
|
}),
|
|
},
|
|
);
|
|
}
|