feat(map): 선박 외곽선 대비 및 줌 스케일링 개선
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
부모
c5d89c5641
커밋
cd60f553ee
@ -115,8 +115,8 @@ export function DashboardPage() {
|
||||
fcLines: true,
|
||||
zones: true,
|
||||
fleetCircles: true,
|
||||
predictVectors: false,
|
||||
shipLabels: false,
|
||||
predictVectors: true,
|
||||
shipLabels: true,
|
||||
});
|
||||
const [fleetRelationSortMode, setFleetRelationSortMode] = useState<FleetRelationSortMode>("count");
|
||||
|
||||
|
||||
@ -47,6 +47,14 @@ export const MAP_SELECTED_SHIP_RGB: [number, number, number] = [34, 211, 238];
|
||||
export const MAP_HIGHLIGHT_SHIP_RGB: [number, number, number] = [245, 158, 11];
|
||||
export const MAP_DEFAULT_SHIP_RGB: [number, number, number] = [100, 116, 139];
|
||||
|
||||
// ── Ship outline / halo contrast colors ──
|
||||
|
||||
export const HALO_OUTLINE_COLOR: [number, number, number, number] = [210, 225, 240, 155];
|
||||
export const HALO_OUTLINE_COLOR_SELECTED: [number, number, number, number] = [14, 234, 255, 230];
|
||||
export const HALO_OUTLINE_COLOR_HIGHLIGHTED: [number, number, number, number] = [245, 158, 11, 210];
|
||||
export const GLOBE_OUTLINE_PERMITTED = 'rgba(210,225,240,0.62)';
|
||||
export const GLOBE_OUTLINE_OTHER = 'rgba(160,175,195,0.35)';
|
||||
|
||||
// ── Flat map icon sizes ──
|
||||
|
||||
export const FLAT_SHIP_ICON_SIZE = 19;
|
||||
@ -152,7 +160,7 @@ export const FLEET_LINE_ML_HL = rgbaCss(OVERLAY_FLEET_RANGE_RGB, 0.95);
|
||||
// ── Bathymetry zoom ranges ──
|
||||
|
||||
export const BATHY_ZOOM_RANGES: BathyZoomRange[] = [
|
||||
{ id: 'bathymetry-fill', mercator: [6, 24], globe: [8, 24] },
|
||||
{ id: 'bathymetry-borders', mercator: [6, 24], globe: [8, 24] },
|
||||
{ id: 'bathymetry-borders-major', mercator: [4, 24], globe: [8, 24] },
|
||||
{ id: 'bathymetry-fill', mercator: [5, 24], globe: [7, 24] },
|
||||
{ id: 'bathymetry-borders', mercator: [5, 24], globe: [7, 24] },
|
||||
{ id: 'bathymetry-borders-major', mercator: [3, 24], globe: [7, 24] },
|
||||
];
|
||||
|
||||
@ -20,7 +20,9 @@ import {
|
||||
EMPTY_MMSI_SET,
|
||||
DEPTH_DISABLED_PARAMS,
|
||||
GLOBE_OVERLAY_PARAMS,
|
||||
LEGACY_CODE_COLORS,
|
||||
HALO_OUTLINE_COLOR,
|
||||
HALO_OUTLINE_COLOR_SELECTED,
|
||||
HALO_OUTLINE_COLOR_HIGHLIGHTED,
|
||||
PAIR_RANGE_NORMAL_DECK,
|
||||
PAIR_RANGE_WARN_DECK,
|
||||
PAIR_LINE_NORMAL_DECK,
|
||||
@ -426,12 +428,7 @@ export function useDeckLayers(
|
||||
getRadius: () => FLAT_LEGACY_HALO_RADIUS,
|
||||
lineWidthUnits: 'pixels',
|
||||
getLineWidth: () => 2,
|
||||
getLineColor: (d) => {
|
||||
const l = legacyHits?.get(d.mmsi);
|
||||
const rgb = l ? LEGACY_CODE_COLORS[l.shipCode] : null;
|
||||
if (!rgb) return [245, 158, 11, 200];
|
||||
return [rgb[0], rgb[1], rgb[2], 200];
|
||||
},
|
||||
getLineColor: () => HALO_OUTLINE_COLOR,
|
||||
getPosition: (d) => [d.lon, d.lat] as [number, number],
|
||||
}),
|
||||
);
|
||||
@ -476,7 +473,7 @@ export function useDeckLayers(
|
||||
}
|
||||
|
||||
if (settings.showShips && legacyOverlayTargets.length > 0) {
|
||||
layers.push(new ScatterplotLayer<AisTarget>({ id: 'legacy-halo-overlay', data: legacyOverlayTargets, pickable: false, billboard: false, parameters: overlayParams, filled: false, stroked: true, radiusUnits: 'pixels', getRadius: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return FLAT_LEGACY_HALO_RADIUS_SELECTED; return FLAT_LEGACY_HALO_RADIUS_HIGHLIGHTED; }, lineWidthUnits: 'pixels', getLineWidth: (d) => (selectedMmsi && d.mmsi === selectedMmsi ? 2.5 : 2.2), getLineColor: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return [14, 234, 255, 230]; const l = legacyHits?.get(d.mmsi); const rgb = l ? LEGACY_CODE_COLORS[l.shipCode] : null; if (!rgb) return [245, 158, 11, 210]; return [rgb[0], rgb[1], rgb[2], 210]; }, getPosition: (d) => [d.lon, d.lat] as [number, number] }));
|
||||
layers.push(new ScatterplotLayer<AisTarget>({ id: 'legacy-halo-overlay', data: legacyOverlayTargets, pickable: false, billboard: false, parameters: overlayParams, filled: false, stroked: true, radiusUnits: 'pixels', getRadius: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return FLAT_LEGACY_HALO_RADIUS_SELECTED; return FLAT_LEGACY_HALO_RADIUS_HIGHLIGHTED; }, lineWidthUnits: 'pixels', getLineWidth: (d) => (selectedMmsi && d.mmsi === selectedMmsi ? 2.5 : 2.2), getLineColor: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return HALO_OUTLINE_COLOR_SELECTED; if (shipHighlightSet.has(d.mmsi)) return HALO_OUTLINE_COLOR_HIGHLIGHTED; return HALO_OUTLINE_COLOR; }, getPosition: (d) => [d.lon, d.lat] as [number, number] }));
|
||||
}
|
||||
|
||||
if (settings.showShips && shipOverlayLayerData.filter((t) => legacyHits?.has(t.mmsi)).length > 0) {
|
||||
@ -621,7 +618,7 @@ export function useDeckLayers(
|
||||
}
|
||||
|
||||
if (settings.showShips && legacyTargetsOrdered.length > 0) {
|
||||
globeLayers.push(new ScatterplotLayer<AisTarget>({ id: 'legacy-halo-globe', data: legacyTargetsOrdered, pickable: false, billboard: false, parameters: overlayParams, filled: false, stroked: true, radiusUnits: 'pixels', getRadius: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return FLAT_LEGACY_HALO_RADIUS_SELECTED; return FLAT_LEGACY_HALO_RADIUS; }, lineWidthUnits: 'pixels', getLineWidth: (d) => (selectedMmsi && d.mmsi === selectedMmsi ? 2.5 : 2), getLineColor: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return [14, 234, 255, 230]; const l = legacyHits?.get(d.mmsi); const rgb = l ? LEGACY_CODE_COLORS[l.shipCode] : null; if (!rgb) return [245, 158, 11, 200]; return [rgb[0], rgb[1], rgb[2], 200]; }, getPosition: (d) => [d.lon, d.lat] as [number, number] }));
|
||||
globeLayers.push(new ScatterplotLayer<AisTarget>({ id: 'legacy-halo-globe', data: legacyTargetsOrdered, pickable: false, billboard: false, parameters: overlayParams, filled: false, stroked: true, radiusUnits: 'pixels', getRadius: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return FLAT_LEGACY_HALO_RADIUS_SELECTED; return FLAT_LEGACY_HALO_RADIUS; }, lineWidthUnits: 'pixels', getLineWidth: (d) => (selectedMmsi && d.mmsi === selectedMmsi ? 2.5 : 2), getLineColor: (d) => { if (selectedMmsi && d.mmsi === selectedMmsi) return HALO_OUTLINE_COLOR_SELECTED; return HALO_OUTLINE_COLOR; }, getPosition: (d) => [d.lon, d.lat] as [number, number] }));
|
||||
}
|
||||
|
||||
const normalizedLayers = sanitizeDeckLayerList(globeLayers);
|
||||
|
||||
@ -8,6 +8,8 @@ import type { Map3DSettings, MapProjectionId } from '../types';
|
||||
import {
|
||||
ANCHORED_SHIP_ICON_ID,
|
||||
GLOBE_ICON_HEADING_OFFSET_DEG,
|
||||
GLOBE_OUTLINE_PERMITTED,
|
||||
GLOBE_OUTLINE_OTHER,
|
||||
DEG2RAD,
|
||||
} from '../constants';
|
||||
import { isFiniteNumber } from '../lib/setUtils';
|
||||
@ -351,8 +353,9 @@ export function useGlobeShips(
|
||||
const iconScale = selected ? selectedScale : highlightScale;
|
||||
const iconSize3 = clampNumber(0.35 * sizeScale * selectedScale, 0.25, 1.3);
|
||||
const iconSize7 = clampNumber(0.45 * sizeScale * selectedScale, 0.3, 1.45);
|
||||
const iconSize10 = clampNumber(0.56 * sizeScale * selectedScale, 0.35, 1.7);
|
||||
const iconSize14 = clampNumber(0.72 * sizeScale * selectedScale, 0.45, 2.1);
|
||||
const iconSize10 = clampNumber(0.58 * sizeScale * selectedScale, 0.35, 1.8);
|
||||
const iconSize14 = clampNumber(0.85 * sizeScale * selectedScale, 0.45, 2.6);
|
||||
const iconSize18 = clampNumber(2.5 * sizeScale * selectedScale, 1.0, 6.0);
|
||||
return {
|
||||
type: 'Feature',
|
||||
...(isFiniteNumber(t.mmsi) ? { id: Math.trunc(t.mmsi) } : {}),
|
||||
@ -373,6 +376,7 @@ export function useGlobeShips(
|
||||
iconSize7: iconSize7 * iconScale,
|
||||
iconSize10: iconSize10 * iconScale,
|
||||
iconSize14: iconSize14 * iconScale,
|
||||
iconSize18: iconSize18 * iconScale,
|
||||
sizeScale,
|
||||
selected: selected ? 1 : 0,
|
||||
highlighted: highlighted ? 1 : 0,
|
||||
@ -474,14 +478,15 @@ export function useGlobeShips(
|
||||
'case',
|
||||
['==', ['get', 'selected'], 1], 'rgba(14,234,255,0.95)',
|
||||
['==', ['get', 'highlighted'], 1], 'rgba(245,158,11,0.95)',
|
||||
['coalesce', ['get', 'shipColor'], '#64748b'],
|
||||
['==', ['get', 'permitted'], 1], GLOBE_OUTLINE_PERMITTED,
|
||||
GLOBE_OUTLINE_OTHER,
|
||||
] as never,
|
||||
'circle-stroke-width': [
|
||||
'case',
|
||||
['==', ['get', 'selected'], 1], 3.4,
|
||||
['==', ['get', 'highlighted'], 1], 2.7,
|
||||
['==', ['get', 'permitted'], 1], 1.8,
|
||||
0.0,
|
||||
0.7,
|
||||
] as never,
|
||||
'circle-stroke-opacity': 0.85,
|
||||
},
|
||||
@ -519,14 +524,15 @@ export function useGlobeShips(
|
||||
'case',
|
||||
['==', ['get', 'selected'], 1], 'rgba(14,234,255,0.95)',
|
||||
['==', ['get', 'highlighted'], 1], 'rgba(245,158,11,0.95)',
|
||||
['coalesce', ['get', 'shipColor'], '#64748b'],
|
||||
['==', ['get', 'permitted'], 1], GLOBE_OUTLINE_PERMITTED,
|
||||
GLOBE_OUTLINE_OTHER,
|
||||
] as never);
|
||||
map.setPaintProperty(outlineId, 'circle-stroke-width', [
|
||||
'case',
|
||||
['==', ['get', 'selected'], 1], 3.4,
|
||||
['==', ['get', 'highlighted'], 1], 2.7,
|
||||
['==', ['get', 'permitted'], 1], 1.8,
|
||||
0.0,
|
||||
0.7,
|
||||
] as never);
|
||||
} catch {
|
||||
// ignore
|
||||
@ -561,8 +567,9 @@ export function useGlobeShips(
|
||||
'interpolate', ['linear'], ['zoom'],
|
||||
3, ['to-number', ['get', 'iconSize3'], 0.35],
|
||||
7, ['to-number', ['get', 'iconSize7'], 0.45],
|
||||
10, ['to-number', ['get', 'iconSize10'], 0.56],
|
||||
14, ['to-number', ['get', 'iconSize14'], 0.72],
|
||||
10, ['to-number', ['get', 'iconSize10'], 0.58],
|
||||
14, ['to-number', ['get', 'iconSize14'], 0.85],
|
||||
18, ['to-number', ['get', 'iconSize18'], 2.5],
|
||||
] as unknown as number[],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-ignore-placement': true,
|
||||
@ -791,8 +798,9 @@ export function useGlobeShips(
|
||||
}),
|
||||
iconSize3: clampNumber(0.35 * sizeScale * scale, 0.25, 1.45),
|
||||
iconSize7: clampNumber(0.45 * sizeScale * scale, 0.3, 1.7),
|
||||
iconSize10: clampNumber(0.56 * sizeScale * scale, 0.35, 2.0),
|
||||
iconSize14: clampNumber(0.72 * sizeScale * scale, 0.45, 2.4),
|
||||
iconSize10: clampNumber(0.58 * sizeScale * scale, 0.35, 2.1),
|
||||
iconSize14: clampNumber(0.85 * sizeScale * scale, 0.45, 3.0),
|
||||
iconSize18: clampNumber(2.5 * sizeScale * scale, 1.0, 7.0),
|
||||
selected: selected ? 1 : 0,
|
||||
permitted: legacy ? 1 : 0,
|
||||
},
|
||||
@ -907,8 +915,9 @@ export function useGlobeShips(
|
||||
'interpolate', ['linear'], ['zoom'],
|
||||
3, ['to-number', ['get', 'iconSize3'], 0.35],
|
||||
7, ['to-number', ['get', 'iconSize7'], 0.45],
|
||||
10, ['to-number', ['get', 'iconSize10'], 0.56],
|
||||
14, ['to-number', ['get', 'iconSize14'], 0.72],
|
||||
10, ['to-number', ['get', 'iconSize10'], 0.58],
|
||||
14, ['to-number', ['get', 'iconSize14'], 0.85],
|
||||
18, ['to-number', ['get', 'iconSize18'], 2.5],
|
||||
] as unknown as number[],
|
||||
'icon-allow-overlap': true,
|
||||
'icon-ignore-placement': true,
|
||||
|
||||
@ -66,11 +66,11 @@ export function injectOceanBathymetryLayers(style: StyleSpecification, maptilerK
|
||||
type: 'fill',
|
||||
source: oceanSourceId,
|
||||
'source-layer': 'contour',
|
||||
minzoom: 6,
|
||||
minzoom: 5,
|
||||
maxzoom: 24,
|
||||
paint: {
|
||||
'fill-color': bathyFillColor,
|
||||
'fill-opacity': ['interpolate', ['linear'], ['zoom'], 0, 0.9, 6, 0.86, 10, 0.78],
|
||||
'fill-opacity': ['interpolate', ['linear'], ['zoom'], 0, 0.9, 5, 0.88, 8, 0.84, 10, 0.78],
|
||||
},
|
||||
} as unknown as LayerSpecification;
|
||||
|
||||
@ -79,7 +79,7 @@ export function injectOceanBathymetryLayers(style: StyleSpecification, maptilerK
|
||||
type: 'line',
|
||||
source: oceanSourceId,
|
||||
'source-layer': 'contour',
|
||||
minzoom: 6,
|
||||
minzoom: 5,
|
||||
maxzoom: 24,
|
||||
paint: {
|
||||
'line-color': 'rgba(255,255,255,0.06)',
|
||||
@ -94,7 +94,7 @@ export function injectOceanBathymetryLayers(style: StyleSpecification, maptilerK
|
||||
type: 'line',
|
||||
source: oceanSourceId,
|
||||
'source-layer': 'contour_line',
|
||||
minzoom: 8,
|
||||
minzoom: 7,
|
||||
paint: {
|
||||
'line-color': [
|
||||
'interpolate',
|
||||
@ -127,7 +127,7 @@ export function injectOceanBathymetryLayers(style: StyleSpecification, maptilerK
|
||||
type: 'line',
|
||||
source: oceanSourceId,
|
||||
'source-layer': 'contour_line',
|
||||
minzoom: 8,
|
||||
minzoom: 7,
|
||||
maxzoom: 24,
|
||||
filter: bathyMajorDepthFilter as unknown as unknown[],
|
||||
paint: {
|
||||
@ -143,14 +143,14 @@ export function injectOceanBathymetryLayers(style: StyleSpecification, maptilerK
|
||||
type: 'line',
|
||||
source: oceanSourceId,
|
||||
'source-layer': 'contour',
|
||||
minzoom: 4,
|
||||
minzoom: 3,
|
||||
maxzoom: 24,
|
||||
filter: bathyMajorDepthFilter as unknown as unknown[],
|
||||
paint: {
|
||||
'line-color': 'rgba(255,255,255,0.14)',
|
||||
'line-opacity': ['interpolate', ['linear'], ['zoom'], 4, 0.14, 8, 0.2, 12, 0.26],
|
||||
'line-blur': ['interpolate', ['linear'], ['zoom'], 4, 0.3, 10, 0.15],
|
||||
'line-width': ['interpolate', ['linear'], ['zoom'], 4, 0.35, 8, 0.55, 12, 0.85],
|
||||
'line-opacity': ['interpolate', ['linear'], ['zoom'], 3, 0.10, 6, 0.16, 8, 0.2, 12, 0.26],
|
||||
'line-blur': ['interpolate', ['linear'], ['zoom'], 3, 0.5, 6, 0.35, 10, 0.15],
|
||||
'line-width': ['interpolate', ['linear'], ['zoom'], 3, 0.25, 6, 0.4, 8, 0.55, 12, 0.85],
|
||||
},
|
||||
} as unknown as LayerSpecification;
|
||||
|
||||
|
||||
@ -45,7 +45,8 @@ export function makeGlobeCircleRadiusExpr() {
|
||||
const base3 = 4;
|
||||
const base7 = 6;
|
||||
const base10 = 8;
|
||||
const base14 = 11;
|
||||
const base14 = 12;
|
||||
const base18 = 32;
|
||||
|
||||
return [
|
||||
'interpolate',
|
||||
@ -58,7 +59,9 @@ export function makeGlobeCircleRadiusExpr() {
|
||||
10,
|
||||
['case', ['==', ['get', 'selected'], 1], 9.0, ['==', ['get', 'highlighted'], 1], 8.2, base10],
|
||||
14,
|
||||
['case', ['==', ['get', 'selected'], 1], 11.8, ['==', ['get', 'highlighted'], 1], 10.8, base14],
|
||||
['case', ['==', ['get', 'selected'], 1], 13.5, ['==', ['get', 'highlighted'], 1], 12.6, base14],
|
||||
18,
|
||||
['case', ['==', ['get', 'selected'], 1], 36, ['==', ['get', 'highlighted'], 1], 34, base18],
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user