fix(D03): 지점 핀 메뉴 「지점 N에서 긋기 시작」 = 지점 자리에서 · 이어 받기는 지점 · 선이 받은 범위 밖일 때만(옛 프로젝트 여는 즉시 받기 없음) (PLAN 591-2)

- 핀 머리를 우클릭해도 긋기 첫 점 = 지점(전엔 누른 자리 = 지점과 18 px 떨어진 새 본선)
- 받은 범위 안 · 가장자리 여유(350 m) 안쪽 선이 있다고 다시 받지 않음 — 받을 때 서버가 여유를 더함

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-30 10:36:15 +09:00
co-authored by Claude Opus 5.5
parent 29512c001b
commit 5bc29bc889
2 changed files with 9 additions and 12 deletions
+7 -11
View File
@@ -107,7 +107,6 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
let redoStack: Snap[] = [];
let surface: PlanSurface | null = null;
let have: [number, number, number, number] | null = null;
let margin = 0; // 서버 받기 여유(m)
let viewCore: Vertex[] = []; // 지도 보기 가운데 상자 두 귀(넓으면 빈 것)
let viewTimer = 0;
let lastTried = ""; // 같은 받기를 거듭하지 않음
@@ -306,21 +305,18 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
marks.pins,
];
/** 점들이 받은 범위 안인가 — `pad` = 둘레 여유(m). */
const inside = (list: Vertex[], pad: number): boolean =>
/** 점들이 받은 범위 안인가. */
const inside = (list: Vertex[]): boolean =>
!!have &&
list.every(
([x, y]) =>
x - pad >= have![0] && x + pad <= have![2] && y - pad >= have![1] && y + pad <= have![3],
);
const outside = (): boolean => !!have && !inside([...points, ...allVertices()], 0);
list.every(([x, y]) => x >= have![0] && x <= have![2] && y >= have![1] && y <= have![3]);
const outside = (): boolean => !!have && !inside([...points, ...allVertices()]);
/** 조용히 받기(요구 453) — 지점 · 선(+ 여유) · 지도 보기 가운데가 받은 범위 밖이면 이어 받음 ·
/** 조용히 받기(요구 453) — 지점 · 선 · 지도 보기 가운데가 받은 범위 밖이면 이어 받음 ·
* 단추 · 막는 창 없음 · 실패 = [다시 받기] · 같은 요청은 한 번만. */
async function ensureCovered(): Promise<void> {
if (!ready || !editable || busy || failed || !epsg || !root.isConnected) return;
const need = [...points, ...allVertices()];
if (have && inside(need, margin) && inside(viewCore, 0)) return;
if (inside(need) && inside(viewCore)) return; // 받을 때 서버가 여유를 더함
const extra = [...need, ...viewCore];
const key = JSON.stringify([have, extra]);
if (key === lastTried) return;
@@ -411,7 +407,7 @@ export async function renderD03PlanAnalysis(root: HTMLElement): Promise<void> {
try {
surface = await fetchPlanSurface(projectId);
const extent = await fetchTerrainExtent(projectId, null, []);
[have, margin] = [extent.have_m, extent.margin_m];
have = extent.have_m;
} catch (error) {
showToast(`지형면을 읽지 못했습니다 — ${errorText(error)}`, "error");
}
@@ -111,7 +111,8 @@ export function createPlanPoints(params: PlanPointsParams): PlanPoints {
if (draw.mode() === "draw" && draw.stroke())
items.push(["긋기 끝", () => draw.setMode("idle")]);
else if (pin !== null) {
items.push([`${name(pin)}에서 긋기 시작`, () => draw.startAt(px, py, map)]);
const [sx, sy] = map.toScreen(params.points()[pin]); // 핀 머리를 눌러도 지점 자리에서
items.push([`${name(pin)}에서 긋기 시작`, () => draw.startAt(sx, sy, map)]);
items.push([`${name(pin)} 지우기`, () => remove(pin)]);
} else {
const label = {