perf(전처리 화면): 343-3 B04 다시 열기 받는 양 — 보관함 저장 실패 고침 · 점군 고정 표본 이진 · 꺼진 층은 켤 때 · 남은 층 ETag · 등고선 주소 한 벌

- b_asset_cache — 쓰기 끝 = 트랜잭션 완료(용량 초과 취소 잡음) · 실패 한 줄 경고(까닭당 한 번) · onblocked · onversionchange · 열기 실패 null 안 기억 · 지우기는 색인 열쇠 · 앞머리 범위(값 안 읽음)
- 점군 미리보기 — 전처리 때 고정 표본 이진(preview_points_{필터}.bin · 원천 지문 같으면 건너뜀) · GET point-cloud/binary(ETag) · 화면은 fetchCachedBytes(IndexedDB) + decodePointPreview(py 짝)
- B04 지도 — 켜진 층만 처음에 받음 · 꺼진 층은 켤 때 · 같은 층 겹쳐 받기 막음
- geojson(지적도 · 행정구역 · 등고선) 파일 그대로 cached_file_response(ETag) · 등고선 단순화본은 원본이 새로우면 다시 만듦
- 등고선 주소 contourAssetUrl 하나(B04 · 미리 받기) — B05 두 곳은 sub5 몫
- 시험 test_343_3_browser_cache 13건 + helper_343_3_asset_cache.cjs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
This commit is contained in:
2026-10-16 11:21:51 +09:00
co-authored by Claude Opus 5.5
parent 1fcdf5aa4c
commit e0051bff72
11 changed files with 830 additions and 131 deletions
+20 -20
View File
@@ -1,9 +1,7 @@
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
import { wheelZoomsIn } from "../A00_Common/touch_bridge";
import { createProgressCircle } from "@ui/ui_template_progress";
import { fetchCachedSheetLayer } from "../A00_Common/b_asset_cache";
import {
fetchGisGeoJson,
fetchPlannedRoute,
fetchVWorldMeta,
getVWorldMapUrl,
@@ -19,6 +17,7 @@ import {
CONTOUR_LABEL_KEYS,
GIS_DEFAULT_ON,
GIS_LAYERS,
fetchGisLayer,
gisLayerColor,
type BackgroundLayer,
type GisLayer,
@@ -36,7 +35,6 @@ import {
drawPreparedLayer,
routeLineColor,
ROUTE_LINE_WIDTH,
type GeoJsonCollection,
type MapRect,
type Normalizer,
type PlanBounds,
@@ -330,6 +328,7 @@ export function createSurfaceMapViewer(): SurfaceMapViewer {
backgroundImages.forEach((image, layer) => {
image.hidden = !activeBackgrounds.has(layer);
});
activeGisLayers.forEach((layer) => void loadLayerOnDemand(layer));
empty.hidden = activeBackgrounds.size > 0 || activeGisLayers.size > 0;
scheduleDraw();
}
@@ -453,6 +452,16 @@ export function createSurfaceMapViewer(): SurfaceMapViewer {
});
}
/** 켠 층이 아직 없으면 그때 받는다(같은 층 겹쳐 받기는 fetchGisLayer 가 막음). */
async function loadLayerOnDemand(layer: GisLayer): Promise<void> {
const projectId = currentProjectId;
if (!projectId || !meta || preparedLayers.has(layer)) return;
const data = await fetchGisLayer(projectId, layer);
if (!data || projectId !== currentProjectId || !normalizer) return;
preparedLayers.set(layer, prepareLayer(data, normalizer, CONTOUR_LABEL_KEYS[layer]));
scheduleDraw();
}
async function loadLayers(): Promise<void> {
if (!currentProjectId) return;
const projectId = currentProjectId;
@@ -471,26 +480,17 @@ export function createSurfaceMapViewer(): SurfaceMapViewer {
fetchVWorldMeta(projectId, "satellite"),
fetchPlannedRoute(projectId).catch(() => ({ status: "error", points: [] })),
]);
// 레이어가 끝나는 대로 진행률을 올린다 — 10종을 다 받을 때까지 화면이 비어 있어서다.
// 켜진 층만 처음에 받는다(꺼진 층은 켤 때) — 끝나는 대로 진행률을 올린다.
const firstLayers = GIS_LAYERS.filter((layer) => activeGisLayers.has(layer));
let done = 0;
const loadedLayers = await Promise.all(
GIS_LAYERS.map(async (layer) => {
try {
// 도엽 레이어는 표시용 사본이라 보관함에 담아 두고 새로고침 때 그대로 쓴다.
const data = (
layer.startsWith("도엽_")
? await fetchCachedSheetLayer<GeoJsonCollection>(projectId, layer)
: await fetchGisGeoJson(projectId, layer)
) as GeoJsonCollection;
return [layer, data] as const;
} catch {
return [layer, null] as const;
} finally {
done += 1;
if (sequence === loadSequence) {
showProgress(done / GIS_LAYERS.length, `도엽 레이어 ${done}/${GIS_LAYERS.length}`);
}
firstLayers.map(async (layer) => {
const data = await fetchGisLayer(projectId, layer);
done += 1;
if (sequence === loadSequence) {
showProgress(done / firstLayers.length, `도엽 레이어 ${done}/${firstLayers.length}`);
}
return [layer, data] as const;
}),
);
if (sequence !== loadSequence) return;