fix(map): scale flat icons and prioritize relation layers

This commit is contained in:
htlee 2026-02-15 14:24:00 +09:00
부모 c31d26124c
커밋 15378ed7ff

파일 보기

@ -182,6 +182,11 @@ const DEPTH_DISABLED_PARAMS = {
depthWriteEnabled: false,
} as const;
const FLAT_SHIP_ICON_SIZE = 19;
const FLAT_SHIP_ICON_SIZE_SELECTED = 28;
const FLAT_LEGACY_HALO_RADIUS = 14;
const FLAT_LEGACY_HALO_RADIUS_SELECTED = 18;
const GLOBE_OVERLAY_PARAMS = {
// In globe mode we want depth-testing against the globe so features on the far side don't draw through.
// Still disable depth writes so our overlays don't interfere with each other.
@ -717,7 +722,12 @@ export function Map3D({
if (!map) return;
let cancelled = false;
let retries = 0;
const maxRetries = 6;
const maxRetries = 18;
const getCurrentProjection = () => {
const projectionConfig = map.getProjection?.();
return projectionConfig && "type" in projectionConfig ? (projectionConfig.type as MapProjectionId | undefined) : undefined;
};
const syncProjectionAndDeck = () => {
if (cancelled) return;
@ -732,7 +742,9 @@ export function Map3D({
const next = projection;
try {
if (getCurrentProjection() !== next) {
map.setProjection({ type: next });
}
map.setRenderWorldCopies(next !== "globe");
} catch (e) {
if (!cancelled && retries < maxRetries) {
@ -781,6 +793,11 @@ export function Map3D({
} catch {
// ignore
}
if (!map.getLayer(layer.id) && !cancelled && retries < maxRetries) {
retries += 1;
window.requestAnimationFrame(() => syncProjectionAndDeck());
return;
}
}
} else {
// Tear down globe custom layer (if present), restore MapboxOverlay interleaved.
@ -1433,7 +1450,7 @@ export function Map3D({
return;
}
const before = map.getLayer("ships-globe-halo") ? "ships-globe-halo" : undefined;
const before = undefined;
if (!map.getLayer(layerId)) {
try {
@ -1525,7 +1542,7 @@ export function Map3D({
return;
}
const before = map.getLayer("ships-globe-halo") ? "ships-globe-halo" : undefined;
const before = undefined;
if (!map.getLayer(layerId)) {
try {
@ -1613,7 +1630,7 @@ export function Map3D({
return;
}
const before = map.getLayer("ships-globe-halo") ? "ships-globe-halo" : undefined;
const before = undefined;
if (!map.getLayer(layerId)) {
try {
@ -1706,7 +1723,7 @@ export function Map3D({
return;
}
const before = map.getLayer("ships-globe-halo") ? "ships-globe-halo" : undefined;
const before = undefined;
if (!map.getLayer(layerId)) {
try {
@ -1805,22 +1822,61 @@ export function Map3D({
);
}
if (overlays.fleetCircles && projection !== "globe" && (fleetCircles?.length ?? 0) > 0) {
if (settings.showShips && projection !== "globe") {
layers.push(
new ScatterplotLayer<FleetCircle>({
id: "fleet-circles",
data: fleetCircles,
new IconLayer<AisTarget>({
id: "ships",
data: shipData,
pickable: true,
// Keep icons horizontal on the sea surface when view is pitched/rotated.
billboard: false,
parameters: overlayParams,
iconAtlas: "/assets/ship.svg",
iconMapping: SHIP_ICON_MAPPING,
getIcon: () => "ship",
getPosition: (d) =>
[d.lon, d.lat] as [number, number],
getAngle: (d) => (isFiniteNumber(d.cog) ? d.cog : 0),
sizeUnits: "pixels",
getSize: (d) => (selectedMmsi && d.mmsi === selectedMmsi ? FLAT_SHIP_ICON_SIZE_SELECTED : FLAT_SHIP_ICON_SIZE),
getColor: (d) => getShipColor(d, selectedMmsi, legacyHits?.get(d.mmsi)?.shipCode ?? null),
alphaCutoff: 0.05,
updateTriggers: {
getSize: [selectedMmsi],
getColor: [selectedMmsi, legacyHits],
},
}),
);
}
if (settings.showShips && projection !== "globe" && legacyTargets.length > 0) {
layers.push(
new ScatterplotLayer<AisTarget>({
id: "legacy-halo",
data: legacyTargets,
pickable: false,
billboard: false,
// This ring is most prone to z-fighting, so force it into pure painter's-order rendering.
parameters: overlayParams,
filled: false,
stroked: true,
radiusUnits: "meters",
getRadius: (d) => d.radiusNm * 1852,
radiusUnits: "pixels",
getRadius: (d) =>
(selectedMmsi && d.mmsi === selectedMmsi ? FLAT_LEGACY_HALO_RADIUS_SELECTED : FLAT_LEGACY_HALO_RADIUS),
lineWidthUnits: "pixels",
getLineWidth: 1,
getLineColor: () => [245, 158, 11, 140],
getPosition: (d) => d.center,
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];
},
getPosition: (d) =>
[d.lon, d.lat] as [number, number],
updateTriggers: {
getRadius: [selectedMmsi],
getLineColor: [legacyHits],
},
}),
);
}
@ -1878,60 +1934,22 @@ export function Map3D({
);
}
if (settings.showShips && projection !== "globe" && legacyTargets.length > 0) {
if (overlays.fleetCircles && projection !== "globe" && (fleetCircles?.length ?? 0) > 0) {
layers.push(
new ScatterplotLayer<AisTarget>({
id: "legacy-halo",
data: legacyTargets,
new ScatterplotLayer<FleetCircle>({
id: "fleet-circles",
data: fleetCircles,
pickable: false,
billboard: false,
// This ring is most prone to z-fighting, so force it into pure painter's-order rendering.
parameters: overlayParams,
filled: false,
stroked: true,
radiusUnits: "pixels",
getRadius: (d) => (selectedMmsi && d.mmsi === selectedMmsi ? 22 : 16),
radiusUnits: "meters",
getRadius: (d) => d.radiusNm * 1852,
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];
},
getPosition: (d) =>
[d.lon, d.lat] as [number, number],
updateTriggers: {
getRadius: [selectedMmsi],
getLineColor: [legacyHits],
},
}),
);
}
if (settings.showShips && projection !== "globe") {
layers.push(
new IconLayer<AisTarget>({
id: "ships",
data: shipData,
pickable: true,
// Keep icons horizontal on the sea surface when view is pitched/rotated.
billboard: false,
parameters: overlayParams,
iconAtlas: "/assets/ship.svg",
iconMapping: SHIP_ICON_MAPPING,
getIcon: () => "ship",
getPosition: (d) =>
[d.lon, d.lat] as [number, number],
getAngle: (d) => (isFiniteNumber(d.cog) ? d.cog : 0),
sizeUnits: "pixels",
getSize: (d) => (selectedMmsi && d.mmsi === selectedMmsi ? 34 : 22),
getColor: (d) => getShipColor(d, selectedMmsi, legacyHits?.get(d.mmsi)?.shipCode ?? null),
alphaCutoff: 0.05,
updateTriggers: {
getSize: [selectedMmsi],
getColor: [selectedMmsi, legacyHits],
},
getLineWidth: 1,
getLineColor: () => [245, 158, 11, 140],
getPosition: (d) => d.center,
}),
);
}