Files
aeris/src/components/map/camera-controller-utils.ts
kew a08f1c7250 feat: add first person view (FPV) functionality and HUD (#9)
* feat: add first person view (FPV) functionality and HUD

- Updated FlightCard component to include FPV toggle button and state management.
- Introduced FpvHud component for displaying flight data in FPV mode.
- Enhanced useFlights hook to support FPV bounding box logic for fetching flights.
- Added keyboard shortcuts for toggling FPV mode.
- Updated settings to include FPV-related configurations (pitch, chase distance).
- Implemented major airports caching for improved performance.
- Added fetchFlightByIcao24 function for single aircraft state retrieval.

* Refactor CameraController and ControlPanel components; enhance flight search functionality

- Simplified CameraController by removing unused refs and effects, and centralized map interaction management.
- Updated ControlPanel to support flight lookup with new props and integrated flight search results.
- Enhanced SearchContent to include flight matching logic and improved user feedback for flight searches.
- Introduced caching for flight callsign lookups in OpenSky API integration to optimize performance.
- Removed unnecessary settings related to FPV pitch and free camera mode from use-settings hook.

* feat: enhance FPV functionality and improve flight data handling

- Added `projectLngLatElevationPixelDelta` function to calculate pixel deltas based on longitude, latitude, and elevation.
- Updated `CameraController` to utilize new FPV parameters and improve camera behavior during flight.
- Enhanced flight data handling in `FlightLayers` to ensure proper tracking and display of flight information.
- Improved UI components for better user experience, including adjustments to the FPV HUD and flight card.
- Added error handling for image loading in the control panel.
- Refactored altitude and speed calculations to ensure they handle non-finite values gracefully.
- Adjusted map attribution behavior for better responsiveness on different screen sizes.
2026-02-21 12:31:17 +05:30

94 lines
2.5 KiB
TypeScript

import type maplibregl from "maplibre-gl";
import { MercatorCoordinate } from "maplibre-gl";
export const FPV_DISTANCE_ZOOM_OFFSET = 1.1;
export function clamp01(value: number): number {
return Math.max(0, Math.min(1, value));
}
export function smoothstep(t: number): number {
return t * t * (3 - 2 * t);
}
export function lerp(from: number, to: number, t: number): number {
return from + (to - from) * t;
}
export function normalizeLng(lng: number): number {
return ((lng + 540) % 360) - 180;
}
export function lerpLng(from: number, to: number, t: number): number {
const delta = ((to - from + 540) % 360) - 180;
return normalizeLng(from + delta * t);
}
export function fpvZoomForAltitude(altMeters: number): number {
if (!Number.isFinite(altMeters)) return 12;
const alt = Math.max(altMeters, 0);
if (alt < 50) return 16.2;
const zoom = 18.1 - 2.0 * Math.log10(Math.max(alt, 50));
return Math.max(10.1, Math.min(16.2, zoom));
}
export function projectLngLatElevationPixelDelta(
map: maplibregl.Map,
lng: number,
lat: number,
elevationMeters: number,
): { dx: number; dy: number } | null {
type Transform3DLike = {
_pixelMatrix3D?: unknown;
centerPoint?: { x: number; y: number };
coordinatePoint: (
coord: MercatorCoordinate,
elevation: number,
pixelMatrix3D: unknown,
) => { x: number; y: number } | null;
};
const tr = (map as unknown as { transform?: Transform3DLike }).transform;
if (!tr || typeof tr.coordinatePoint !== "function") return null;
const pixelMatrix3D = tr._pixelMatrix3D;
const centerPoint = tr.centerPoint;
if (!pixelMatrix3D || !centerPoint) return null;
let p: { x: number; y: number } | null = null;
try {
p = tr.coordinatePoint(
MercatorCoordinate.fromLngLat({ lng, lat }),
elevationMeters,
pixelMatrix3D,
);
} catch {
return null;
}
if (!p || !Number.isFinite(p.x) || !Number.isFinite(p.y)) return null;
return { dx: p.x - centerPoint.x, dy: p.y - centerPoint.y };
}
export function setMapInteractionsEnabled(
map: maplibregl.Map,
enabled: boolean,
): void {
if (enabled) {
map.dragPan.enable();
map.dragRotate.enable();
map.scrollZoom.enable();
map.touchZoomRotate.enable();
map.doubleClickZoom.enable();
map.keyboard.enable();
return;
}
map.dragPan.disable();
map.dragRotate.disable();
map.scrollZoom.disable();
map.touchZoomRotate.disable();
map.doubleClickZoom.disable();
map.keyboard.disable();
}