diff --git a/src/lib/stores/url-hash-store.ts b/src/lib/stores/url-hash-store.ts deleted file mode 100644 index 4adf409..0000000 --- a/src/lib/stores/url-hash-store.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { type Writable, writable } from 'svelte/store'; - -import { browser } from '$app/environment'; -import { goto } from '$app/navigation'; -import { page } from '$app/state'; - -import { debounce, isNumeric } from '$lib/utils'; - -import type { Parameters } from '$lib/types'; - -export type UrlHashStore = Writable; - -export const urlHashStore = (initialValues: Parameters): UrlHashStore => { - const urlHashes: Writable = writable({}); - - const defaultValues = JSON.parse(JSON.stringify(initialValues)); - urlHashes.set(JSON.parse(JSON.stringify(defaultValues))); - - function updateURL() { - const searchParams = page.url.searchParams.toString().replaceAll('%2C', ','); - const link = `?${searchParams}${page.url.hash ?? ''}`; - if (page.url.search !== window.location.search) { - goto(link, { - noScroll: true, - keepFocus: true - }); - } - } - - const processURLParamsUpdate = debounce(() => updateURL()); - - const updateURLParams = (values: Parameters) => { - if (browser) { - let changedParams = false; - - for (const [key, value] of Object.entries(values)) { - let defaultValue = defaultValues[key]; - - // params key is array - if (defaultValue && Array === defaultValue.constructor) { - if (JSON.stringify(value) === JSON.stringify(defaultValue)) { - if (page.url.searchParams.has(key) && page.url.searchParams.get(key) !== value) { - page.url.searchParams.delete(key); - changedParams = true; - } - } else { - let array = value as any[]; - // remove empty string when array has more then 1 values - if (array.length > 1 && array.includes('')) { - array = array.filter((e: string) => e !== ''); - } - page.url.searchParams.set(key, array.join(',')); - changedParams = true; - } - } else { - let val: number | string = value as number | string; - if (isNumeric(defaultValue)) { - defaultValue = Number(defaultValue); - } - if (isNumeric(value as number | string)) { - val = Number(value); - } - - if (val != defaultValue) { - page.url.searchParams.set(key, String(val)); - changedParams = true; - } else { - if (page.url.searchParams.has(key) && page.url.searchParams.get(key) !== val) { - page.url.searchParams.delete(key); - changedParams = true; - } - } - } - if (page.url.searchParams.has(key) && page.url.searchParams.get(key) === '') { - if ( - defaultValue === undefined || - (defaultValue && Array === defaultValue.constructor && defaultValue.length === 0) || - defaultValue === '0' - ) { - page.url.searchParams.delete(key); - changedParams = true; - } - } - } - - if (changedParams) { - processURLParamsUpdate(); - } - } - }; - - // check if urlParams overrides any default values OR stored values - if (browser && page.url.search) { - for (const [key, value] of page.url.searchParams.entries()) { - let defaultValue = defaultValues[key]; - - if (defaultValue && defaultValue.constructor === Array) { - if (JSON.stringify(defaultValue) !== JSON.stringify(value)) { - urlHashes.update((urlValues) => { - urlValues[key] = value.split(/,|%2C/); - return urlValues; - }); - } - } else { - let val: number | string = value; - if (isNumeric(defaultValue)) { - defaultValue = Number(defaultValue); - } - if (isNumeric(value)) { - val = Number(value); - } - if (defaultValue !== val) { - urlHashes.update((urlValues) => { - urlValues[key] = val; - return urlValues; - }); - } - } - } - } - - urlHashes.subscribe((values) => { - updateURLParams(values); - }); - - return urlHashes; -}; diff --git a/src/routes/weather/+layout.svelte b/src/routes/weather/+layout.svelte index 068b3af..3569560 100644 --- a/src/routes/weather/+layout.svelte +++ b/src/routes/weather/+layout.svelte @@ -16,18 +16,66 @@ let { children }: Props = $props(); + interface CurrentWeather { + current: { + temperature_2m: number; + weather_code: number; + }; + } + let location = $state(get(storedLocation)); let mounted = $state(false); + let currentWeather = $state(null); // Subscribe to location changes storedLocation.subscribe((value) => { location = value; + if (mounted) { + loadCurrentWeather(); + } }); onMount(() => { mounted = true; + loadCurrentWeather(); }); + const loadCurrentWeather = async () => { + if (!location?.latitude) return; + + try { + const response = await fetch( + `https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}¤t=temperature_2m,weather_code&forecast_days=1` + ); + const data = await response.json(); + currentWeather = data; + } catch (error) { + console.error('Failed to load current weather:', error); + } + }; + + const getWeatherIcon = (code: number): string => { + const iconMap: Record = { + 0: '☀️', + 1: '🌤️', + 2: '⛅', + 3: '☁️', + 45: '🌫️', + 48: '🌫️', + 51: '🌦️', + 53: '🌦️', + 55: '🌦️', + 61: '🌧️', + 63: '🌧️', + 65: '🌧️', + 71: '🌨️', + 73: '🌨️', + 75: '❄️', + 95: '⛈️' + }; + return iconMap[code] || '☁️'; + }; + const getPageTitle = () => { const path = $page.url.pathname; if (path.includes('/compare')) return 'Model Comparison'; @@ -91,6 +139,20 @@

+ + + {#if currentWeather} +
+
+
+ {getWeatherIcon(currentWeather.current.weather_code)} +
+
+ {Math.round(currentWeather.current.temperature_2m)}°C +
+
+
+ {/if} {/if} diff --git a/src/routes/weather/+page.ts b/src/routes/weather/+page.ts index 0486d7d..6b8351d 100644 --- a/src/routes/weather/+page.ts +++ b/src/routes/weather/+page.ts @@ -1,6 +1,6 @@ import { redirect } from '@sveltejs/kit'; -import type { PageLoad } from '$types'; +import type { PageLoad } from './$types'; export const prerender = true; diff --git a/src/routes/weather/14-day/+page.svelte b/src/routes/weather/14-day/+page.svelte index a3e4eb8..85227ab 100644 --- a/src/routes/weather/14-day/+page.svelte +++ b/src/routes/weather/14-day/+page.svelte @@ -6,7 +6,6 @@ import { dev } from '$app/environment'; import { storedLocation } from '$lib/stores/settings'; - import { urlHashStore } from '$lib/stores/url-hash-store'; import { Label } from '$lib/components/ui/label'; import { Switch } from '$lib/components/ui/switch'; @@ -15,16 +14,16 @@ import { defaultParameters } from './options'; let node: HTMLElement; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let charts: any[] = []; - let Highcharts = $state(null); + let chart: any; + let Highcharts = $state(null); let showLegend = $state(false); let averageOnly = $state(false); const location = get(storedLocation); - const params = urlHashStore({ + // Local component state for chart configuration + let params = $state({ latitude: [52.52], longitude: [13.41], ...defaultParameters, @@ -37,44 +36,40 @@ /// Highcharts needs to be loaded in `onMount` to work with prerendered SSG Highcharts = (await import('highcharts')).default; const more = (await import('highcharts/highcharts-more')).default; - (more as any)(Highcharts); + // more(Highcharts); if (dev) { // const HighchartsDebugger = await import('highcharts/modules/debugger'); // HighchartsDebugger.default(Highcharts); - // @ts-ignore const Debugger = ( - (await import('highcharts/es-modules/Extensions/Debugger/Debugger.js')) as any + await import('highcharts/es-modules/Extensions/Debugger/Debugger.js' as any) ).default; - // @ts-ignore const ErrorMessages = ( - (await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js')) as any + await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js' as any) ).default; - Highcharts.errorMessages = ErrorMessages; - Debugger.compose(Highcharts.Chart); + if (Highcharts) { + (Highcharts as any).errorMessages = ErrorMessages; + Debugger.compose(Highcharts.Chart); + } } }); $effect(() => { - count = 0; - if (Highcharts) { - charts.forEach((c) => c.destroy()); - charts = []; - // eslint-disable-next-line svelte/no-dom-manipulating - (node as any).replaceChildren(); + const loadData = async () => { + count = 0; + if (Highcharts) { + node.replaceChildren(); - (async () => { const dataDaily = await fetch( `https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}&timeformat=unixtime&daily=sunset,sunrise&forecast_days=14` ); const wd = await dataDaily.json(); const dataReq = await fetch( - `https://ensemble-api.open-meteo.com/v1/ensemble?latitude=${location.latitude}&longitude=${location.longitude}&hourly=${$params.hourly?.join(',')}&models=${$params.models?.join(',')}&timeformat=unixtime&forecast_days=14` + `https://ensemble-api.open-meteo.com/v1/ensemble?latitude=${location.latitude}&longitude=${location.longitude}&hourly=${params.hourly?.join(',') || ''}&models=${params.models?.join(',') || ''}&timeformat=unixtime&forecast_days=14` ); const data = await dataReq.json(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any let plotBands: any = []; if ('daily' in wd && 'sunrise' in wd.daily && 'sunset' in wd.daily) { let rise = wd.daily.sunrise; @@ -91,7 +86,7 @@ let minValues = new Array(data.hourly.time.length).fill(undefined); let maxValues = new Array(data.hourly.time.length).fill(undefined); - for (let variable of $params.hourly || []) { + for (let variable of params.hourly || []) { const chartDiv = document.createElement('div'); let unit; @@ -171,19 +166,20 @@ className: 'highcharts-average-series' }); - const chart = new Highcharts.Chart(chartDiv, { - credits: { - text: count === ($params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '', - href: 'http://open-meteo.com' - }, - + new Highcharts!.Chart({ chart: { + renderTo: chartDiv, height: showLegend ? '400px' : '300px', styledMode: true, - marginLeft: '50', + marginLeft: 50, marginRight: 0 }, + credits: { + text: count === (params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '', + href: 'http://open-meteo.com' + }, + lang: { locale: 'en-GB' }, @@ -196,8 +192,8 @@ subtitle: { text: count === 0 - ? `Compare ${$params.hourly?.join(', ')} in models: ` + - $params.models?.join(', ') + + ? `Compare ${params.hourly?.join(', ') || ''} in models: ` + + (params.models?.join(', ') || '') + '' : '', align: 'left' @@ -245,7 +241,7 @@ verticalAlign: 'bottom' }, - series: series, + series: series as any, responsive: { rules: [ @@ -264,22 +260,23 @@ }); count++; - charts.push(chart); - // eslint-disable-next-line svelte/no-dom-manipulating node.appendChild(chartDiv); } - })(); - } + } + }; + loadData(); }); onDestroy(() => { - charts.forEach((c) => c.destroy()); + if (chart) { + chart.destroy(); + } });
@@ -314,7 +311,7 @@ name="Show legend" bind:checked={showLegend} onCheckedChange={() => { - $params.hourly = $params.hourly; + params.hourly = params.hourly; }} /> @@ -325,7 +322,7 @@ name="Average only" bind:checked={averageOnly} onCheckedChange={() => { - $params.hourly = $params.hourly; + params.hourly = params.hourly; }} /> diff --git a/src/routes/weather/14-day/options.ts b/src/routes/weather/14-day/options.ts index 8727075..970b98d 100644 --- a/src/routes/weather/14-day/options.ts +++ b/src/routes/weather/14-day/options.ts @@ -1,31 +1,5 @@ +// Default configuration for 14-day ensemble forecast charts export const defaultParameters = { - daily: [], - hourly: [], - models: [], - current: [], - minutely_15: [], - - timezone: 'UTC', - location_mode: 'location_search', - csv_coordinates: undefined, - - time_mode: 'forecast_days', - past_days: '0', - forecast_days: '14', - - end_date: undefined, - start_date: undefined, - - past_hours: undefined, - cell_selection: undefined, - forecast_hours: undefined, - past_minutely_15: undefined, - temporal_resolution: undefined, - forecast_minutely_15: undefined, - - tilt: '0', - azimuth: '0', - timeformat: 'iso8601', wind_speed_unit: 'kmh', temperature_unit: 'celsius', diff --git a/src/routes/weather/compare/+page.svelte b/src/routes/weather/compare/+page.svelte index f95d888..2091673 100644 --- a/src/routes/weather/compare/+page.svelte +++ b/src/routes/weather/compare/+page.svelte @@ -6,7 +6,6 @@ import { dev } from '$app/environment'; import { storedLocation } from '$lib/stores/settings'; - import { urlHashStore } from '$lib/stores/url-hash-store'; import { Checkbox } from '$lib/components/ui/checkbox'; import { Label } from '$lib/components/ui/label'; @@ -17,16 +16,15 @@ import { defaultParameters } from './options'; let node: HTMLElement; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let charts: any[] = []; - let Highcharts = $state(); + let chart: any; + let Highcharts = $state(null); let showLegend = $state(false); let averageOnly = $state(false); const location = get(storedLocation); - const params = urlHashStore({ + let params = $state({ latitude: [52.52], longitude: [13.41], ...defaultParameters, @@ -48,38 +46,34 @@ if (dev) { // const HighchartsDebugger = await import('highcharts/modules/debugger'); // HighchartsDebugger.default(Highcharts); - // @ts-ignore const Debugger = ( - (await import('highcharts/es-modules/Extensions/Debugger/Debugger.js')) as any + await import('highcharts/es-modules/Extensions/Debugger/Debugger.js' as any) ).default; - // @ts-ignore const ErrorMessages = ( - (await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js')) as any + await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js' as any) ).default; - Highcharts.errorMessages = ErrorMessages; - Debugger.compose(Highcharts.Chart); + if (Highcharts) { + (Highcharts as any).errorMessages = ErrorMessages; + Debugger.compose(Highcharts.Chart); + } } }); $effect(() => { - count = 0; - if (Highcharts) { - charts.forEach((c) => c.destroy()); - charts = []; - // eslint-disable-next-line svelte/no-dom-manipulating - (node as any).replaceChildren(); + const loadData = async () => { + count = 0; + if (Highcharts) { + node.replaceChildren(); - (async () => { const dataReq = await fetch( - `https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}&hourly=${$params.hourly?.join(',')}&models=${$params.models?.join(',')}&timeformat=unixtime&daily=sunset,sunrise` + `https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}&hourly=${params.hourly?.join(',') || ''}&models=${params.models?.join(',') || ''}&timeformat=unixtime&daily=sunset,sunrise` ); const data = await dataReq.json(); - let dailyFirstModelKeyParts = Object.keys(data.daily)[1].split('_'); - dailyFirstModelKeyParts.shift(); - const dailyFirstModelKey = dailyFirstModelKeyParts.join('_'); + let dailyFirstModelKey: string[] | string = Object.keys(data.daily)[1].split('_'); + dailyFirstModelKey.shift(); + dailyFirstModelKey = dailyFirstModelKey.join('_'); - // eslint-disable-next-line @typescript-eslint/no-explicit-any let plotBands: any = []; if ( 'daily' in data && @@ -97,7 +91,7 @@ }); } - for (let variable of $params.hourly || []) { + for (let variable of params.hourly || []) { const chartDiv = document.createElement('div'); let unit; @@ -169,19 +163,20 @@ className: 'highcharts-average-series' }); - const chart = new Highcharts.Chart(chartDiv, { - credits: { - text: count === ($params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '', - href: 'http://open-meteo.com' - }, - + new Highcharts!.Chart({ chart: { + renderTo: chartDiv, height: showLegend ? '400px' : '300px', styledMode: true, - marginLeft: '50', + marginLeft: 50, marginRight: 0 }, + credits: { + text: count === (params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '', + href: 'http://open-meteo.com' + }, + lang: { locale: 'en-GB' }, @@ -194,8 +189,8 @@ subtitle: { text: count === 0 - ? `Compare ${$params.hourly?.join(', ')} in models: ` + - $params.models?.join(', ') + + ? `Compare ${params.hourly?.join(', ') || ''} in models: ` + + (params.models?.join(', ') || '') + '' : '', align: 'left' @@ -243,7 +238,7 @@ verticalAlign: 'bottom' }, - series: series, + series: series as any, responsive: { rules: [ @@ -262,22 +257,23 @@ }); count++; - charts.push(chart); - // eslint-disable-next-line svelte/no-dom-manipulating node.appendChild(chartDiv); } - })(); - } + } + }; + loadData(); }); onDestroy(() => { - charts.forEach((c) => c.destroy()); + if (chart) { + chart.destroy(); + } });
@@ -312,7 +308,7 @@ name="Show legend" bind:checked={showLegend} onCheckedChange={() => { - $params.hourly = $params.hourly; + params.hourly = params.hourly; }} /> @@ -323,7 +319,7 @@ name="Average only" bind:checked={averageOnly} onCheckedChange={() => { - $params.hourly = $params.hourly; + params.hourly = params.hourly; }} /> @@ -334,48 +330,46 @@

Models

- {#if $params.models && $params.models.length > 0} + {#if params.models && params.models.length > 0}
- {$params.models?.length} / {models.flat().length} + {params.models?.length || 0} / {models.length}
{/if}
- {#each models as group, i (i)} -
- {#each group as { value, label } (value)} -
- { - if ($params.models?.includes(value)) { - $params.models = $params.models.filter((item: string) => { - return item !== value; - }); - } else if ($params.models) { - $params.models.push(value); - $params.models = $params.models; - } - }} - /> - -
- {/each} -
- {/each} +
+ {#each models as { value, label } (value)} +
+ { + if (params.models?.includes(value)) { + params.models = params.models.filter((item) => { + return item !== value; + }); + } else if (params.models) { + params.models.push(value); + params.models = params.models; + } + }} + /> + +
+ {/each} +
@@ -386,12 +380,12 @@ Hourly Weather Variables - {#if $params.hourly && $params.hourly.length > 0} + {#if params.hourly && params.hourly.length > 0}
- {$params.hourly?.length} / {hourly.flat().length} + {params.hourly?.length || 0} / {hourly.flat().length}
{/if} @@ -408,16 +402,16 @@ id="{value}_hourly" class="border-border-dark cursor-pointer bg-muted/50 duration-100 group-hover:border-[currentColor]" {value} - checked={$params.hourly?.includes(value)} + checked={params.hourly?.includes(value)} aria-labelledby="{value}_label" onCheckedChange={() => { - if ($params.hourly?.includes(value)) { - $params.hourly = $params.hourly.filter((item: string) => { + if (params.hourly?.includes(value)) { + params.hourly = params.hourly.filter((item) => { return item !== value; }); - } else if ($params.hourly) { - $params.hourly.push(value); - $params.hourly = $params.hourly; + } else if (params.hourly) { + params.hourly.push(value); + params.hourly = params.hourly; } }} /> diff --git a/src/routes/weather/compare/options.ts b/src/routes/weather/compare/options.ts index a0a4378..463bab5 100644 --- a/src/routes/weather/compare/options.ts +++ b/src/routes/weather/compare/options.ts @@ -1,31 +1,5 @@ +// Default configuration for weather comparison charts export const defaultParameters = { - daily: [], - hourly: [], - models: [], - current: [], - minutely_15: [], - - timezone: 'UTC', - location_mode: 'location_search', - csv_coordinates: undefined, - - time_mode: 'forecast_days', - past_days: '0', - forecast_days: '7', - - end_date: undefined, - start_date: undefined, - - past_hours: undefined, - cell_selection: undefined, - forecast_hours: undefined, - past_minutely_15: undefined, - temporal_resolution: undefined, - forecast_minutely_15: undefined, - - tilt: '0', - azimuth: '0', - timeformat: 'iso8601', wind_speed_unit: 'kmh', temperature_unit: 'celsius', diff --git a/src/routes/weather/options.ts b/src/routes/weather/options.ts index 7c2c742..a58f330 100644 --- a/src/routes/weather/options.ts +++ b/src/routes/weather/options.ts @@ -6,20 +6,18 @@ export const defaultParameters = { }; export const models = [ - [{ value: 'best_match', label: 'Best match' }], - [ - { value: 'gfs_seamless', label: 'NCEP GFS Seamless' }, - { value: 'jma_seamless', label: 'JMA Seamless' }, - { value: 'kma_seamless', label: 'KMA Seamless' }, - { value: 'icon_seamless', label: 'DWD ICON Seamless' }, - { value: 'gem_seamless', label: 'GEM Seamless' }, - { value: 'meteofrance_seamless', label: 'Météo-France Seamless' }, - { value: 'arpae_cosmo_seamless', label: 'ARPAE Seamless' }, - { value: 'metno_seamless', label: 'MET Norway Seamless (with ECMWF)' }, - { value: 'knmi_seamless', label: 'KNMI Seamless (with ECMWF)' }, - { value: 'dmi_seamless', label: 'DMI Seamless (with ECMWF)' }, - { value: 'ukmo_seamless', label: 'UK Met Office Seamless' } - ] + { value: 'best_match', label: 'Best match' }, + { value: 'gfs_seamless', label: 'NCEP GFS Seamless' }, + { value: 'jma_seamless', label: 'JMA Seamless' }, + { value: 'kma_seamless', label: 'KMA Seamless' }, + { value: 'icon_seamless', label: 'DWD ICON Seamless' }, + { value: 'gem_seamless', label: 'GEM Seamless' }, + { value: 'meteofrance_seamless', label: 'Météo-France Seamless' }, + { value: 'arpae_cosmo_seamless', label: 'ARPAE Seamless' }, + { value: 'metno_seamless', label: 'MET Norway Seamless (with ECMWF)' }, + { value: 'knmi_seamless', label: 'KNMI Seamless (with ECMWF)' }, + { value: 'dmi_seamless', label: 'DMI Seamless (with ECMWF)' }, + { value: 'ukmo_seamless', label: 'UK Met Office Seamless' } ]; export const hourly = [ diff --git a/src/routes/weather/utils/weather-codes.ts b/src/routes/weather/utils/weather-codes.ts index c562ce1..9e7b191 100644 --- a/src/routes/weather/utils/weather-codes.ts +++ b/src/routes/weather/utils/weather-codes.ts @@ -1,4 +1,4 @@ -const map: Record = { +const weatherCodes: Record = { 0: 'clear', 1: 'clear', 2: 'cloudy', @@ -79,4 +79,5 @@ const map: Record = { 96: 'thunderstorm', 99: 'tornado' }; -export default map; + +export default weatherCodes; diff --git a/src/routes/weather/week/+page.ts b/src/routes/weather/week/+page.ts index e2a912f..b2c878b 100644 --- a/src/routes/weather/week/+page.ts +++ b/src/routes/weather/week/+page.ts @@ -6,7 +6,7 @@ import { storedLocation } from '$lib/stores/settings'; import { geoLocationNameToRoute } from '$lib/utils/meteo'; -import type { PageLoad } from '$types'; +import type { PageLoad } from './$types'; export const prerender = true; diff --git a/src/routes/weather/week/[location]/+page.svelte b/src/routes/weather/week/[location]/+page.svelte index 7ccefe4..7d20f68 100644 --- a/src/routes/weather/week/[location]/+page.svelte +++ b/src/routes/weather/week/[location]/+page.svelte @@ -1,12 +1,11 @@ @@ -363,10 +313,12 @@ style="min-height: 256px" class="weather-week gap-md-2 mb-4 flex flex-col md:flex-row" > - {#if weatherDaily} - {#each weatherDaily.time as time, index (index)} + {#await weatherDaily then wd} + {#each wd.daily.time as time, index (index)} {@const selected = time.getDate() === selectedDay.getDate()} - {#if !isNaN(weatherDaily.temperature_2m_max.values(index)!)} + {#if wd.daily.temperature_2m_max.values(index) != null && !isNaN(Number(wd.daily.temperature_2m_max + .values(index)! + .toFixed(1)))}
= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`} + class="weather-temp-max flex min-w-[65px] justify-center rounded-t p-1 text-sm" + style={`background-color: ${getColor(Math.round(wd.daily.temperature_2m_max.values(index) ?? 0), params.temperature_unit)}; color: ${(wd.daily.temperature_2m_min.values(index) ?? 0) < (params.temperature_unit === 'celsius' ? 4 : 7) || (wd.daily.temperature_2m_min.values(index) ?? 0) >= (params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`} > - {weatherDaily.temperature_2m_max.values(index)!.toFixed(1)} - {$params.temperature_unit === 'celsius' ? '°C' : '°F'} + {wd.daily.temperature_2m_max.values(index)?.toFixed(1)} + {params.temperature_unit === 'celsius' ? '°C' : '°F'}
= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`} + class="weather-temp-min flex min-w-[65px] justify-center rounded-b p-1 text-sm" + style={`background: ${getColor(Math.round(wd.daily.temperature_2m_min.values(index) ?? 0), params.temperature_unit)}; color: ${(wd.daily.temperature_2m_min.values(index) ?? 0) < (params.temperature_unit === 'celsius' ? 4 : 7) || (wd.daily.temperature_2m_min.values(index) ?? 0) >= (params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`} > - {weatherDaily.temperature_2m_min.values(index)!.toFixed(1)} - {$params.temperature_unit === 'celsius' ? '°C' : '°F'} + {wd.daily.temperature_2m_min.values(index)?.toFixed(1)} + {params.temperature_unit === 'celsius' ? '°C' : '°F'}
@@ -427,7 +379,7 @@
- {Number(weatherDaily.sunshine_duration.values(index)! / 3600).toFixed(0)}h + {Number((wd.daily.sunshine_duration.values(index) ?? 0) / 3600).toFixed(0)}h
@@ -440,17 +392,17 @@
- {Number(weatherDaily.precipitation_sum.values(index)).toFixed( + {Number(wd.daily.precipitation_sum.values(index)).toFixed( 1 - )}{$params.precipitation_unit === 'mm' ? 'mm' : "'"} + )}{params.precipitation_unit === 'mm' ? 'mm' : "'"}
{/if} {/each} - {:else} -

Loading...

- {/if} + {:catch error} +

{error.message}

+ {/await}

@@ -469,7 +421,7 @@
Weather Week {location.name} - {#if weather && weather.entries[0] && weather.entries[0].values} + {#await weather then weather} {weather.hourlyTime[index].getHours() < 10 ? '0' : ''}{weather.hourlyTime[ index ].getHours()} - {@const tempVal = weather.entries[0].values?.[index] ?? '0'} @@ -549,9 +503,9 @@ >Temp graph {#each weather.indexes as index, j (j)} - {@const temp = Number(weather.entries[0].values?.[index])} + {@const temp = weather.entries?.[0]?.values?.[index]} - {#if !isNaN(temp)} + {#if temp !== undefined && !isNaN(temp)} {temp.toFixed(0)}{temp?.toFixed(0)} {/if} {/each} @@ -578,29 +532,49 @@ > {#each weather.indexes as index, j (j)} - {@const val = entry.values?.[index]} - {@const valNum = Number(val)} - {@const bgColor = getColor(valNum, $params.temperature_unit)} - {#if val !== undefined && !isNaN(valNum)} - + {#if entry.values && !isNaN(entry.values[index])} = + (params.temperature_unit === 'celsius' ? 40 : 104) + ? 'white' + : 'black') : ''}; {entry.name === 'precipitation_probability' - ? 'color: ' + (valNum > 50 ? 'white' : 'hsl(var(--foreground)') + ? 'background: rgba(0, 0, 230,' + + weather.entries[2].values![index] / 120 + + ')' + : ''}; + {entry.name === 'precipitation_probability' + ? 'color: ' + + (weather.entries[2].values![index] > 50 + ? 'white' + : 'hsl(var(--foreground)') : ''}; {entry.name === 'relative_humidity_2m' - ? 'background: rgba(0, 240, 240,' + valNum ** 3.8 / 10 ** 8.2 + ')' - : ''};">{val}{entry.name === 'precipitation' || entry.name === 'temperature_2m' + ? entry.values![index].toFixed(1) + : entry.values![index]} {/if} {/each} @@ -622,10 +596,10 @@ today.getDate() && weather.hourlyTime[index].getHours() === today.getHours() ? 'now' : ''}" - style="transform: rotate({weather.windDirections[ + style="transform: rotate({weather.windDirections![ index - ]}deg);min-width: {5000 / weather.entriesLength}px; max-width: {5000 / - weather.entriesLength}px;" + ]}deg);min-width: {5000 / (weather.entriesLength || 1)}px; max-width: {5000 / + (weather.entriesLength || 1)}px;" > {/if} - {/if} + {/await}

- {#if weatherDaily} - {@const sunrise = new Date(Number(weatherDaily.sunrise.valuesInt64(selectedDayIndex)) * 1000)} - {@const sunset = new Date(Number(weatherDaily.sunset.valuesInt64(selectedDayIndex)) * 1000)} + {#await weatherDaily then wd} + {@const sunrise = new Date(Number(wd.daily.sunrise.valuesInt64(selectedDayIndex)) * 1000)} + {@const sunset = new Date(Number(wd.daily.sunset.valuesInt64(selectedDayIndex)) * 1000)}
@@ -655,31 +629,36 @@ Sunset: {pad(sunset.getHours())}:{pad(sunset.getMinutes())}
- {/if} + {/await}
- { - $params.models = [v]; - }} - > - {modelSelected?.label} 0} + {@const modelValue = params.models[0]} + { + if (params.models && val) { + params.models = [val]; + } + }} > - - {#each models.flat() as mo (mo.value)} - {mo.label} - {/each} - - - + {modelSelected?.label} + + {#each models as mo (mo.value)} + {mo.label} + {/each} + + + + {/if}
diff --git a/src/routes/weather/week/[location]/+page.ts b/src/routes/weather/week/[location]/+page.ts index 863f111..a1a5166 100644 --- a/src/routes/weather/week/[location]/+page.ts +++ b/src/routes/weather/week/[location]/+page.ts @@ -31,14 +31,28 @@ export const load: PageLoad = async (event) => { // lat, long coordinates if (urlLocation.includes('N') && urlLocation.includes('E')) { urlLocationSplit = urlLocation.split(/N|E/); - const latitude = Number(urlLocationSplit[0]); - const longitude = Number(urlLocationSplit[1]); + const latitude = parseFloat(urlLocationSplit[0]); + const longitude = parseFloat(urlLocationSplit[1]); location = { - //id: undefined, + id: 0, name: `${latitude}N° ${longitude}E°`, latitude: latitude, - longitude: longitude + longitude: longitude, + elevation: 0, + feature_code: 'COORD', + country_code: undefined, + admin1_id: undefined, + admin3_id: undefined, + admin4_id: undefined, + timezone: 'UTC', + population: undefined, + postcodes: undefined, + country_id: undefined, + country: undefined, + admin1: undefined, + admin3: undefined, + admin4: undefined }; } else { if (urlLocationId) {