Added airport data to map

This commit is contained in:
2025-04-10 18:08:06 -04:00
parent 0f8edc192b
commit 05c49dee4c
20 changed files with 653 additions and 78 deletions

View File

@@ -0,0 +1,104 @@
import { useState } from 'react';
import { Airport, AirportCategory } from '@lib/airport.types.ts';
import { Marker, Popup, useMapEvents } from 'react-leaflet';
import { getAirports } from '@lib/airport.ts';
import L from 'leaflet';
interface Bounds {
northEast: { lat: number; lon: number };
southWest: { lat: number; lon: number };
}
export default function AirportLayer() {
const [airports, setAirports] = useState<Airport[]>([]);
useMapEvents({
moveend: (event) => {
const map = event.target;
const bounds = map.getBounds();
const boundsParam: Bounds = {
northEast: {
lat: bounds.getNorth(),
lon: bounds.getEast()
},
southWest: {
lat: bounds.getSouth(),
lon: bounds.getWest()
}
};
// Call getAirports with the current map bounds and desired parameters.
getAirports({
bounds: boundsParam,
metars: true,
categories: [AirportCategory.SMALL, AirportCategory.MEDIUM, AirportCategory.LARGE],
limit: 200
})
.then((response) => {
console.log(response);
setAirports(response.data);
})
.catch((error) => {
console.error('Error fetching airports:', error);
setAirports([]);
});
}
});
return (
<>
{airports.map((airport, index) => {
const markerColor = getMarkerColor(airport);
const icon = createCustomIcon(markerColor);
return (
<Marker key={index} position={[airport.latitude, airport.longitude]} icon={icon}>
<Popup>
<div>
<h3>{airport.name || 'Unnamed Airport'}</h3>
<p>ICAO: {airport.icao || 'N/A'}</p>
<p>Flight Category: {airport.latest_metar ? airport.latest_metar.flight_category : 'No METAR Data'}</p>
</div>
</Popup>
</Marker>
);
})}
</>
);
}
function getMarkerColor(airport: Airport): string {
if (airport.latest_metar) {
switch (airport.latest_metar.flight_category.toUpperCase()) {
case 'IFR':
return '#ff0100';
case 'LIFR':
return '#7f007f';
case 'MVFR':
return '#00f';
case 'VFR':
return '#018000';
case 'UNKNOWN':
return '#3e3e3e';
default:
return '#3e3e3e';
}
} else {
return '#696969';
}
}
function createCustomIcon(color: string): L.DivIcon {
return L.divIcon({
html: `<div style="
background-color: ${color};
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid #fff;
"></div>`,
className: '',
iconSize: [20, 20],
iconAnchor: [10, 10]
});
}

View File

@@ -1,6 +1,5 @@
.header {
height: 56px;
margin-bottom: 120px;
background-color: var(--mantine-color-body);
border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
}

View File

@@ -1,12 +1,13 @@
import { useState } from 'react';
import { Burger, Container, Group, Text } from '@mantine/core';
import { Avatar, Burger, Container, Group, Text } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
// import { ReactComponent as Logo } from '../../../public/logo.svg';
import classes from './Header.module.css';
const links = [
{ link: '/', label: 'Map' },
{ link: '/airports', label: 'Airports' },
{ link: '/metars', label: 'METARs' }
{ link: '/metars', label: 'Metars' }
];
export function Header() {
@@ -31,7 +32,11 @@ export function Header() {
return (
<header className={classes.header}>
<Container size='md' className={classes.inner}>
<Text>Aviation Weather</Text>
<span style={{ display: 'flex', flexDirection: 'row' }}>
<Text>Aviation Weather</Text>
<Avatar src='../../../public/logo.svg' alt="it's me" />
</span>
{/*<Logo />*/}
<Group gap={5} visibleFrom='xs'>
{items}
</Group>