feat(prediction): 확산예측 지도 밝은 해도 스타일 적용 (육지 회색 + 바다 파랑)
고객요청사항 - 지도를 밝게 하거나, 선명하게 해서 확실히 구분해주세요. - MapView에 lightMode prop 추가 및 해도 스타일(LIGHT_STYLE) 구현 - OpenFreeMap 벡터타일 기반: 육지(회색 #e8e8e8) + 바다(파랑 #a8cce0) 명확 구분 - 한글 지명 라벨 우선 표시 (name:ko → name 폴백) - 도로/건물/경계선 회색 톤 통일, 해양 지명 이탤릭 표시 - 확산예측(OilSpillView)에 lightMode 적용 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
부모
a470df5518
커밋
9c834c4e5e
@ -47,6 +47,166 @@ const BASE_STYLE: StyleSpecification = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarineTraffic 스타일 — 깔끔한 연한 회색 육지 + 흰색 바다 + 한글 라벨
|
||||||
|
const LIGHT_STYLE: StyleSpecification = {
|
||||||
|
version: 8,
|
||||||
|
glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
|
||||||
|
sources: {
|
||||||
|
'ofm-chart': {
|
||||||
|
type: 'vector',
|
||||||
|
url: 'https://tiles.openfreemap.org/planet',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
layers: [
|
||||||
|
// ── 배경 = 육지 (연한 회색) ──
|
||||||
|
{
|
||||||
|
id: 'land-bg',
|
||||||
|
type: 'background',
|
||||||
|
paint: { 'background-color': '#e8e8e8' },
|
||||||
|
},
|
||||||
|
// ── 바다/호수/강 = water 레이어 (파란색) ──
|
||||||
|
{
|
||||||
|
id: 'water',
|
||||||
|
type: 'fill',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'water',
|
||||||
|
paint: { 'fill-color': '#a8cce0' },
|
||||||
|
},
|
||||||
|
// ── 주요 도로 (zoom 9+) ──
|
||||||
|
{
|
||||||
|
id: 'roads-major',
|
||||||
|
type: 'line',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'transportation',
|
||||||
|
minzoom: 9,
|
||||||
|
filter: ['in', 'class', 'motorway', 'trunk', 'primary'],
|
||||||
|
paint: {
|
||||||
|
'line-color': '#c0c0c0',
|
||||||
|
'line-width': ['interpolate', ['linear'], ['zoom'], 9, 0.4, 14, 1.5],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// ── 보조 도로 (zoom 12+) ──
|
||||||
|
{
|
||||||
|
id: 'roads-secondary',
|
||||||
|
type: 'line',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'transportation',
|
||||||
|
minzoom: 12,
|
||||||
|
filter: ['in', 'class', 'secondary', 'tertiary'],
|
||||||
|
paint: {
|
||||||
|
'line-color': '#cccccc',
|
||||||
|
'line-width': ['interpolate', ['linear'], ['zoom'], 12, 0.3, 14, 1],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// ── 건물 (zoom 14+) ──
|
||||||
|
{
|
||||||
|
id: 'buildings',
|
||||||
|
type: 'fill',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'building',
|
||||||
|
minzoom: 14,
|
||||||
|
paint: { 'fill-color': '#cbcbcb', 'fill-opacity': 0.5 },
|
||||||
|
},
|
||||||
|
// ── 국경선 ──
|
||||||
|
{
|
||||||
|
id: 'boundaries-country',
|
||||||
|
type: 'line',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'boundary',
|
||||||
|
filter: ['==', 'admin_level', 2],
|
||||||
|
paint: { 'line-color': '#999999', 'line-width': 1, 'line-dasharray': [4, 2] },
|
||||||
|
},
|
||||||
|
// ── 시도 경계 (zoom 5+) ──
|
||||||
|
{
|
||||||
|
id: 'boundaries-province',
|
||||||
|
type: 'line',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'boundary',
|
||||||
|
minzoom: 5,
|
||||||
|
filter: ['==', 'admin_level', 4],
|
||||||
|
paint: { 'line-color': '#bbbbbb', 'line-width': 0.5, 'line-dasharray': [3, 2] },
|
||||||
|
},
|
||||||
|
// ── 국가/시도 라벨 (한글) ──
|
||||||
|
{
|
||||||
|
id: 'place-labels-major',
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'place',
|
||||||
|
minzoom: 3,
|
||||||
|
filter: ['in', 'class', 'country', 'state'],
|
||||||
|
layout: {
|
||||||
|
'text-field': ['coalesce', ['get', 'name:ko'], ['get', 'name']],
|
||||||
|
'text-font': ['Open Sans Bold'],
|
||||||
|
'text-size': ['interpolate', ['linear'], ['zoom'], 3, 11, 8, 16],
|
||||||
|
'text-max-width': 8,
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'text-color': '#555555',
|
||||||
|
'text-halo-color': '#ffffff',
|
||||||
|
'text-halo-width': 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'place-labels-city',
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'place',
|
||||||
|
minzoom: 5,
|
||||||
|
filter: ['in', 'class', 'city', 'town'],
|
||||||
|
layout: {
|
||||||
|
'text-field': ['coalesce', ['get', 'name:ko'], ['get', 'name']],
|
||||||
|
'text-font': ['Open Sans Regular'],
|
||||||
|
'text-size': ['interpolate', ['linear'], ['zoom'], 5, 10, 12, 14],
|
||||||
|
'text-max-width': 7,
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'text-color': '#666666',
|
||||||
|
'text-halo-color': '#ffffff',
|
||||||
|
'text-halo-width': 1.5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// ── 해양 지명 (water_name) ──
|
||||||
|
{
|
||||||
|
id: 'water-labels',
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'water_name',
|
||||||
|
layout: {
|
||||||
|
'text-field': ['coalesce', ['get', 'name:ko'], ['get', 'name']],
|
||||||
|
'text-font': ['Open Sans Italic'],
|
||||||
|
'text-size': ['interpolate', ['linear'], ['zoom'], 3, 10, 8, 14],
|
||||||
|
'text-max-width': 10,
|
||||||
|
'text-letter-spacing': 0.15,
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'text-color': '#8899aa',
|
||||||
|
'text-halo-color': 'rgba(168,204,224,0.7)',
|
||||||
|
'text-halo-width': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// ── 마을/소지명 (zoom 10+) ──
|
||||||
|
{
|
||||||
|
id: 'place-labels-village',
|
||||||
|
type: 'symbol',
|
||||||
|
source: 'ofm-chart',
|
||||||
|
'source-layer': 'place',
|
||||||
|
minzoom: 10,
|
||||||
|
filter: ['in', 'class', 'village', 'suburb', 'hamlet'],
|
||||||
|
layout: {
|
||||||
|
'text-field': ['coalesce', ['get', 'name:ko'], ['get', 'name']],
|
||||||
|
'text-font': ['Open Sans Regular'],
|
||||||
|
'text-size': ['interpolate', ['linear'], ['zoom'], 10, 9, 14, 12],
|
||||||
|
'text-max-width': 6,
|
||||||
|
},
|
||||||
|
paint: {
|
||||||
|
'text-color': '#777777',
|
||||||
|
'text-halo-color': '#ffffff',
|
||||||
|
'text-halo-width': 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
// 3D 위성 스타일 (VWorld 위성 배경 + OpenFreeMap 건물 extrusion)
|
// 3D 위성 스타일 (VWorld 위성 배경 + OpenFreeMap 건물 extrusion)
|
||||||
// VWorld WMTS: {z}/{y}/{x} (row/col 순서)
|
// VWorld WMTS: {z}/{y}/{x} (row/col 순서)
|
||||||
// OpenFreeMap: CORS 허용, 전 세계 OSM 벡터타일 (building: render_height 포함)
|
// OpenFreeMap: CORS 허용, 전 세계 OSM 벡터타일 (building: render_height 포함)
|
||||||
@ -178,6 +338,8 @@ interface MapViewProps {
|
|||||||
}
|
}
|
||||||
sensitiveResources?: SensitiveResource[]
|
sensitiveResources?: SensitiveResource[]
|
||||||
mapCaptureRef?: React.MutableRefObject<(() => string | null) | null>
|
mapCaptureRef?: React.MutableRefObject<(() => string | null) | null>
|
||||||
|
/** 밝은 톤 지도 스타일 사용 (CartoDB Positron) */
|
||||||
|
lightMode?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
// deck.gl 오버레이 컴포넌트 (MapLibre 컨트롤로 등록, interleaved)
|
// deck.gl 오버레이 컴포넌트 (MapLibre 컨트롤로 등록, interleaved)
|
||||||
@ -262,6 +424,7 @@ export function MapView({
|
|||||||
backtrackReplay,
|
backtrackReplay,
|
||||||
sensitiveResources = [],
|
sensitiveResources = [],
|
||||||
mapCaptureRef,
|
mapCaptureRef,
|
||||||
|
lightMode = false,
|
||||||
}: MapViewProps) {
|
}: MapViewProps) {
|
||||||
const { mapToggles } = useMapStore()
|
const { mapToggles } = useMapStore()
|
||||||
const [currentPosition, setCurrentPosition] = useState<[number, number]>(DEFAULT_CENTER)
|
const [currentPosition, setCurrentPosition] = useState<[number, number]>(DEFAULT_CENTER)
|
||||||
@ -732,8 +895,8 @@ export function MapView({
|
|||||||
sensitiveResources,
|
sensitiveResources,
|
||||||
])
|
])
|
||||||
|
|
||||||
// 3D 모드에 따른 지도 스타일 전환
|
// 3D 모드 / 밝은 톤에 따른 지도 스타일 전환
|
||||||
const currentMapStyle = mapToggles.threeD ? SATELLITE_3D_STYLE : BASE_STYLE
|
const currentMapStyle = mapToggles.threeD ? SATELLITE_3D_STYLE : lightMode ? LIGHT_STYLE : BASE_STYLE
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full relative">
|
<div className="w-full h-full relative">
|
||||||
|
|||||||
@ -461,6 +461,7 @@ export function OilSpillView() {
|
|||||||
layerOpacity={layerOpacity}
|
layerOpacity={layerOpacity}
|
||||||
layerBrightness={layerBrightness}
|
layerBrightness={layerBrightness}
|
||||||
sensitiveResources={sensitiveResources}
|
sensitiveResources={sensitiveResources}
|
||||||
|
lightMode
|
||||||
backtrackReplay={isReplayActive && replayShips.length > 0 ? {
|
backtrackReplay={isReplayActive && replayShips.length > 0 ? {
|
||||||
isActive: true,
|
isActive: true,
|
||||||
ships: replayShips,
|
ships: replayShips,
|
||||||
|
|||||||
불러오는 중...
Reference in New Issue
Block a user