This commit is contained in:
2026-07-15 18:33:33 +09:00
parent 6b1583103f
commit f533082537
201 changed files with 19776 additions and 563 deletions
@@ -3,11 +3,13 @@ import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
import { PLYLoader } from "three/examples/jsm/loaders/PLYLoader.js";
import type { SurfaceModelSummary } from "./B04_wf1_Surface_Api_Fetch";
import { API_BASE_URL } from "@config/config_frontend";
import { fetchVWorldMeta, getVWorldMapUrl, fetchGisGeoJson } from "./B04_wf1_Surface_Api_Fetch";
export interface SurfaceTerrainViewer {
root: HTMLElement;
render: (projectId: string, models: readonly SurfaceModelSummary[]) => void;
updateBgMap: (projectId: string, bgLayer: string) => void;
updateGisLayer: (projectId: string, gisLayer: string) => void;
dispose: () => void;
}
@@ -263,6 +265,46 @@ export function createSurfaceTerrainViewer(): SurfaceTerrainViewer {
scaleBar.append(scaleLabel);
viewerArea.append(scaleBar);
// Elevation bounds legend bar overlay (I-403)
const legendBar = document.createElement("div");
legendBar.style.position = "absolute";
legendBar.style.top = "16px";
legendBar.style.right = "16px";
legendBar.style.background = "rgba(255, 255, 255, 0.9)";
legendBar.style.border = "1px solid #cbd5e1";
legendBar.style.borderRadius = "6px";
legendBar.style.padding = "8px";
legendBar.style.width = "50px";
legendBar.style.display = "none"; // hidden until contours are loaded
legendBar.style.flexDirection = "column";
legendBar.style.alignItems = "center";
legendBar.style.zIndex = "10";
legendBar.style.boxShadow = "0 2px 6px rgba(0,0,0,0.08)";
legendBar.style.pointerEvents = "none";
const maxValSpan = document.createElement("span");
maxValSpan.style.fontSize = "10px";
maxValSpan.style.fontWeight = "bold";
maxValSpan.style.color = "#b91c1c";
maxValSpan.style.marginBottom = "4px";
const gradientDiv = document.createElement("div");
gradientDiv.style.width = "12px";
gradientDiv.style.height = "120px";
gradientDiv.style.background =
"linear-gradient(to bottom, #d60000 0%, #ff5100 25%, #e6a100 50%, #228b22 75%, #3a85ff 100%)";
gradientDiv.style.borderRadius = "2px";
gradientDiv.style.border = "1px solid #94a3b8";
const minValSpan = document.createElement("span");
minValSpan.style.fontSize = "10px";
minValSpan.style.fontWeight = "bold";
minValSpan.style.color = "#1d4ed8";
minValSpan.style.marginTop = "4px";
legendBar.append(maxValSpan, gradientDiv, minValSpan);
viewerArea.append(legendBar);
// Three.js context variables
let currentProjectId = "";
let currentModelsList: readonly SurfaceModelSummary[] = [];
@@ -324,6 +366,7 @@ export function createSurfaceTerrainViewer(): SurfaceTerrainViewer {
}
labelElements.forEach((el) => el.remove());
labelElements.length = 0;
legendBar.style.display = "none";
}
const getFitParams = (object: THREE.Object3D) => {
@@ -376,8 +419,7 @@ export function createSurfaceTerrainViewer(): SurfaceTerrainViewer {
const match = currentModelsList.find((m) => {
const typeMatches = m.model_type.toLowerCase() === activeMethod.toLowerCase();
const pathContainsFilter =
m.model_file_path &&
m.model_file_path.toLowerCase().includes(activeFilter.toLowerCase());
m.model_file_path && m.model_file_path.toLowerCase().includes(activeFilter.toLowerCase());
return typeMatches && pathContainsFilter;
});
@@ -471,7 +513,13 @@ export function createSurfaceTerrainViewer(): SurfaceTerrainViewer {
});
};
let minH = Infinity;
let maxH = -Infinity;
data.contours.forEach((c: any) => {
if (c.level < minH) minH = c.level;
if (c.level > maxH) maxH = c.level;
const points = transform(c.coordinates);
if (points.length < 2) return;
@@ -531,8 +579,18 @@ export function createSurfaceTerrainViewer(): SurfaceTerrainViewer {
labelElements.push(labelDiv);
}
});
if (minH !== Infinity && maxH !== -Infinity) {
const nearestMin10 = Math.round(minH / 10) * 10;
const nearestMax10 = Math.round(maxH / 10) * 10;
maxValSpan.textContent = `${nearestMax10}m`;
minValSpan.textContent = `${nearestMin10}m`;
legendBar.style.display = "flex";
} else {
legendBar.style.display = "none";
}
} catch (e) {
// Contour loading failed or silent fail
legendBar.style.display = "none";
}
}
@@ -652,6 +710,12 @@ export function createSurfaceTerrainViewer(): SurfaceTerrainViewer {
currentModelsList = models;
updateSelectedModel();
},
updateBgMap(projectId, bgLayer) {
// 포인트클라우드 뷰어와 인터페이스 조화를 위해 빈 껍데기 함수 정의
},
updateGisLayer(projectId, gisLayer) {
// 포인트클라우드 뷰어와 인터페이스 조화를 위해 빈 껍데기 함수 정의
},
dispose() {
cancelAnimationFrame(animationFrameId);
resizeObserver.disconnect();