- 백엔드 vessels 라우터/서비스/스케줄러 추가 (1분 주기 한국 해역 폴링) - 공통 useVesselSignals 훅 + vesselApi/vesselSignalClient 서비스 추가 - MapView에 VesselLayer/VesselInteraction/MapBoundsTracker 통합 (호버·팝업·상세 모달) - OilSpill/HNS/Rescue/Incidents 뷰에 선박 신호 연동 - vesselMockData 정리, aerial IMAGE_API_URL 기본값 변경
56 lines
1.5 KiB
TypeScript
56 lines
1.5 KiB
TypeScript
import type { VesselPosition, BoundingBox } from './vesselTypes.js';
|
|
|
|
const VESSEL_TTL_MS = 10 * 60 * 1000; // 10분
|
|
|
|
const cachedVessels = new Map<string, VesselPosition>();
|
|
let lastUpdated: Date | null = null;
|
|
|
|
// lastUpdate가 TTL을 초과한 선박을 캐시에서 제거.
|
|
// lastUpdate 파싱이 불가능한 경우 보수적으로 유지한다.
|
|
function evictStale(): void {
|
|
const now = Date.now();
|
|
for (const [mmsi, vessel] of cachedVessels) {
|
|
const ts = Date.parse(vessel.lastUpdate);
|
|
if (Number.isNaN(ts)) continue;
|
|
if (now - ts > VESSEL_TTL_MS) {
|
|
cachedVessels.delete(mmsi);
|
|
}
|
|
}
|
|
}
|
|
|
|
export function updateVesselCache(vessels: VesselPosition[]): void {
|
|
for (const vessel of vessels) {
|
|
if (!vessel.mmsi) continue;
|
|
cachedVessels.set(vessel.mmsi, vessel);
|
|
}
|
|
evictStale();
|
|
lastUpdated = new Date();
|
|
}
|
|
|
|
export function getVesselsInBounds(bounds: BoundingBox): VesselPosition[] {
|
|
const result: VesselPosition[] = [];
|
|
for (const v of cachedVessels.values()) {
|
|
if (
|
|
v.lon >= bounds.minLon &&
|
|
v.lon <= bounds.maxLon &&
|
|
v.lat >= bounds.minLat &&
|
|
v.lat <= bounds.maxLat
|
|
) {
|
|
result.push(v);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export function getCacheStatus(): {
|
|
count: number;
|
|
bangjeCount: number;
|
|
lastUpdated: Date | null;
|
|
} {
|
|
let bangjeCount = 0;
|
|
for (const v of cachedVessels.values()) {
|
|
if (v.shipNm && v.shipNm.toUpperCase().includes('BANGJE')) bangjeCount++;
|
|
}
|
|
return { count: cachedVessels.size, bangjeCount, lastUpdated };
|
|
}
|