빈 도각이던 계획평면도(지형·노선배치도·배치도)를 실제 도면으로 만듦. - 배경 공용화: map_background() 한 창구로 도엽 등고선·세류선 읽기·좌표 환산· 도곽 절취를 모음. 유역도와 계획평면도가 같은 것을 부르고, 환산 결과는 파일 mtime 을 키로 캐시(_metric_lines_cached). - 축척 1/1,200 고정(DRAWING_SCALE_PLAN) — 지식DB 「설계제원_총괄」 측량·도면 기준. 횡단면도와 같은 원칙으로 줄이지 않고 안 들어가면 장을 나눔(plan_chunks, 종단 측점 기준·경계 측점 1개 중복). - 세 장이 같은 배경·같은 도곽 배치를 쓰고 주제만 다름. 측점 눈금은 종단 측점 좌표로 찍고, 구조물은 pipe_points.json 정본을 읽어 마름모+이름으로 표기. - 도면 목록·단건 조회에 kind="plan" 추가. 화면 목록은 id 접두어로 묶어 장이 나뉘어도 한 그룹으로 보임. 검증(용화_LAS): 콘텐츠 734.3x489.2 mm ≤ A1 작도영역 739.2x499.2, 노선 실거리 630.1x214.2 m → 종이 525.12x178.46 mm(실측 0.83333 mm/m = 1/1,200 일치), 배경 등고선 180줄이 세 장 동일, 유역도 8.5초 → 계획평면도 1.3초(캐시 적중, 파일 재읽기 없음). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
210 lines
5.5 KiB
TypeScript
210 lines
5.5 KiB
TypeScript
/* B07 상세 설계 도면 목록·단건 API 클라이언트. */
|
|
|
|
import { API_BASE_URL, API_TIMEOUT_MS } from "@config/config_frontend";
|
|
|
|
export interface DesignDrawingItem {
|
|
id: string;
|
|
// blank: 아직 내용을 만들지 않은 도면 — 도각만 실려 온다.
|
|
kind:
|
|
| "cover"
|
|
| "longitudinal"
|
|
| "cross"
|
|
| "mass_haul"
|
|
| "watershed"
|
|
| "plan"
|
|
| "blank";
|
|
label: string;
|
|
chainage_m: number | null;
|
|
confirmed: boolean;
|
|
}
|
|
|
|
export interface CadDrawing {
|
|
entities: Record<string, unknown>[];
|
|
/** 횡단도 측점별 실좌표(m) → 종이(mm) 변환값. 구조물을 같은 자리에 얹는 데 쓴다. */
|
|
cross_placements?: {
|
|
chainage_m: number;
|
|
ox: number;
|
|
oy: number;
|
|
dy: number;
|
|
mm_per_m: number;
|
|
x0: number;
|
|
x1: number;
|
|
}[];
|
|
layers: {
|
|
id: string;
|
|
name: string;
|
|
isVisible: boolean;
|
|
isLocked: boolean;
|
|
}[];
|
|
}
|
|
|
|
export interface DesignDrawingListResponse {
|
|
status: string;
|
|
project_id: string;
|
|
route_id: number;
|
|
drawings: DesignDrawingItem[];
|
|
}
|
|
|
|
/** 수량 산출표 값 (미산정 항목은 null). 백엔드 `_quantity_table`의 키와 대응. */
|
|
export type QuantityTable = Record<string, number | null>;
|
|
|
|
/** 측구 형식별 규격 (B06 엔진 ditch_spec 신구조와 1:1). */
|
|
export type DitchSpec =
|
|
| { type: "none" }
|
|
| {
|
|
type: "standard";
|
|
top_width_m: number;
|
|
bottom_width_m: number;
|
|
depth_m: number;
|
|
}
|
|
| { type: "l_type"; width_m: number; depth_m: number };
|
|
|
|
/** B06에서 지정한 설계(지반정보·계획정보). 횡단도에만 존재. status로 잠정/확정 구분. */
|
|
export interface CrossDesignInfo {
|
|
ground_type: "soil" | "ripping_rock" | "blasting_rock";
|
|
geometry_preset: "soil" | "rock";
|
|
section_mode: "left_cut" | "right_cut" | "both_cut" | "both_fill";
|
|
ditch_side: "left" | "right";
|
|
ditch_type?: "standard" | "l_type" | null;
|
|
ditch_enabled?: boolean;
|
|
cut_slope_ratio: number;
|
|
fill_slope_ratio: number;
|
|
roadbed_width_m: number;
|
|
carriageway_width_m?: number;
|
|
cross_slope_pct?: number;
|
|
paved?: boolean;
|
|
ditch: DitchSpec;
|
|
road_edges?: Record<
|
|
"left" | "right",
|
|
{ offset_m: number; elevation_m: number }
|
|
>;
|
|
design_elevation_m: number;
|
|
cut_area_m2: number;
|
|
fill_area_m2: number;
|
|
status?: "provisional" | "confirmed";
|
|
}
|
|
|
|
export interface DesignDrawingResponse {
|
|
status: string;
|
|
project_id: string;
|
|
route_id: number;
|
|
id: string;
|
|
// blank: 아직 내용을 만들지 않은 도면 — 도각만 실려 온다.
|
|
kind:
|
|
| "cover"
|
|
| "longitudinal"
|
|
| "cross"
|
|
| "mass_haul"
|
|
| "watershed"
|
|
| "plan"
|
|
| "blank";
|
|
label: string;
|
|
drawing: CadDrawing;
|
|
confirmed: boolean;
|
|
quantity_table?: QuantityTable | null;
|
|
design?: CrossDesignInfo | null;
|
|
}
|
|
|
|
export interface DesignDrawingConfirmResponse {
|
|
status: string;
|
|
project_id: string;
|
|
id: string;
|
|
confirmed: boolean;
|
|
all_confirmed: boolean;
|
|
design?: CrossDesignInfo | null;
|
|
}
|
|
|
|
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" },
|
|
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);
|
|
}
|
|
}
|
|
|
|
export function fetchDesignDrawingList(
|
|
projectId: string,
|
|
): Promise<DesignDrawingListResponse> {
|
|
return requestJson(`/projects/${projectId}/design-drawings`);
|
|
}
|
|
|
|
export function fetchDesignDrawing(
|
|
projectId: string,
|
|
drawingId: string,
|
|
): Promise<DesignDrawingResponse> {
|
|
return requestJson(
|
|
`/projects/${projectId}/design-drawings/${encodeURIComponent(drawingId)}`,
|
|
);
|
|
}
|
|
|
|
export function confirmDesignDrawing(
|
|
projectId: string,
|
|
drawingId: string,
|
|
drawing: CadDrawing,
|
|
quantityTable?: QuantityTable | null,
|
|
): Promise<DesignDrawingConfirmResponse> {
|
|
return requestJson(
|
|
`/projects/${projectId}/design-drawings/${encodeURIComponent(drawingId)}/confirm`,
|
|
{
|
|
method: "PUT",
|
|
body: JSON.stringify({ drawing, quantity_table: quantityTable ?? null }),
|
|
},
|
|
);
|
|
}
|
|
|
|
export function invalidateDesignDrawing(
|
|
projectId: string,
|
|
drawingId: string,
|
|
): Promise<void> {
|
|
return requestJson(
|
|
`/projects/${projectId}/design-drawings/${encodeURIComponent(drawingId)}/invalidate`,
|
|
{ method: "POST" },
|
|
);
|
|
}
|
|
|
|
/** 도각 편집 화면이 싣는 도각 한 장 (실치수 1:1, 잠금 없는 도면층). */
|
|
export interface FrameTemplateResponse {
|
|
status: string;
|
|
project_id: string;
|
|
drawing: CadDrawing;
|
|
/** 회사가 고친 도각을 쓰고 있으면 true, 프로그램 기본 도각이면 false. */
|
|
customized: boolean;
|
|
}
|
|
|
|
export function fetchFrameTemplate(
|
|
projectId: string,
|
|
): Promise<FrameTemplateResponse> {
|
|
return requestJson(`/projects/${projectId}/frame-template`);
|
|
}
|
|
|
|
export function saveFrameTemplate(
|
|
projectId: string,
|
|
drawing: CadDrawing,
|
|
): Promise<void> {
|
|
return requestJson(`/projects/${projectId}/frame-template`, {
|
|
method: "PUT",
|
|
body: JSON.stringify({ drawing }),
|
|
});
|
|
}
|
|
|
|
/** 회사 도각을 지우고 프로그램 기본 도각으로 되돌린다. */
|
|
export function resetFrameTemplate(projectId: string): Promise<void> {
|
|
return requestJson(`/projects/${projectId}/frame-template`, {
|
|
method: "DELETE",
|
|
});
|
|
}
|