diff --git a/service/src/metars/model.rs b/service/src/metars/model.rs index 89240c5..e145a5e 100644 --- a/service/src/metars/model.rs +++ b/service/src/metars/model.rs @@ -15,9 +15,9 @@ pub struct QualityControlFlags { #[derive(Serialize, Deserialize, Debug)] pub struct SkyCondition { - #[serde(rename = "@sky_cover")] + #[serde(rename(deserialize = "@sky_cover"))] pub sky_cover: String, - #[serde(rename = "@cloud_base_ft_agl")] + #[serde(rename(deserialize = "@cloud_base_ft_agl"))] pub cloud_base_ft_agl: Option } diff --git a/ui/package-lock.json b/ui/package-lock.json index 8404614..7a19aa2 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -12,9 +12,11 @@ "@mantine/hooks": "^7.0.0", "@mantine/modals": "^7.0.0", "axios": "^1.4.0", + "chart.js": "^4.4.0", "leaflet": "^1.9.4", "next": "^13.4.19", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-icons": "^4.11.0", "react-leaflet": "^4.2.1", @@ -196,6 +198,11 @@ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", "dev": true }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@mantine/core": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.0.0.tgz", @@ -1219,6 +1226,17 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chart.js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.0.tgz", + "integrity": "sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -4042,6 +4060,15 @@ "node": ">=0.10.0" } }, + "node_modules/react-chartjs-2": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-5.2.0.tgz", + "integrity": "sha512-98iN5aguJyVSxp5U3CblRLH67J8gkfyGNbiK3c+l1QI/G4irHMPQw44aEPmjVag+YKTyQ260NcF82GTQ3bdscA==", + "peerDependencies": { + "chart.js": "^4.1.1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", diff --git a/ui/package.json b/ui/package.json index 3635b9f..7c82298 100644 --- a/ui/package.json +++ b/ui/package.json @@ -13,9 +13,11 @@ "@mantine/hooks": "^7.0.0", "@mantine/modals": "^7.0.0", "axios": "^1.4.0", + "chart.js": "^4.4.0", "leaflet": "^1.9.4", "next": "^13.4.19", "react": "^18.2.0", + "react-chartjs-2": "^5.2.0", "react-dom": "^18.2.0", "react-icons": "^4.11.0", "react-leaflet": "^4.2.1", diff --git a/ui/src/api/metar.types.ts b/ui/src/api/metar.types.ts index c2b75b5..02afa45 100644 --- a/ui/src/api/metar.types.ts +++ b/ui/src/api/metar.types.ts @@ -1,3 +1,13 @@ +export interface SkyCondition { + sky_cover: string; + cloud_base_ft_agl: number; +} + +export interface QualityControlFlags { + auto: boolean; + auto_station: boolean; +} + export interface Metar { raw_text: string; station_id: string; @@ -11,15 +21,9 @@ export interface Metar { visibility_statute_mi: string; altim_in_hg: number; sea_level_pressure_mb: number; - quality_control_flags: { - auto: boolean; - auto_station: boolean; - }; + quality_control_flags: QualityControlFlags; wx_string: string; - sky_condition: { - sky_cover: string; - cloud_base_ft_agl: number; - }[]; + sky_condition: SkyCondition[]; flight_category: 'VFR' | 'MVFR' | 'LIFR' | 'IFR' | 'UNKN'; three_hr_pressure_tendency_mb: number; metar_type: string; diff --git a/ui/src/components/Metars/MetarModal.tsx b/ui/src/components/Metars/MetarModal.tsx index c8be480..0f4ce39 100644 --- a/ui/src/components/Metars/MetarModal.tsx +++ b/ui/src/components/Metars/MetarModal.tsx @@ -15,6 +15,7 @@ import { import { useState } from 'react'; import { Grid, Modal, Tooltip } from '@mantine/core'; import './metars.css'; +import SkyConditions from './SkyConditions'; interface MetarModalProps { airport: Airport; @@ -137,7 +138,9 @@ function MetarInfo({ metar }: { metar: Metar }) { {metar.wind_speed_kt != undefined && metar.wind_speed_kt > 0 ? `${metar.wind_speed_kt} KT` : 'CALM'} - {/* {metar.sky_condition != undefined && metar.sky_condition.map((skyCondition) => <>test)} */} + + + {metar.wx_string && metar.wx_string.split(' ').map((wx) => )} diff --git a/ui/src/components/Metars/SkyConditions.tsx b/ui/src/components/Metars/SkyConditions.tsx new file mode 100644 index 0000000..7ea9435 --- /dev/null +++ b/ui/src/components/Metars/SkyConditions.tsx @@ -0,0 +1,66 @@ +'use client'; + +import { Metar, SkyCondition } from '@/api/metar.types'; +import { + Chart as ChartJS, + CategoryScale, + LinearScale, + PointElement, + LineElement, + Title, + Tooltip, + Filler, + Legend +} from 'chart.js'; +import { Line } from 'react-chartjs-2'; + +ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Filler, Legend); + +export default function SkyConditions({ metar }: { metar: Metar }) { + function skyConditionColor(skyCondition: SkyCondition) { + if (skyCondition.sky_cover == 'CLR') { + return '#FFFFFF'; + } else if (skyCondition.sky_cover == 'FEW') { + return '#19c4e6'; + } else if (skyCondition.sky_cover == 'SCT') { + return '#6119e6'; + } else if (skyCondition.sky_cover == 'BKN') { + return '#e6c019'; + } else if (skyCondition.sky_cover == 'OVC') { + return '#e68019'; + } else { + return '#e6194b'; + } + } + function createDataset(skyCondition: SkyCondition) { + return { + fill: true, + label: skyCondition.sky_cover, + data: [skyCondition.cloud_base_ft_agl, skyCondition.cloud_base_ft_agl], + backgroundColor: skyConditionColor(skyCondition) + }; + } + if (metar.sky_condition && metar.sky_condition.length > 0) { + console.log(metar); + const options = { + responsive: true, + plugins: { + legend: { + display: false + }, + title: { + display: true, + text: 'Sky Conditions' + } + } + }; + const data = { + labels: ['', ''], + datasets: metar.sky_condition.map((skyCondition) => createDataset(skyCondition)) + }; + + return ; + } else { + return <>; + } +}