fix(B04·B05): 지도 경위도 겹 · 위성 그림을 TM 겹 자리로 맞춤 (PLAN 589-1 · 요구 447 · 448)
- 서버 위성 메타 응답에 그림 상자 9×9 TM 격자(tm_grid · pyproj · 응답 때 셈 · 귀퉁이 1 cm 넘게 다르면 안 줌) - 공용 createTmNormalizer(고를 칸 toNorm · 격자 쌍선형) · normOfLonLat · imageScreenMatrix · placeMapImage - B04 하단 지도 · B05 배수유역도 · B05 평면 노선 편집이 TM 자리로 · 위성 그림 = 화면 가운데 1차 행렬(contain 뺌) - Watershed 손 베낌 식 넷 · 줌아웃 한계 = 공용 식 · D03 길(createNormalizer) 바뀜 없음 · TM 길 손 안 댐 - 지킴 시험 test_589_tm_grid(Bessel · GRS80 · 격자 = pyproj 0.02 m · 그림 행렬 0.5 px · 옛 꼴 어긋남) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,11 @@
|
||||
import { themeColor } from "@ui/ui_template_palette";
|
||||
import type { VWorldMeta } from "./B04_PreProcess_Api_Fetch";
|
||||
import { pickLodList } from "./logic/B04_PreProcess_UI_MapRender_Prepare";
|
||||
import {
|
||||
imageScreenMatrix,
|
||||
normOfLonLat,
|
||||
pickLodList,
|
||||
tmGridNormOf,
|
||||
} from "./logic/B04_PreProcess_UI_MapRender_Prepare";
|
||||
|
||||
// 2D 지도 벡터 레이어 렌더 엔진.
|
||||
// GeoJSON 좌표를 로드 시 1회만 정규화 맵 좌표(0~1)로 사전 투영해 두고,
|
||||
@@ -84,6 +89,8 @@ export type Normalizer = {
|
||||
lonRange: number;
|
||||
latRange: number;
|
||||
aspect: number;
|
||||
/** 경위도 → 정규화 고를 칸(589-1 · `createTmNormalizer`) — 있으면 TM 자리 · 없으면 같은 네모 식. */
|
||||
toNorm?: (lon: number, lat: number) => [number, number];
|
||||
};
|
||||
|
||||
/** 뷰포트 안에서 지도 이미지가 차지하는 사각형(기존 getMapRect와 동일 계산). */
|
||||
@@ -114,6 +121,13 @@ export function createNormalizer(meta: VWorldMeta): Normalizer {
|
||||
};
|
||||
}
|
||||
|
||||
/** 경위도 겹을 TM 겹 자리로 놓는 변환 계수(589-1 · B04 · B05) — 메타에 `tm_grid` 가 없으면 `createNormalizer` 그대로. */
|
||||
export function createTmNormalizer(meta: VWorldMeta): Normalizer {
|
||||
const base = createNormalizer(meta);
|
||||
const toNorm = tmGridNormOf(meta);
|
||||
return toNorm ? { ...base, toNorm } : base;
|
||||
}
|
||||
|
||||
export function computeMapRect(meta: VWorldMeta | null, width: number, height: number): MapRect {
|
||||
if (!meta) return { x: 0, y: 0, width, height };
|
||||
const mapRatio = meta.width_meters / Math.max(meta.height_meters, 1);
|
||||
@@ -338,11 +352,35 @@ export function lonLatToScreen(
|
||||
lon: number,
|
||||
lat: number,
|
||||
): [x: number, y: number] {
|
||||
return normalizedToScreen(
|
||||
view,
|
||||
(lon - normalizer.lonMin) / normalizer.lonRange,
|
||||
1 - (lat - normalizer.latMin) / normalizer.latRange,
|
||||
const [nx, ny] = normOfLonLat(normalizer, lon, lat);
|
||||
return normalizedToScreen(view, nx, ny);
|
||||
}
|
||||
|
||||
/**
|
||||
* 위성 그림(`<img>` 제 크기 · 원점 왼위)을 화면에 놓음(589-1) — 그림 px → 경위도 → `normalizer` → 화면을
|
||||
* 화면 가운데에서 1차로 맞춘 행렬. 그림 · 메타가 아직 없으면 그대로 둠.
|
||||
*/
|
||||
export function placeMapImage(
|
||||
image: HTMLImageElement,
|
||||
meta: VWorldMeta | null,
|
||||
normalizer: Normalizer | null,
|
||||
view: ViewState,
|
||||
): void {
|
||||
if (!meta || !normalizer || !image.naturalWidth || !image.naturalHeight) return;
|
||||
const affine = affineOf(view);
|
||||
const matrix = imageScreenMatrix(
|
||||
meta,
|
||||
normalizer,
|
||||
image.naturalWidth,
|
||||
image.naturalHeight,
|
||||
(nx, ny) => [nx * affine.ax + affine.bx, ny * affine.ay + affine.by],
|
||||
view.width / 2,
|
||||
view.height / 2,
|
||||
);
|
||||
image.style.transform = `matrix(${matrix.join(", ")})`;
|
||||
// 크게 당기면 배경 그림이 뭉개진다 — 흐림 보간을 끄고 픽셀을 그대로 보인다
|
||||
// (2026-09-04 사용자 지시). 실제 크기는 축척 막대로 읽는다.
|
||||
image.style.imageRendering = view.scale > 4 ? "pixelated" : "auto";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user