/* ============================================================================= * B05_Profile_UI_Panel_Plan.ts * 종단 보기 왼쪽 「위치」 · 「그리기」 칸 자리(PLAN 635-3 · 요구 546) — 예정노선 키트(`createPlanKit` 「b05」) 칸을 앉힘. * 칸은 페이지가 먼저 세우고 편집기는 노선을 읽은 뒤 섬 → 칸 = 빈 자리(order 1 · 2) · 편집기가 `fillPlanPanel` 로 채움. * 늦게 온 쪽이 붙임 · 새 칸 자리(페이지 다시 세움) = 떠난 편집기 칸(문서 밖) 버림 · 비면 자리 숨김. * 좌표 변환(635-5) = 서버(`POST /route/coords` · 사업지 좌표계 pyproj) — 찾기 이동 · 속성 판 경위도 때만 부름. * ========================================================================== */ import { registerPanelSection } from "../A00_Common/design/design_panel"; import { requestJson } from "./B05_Profile_Api_Replan"; type Vertex = [number, number]; type Slot = "site" | "draw"; const holders: Partial> = {}; const cards: Partial> = {}; const attach = (slot: Slot): void => { const holder = holders[slot]; if (!holder) return; const card = cards[slot]; holder.replaceChildren(...(card ? [card] : [])); holder.hidden = !card; }; for (const [slot, order, title] of [ ["site", 1, "위치"], ["draw", 2, "그리기"], ] as const) registerPanelSection({ id: `plan-${slot}`, title, views: ["profile"], order, build: () => { const holder = document.createElement("div"); holder.className = "b05-route__plan-slot"; holders[slot] = holder; if (!cards[slot]?.isConnected) cards[slot] = null; // 떠난 편집기 칸 attach(slot); return holder; }, }); /** 키트 칸을 앉힘(새 편집기가 갈음). */ export function fillPlanPanel(next: Record): void { for (const slot of ["site", "draw"] as const) { cards[slot] = next[slot]; attach(slot); } } export interface PlanCoords { /** 경위도 → 사업지 [x, y] m(못 받으면 null). */ toPlan: (lon: number, lat: number) => Promise; /** 사업지 → 경위도 — 받은 점이면 바로 · 처음이면 null 주고 한 번 물음(오면 `onLonLat`). */ lonLat: (x: number, y: number) => Vertex | null; } /** 경위도 ↔ 사업지 좌표 서버 변환 · 같은 점은 캐시(끌기 중엔 안 부름 — 판은 고름 · 놓음 때만 그림). */ export function createPlanCoords(projectId: string, onLonLat: () => void): PlanCoords { const ask = (to: "plan" | "lonlat", point: Vertex): Promise => requestJson<{ points: Vertex[] }>( `/projects/${projectId}/route/coords`, { method: "POST", body: JSON.stringify({ to, points: [point] }) }, 15_000, ).then( (answer) => answer.points[0] ?? null, () => null, ); const known = new Map(); return { toPlan: (lon, lat) => ask("plan", [lon, lat]), lonLat(x, y) { const key = `${x},${y}`; if (known.has(key)) return known.get(key) ?? null; known.set(key, null); // 묻는 중 void ask("lonlat", [x, y]).then((at) => { if (at) known.set(key, at); else known.delete(key); // 실패 = 다음 고름 때 다시 if (at) onLonLat(); }); return null; }, }; }