feat(D03): 평면 도구줄을 공용 지도 도구줄로 — [?] 안내 · 측점 · 기울기 켬끔 더해 아홉 · 겹 단추 아이콘 (PLAN 568-3 · 569-2 · 요구 395)

- UI_Map — createMapTools · mapToolButton(되돌리기 · 다시하기 · 3D · 돌리기 둘 · 측점 · 기울기 · 거리 재기) · [?] 조작 안내 여덟 줄 · ScreenMap 에 측점 · 기울기 켬 실음
- UI_Draw — 측점 눈금 · 구간 길이 · 기울기 글을 켬끔 따라 그림
- UI_Layers — 겹 단추 글자 대신 공용 아이콘(satellite · droplet · borderAll · angle)
- test_553_4 — 옛 createToolbar · createIconButton 지킴을 공용 도구줄 · 겹 단추 지킴으로 바꿈

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-28 07:04:02 +09:00
co-authored by Claude Opus 5.5
parent 7f17af513c
commit b4e1b89943
5 changed files with 83 additions and 52 deletions
+13 -12
View File
@@ -360,18 +360,19 @@ export function createPlanDraw(params: PlanDrawParams): PlanDraw {
else context.moveTo(sx, sy);
});
context.stroke();
drawStationTicks(
context,
line.vertices.map(([x, y]) => ({ x, y })),
{
intervalM: PLAN_STATION_INTERVAL_M,
pxPerMeter: map.pxPerMeter,
toScreen: (x, y) => map.toScreen([x, y]),
uprightRad: map.uprightRad,
chainages: stationChainages(lineLength(line.vertices), PLAN_STATION_INTERVAL_M),
},
);
segmentLabels(context, map, line.vertices);
if (map.stations)
drawStationTicks(
context,
line.vertices.map(([x, y]) => ({ x, y })),
{
intervalM: PLAN_STATION_INTERVAL_M,
pxPerMeter: map.pxPerMeter,
toScreen: (x, y) => map.toScreen([x, y]),
uprightRad: map.uprightRad,
chainages: stationChainages(lineLength(line.vertices), PLAN_STATION_INTERVAL_M),
},
);
if (map.slopes) segmentLabels(context, map, line.vertices);
});
// 꺾임점
context.fillStyle = "#ffffff";
@@ -44,20 +44,18 @@ export type PlanLayerKey = "satellite" | "contour" | "stream" | "cadastral" | "r
interface LayerDef {
key: PlanLayerKey;
/** 아이콘 — 없으면 글자 단추(`glyph`). */
icon?: IconName;
glyph?: string;
icon: IconName;
title: string;
on: boolean;
}
const LAYERS: readonly LayerDef[] = [
{ key: "satellite", glyph: "위성", title: "위성사진", on: true },
{ key: "satellite", icon: "satellite", title: "위성사진", on: true },
{ key: "contour", icon: "mountain", title: "도엽 등고선(굵은 선 = 계곡선 25 m)", on: true },
{ key: "stream", glyph: "세류", title: "세류선(가는 파랑) · 하천(굵은 파랑)", on: true },
{ key: "cadastral", glyph: "지적", title: "지적도(참고용 · 법적 효력 없음)", on: false },
{ key: "stream", icon: "droplet", title: "세류선(가는 파랑) · 하천(굵은 파랑)", on: true },
{ key: "cadastral", icon: "borderAll", title: "지적도(참고용 · 법적 효력 없음)", on: false },
{ key: "road", icon: "road", title: "기존 도로(도엽 도로중심선)", on: false },
{ key: "slope", glyph: "경사", title: "급경사 색(15° · 35° · 40° 이상)", on: false },
{ key: "slope", icon: "angle", title: "급경사 색(15° · 35° · 40° 이상)", on: false },
];
const PREF_KEY = "aislo:pref:d03.layers";
/** 등고선을 받아 올 그은 선 둘레(m). */
@@ -128,7 +126,6 @@ export function createPlanLayers(opts: {
for (const def of LAYERS) {
const button = createIconButton({
icon: def.icon,
glyph: def.glyph,
title: def.title,
pressed: on[def.key],
onClick: () => toggle(def.key),
+40 -29
View File
@@ -1,7 +1,7 @@
/* =============================================================================
* D03_PlanAnalysis_UI_Map.ts
* D03 메인 평면(PLAN 530-3 · 562-3) — 위성 바탕 · 위에 겹(바탕 겹 569-2 · 그리기 · 거리재기) · 도구줄 여섯
* (되돌리기 · 다시하기 · 3D · 좌회전 · 우회전 · 거리재기).
* D03 메인 평면(PLAN 530-3 · 562-3) — 위성 바탕 · 위에 겹(바탕 겹 569-2 · 그리기 · 거리재기) · 도구줄 아홉
* (공용 `createMapTools` 568-3 — [?] · 되돌리기 · 다시하기 · 3D · 좌회전 · 우회전 · 측점 · 기울기 · 거리재기).
*
* 셈 · 그리기는 B 부품 그대로(import 만) — 좌표 `createMetricProjector` · `screenToMetric` · 맞춤
* `computeRouteView` · 휠 확대 · 가운데 단추 옮기기 `bindRouteEditNavigation` ·
@@ -10,9 +10,8 @@
* 겹은 돌리기 전 그림 좌표로 그리고 받음 — 누른 자리는 `unrotate` 로 되돌려 넘김.
* ========================================================================== */
import { createIconButton } from "@ui/ui_template_button";
import { createMapTools, mapToolButton } from "@ui/ui_template_map_tools";
import { themeColor } from "@ui/ui_template_palette";
import { createToolbar } from "@ui/ui_template_toolbar";
import type { VWorldMeta } from "../B04_PreProcess/B04_PreProcess_Api_Fetch";
import {
computeMapRect,
@@ -56,6 +55,9 @@ export interface ScreenMap {
pxPerMeter: number;
/** B04 그리기 틀(`drawPreparedLayer` 등이 받음). */
view: ViewState;
/** 측점 눈금 · 구간 길이 · 기울기 글 켬(도구줄 측점 · 기울기). */
stations: boolean;
slopes: boolean;
/** 글자를 세울 각(라디안 · 안 돌렸으면 0). */
uprightRad: number;
/** (x, y) 둘레로 글자를 눈높이로 세워 그림(돌린 지도). */
@@ -129,36 +131,44 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
const root = document.createElement("div");
root.className = "d03-plan__map";
const canvas = document.createElement("canvas");
const tool = (
act: string,
icon: Parameters<typeof createIconButton>[0]["icon"],
title: string,
onClick?: () => void,
): HTMLButtonElement => {
const button = createIconButton({ icon, title, onClick });
button.dataset.act = act;
let marks = { stations: true, slopes: true };
const markButton = (act: "stations" | "slopes", title: string): HTMLButtonElement => {
const button = mapToolButton(act, {
title,
pressed: true,
onClick: () => {
marks = { ...marks, [act]: !marks[act] };
button.setAttribute("aria-pressed", String(marks[act]));
draw();
},
});
return button;
};
const undoButton = tool("undo", "undo", "되돌리기(Ctrl+Z)", events.onUndo);
const redoButton = tool("redo", "redo", "다시하기(Ctrl+Y)", events.onRedo);
const viewButton = tool("view3d", "cube", "3D 보기", events.on3d);
const measureButton = tool("measure", "ruler", "거리재기", events.onMeasure);
measureButton.setAttribute("aria-pressed", "false");
const toolbar = createToolbar({
glass: true,
size: 32,
const undoButton = mapToolButton("undo", { onClick: events.onUndo });
const redoButton = mapToolButton("redo", { onClick: events.onRedo });
const viewButton = mapToolButton("view3d", { onClick: events.on3d });
const measureButton = mapToolButton("measure", { onClick: events.onMeasure });
const tools = createMapTools({
label: "평면 도구",
items: [
undoButton,
redoButton,
"divider",
viewButton,
tool("rotate-ccw", "rotateCcw", "왼쪽으로 돌림(15°)"),
tool("rotate-cw", "rotateCw", "오른쪽으로 돌림(15°)"),
"divider",
measureButton,
hint: {
lines: [
"누름 = 점 찍기",
"Enter = 긋기 끝",
"Esc = 긋기 취소",
"Backspace = 마지막 점 빼기",
"오른쪽 누름 = 마크 지움",
"가운데 끌기 = 옮기기",
"휠 = 확대 · 축소",
"Ctrl+휠 = 돌리기",
],
},
groups: [
[undoButton, redoButton],
[viewButton, "rotate-ccw", "rotate-cw"],
[markButton("stations", "측점"), markButton("slopes", "구간 길이 · 기울기"), measureButton],
],
});
const toolbar = tools.root;
toolbar.classList.add("d03-plan__tools");
root.append(canvas, toolbar);
const context = canvas.getContext("2d")!;
@@ -187,6 +197,7 @@ export function createPlanMap(events: PlanMapEvents): PlanMap {
toMetric: (px, py) => screenToMetric(meta!, view, px, py),
pxPerMeter: projector.pxPerMeter,
view,
...marks,
uprightRad: rotation.uprightRad(),
upright: (ctx, x, y, paint) => {
const angle = rotation.uprightRad();
@@ -29,6 +29,7 @@
position: absolute;
top: var(--spacing-8);
right: var(--spacing-8);
align-items: flex-end;
}
/* 569-2 바탕 겹 단추 — 왼쪽 위 · 급경사 범례 — 왼쪽 아래 */