import { IconLayer, TextLayer } from '@deck.gl/layers'; import type { PickingInfo, Layer } from '@deck.gl/core'; import { svgToDataUri } from '../../utils/svgToDataUri'; import { ME_FACILITIES } from '../../data/middleEastFacilities'; import type { MEFacility } from '../../data/middleEastFacilities'; export const ME_FACILITY_COUNT = ME_FACILITIES.length; // ─── Type colors ────────────────────────────────────────────────────────────── const TYPE_COLORS: Record = { naval: '#3b82f6', military_hq: '#ef4444', missile: '#dc2626', intelligence: '#8b5cf6', government: '#f59e0b', radar: '#06b6d4', }; // ─── SVG generators ────────────────────────────────────────────────────────── // naval: anchor symbol function navalSvg(color: string): string { return ` `; } // military_hq: star symbol function militaryHqSvg(color: string): string { return ` `; } // missile: upward arrow / rocket shape function missileSvg(color: string): string { return ` `; } // intelligence: magnifying glass function intelligenceSvg(color: string): string { return ` `; } // government: pillars / building function governmentSvg(color: string): string { return ` `; } // radar: radio waves / dish function radarSvg(color: string): string { return ` `; } function buildMESvg(type: MEFacility['type'], color: string): string { switch (type) { case 'naval': return navalSvg(color); case 'military_hq': return militaryHqSvg(color); case 'missile': return missileSvg(color); case 'intelligence': return intelligenceSvg(color); case 'government': return governmentSvg(color); case 'radar': return radarSvg(color); } } // ─── Module-level icon cache ───────────────────────────────────────────────── const meIconCache = new Map(); function getMEIconUrl(type: MEFacility['type']): string { if (!meIconCache.has(type)) { meIconCache.set(type, svgToDataUri(buildMESvg(type, TYPE_COLORS[type]))); } return meIconCache.get(type)!; } // ─── Label color ───────────────────────────────────────────────────────────── function getMELabelColor(type: MEFacility['type']): [number, number, number, number] { const hex = TYPE_COLORS[type]; 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, 255]; } // ─── Public interface ──────────────────────────────────────────────────────── export interface MEFacilityLayerConfig { visible: boolean; sc: number; onPick: (f: MEFacility) => void; } export function createMEFacilityLayers(config: MEFacilityLayerConfig): Layer[] { if (!config.visible) return []; const { sc, onPick } = config; return [ new IconLayer({ id: 'me-facility-icon', data: ME_FACILITIES, getPosition: (d) => [d.lng, d.lat], getIcon: (d) => ({ url: getMEIconUrl(d.type), width: 36, height: 36, anchorX: 18, anchorY: 18, }), getSize: 16 * sc, updateTriggers: { getSize: [sc] }, pickable: true, onClick: (info: PickingInfo) => { if (info.object) onPick(info.object); return true; }, }), new TextLayer({ id: 'me-facility-label', data: ME_FACILITIES, getPosition: (d) => [d.lng, d.lat], getText: (d) => (d.nameKo.length > 12 ? d.nameKo.slice(0, 12) + '..' : d.nameKo), getSize: 9 * sc, updateTriggers: { getSize: [sc] }, getColor: (d) => getMELabelColor(d.type), getTextAnchor: 'middle', getAlignmentBaseline: 'top', getPixelOffset: [0, 10], fontFamily: 'monospace', fontWeight: 700, outlineWidth: 2, outlineColor: [0, 0, 0, 200], billboard: false, characterSet: 'auto', }), ]; }