perf(B04/B05): 도엽 표시본 도입 + 배경 범위·도엽 기준을 계획노선으로 변경

- 도엽 병합본의 화면 표시용 사본 생성(B04_wf1_Surface_Engine_SheetView):
  프로젝트 + 500m로 잘라내고 좌표를 소수 7자리로 줄여 등고선 37.3MB -> 0.97MB.
  분석용 원본은 그대로 둔다(배수유역은 상류까지 넓은 범위가 필요).
- /geojson의 도엽 레이어를 파일 그대로 + ETag로 전송 — 매 요청 2.0초 재직렬화 제거(0.008초).
- /vworld-map도 ETag 전송으로 바꾸고, 프론트가 붙이던 `&_t=` 시간꼬리표 제거.
- 도엽 레이어를 브라우저 보관함 경유로 조회하고 준비화면에서 미리 담는다.
- 배경 지도 범위 = 라이다 범위 + 계획노선 범위 + 여유 300m(SURFACE_MAP_MARGIN_M).
- 도엽 기준 좌표 = 계획노선 시점·종점(없으면 라이다 중심). 같은 도엽이면 9매,
  이웃 도엽에 걸치면 12매. 표본 프로젝트는 기존 9매와 동일(회귀 없음).
- 선정에서 빠진 도엽 zip 정리(prune_sheets) — 표본에서 30매 중 21매가 다른 지역 잔재.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-01 11:47:29 +09:00
co-authored by Claude Opus 5
parent 96561de120
commit a9caf872b2
10 changed files with 437 additions and 35 deletions
@@ -1,6 +1,6 @@
import { createWorkflowPanelHandle } from "@ui/ui_template_overlay";
import { DRAINAGE_SHEET_LAYERS, fetchCachedSheetLayer } from "../A00_Common/b_asset_cache";
import {
fetchGisGeoJson,
fetchVWorldMeta,
getVWorldMapUrl,
type VWorldMeta,
@@ -40,7 +40,7 @@ import { createProgressCircle } from "@ui/ui_template_progress";
/** 배수유역 산정의 근거가 되는 도엽 레이어. 3D는 쓰지 않는다(사용자 지시).
* 표고점은 유효 데이터가 적어 산정에서 제외했으므로 배경에도 띄우지 않는다(2026-07-31). */
const DRAINAGE_LAYERS = ["도엽_등고선", "도엽_하천중심선"] as const;
const DRAINAGE_LAYERS = DRAINAGE_SHEET_LAYERS;
type DrainageLayer = (typeof DRAINAGE_LAYERS)[number];
const LAYER_COLORS: Record<DrainageLayer, string> = {
@@ -522,7 +522,8 @@ export function createDrainagePanel(): DrainagePanel {
const loaded = await Promise.all(
DRAINAGE_LAYERS.map(async (layer) => {
try {
const data = (await fetchGisGeoJson(activeProjectId, layer)) as GeoJsonCollection;
// 도엽 레이어는 표시용 사본이라 보관함에 담아 두고 새로고침 때 그대로 쓴다.
const data = await fetchCachedSheetLayer<GeoJsonCollection>(activeProjectId, layer);
return [layer, data] as const;
} catch {
return [layer, null] as const;
@@ -538,7 +539,8 @@ export function createDrainagePanel(): DrainagePanel {
featureCount += data.features?.length ?? 0;
preparedLayers.set(layer, prepareLayer(data, normalizer!));
});
backgroundImage.src = `${getVWorldMapUrl(activeProjectId, "satellite")}&_t=${Date.now()}`;
// 주소에 시각을 붙이면 브라우저가 매번 다시 받는다. 서버가 ETag를 주므로 그대로 쓴다.
backgroundImage.src = getVWorldMapUrl(activeProjectId, "satellite");
if (routePoints.length > 1) routeLayer = prepareMetricPolyline(routePoints, nextMeta);
pipeEditor.setContext(nextMeta, routePoints);
status.hidden = featureCount > 0;