import { memo, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Marker, Popup, Source, Layer } from 'react-map-gl/maplibre'; import type { SatellitePosition } from '../types'; interface Props { satellites: SatellitePosition[]; } const CAT_COLORS: Record = { reconnaissance: '#ef4444', communications: '#3b82f6', navigation: '#22c55e', weather: '#a855f7', other: '#6b7280', }; const CAT_LABELS: Record = { reconnaissance: 'RECON', communications: 'COMMS', navigation: 'NAV', weather: 'WX', other: 'SAT', }; const SVG_RECON = ( <> ); const SVG_COMMS = ( <> ); const SVG_NAV = ( <> ); const SVG_WEATHER = ( <> ); const SVG_OTHER = ( <> ); const SVG_MAP: Record = { reconnaissance: SVG_RECON, communications: SVG_COMMS, navigation: SVG_NAV, weather: SVG_WEATHER, other: SVG_OTHER, }; export function SatelliteLayer({ satellites }: Props) { const trackData = useMemo(() => { const features: GeoJSON.Feature[] = []; for (const sat of satellites) { if (!sat.groundTrack || sat.groundTrack.length < 2) continue; const color = CAT_COLORS[sat.category]; let segment: [number, number][] = []; for (let i = 0; i < sat.groundTrack.length; i++) { const [lat, lng] = sat.groundTrack[i]; if (i > 0) { const [, prevLng] = sat.groundTrack[i - 1]; if (Math.abs(lng - prevLng) > 180) { if (segment.length > 1) { features.push({ type: 'Feature', properties: { color }, geometry: { type: 'LineString', coordinates: segment.map(([la, lo]) => [lo, la]) }, }); } segment = []; } } segment.push([lat, lng]); } if (segment.length > 1) { features.push({ type: 'Feature', properties: { color }, geometry: { type: 'LineString', coordinates: segment.map(([la, lo]) => [lo, la]) }, }); } } return { type: 'FeatureCollection' as const, features }; }, [satellites]); return ( <> {trackData.features.length > 0 && ( )} {satellites.map(sat => ( ))} ); } const SatelliteMarker = memo(function SatelliteMarker({ sat }: { sat: SatellitePosition }) { const [showPopup, setShowPopup] = useState(false); const { t } = useTranslation(); const color = CAT_COLORS[sat.category]; const svgBody = SVG_MAP[sat.category]; const size = 22; return ( <>
{ e.stopPropagation(); setShowPopup(true); }} > {svgBody}
{sat.name}
{showPopup && ( setShowPopup(false)} closeOnClick={false} anchor="bottom" maxWidth="200px" className="gl-popup">
{CAT_LABELS[sat.category]} {sat.name}
{t('satellite.norad')}{sat.noradId}
{t('satellite.lat')}{sat.lat.toFixed(2)}°
{t('satellite.lng')}{sat.lng.toFixed(2)}°
{t('satellite.alt')}{Math.round(sat.altitude)} km
)} ); });