feat: 3D aircraft model overhaul and multi-source flight data proxy (Resolves #15) (#16)

* Refactor aircraft photo and hero banner components to reset loading state on photo change

- Updated Lightbox component to reset image loading state when navigating between photos.
- Modified HeroBanner component to reset loading state when the photo changes.

Clean up control panel search logic

- Removed unnecessary hasResults variable in SearchContent component.

Implement flight API client with fallback mechanism

- Added flight-api-client to handle fetching flight data from multiple sources (airplanes.live, adsb.lol, OpenSky).
- Introduced flight-api-parsing module to convert raw API responses into standardized FlightState objects.
- Created flight-api-types for shared types between API responses.

Refactor useFlights hook to utilize new flight API client

- Updated useFlights hook to fetch flights using the new flight API client.
- Removed credit management logic as it is no longer applicable with the new API structure.

Fix useFlightMonitors to fetch flight data by hex address

- Changed useFlightMonitors to use fetchFlightByHex instead of fetchFlightByIcao24.

Update geo utility function for better readability

- Refactored splitAtAntimeridian function to improve variable naming and clarity.

Enhance OpenSky types with additional fields

- Added typeCode and registration fields to FlightState type for better integration with readsb data.

* fix: correct 6 files that diverged during rebase (iata code, globe mode ref, terrain attribution, cache eviction, opensky parsing)

* fix: improve keyboard shortcuts help focus trapping

feat: add showAirspace option to MapAttribution component

fix: clear hideTimer on ScrollArea cleanup

refactor: change pendingFpvRef to MutableRefObject in useFlightMonitors

fix: handle sessionStorage availability in useFlightTrack

refactor: increase POLL_INTERVAL_MS in useFlights for better performance

fix: optimize keyboard shortcuts dialog check

refactor: optimize useMergedTrails by caching selected flight position

feat: extend Settings type with airspace options

refactor: improve airline logo normalization functions

refactor: enhance flight API client with serialized rate limiting

refactor: optimize registration country lookup with pre-built maps

refactor: enhance logo cache management with size limits

feat: update map attribution to include airspace option

fix: validate rawState in parseStateRow function

refactor: improve utility functions with clamp implementation

* feat: add ATC lookup functionality and GPU memory monitoring

- Implemented ATC lookup functions in `atc-lookup.ts` for converting IATA to ICAO codes, finding nearby ATC feeds, and looking up ATC feeds by code.
- Introduced `atc-types.ts` to define types and priorities for ATC feeds.
- Added GPU memory monitoring in `gpu-memory-monitor.ts` to track WebGL resource allocations and provide memory reports.
- Enhanced trail stitching logic in `trail-stitching.ts` by adding a function to clear the splined track cache and optimizing altitude checks.

* feat: enhance flight data handling and improve API resilience

- Implemented a maximum empty response streak guard in useFlights to prevent data loss during transient API failures.
- Added immediate fetch on network reconnect in useFlights to ensure timely data retrieval.
- Updated useMergedTrails to include timestamps for trail points.
- Removed smoothAnimations setting from useSettings as it is no longer needed.
- Enhanced useTrailHistory to preserve last-known trails during empty flight responses and added dynamic jump detection for tab resume scenarios.
- Improved flight API client with a circuit breaker mechanism to handle provider failures and prevent excessive retries.
- Updated flight API parsing to reject non-JSON responses from OpenSky and other providers.
- Enhanced trail smoothing and stitching logic to ensure better continuity at junctions between historical and live data.

* feat: migrate aircraft models to Cloudinary CDN and update mapping logic

* fix: adjust UI component styles and improve trail smoothing parameters

* fix: adjust base aircraft size for improved rendering

* feat: update changelog with recent enhancements and modify data source attribution

* fix: update model optimization details and remove Draco compression dependency

* feat: update changelog with recent code review fixes and fallback provider adjustments
This commit is contained in:
kew
2026-03-23 01:25:11 +05:30
committed by GitHub
parent 147b69b944
commit eb1103f63f
95 changed files with 9667 additions and 1384 deletions

227
src/lib/atc-lookup.ts Normal file
View File

@ -0,0 +1,227 @@
import type { AtcFeed, AtcFeedType } from "./atc-types";
import { FEED_TYPE_PRIORITY } from "./atc-types";
import { ATC_FEEDS, getFeedsByIcao } from "./atc-feeds";
import { AIRPORTS, type Airport } from "./airports";
// ── Constants ──────────────────────────────────────────────────────────
/** Approximate nautical miles per degree of latitude. */
const NM_PER_DEG = 60;
/** Maximum search radius in nautical miles for auto-feed discovery. */
const MAX_SEARCH_RADIUS_NM = 60;
// ── IATA → ICAO mapping ───────────────────────────────────────────────
/**
* Common IATA → ICAO mapping for airports in the feed database.
* Only includes airports that have ATC feeds to keep the map small.
*/
const IATA_TO_ICAO: Record<string, string> = {
// United States
JFK: "KJFK",
LAX: "KLAX",
ORD: "KORD",
ATL: "KATL",
DFW: "KDFW",
DEN: "KDEN",
SFO: "KSFO",
LAS: "KLAS",
MIA: "KMIA",
EWR: "KEWR",
SEA: "KSEA",
BOS: "KBOS",
MSP: "KMSP",
PHX: "KPHX",
DTW: "KDTW",
FLL: "KFLL",
IAD: "KIAD",
CLT: "KCLT",
DCA: "KDCA",
HNL: "PHNL",
ANC: "PANC",
// Europe
LHR: "EGLL",
CDG: "LFPG",
AMS: "EHAM",
FRA: "EDDF",
MAD: "LEMD",
MUC: "EDDM",
IST: "LTFM",
LGW: "EGKK",
BCN: "LEBL",
FCO: "LIRF",
ZRH: "LSZH",
DUB: "EIDW",
VIE: "LOWW",
OSL: "ENGM",
CPH: "EKCH",
ARN: "ESSA",
WAW: "EPWA",
LIS: "LPPT",
// Middle East
DXB: "OMDB",
DOH: "OTHH",
JED: "OEJN",
// Asia Pacific
HND: "RJTT",
NRT: "RJAA",
SIN: "WSSS",
HKG: "VHHH",
ICN: "RKSI",
BKK: "VTBS",
KUL: "WMKK",
DEL: "VIDP",
BOM: "VABB",
// Australia / Oceania
SYD: "YSSY",
MEL: "YMML",
AKL: "NZAA",
// Americas (non-US)
YYZ: "CYYZ",
YVR: "CYVR",
MEX: "MMMX",
GRU: "SBGR",
EZE: "SAEZ",
SCL: "SCEL",
BOG: "SKBO",
// Africa
JNB: "FAOR",
CAI: "HECA",
};
const ICAO_TO_IATA: Record<string, string> = {};
for (const [iata, icao] of Object.entries(IATA_TO_ICAO)) {
ICAO_TO_IATA[icao] = iata;
}
/**
* Convert IATA code to ICAO code for airports in the feed database.
*/
export function iataToIcao(iata: string): string | null {
return IATA_TO_ICAO[iata.toUpperCase()] ?? null;
}
/**
* Convert ICAO code to IATA code for airports in the feed database.
*/
export function icaoToIata(icao: string): string | null {
return ICAO_TO_IATA[icao.toUpperCase()] ?? null;
}
/** ICAO codes of airports that have ATC feeds. */
const ICAO_SET = new Set(Object.keys(ATC_FEEDS));
/** Precomputed list of airports that have ATC feeds. */
const ATC_AIRPORTS: Airport[] = AIRPORTS.filter((a) => {
// Match by converting IATA → ICAO convention for known airports
// LiveATC uses ICAO codes; our airport DB uses IATA
const icao = iataToIcao(a.iata);
return icao !== null && ICAO_SET.has(icao);
});
// ── Lookup Functions ───────────────────────────────────────────────────
/**
* Simple distance approximation in nautical miles (good enough for feed lookup).
* Uses equirectangular approximation — accurate to ~1% within 60nm.
*/
function approxDistanceNm(
lat1: number,
lng1: number,
lat2: number,
lng2: number,
): number {
const dLat = (lat2 - lat1) * NM_PER_DEG;
const dLng =
(lng2 - lng1) *
NM_PER_DEG *
Math.cos(((lat1 + lat2) / 2) * (Math.PI / 180));
return Math.sqrt(dLat * dLat + dLng * dLng);
}
export type NearbyAtcResult = {
/** Airport ICAO code */
icao: string;
/** Airport IATA code (if known) */
iata: string | null;
/** Airport name */
name: string;
/** Distance in nautical miles */
distanceNm: number;
/** Available ATC feeds, sorted by type priority */
feeds: AtcFeed[];
};
/**
* Find airports with ATC feeds near a given latitude/longitude.
* Returns results sorted by distance (nearest first).
*
* @param lat Latitude in degrees
* @param lng Longitude in degrees
* @param radiusNm Search radius in nautical miles (default: 60)
* @param limit Maximum results to return (default: 5)
*/
export function findNearbyAtcFeeds(
lat: number,
lng: number,
radiusNm: number = MAX_SEARCH_RADIUS_NM,
limit: number = 5,
): NearbyAtcResult[] {
const clampedRadius = Math.min(radiusNm, MAX_SEARCH_RADIUS_NM);
const results: NearbyAtcResult[] = [];
for (const airport of ATC_AIRPORTS) {
const dist = approxDistanceNm(lat, lng, airport.lat, airport.lng);
if (dist > clampedRadius) continue;
const icao = iataToIcao(airport.iata);
if (!icao) continue;
const feeds = getFeedsByIcao(icao).sort(
(a, b) => FEED_TYPE_PRIORITY[a.type] - FEED_TYPE_PRIORITY[b.type],
);
if (feeds.length === 0) continue;
results.push({
icao,
iata: airport.iata,
name: airport.name,
distanceNm: Math.round(dist * 10) / 10,
feeds,
});
}
results.sort((a, b) => a.distanceNm - b.distanceNm);
return results.slice(0, limit);
}
/**
* Find the single nearest airport with ATC feeds.
* Convenience wrapper around findNearbyAtcFeeds.
*/
export function findNearestAtcFeed(
lat: number,
lng: number,
): NearbyAtcResult | null {
const results = findNearbyAtcFeeds(lat, lng, MAX_SEARCH_RADIUS_NM, 1);
return results[0] ?? null;
}
/**
* Look up ATC feeds by IATA or ICAO code.
*/
export function lookupAtcFeeds(code: string): AtcFeed[] {
const upper = code.toUpperCase();
// Try ICAO first
const icaoFeeds = getFeedsByIcao(upper);
if (icaoFeeds.length > 0) return icaoFeeds;
// Try IATA → ICAO
const icao = iataToIcao(upper);
if (icao) return getFeedsByIcao(icao);
return [];
}