Added airport data to map
This commit is contained in:
104
ui/src/components/AirportLayer.tsx
Normal file
104
ui/src/components/AirportLayer.tsx
Normal 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]
|
||||
});
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user