import { IconLayer, TextLayer } from '@deck.gl/layers'; import type { Layer, PickingInfo } from '@deck.gl/core'; import { svgToDataUri } from '../../utils/svgToDataUri'; import { middleEastAirports } from '../../data/airports'; import type { Airport } from '../../data/airports'; export { type Airport }; export const IRAN_AIRPORT_COUNT = middleEastAirports.length; const US_BASE_ICAOS = new Set([ 'OMAD', 'OTBH', 'OKAJ', 'LTAG', 'OEPS', 'ORAA', 'ORBD', 'OBBS', 'OMTH', 'HDCL', ]); function getAirportColor(airport: Airport): string { const isMil = airport.type === 'military'; const isUS = isMil && US_BASE_ICAOS.has(airport.icao); if (isUS) return '#3b82f6'; if (isMil) return '#ef4444'; if (airport.type === 'international') return '#f59e0b'; return '#7c8aaa'; } function airportSvg(color: string, size: number): string { return ` `; } const iconCache = new Map(); function getIconUrl(airport: Airport): string { const color = getAirportColor(airport); const size = airport.type === 'military' && US_BASE_ICAOS.has(airport.icao) ? 48 : 40; const key = `${color}-${size}`; if (!iconCache.has(key)) { iconCache.set(key, svgToDataUri(airportSvg(color, size))); } return iconCache.get(key)!; } function hexToRgba(hex: string, alpha = 255): [number, number, number, number] { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); return [r, g, b, alpha]; } export interface IranAirportLayerConfig { visible: boolean; sc: number; fs?: number; onPick: (airport: Airport) => void; } export function createIranAirportLayers(config: IranAirportLayerConfig): Layer[] { const { visible, sc, onPick } = config; const fs = config.fs ?? 1; if (!visible) return []; const iconLayer = new IconLayer({ id: 'iran-airport-icon', data: middleEastAirports, getPosition: (d) => [d.lng, d.lat], getIcon: (d) => { const isMilUS = d.type === 'military' && US_BASE_ICAOS.has(d.icao); const sz = isMilUS ? 48 : 40; return { url: getIconUrl(d), width: sz, height: sz, anchorX: sz / 2, anchorY: sz / 2 }; }, getSize: (d) => (d.type === 'military' && US_BASE_ICAOS.has(d.icao) ? 20 : 16) * sc, updateTriggers: { getSize: [sc] }, pickable: true, onClick: (info: PickingInfo) => { if (info.object) onPick(info.object); return true; }, }); const labelLayer = new TextLayer({ id: 'iran-airport-label', data: middleEastAirports, getPosition: (d) => [d.lng, d.lat], getText: (d) => { const nameKo = d.nameKo ?? d.name; return nameKo.length > 10 ? nameKo.slice(0, 10) + '..' : nameKo; }, getSize: 11 * sc * fs, updateTriggers: { getSize: [sc] }, getColor: (d) => hexToRgba(getAirportColor(d)), getTextAnchor: 'middle', getAlignmentBaseline: 'top', getPixelOffset: [0, 10], fontFamily: 'monospace', fontWeight: 600, fontSettings: { sdf: true }, outlineWidth: 8, outlineColor: [0, 0, 0, 255], billboard: false, characterSet: 'auto', }); return [iconLayer, labelLayer]; }