feat(D 예정노선): 고름 층 노드 → 선 → 구역 → 전체 · 판 하나 재활용 · 우클릭 메뉴 · 지점 ↔ 노드 붙음 (PLAN 598-1~3 · 요구 463)

- 새 logic/D03_PlanAnalysis_Select.ts — 토막 판정 · 끌어 고르기 토막 · 토막 지움(가운데 = 구역 둘 · 뒤쪽 새 구역) · 전체 지움 · 지점 ↔ 노드 같은 자리
- 새 UI_Select.ts — 고름 상태(다시 누름 = 한 단 넓힘 · 같은 해 = 왼쪽 해 줄로만)
- UI_Edit.ts — 우클릭 즉시 지움 걷음 · 노드 끌면 지점 같이 · 지점 12 px 안 붙음 · 지움 창구(dropNode · dropRegion · dropAll · splitAt)
- UI_Props.ts — 판 = 노드 · 선 · 구역 · 전체 · 구간 · 마커 몸 · [방향 바꾸기] · [갈래 시점 지정] 걷음
- 새 UI_Menu.ts — 우클릭 메뉴(Points 에서 옮김 · 노드 지움 · 지점으로 · 연도 구역 나눔)
- UI_Points.ts — 핀 놓을 때 노드 12 px 안 붙음(nodeNear)
- key_registry plan 줄 · 시험 585_2 갈래 시점 지정 줄 지움(요구 463-15 걷음) · 553_4 메뉴 자리 · 새 시험 598_1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-30 14:47:39 +09:00
co-authored by Claude Opus 5.5
parent ad8506b186
commit ff49f2c8a2
11 changed files with 1252 additions and 427 deletions
+21 -3
View File
@@ -55,6 +55,7 @@ import {
type PlanMapLayer,
} from "./D03_PlanAnalysis_UI_Map";
import { createPlanMeasure } from "./D03_PlanAnalysis_UI_Measure";
import { createPlanMenu } from "./D03_PlanAnalysis_UI_Menu";
import { createPlanPoints } from "./D03_PlanAnalysis_UI_Points";
import { createPlanPieces } from "./D03_PlanAnalysis_UI_Pieces";
import { createPlanProps } from "./D03_PlanAnalysis_UI_Props";
@@ -139,6 +140,11 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
network = normalizePieces(next, yearStart);
map.redraw();
},
commitPoints: (nextPoints, next) => step({ network: next, points: nextPoints, pointNames }),
showPoints: (nextPoints, next) => {
[points, network] = [nextPoints, next];
map.redraw();
},
enabled: () => draw.mode() === "idle" && !measuring,
editable: () => editable,
onSelect: () => refresh(),
@@ -149,6 +155,10 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
editable: () => editable,
yearStart: () => yearStart,
commit,
heightAt: () => z,
epsg: () => epsg,
points: () => points,
addPoint: (at) => step({ network, points: [...points, at], pointNames: [...pointNames, ""] }),
});
const pieces = createPlanPieces({ network: () => network, edit });
const measure = createPlanMeasure(() => z);
@@ -157,7 +167,6 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
network: () => network,
names: () => pointNames,
epsg: () => epsg,
draw,
commit: (nextPoints, nextNames, next) =>
step({ network: next, points: nextPoints, pointNames: nextNames }),
center: () => viewMid,
@@ -168,6 +177,14 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
idle: () => draw.mode() === "idle" && !measuring,
editable: () => editable,
});
const menu = createPlanMenu({
network: () => network,
points: () => points,
marks,
edit,
draw,
editable: () => editable,
});
// 지도 보기 가운데 상자 — 그릴 때마다 보이는 화면 가운데 반을 작업 좌표로(바뀌면 잠시 뒤 범위 봄)
const viewWatch: PlanMapLayer = {
draw(context, screen) {
@@ -307,7 +324,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
refresh();
}
/** 겹 차례 — 핀 누름(`marks.input`)은 고르기보다 앞 · 핀 그림 · 우클릭 메뉴(`marks.pins`)는 맨 끝(맨 위). */
/** 겹 차례 — 핀 누름(`marks.input`)은 고르기보다 앞 · 핀 그림(`marks.pins`) · 우클릭 메뉴(`menu.layer`)는 맨 끝(맨 위). */
const layerList = (): PlanMapLayer[] => [
...backdrop.layers,
draw.layer,
@@ -316,6 +333,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
...(measuring ? [measure.layer] : []),
viewWatch,
marks.pins,
menu.layer,
];
/** 점들이 받은 범위 안인가. */
@@ -553,7 +571,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
};
window.addEventListener("keydown", onKey);
map.setLayers(layerList());
marks.mount(map.root);
menu.mount(map.root);
try {
editable = !!(await fetchSessionUser())?.is_forest_service;