From 155f91c240df8fccc17b9e738a959732afa2ce30 Mon Sep 17 00:00:00 2001 From: Luke Else Date: Wed, 18 Jun 2025 21:46:32 +0100 Subject: [PATCH] FEAT: Added ability to query weatherservice API --- src/lib/api/types/metar.ts | 42 ++++++++++++++++++++++++ src/lib/api/weatherservice.ts | 16 ++++++++++ src/routes/+layout.svelte | 11 ++----- src/routes/+page.svelte | 44 ++++++++++++++++++++++++-- src/routes/api/metar/[icao]/+server.ts | 19 +++++++++++ 5 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 src/lib/api/types/metar.ts create mode 100644 src/lib/api/weatherservice.ts create mode 100644 src/routes/api/metar/[icao]/+server.ts diff --git a/src/lib/api/types/metar.ts b/src/lib/api/types/metar.ts new file mode 100644 index 0000000..1e4991e --- /dev/null +++ b/src/lib/api/types/metar.ts @@ -0,0 +1,42 @@ +export type MetarCloud = { + cover: string; + base: number | null; +}; + +export type Metar = { + metar_id: number; + icaoId: string; + receiptTime: string; + obsTime: number; + reportTime: string; + temp: number; + dewp: number; + wdir: number; + wspd: number; + wgst: number | null; + visib: string; + altim: number; + slp: number | null; + qcField: number; + wxString: string | null; + presTend: string | null; + maxT: number | null; + minT: number | null; + maxT24: number | null; + minT24: number | null; + precip: number | null; + pcp3hr: number | null; + pcp6hr: number | null; + pcp24hr: number | null; + snow: number | null; + vertVis: number | null; + metarType: string; + rawOb: string; + mostRecent: number; + lat: number; + lon: number; + elev: number; + prior: number; + name: string; + clouds: MetarCloud[]; +}; \ No newline at end of file diff --git a/src/lib/api/weatherservice.ts b/src/lib/api/weatherservice.ts new file mode 100644 index 0000000..bf84f89 --- /dev/null +++ b/src/lib/api/weatherservice.ts @@ -0,0 +1,16 @@ +import type { Metar } from '$lib/api/types/metar'; + +export const aviationWeatherApi = { + /** + * Fetches METAR data for a given ICAO code. + * @param icao - The ICAO code (e.g., "EGKK") + * @returns A Promise resolving to a Metar object or null if not found. + */ + async fetchMetar(icao: string): Promise { + const url = `/api/metar/${encodeURIComponent(icao)}`; + const response = await fetch(url); + if (!response.ok) throw new Error(`Failed to fetch METAR: ${response.statusText}`); + const data = await response.json(); + return data as Metar | null; + } +}; \ No newline at end of file diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 71ec0da..97e27ea 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -10,7 +10,7 @@ let icaoInput = $state(''); $effect(() => { - // Watch for changes to icaoInput and update the store if exactly 4 chars + // Watch for changes to icaoInput and update the store if exactly 4 chars if (icaoInput.length === 4) { icao.set(icaoInput); } else { @@ -18,8 +18,6 @@ } }); - - let { children } = $props(); @@ -30,7 +28,7 @@ class="text-xxl flex w-full items-center justify-center gap-10 px-8 py-4 font-semibold text-green-600" > METAR ⛅ - TAF ☔ + TAF ☔
@@ -39,10 +37,7 @@

METARIUS

Start Here

-
+

diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 9941a42..f1da9ad 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -5,6 +5,32 @@ import Loading from '$lib/components/Loading.svelte'; import { icao } from '$lib/stores/icao'; + import { aviationWeatherApi } from '$lib/api/weatherservice'; + import type { Metar } from '$lib/api/types/metar' + import { onDestroy } from 'svelte'; + + let metar: Metar | null = null; + let loading = false; + let error: string | null = null; + + let unsubscribe = icao.subscribe(async (val) => { + metar = null; + error = null; + if (val && val.length === 4) { + loading = true; + try { + const result = await aviationWeatherApi.fetchMetar(val); + metar = result; + if (!metar) error = 'No METAR found for this ICAO code.'; + } catch (e) { + error = e instanceof Error ? e.message : 'Unknown error'; + } finally { + loading = false; + } + } + }); + + onDestroy(() => unsubscribe());

@@ -22,8 +48,22 @@

METAR

Results

- {#if $icao} - ICAO: {$icao} + {#if loading} + + {:else if error} + {error} + {:else if metar} +
+
ICAO: {metar.icaoId}
+
Airport: {metar.name}
+
Observed: {metar.reportTime}
+
Temperature: {metar.temp}°C
+
Dew Point: {metar.dewp}°C
+
Wind: {metar.wdir}° at {metar.wspd}kt
+
Visibility: {metar.visib}
+
Pressure: {metar.altim} hPa
+
Raw METAR: {metar.rawOb}
+
{:else} {/if} diff --git a/src/routes/api/metar/[icao]/+server.ts b/src/routes/api/metar/[icao]/+server.ts new file mode 100644 index 0000000..2e7dec1 --- /dev/null +++ b/src/routes/api/metar/[icao]/+server.ts @@ -0,0 +1,19 @@ +import type { RequestHandler } from '@sveltejs/kit'; +import type { Metar } from '$lib/api/types/metar'; + +export const GET: RequestHandler = async ({ params }) => { + const icao = params.icao?.toUpperCase(); + if (!icao || icao.length !== 4) { + return new Response(JSON.stringify({ error: 'Invalid ICAO' }), { status: 400 }); + } + + const url = `https://aviationweather.gov/api/data/metar?ids=${icao}&format=json`; + const res = await fetch(url); + if (!res.ok) { + return new Response(JSON.stringify({ error: 'Failed to fetch METAR' }), { status: 502 }); + } + const data: Metar[] = await res.json(); + return new Response(JSON.stringify(data[0] ?? null), { + headers: { 'Content-Type': 'application/json' } + }); +}; \ No newline at end of file