fix: type errors #3
@@ -1,10 +1,45 @@
|
|||||||
import { writable } from 'svelte/store';
|
import { type Writable, writable } from 'svelte/store';
|
||||||
|
|
||||||
|
export interface UrlHashParams {
|
||||||
|
latitude?: number[];
|
||||||
|
longitude?: number[];
|
||||||
|
daily?: string[];
|
||||||
|
hourly?: string[];
|
||||||
|
models?: string[];
|
||||||
|
current?: string[];
|
||||||
|
minutely_15?: string[];
|
||||||
|
timezone?: string;
|
||||||
|
location_mode?: string;
|
||||||
|
csv_coordinates?: string;
|
||||||
|
time_mode?: string;
|
||||||
|
past_days?: string;
|
||||||
|
forecast_days?: string;
|
||||||
|
end_date?: string;
|
||||||
|
start_date?: string;
|
||||||
|
past_hours?: string;
|
||||||
|
cell_selection?: string;
|
||||||
|
forecast_hours?: string;
|
||||||
|
past_minutely_15?: string;
|
||||||
|
temporal_resolution?: string;
|
||||||
|
forecast_minutely_15?: string;
|
||||||
|
tilt?: string;
|
||||||
|
azimuth?: string;
|
||||||
|
timeformat?: string;
|
||||||
|
wind_speed_unit?: string;
|
||||||
|
temperature_unit?: string;
|
||||||
|
precipitation_unit?: string;
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UrlHashStore extends Writable<UrlHashParams> {
|
||||||
|
updateParam: (key: string, value: unknown) => void;
|
||||||
|
}
|
||||||
|
|
||||||
// Placeholder function for urlHashStore
|
// Placeholder function for urlHashStore
|
||||||
// In a real application, this would handle URL hash parameters
|
// In a real application, this would handle URL hash parameters
|
||||||
// and return a Svelte store that reflects those parameters.
|
// and return a Svelte store that reflects those parameters.
|
||||||
export function urlHashStore(initialValue: Record<string, unknown>) {
|
export function urlHashStore(initialValue: UrlHashParams): UrlHashStore {
|
||||||
const { subscribe, set, update } = writable(initialValue);
|
const { subscribe, set, update } = writable<UrlHashParams>(initialValue);
|
||||||
|
|
||||||
// In a full implementation, you would add logic here to:
|
// In a full implementation, you would add logic here to:
|
||||||
// 1. Read the URL hash on initialization
|
// 1. Read the URL hash on initialization
|
||||||
|
|||||||
@@ -16,9 +16,16 @@
|
|||||||
|
|
||||||
let { children }: Props = $props();
|
let { children }: Props = $props();
|
||||||
|
|
||||||
|
interface CurrentWeather {
|
||||||
|
current: {
|
||||||
|
temperature_2m: number;
|
||||||
|
weather_code: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let location = $state(get(storedLocation));
|
let location = $state(get(storedLocation));
|
||||||
let mounted = $state(false);
|
let mounted = $state(false);
|
||||||
let currentWeather = $state(null);
|
let currentWeather = $state<CurrentWeather | null>(null);
|
||||||
|
|
||||||
// Subscribe to location changes
|
// Subscribe to location changes
|
||||||
storedLocation.subscribe((value) => {
|
storedLocation.subscribe((value) => {
|
||||||
@@ -47,8 +54,8 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getWeatherIcon = (code) => {
|
const getWeatherIcon = (code: number): string => {
|
||||||
const iconMap = {
|
const iconMap: Record<number, string> = {
|
||||||
0: '☀️',
|
0: '☀️',
|
||||||
1: '🌤️',
|
1: '🌤️',
|
||||||
2: '⛅',
|
2: '⛅',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { redirect } from '@sveltejs/kit';
|
import { redirect } from '@sveltejs/kit';
|
||||||
|
|
||||||
import type { PageLoad } from '$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
let node: HTMLElement;
|
let node: HTMLElement;
|
||||||
let chart: any;
|
let chart: any;
|
||||||
let Highcharts = $state(null);
|
let Highcharts = $state<typeof import('highcharts') | null>(null);
|
||||||
|
|
||||||
let showLegend = $state(false);
|
let showLegend = $state(false);
|
||||||
let averageOnly = $state(false);
|
let averageOnly = $state(false);
|
||||||
@@ -41,20 +41,24 @@
|
|||||||
if (dev) {
|
if (dev) {
|
||||||
// const HighchartsDebugger = await import('highcharts/modules/debugger');
|
// const HighchartsDebugger = await import('highcharts/modules/debugger');
|
||||||
// HighchartsDebugger.default(Highcharts);
|
// HighchartsDebugger.default(Highcharts);
|
||||||
const Debugger = (await import('highcharts/es-modules/Extensions/Debugger/Debugger.js'))
|
const Debugger = (
|
||||||
.default;
|
await import('highcharts/es-modules/Extensions/Debugger/Debugger.js' as any)
|
||||||
const ErrorMessages = (
|
|
||||||
await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js')
|
|
||||||
).default;
|
).default;
|
||||||
Highcharts.errorMessages = ErrorMessages;
|
const ErrorMessages = (
|
||||||
|
await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js' as any)
|
||||||
|
).default;
|
||||||
|
if (Highcharts) {
|
||||||
|
(Highcharts as any).errorMessages = ErrorMessages;
|
||||||
Debugger.compose(Highcharts.Chart);
|
Debugger.compose(Highcharts.Chart);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(async () => {
|
$effect(() => {
|
||||||
|
const loadData = async () => {
|
||||||
count = 0;
|
count = 0;
|
||||||
if (Highcharts) {
|
if (Highcharts) {
|
||||||
node.replaceChildren([]);
|
node.replaceChildren();
|
||||||
|
|
||||||
const dataDaily = await fetch(
|
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`
|
`https://api.open-meteo.com/v1/forecast?latitude=${location.latitude}&longitude=${location.longitude}&timeformat=unixtime&daily=sunset,sunrise&forecast_days=14`
|
||||||
@@ -62,7 +66,7 @@
|
|||||||
const wd = await dataDaily.json();
|
const wd = await dataDaily.json();
|
||||||
|
|
||||||
const dataReq = await fetch(
|
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();
|
const data = await dataReq.json();
|
||||||
|
|
||||||
@@ -70,7 +74,7 @@
|
|||||||
if ('daily' in wd && 'sunrise' in wd.daily && 'sunset' in wd.daily) {
|
if ('daily' in wd && 'sunrise' in wd.daily && 'sunset' in wd.daily) {
|
||||||
let rise = wd.daily.sunrise;
|
let rise = wd.daily.sunrise;
|
||||||
let set = wd.daily.sunset;
|
let set = wd.daily.sunset;
|
||||||
plotBands = rise.map(function (r, i) {
|
plotBands = rise.map(function (r: any, i: number) {
|
||||||
return {
|
return {
|
||||||
color: 'rgba(255, 255, 194, 0.5)',
|
color: 'rgba(255, 255, 194, 0.5)',
|
||||||
from: (r + data.utc_offset_seconds) * 1000,
|
from: (r + data.utc_offset_seconds) * 1000,
|
||||||
@@ -82,7 +86,7 @@
|
|||||||
let minValues = new Array(data.hourly.time.length).fill(undefined);
|
let minValues = new Array(data.hourly.time.length).fill(undefined);
|
||||||
let maxValues = 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');
|
const chartDiv = document.createElement('div');
|
||||||
|
|
||||||
let unit;
|
let unit;
|
||||||
@@ -99,7 +103,7 @@
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (model.startsWith(variable)) {
|
if (model.startsWith(variable)) {
|
||||||
for (let [index, val] of values.entries()) {
|
for (let [index, val] of (values as any[]).entries()) {
|
||||||
if (val) {
|
if (val) {
|
||||||
let avVal = average[index];
|
let avVal = average[index];
|
||||||
average[index] = avVal + val;
|
average[index] = avVal + val;
|
||||||
@@ -145,7 +149,9 @@
|
|||||||
dashStyle: 'ShortDashDot',
|
dashStyle: 'ShortDashDot',
|
||||||
color: '#5e5e5e',
|
color: '#5e5e5e',
|
||||||
type:
|
type:
|
||||||
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²' ? 'column' : 'spline',
|
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²'
|
||||||
|
? 'column'
|
||||||
|
: 'spline',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
valueSuffix: ' ' + unit
|
valueSuffix: ' ' + unit
|
||||||
},
|
},
|
||||||
@@ -160,19 +166,20 @@
|
|||||||
className: 'highcharts-average-series'
|
className: 'highcharts-average-series'
|
||||||
});
|
});
|
||||||
|
|
||||||
new Highcharts.Chart(chartDiv, {
|
new Highcharts!.Chart({
|
||||||
credits: {
|
|
||||||
text: count === $params.hourly.length - 1 ? 'Open-Meteo.com' : '',
|
|
||||||
href: 'http://open-meteo.com'
|
|
||||||
},
|
|
||||||
|
|
||||||
chart: {
|
chart: {
|
||||||
|
renderTo: chartDiv,
|
||||||
height: showLegend ? '400px' : '300px',
|
height: showLegend ? '400px' : '300px',
|
||||||
styledMode: true,
|
styledMode: true,
|
||||||
marginLeft: '50',
|
marginLeft: 50,
|
||||||
marginRight: 0
|
marginRight: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
credits: {
|
||||||
|
text: count === ($params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '',
|
||||||
|
href: 'http://open-meteo.com'
|
||||||
|
},
|
||||||
|
|
||||||
lang: {
|
lang: {
|
||||||
locale: 'en-GB'
|
locale: 'en-GB'
|
||||||
},
|
},
|
||||||
@@ -185,8 +192,8 @@
|
|||||||
subtitle: {
|
subtitle: {
|
||||||
text:
|
text:
|
||||||
count === 0
|
count === 0
|
||||||
? `Compare <span class="font-bold">${$params.hourly.join(', ')}</span> in models: <span class="font-bold">` +
|
? `Compare <span class="font-bold">${$params.hourly?.join(', ') || ''}</span> in models: <span class="font-bold">` +
|
||||||
$params.models.join(', ') +
|
($params.models?.join(', ') || '') +
|
||||||
'</span>'
|
'</span>'
|
||||||
: '',
|
: '',
|
||||||
align: 'left'
|
align: 'left'
|
||||||
@@ -234,7 +241,7 @@
|
|||||||
verticalAlign: 'bottom'
|
verticalAlign: 'bottom'
|
||||||
},
|
},
|
||||||
|
|
||||||
series: series,
|
series: series as any,
|
||||||
|
|
||||||
responsive: {
|
responsive: {
|
||||||
rules: [
|
rules: [
|
||||||
@@ -256,6 +263,8 @@
|
|||||||
node.appendChild(chartDiv);
|
node.appendChild(chartDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
loadData();
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@@ -266,7 +275,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- min-h-[302px] min-h-[602px] min-h-[902px] min-h-[1202px] -->
|
<!-- min-h-[302px] min-h-[602px] min-h-[902px] min-h-[1202px] -->
|
||||||
<div class="container-wrapper relative -mx-6 md:mx-0 min-h-[{300 * $params.hourly.length + 2}px]">
|
<div
|
||||||
|
class="container-wrapper relative -mx-6 md:mx-0 min-h-[{300 * ($params.hourly?.length || 0) +
|
||||||
|
2}px]"
|
||||||
|
>
|
||||||
<div in:fade={{ duration: 300 }} out:fade={{ duration: 300 }} bind:this={node}></div>
|
<div in:fade={{ duration: 300 }} out:fade={{ duration: 300 }} bind:this={node}></div>
|
||||||
<div
|
<div
|
||||||
class="{count > 0
|
class="{count > 0
|
||||||
|
|||||||
@@ -12,13 +12,16 @@
|
|||||||
import { Label } from '$lib/components/ui/label';
|
import { Label } from '$lib/components/ui/label';
|
||||||
import { Switch } from '$lib/components/ui/switch';
|
import { Switch } from '$lib/components/ui/switch';
|
||||||
|
|
||||||
import { hourly, models } from '../options';
|
import { hourly, models as modelsFlat } from '../options';
|
||||||
import './highcharts.css';
|
import './highcharts.css';
|
||||||
import { defaultParameters } from './options';
|
import { defaultParameters } from './options';
|
||||||
|
|
||||||
|
// Wrap models in array to match template expectation of nested arrays like hourly
|
||||||
|
const models = [modelsFlat];
|
||||||
|
|
||||||
let node: HTMLElement;
|
let node: HTMLElement;
|
||||||
let chart: any;
|
let chart: any;
|
||||||
let Highcharts = $state();
|
let Highcharts = $state<typeof import('highcharts') | null>(null);
|
||||||
|
|
||||||
let showLegend = $state(false);
|
let showLegend = $state(false);
|
||||||
let averageOnly = $state(false);
|
let averageOnly = $state(false);
|
||||||
@@ -47,27 +50,31 @@
|
|||||||
if (dev) {
|
if (dev) {
|
||||||
// const HighchartsDebugger = await import('highcharts/modules/debugger');
|
// const HighchartsDebugger = await import('highcharts/modules/debugger');
|
||||||
// HighchartsDebugger.default(Highcharts);
|
// HighchartsDebugger.default(Highcharts);
|
||||||
const Debugger = (await import('highcharts/es-modules/Extensions/Debugger/Debugger.js'))
|
const Debugger = (
|
||||||
.default;
|
await import('highcharts/es-modules/Extensions/Debugger/Debugger.js' as any)
|
||||||
const ErrorMessages = (
|
|
||||||
await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js')
|
|
||||||
).default;
|
).default;
|
||||||
Highcharts.errorMessages = ErrorMessages;
|
const ErrorMessages = (
|
||||||
|
await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js' as any)
|
||||||
|
).default;
|
||||||
|
if (Highcharts) {
|
||||||
|
(Highcharts as any).errorMessages = ErrorMessages;
|
||||||
Debugger.compose(Highcharts.Chart);
|
Debugger.compose(Highcharts.Chart);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(async () => {
|
$effect(() => {
|
||||||
|
const loadData = async () => {
|
||||||
count = 0;
|
count = 0;
|
||||||
if (Highcharts) {
|
if (Highcharts) {
|
||||||
node.replaceChildren([]);
|
node.replaceChildren();
|
||||||
|
|
||||||
const dataReq = await fetch(
|
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();
|
const data = await dataReq.json();
|
||||||
|
|
||||||
let dailyFirstModelKey = Object.keys(data.daily)[1].split('_');
|
let dailyFirstModelKey: string[] | string = Object.keys(data.daily)[1].split('_');
|
||||||
dailyFirstModelKey.shift();
|
dailyFirstModelKey.shift();
|
||||||
dailyFirstModelKey = dailyFirstModelKey.join('_');
|
dailyFirstModelKey = dailyFirstModelKey.join('_');
|
||||||
|
|
||||||
@@ -79,7 +86,7 @@
|
|||||||
) {
|
) {
|
||||||
let rise = data.daily['sunrise_' + dailyFirstModelKey];
|
let rise = data.daily['sunrise_' + dailyFirstModelKey];
|
||||||
let set = data.daily['sunset_' + dailyFirstModelKey];
|
let set = data.daily['sunset_' + dailyFirstModelKey];
|
||||||
plotBands = rise.map(function (r, i) {
|
plotBands = rise.map(function (r: any, i: number) {
|
||||||
return {
|
return {
|
||||||
color: 'rgba(255, 255, 194, 0.5)',
|
color: 'rgba(255, 255, 194, 0.5)',
|
||||||
from: (r + data.utc_offset_seconds) * 1000,
|
from: (r + data.utc_offset_seconds) * 1000,
|
||||||
@@ -88,7 +95,7 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let variable of $params.hourly) {
|
for (let variable of $params.hourly || []) {
|
||||||
const chartDiv = document.createElement('div');
|
const chartDiv = document.createElement('div');
|
||||||
|
|
||||||
let unit;
|
let unit;
|
||||||
@@ -105,7 +112,7 @@
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (model.startsWith(variable)) {
|
if (model.startsWith(variable)) {
|
||||||
for (let [index, val] of values.entries()) {
|
for (let [index, val] of (values as any[]).entries()) {
|
||||||
if (val) {
|
if (val) {
|
||||||
let avVal = average[index];
|
let avVal = average[index];
|
||||||
average[index] = avVal + val;
|
average[index] = avVal + val;
|
||||||
@@ -143,7 +150,9 @@
|
|||||||
dashStyle: 'ShortDashDot',
|
dashStyle: 'ShortDashDot',
|
||||||
color: '#5e5e5e',
|
color: '#5e5e5e',
|
||||||
type:
|
type:
|
||||||
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²' ? 'column' : 'spline',
|
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²'
|
||||||
|
? 'column'
|
||||||
|
: 'spline',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
valueSuffix: ' ' + unit
|
valueSuffix: ' ' + unit
|
||||||
},
|
},
|
||||||
@@ -158,19 +167,20 @@
|
|||||||
className: 'highcharts-average-series'
|
className: 'highcharts-average-series'
|
||||||
});
|
});
|
||||||
|
|
||||||
new Highcharts.Chart(chartDiv, {
|
new Highcharts!.Chart({
|
||||||
credits: {
|
|
||||||
text: count === $params.hourly.length - 1 ? 'Open-Meteo.com' : '',
|
|
||||||
href: 'http://open-meteo.com'
|
|
||||||
},
|
|
||||||
|
|
||||||
chart: {
|
chart: {
|
||||||
|
renderTo: chartDiv,
|
||||||
height: showLegend ? '400px' : '300px',
|
height: showLegend ? '400px' : '300px',
|
||||||
styledMode: true,
|
styledMode: true,
|
||||||
marginLeft: '50',
|
marginLeft: 50,
|
||||||
marginRight: 0
|
marginRight: 0
|
||||||
},
|
},
|
||||||
|
|
||||||
|
credits: {
|
||||||
|
text: count === ($params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '',
|
||||||
|
href: 'http://open-meteo.com'
|
||||||
|
},
|
||||||
|
|
||||||
lang: {
|
lang: {
|
||||||
locale: 'en-GB'
|
locale: 'en-GB'
|
||||||
},
|
},
|
||||||
@@ -183,8 +193,8 @@
|
|||||||
subtitle: {
|
subtitle: {
|
||||||
text:
|
text:
|
||||||
count === 0
|
count === 0
|
||||||
? `Compare <span class="font-bold">${$params.hourly.join(', ')}</span> in models: <span class="font-bold">` +
|
? `Compare <span class="font-bold">${$params.hourly?.join(', ') || ''}</span> in models: <span class="font-bold">` +
|
||||||
$params.models.join(', ') +
|
($params.models?.join(', ') || '') +
|
||||||
'</span>'
|
'</span>'
|
||||||
: '',
|
: '',
|
||||||
align: 'left'
|
align: 'left'
|
||||||
@@ -232,7 +242,7 @@
|
|||||||
verticalAlign: 'bottom'
|
verticalAlign: 'bottom'
|
||||||
},
|
},
|
||||||
|
|
||||||
series: series,
|
series: series as any,
|
||||||
|
|
||||||
responsive: {
|
responsive: {
|
||||||
rules: [
|
rules: [
|
||||||
@@ -254,6 +264,8 @@
|
|||||||
node.appendChild(chartDiv);
|
node.appendChild(chartDiv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
loadData();
|
||||||
});
|
});
|
||||||
|
|
||||||
onDestroy(() => {
|
onDestroy(() => {
|
||||||
@@ -264,7 +276,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- min-h-[302px] min-h-[602px] min-h-[902px] min-h-[1202px] -->
|
<!-- min-h-[302px] min-h-[602px] min-h-[902px] min-h-[1202px] -->
|
||||||
<div class="container-wrapper relative -mx-6 md:mx-0 min-h-[{300 * $params.hourly.length + 2}px]">
|
<div
|
||||||
|
class="container-wrapper relative -mx-6 md:mx-0 min-h-[{300 * ($params.hourly?.length || 0) +
|
||||||
|
2}px]"
|
||||||
|
>
|
||||||
<div in:fade={{ duration: 300 }} out:fade={{ duration: 300 }} bind:this={node}></div>
|
<div in:fade={{ duration: 300 }} out:fade={{ duration: 300 }} bind:this={node}></div>
|
||||||
<div
|
<div
|
||||||
class="{count > 0
|
class="{count > 0
|
||||||
@@ -319,12 +334,12 @@
|
|||||||
<div class="mt-4 md:mt-8">
|
<div class="mt-4 md:mt-8">
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<a href="#models"><h2 id="models" class="text-2xl md:text-3xl">Models</h2></a>
|
<a href="#models"><h2 id="models" class="text-2xl md:text-3xl">Models</h2></a>
|
||||||
{#if $params.models.length > 0}
|
{#if $params.models && $params.models.length > 0}
|
||||||
<div transition:fade={{ duration: 200 }} class="relative mt-[5px]">
|
<div transition:fade={{ duration: 200 }} class="relative mt-[5px]">
|
||||||
<div
|
<div
|
||||||
class="absolute -top-1 ml-2 rounded-full border-2 border-foreground/25 bg-secondary px-3 py-1 text-sm no-underline"
|
class="absolute -top-1 ml-2 rounded-full border-2 border-foreground/25 bg-secondary px-3 py-1 text-sm no-underline"
|
||||||
>
|
>
|
||||||
{$params.models.length} / {models.flat().length}
|
{$params.models?.length || 0} / {models.flat().length}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -333,7 +348,8 @@
|
|||||||
<div class="mt-2 grid sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
|
<div class="mt-2 grid sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
|
||||||
{#each models as group, i (i)}
|
{#each models as group, i (i)}
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{#each group as { value, label } (value)}
|
{#each group as item (item.value)}
|
||||||
|
{@const { value, label } = item as { value: string; label: string }}
|
||||||
<div class="group flex items-center" title={label}>
|
<div class="group flex items-center" title={label}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="{value}_model"
|
id="{value}_model"
|
||||||
@@ -371,12 +387,12 @@
|
|||||||
Hourly Weather Variables
|
Hourly Weather Variables
|
||||||
</h2></a
|
</h2></a
|
||||||
>
|
>
|
||||||
{#if $params.hourly.length > 0}
|
{#if $params.hourly && $params.hourly.length > 0}
|
||||||
<div transition:fade={{ duration: 200 }} class="relative mt-[5px]">
|
<div transition:fade={{ duration: 200 }} class="relative mt-[5px]">
|
||||||
<div
|
<div
|
||||||
class="absolute -top-1 ml-2 rounded-full border-2 border-foreground/25 bg-secondary px-3 py-1 text-sm no-underline"
|
class="absolute -top-1 ml-2 rounded-full border-2 border-foreground/25 bg-secondary px-3 py-1 text-sm no-underline"
|
||||||
>
|
>
|
||||||
{$params.hourly.length} / {hourly.flat().length}
|
{$params.hourly?.length || 0} / {hourly.flat().length}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export default {
|
const weatherCodes: Record<number, string> = {
|
||||||
0: 'clear',
|
0: 'clear',
|
||||||
1: 'clear',
|
1: 'clear',
|
||||||
2: 'cloudy',
|
2: 'cloudy',
|
||||||
@@ -79,3 +79,5 @@ export default {
|
|||||||
96: 'thunderstorm',
|
96: 'thunderstorm',
|
||||||
99: 'tornado'
|
99: 'tornado'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default weatherCodes;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { storedLocation } from '$lib/stores/settings';
|
|||||||
|
|
||||||
import { geoLocationNameToRoute } from '$lib/utils/meteo';
|
import { geoLocationNameToRoute } from '$lib/utils/meteo';
|
||||||
|
|
||||||
import type { PageLoad } from '$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
const params = urlHashStore({
|
const params = urlHashStore({
|
||||||
latitude: [$storedLocation.latitude],
|
latitude: [$storedLocation.latitude],
|
||||||
longitude: [$storedLocation.longitude],
|
longitude: [$storedLocation.longitude],
|
||||||
models: 'best_match',
|
models: ['best_match'],
|
||||||
...defaultParameters
|
...defaultParameters
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@
|
|||||||
|
|
||||||
const maxX = 10000;
|
const maxX = 10000;
|
||||||
const maxY = 500;
|
const maxY = 500;
|
||||||
const deltaX = 10000 / hourly.variables(0)?.valuesArray()?.length;
|
const deltaX = 10000 / (hourly.variables(0)?.valuesArray()?.length || 1);
|
||||||
|
|
||||||
const ctx = canvasElement?.getContext('2d');
|
const ctx = canvasElement?.getContext('2d');
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
@@ -119,10 +119,10 @@
|
|||||||
|
|
||||||
// create canvas
|
// create canvas
|
||||||
daylight(ctx, config, hourlyTime);
|
daylight(ctx, config, hourlyTime);
|
||||||
raster(ctx, config, hourlyTime, today, canvasElement);
|
raster(ctx, config, hourlyTime, today, canvasElement!);
|
||||||
tempGradient(ctx, config, hourlyTemps, $params.temperature_unit);
|
tempGradient(ctx, config, hourlyTemps, $params.temperature_unit);
|
||||||
cloudCover(ctx, config, hourlyCloudCover, canvasElement);
|
cloudCover(ctx, config, hourlyCloudCover, canvasElement!);
|
||||||
precip(ctx, config, hourlyPrecip, canvasElement);
|
precip(ctx, config, hourlyPrecip, canvasElement!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
values: hourly
|
values: hourly
|
||||||
.variables(2)
|
.variables(2)
|
||||||
?.valuesArray()
|
?.valuesArray()
|
||||||
?.map((t) => t.toFixed(1))
|
?.map((t) => Number(t.toFixed(1)))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
@@ -143,7 +143,7 @@
|
|||||||
values: hourly
|
values: hourly
|
||||||
.variables(0)
|
.variables(0)
|
||||||
?.valuesArray()
|
?.valuesArray()
|
||||||
?.map((p) => p.toFixed(1))
|
?.map((p) => Number(p.toFixed(1)))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
values: hourly
|
values: hourly
|
||||||
.variables(1)
|
.variables(1)
|
||||||
?.valuesArray()
|
?.valuesArray()
|
||||||
?.map((p) => p.toFixed(0))
|
?.map((p) => Number(p.toFixed(0)))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
@@ -161,7 +161,7 @@
|
|||||||
values: hourly
|
values: hourly
|
||||||
.variables(4)
|
.variables(4)
|
||||||
?.valuesArray()
|
?.valuesArray()
|
||||||
?.map((p) => p.toFixed(0))
|
?.map((p) => Number(p.toFixed(0)))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 4,
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
values: hourly
|
values: hourly
|
||||||
.variables(7)
|
.variables(7)
|
||||||
?.valuesArray()
|
?.valuesArray()
|
||||||
?.map((p) => p.toFixed(0))
|
?.map((p) => Number(p.toFixed(0)))
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
entriesLength: hourly.variables(0)?.valuesArray()?.length,
|
entriesLength: hourly.variables(0)?.valuesArray()?.length,
|
||||||
@@ -237,17 +237,18 @@
|
|||||||
let winddir = true;
|
let winddir = true;
|
||||||
entries = 6;
|
entries = 6;
|
||||||
|
|
||||||
let scrollDiv: HTMLElement = $state();
|
let scrollDiv: HTMLElement | undefined = $state();
|
||||||
let tableCells;
|
let tableCells;
|
||||||
|
|
||||||
const switchDay = (date: SvelteDate, index: number) => {
|
const switchDay = (date: Date, index: number) => {
|
||||||
selectedDay = date;
|
selectedDay = date;
|
||||||
|
|
||||||
tableCells = document.querySelectorAll('td.time');
|
tableCells = document.querySelectorAll('td.time');
|
||||||
|
|
||||||
for (let tableCell of tableCells) {
|
for (let tableCell of tableCells) {
|
||||||
if (Number(tableCell.dataset['date']) === selectedDay.getDate()) {
|
const htmlCell = tableCell as HTMLElement;
|
||||||
scrollDiv.scrollTo({ left: tableCell.offsetLeft - 110, behavior: 'smooth' });
|
if (Number(htmlCell.dataset['date']) === selectedDay.getDate()) {
|
||||||
|
scrollDiv?.scrollTo({ left: htmlCell.offsetLeft - 110, behavior: 'smooth' });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,34 +260,35 @@
|
|||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
tableCells = document.querySelectorAll('td.time');
|
tableCells = document.querySelectorAll('td.time');
|
||||||
for (let tableCell of tableCells) {
|
for (let tableCell of tableCells) {
|
||||||
if (Number(tableCell.dataset['date']) === selectedDay.getDate()) {
|
const htmlCell = tableCell as HTMLElement;
|
||||||
scrollDiv.scrollTo({ left: tableCell.offsetLeft - 110 });
|
if (Number(htmlCell.dataset['date']) === selectedDay.getDate()) {
|
||||||
|
scrollDiv?.scrollTo({ left: htmlCell.offsetLeft - 110 });
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 150);
|
}, 150);
|
||||||
|
|
||||||
document.onkeydown = (e) => {
|
document.onkeydown = (e) => {
|
||||||
if (!scrollDiv === document.activeElement || !scrollDiv.contains(document.activeElement)) {
|
if (scrollDiv !== document.activeElement && !scrollDiv?.contains(document.activeElement)) {
|
||||||
if (e.key === 'ArrowLeft') {
|
if (e.key === 'ArrowLeft') {
|
||||||
if (selectedDay.getDate() >= today.getDate()) {
|
if (selectedDay.getDate() >= today.getDate()) {
|
||||||
let newDate = new SvelteDate();
|
let newDate = new Date();
|
||||||
newDate.setDate(selectedDay.getDate() - 1);
|
newDate.setDate(selectedDay.getDate() - 1);
|
||||||
switchDay(newDate);
|
switchDay(newDate, selectedDayIndex - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (e.key === 'ArrowRight') {
|
if (e.key === 'ArrowRight') {
|
||||||
if (selectedDay.getDate() <= today.getDate() + 4) {
|
if (selectedDay.getDate() <= today.getDate() + 4) {
|
||||||
let newDate = new SvelteDate();
|
let newDate = new Date();
|
||||||
newDate.setDate(selectedDay.getDate() + 1);
|
newDate.setDate(selectedDay.getDate() + 1);
|
||||||
switchDay(newDate);
|
switchDay(newDate, selectedDayIndex + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
let modelSelected = $derived(models.find((mo) => String(mo.value) === $params.models));
|
let modelSelected = $derived(models.find((mo) => String(mo.value) === $params.models?.[0]));
|
||||||
// let modelSelectedValue = $derived($params.models[0]);
|
// let modelSelectedValue = $derived($params.models[0]);
|
||||||
//
|
//
|
||||||
</script>
|
</script>
|
||||||
@@ -308,7 +310,9 @@
|
|||||||
{#await weatherDaily then wd}
|
{#await weatherDaily then wd}
|
||||||
{#each wd.daily.time as time, index (index)}
|
{#each wd.daily.time as time, index (index)}
|
||||||
{@const selected = time.getDate() === selectedDay.getDate()}
|
{@const selected = time.getDate() === selectedDay.getDate()}
|
||||||
{#if !isNaN(wd.daily.temperature_2m_max.values(index).toFixed(1))}
|
{#if wd.daily.temperature_2m_max.values(index) != null && !isNaN(Number(wd.daily.temperature_2m_max
|
||||||
|
.values(index)!
|
||||||
|
.toFixed(1)))}
|
||||||
<button
|
<button
|
||||||
style="transition: 300ms; min-width: 13%; {selected ? 'transform: scale(1.025)' : ''}"
|
style="transition: 300ms; min-width: 13%; {selected ? 'transform: scale(1.025)' : ''}"
|
||||||
class="cursor-pointer"
|
class="cursor-pointer"
|
||||||
@@ -338,23 +342,23 @@
|
|||||||
<svg class="fill-foreground" width="60px" height="60px">
|
<svg class="fill-foreground" width="60px" height="60px">
|
||||||
<use
|
<use
|
||||||
xlink:href="/images/weather-icons/wi-day-{weatherCodes[
|
xlink:href="/images/weather-icons/wi-day-{weatherCodes[
|
||||||
wd.daily.weather_code.values(index)
|
(wd.daily.weather_code.values(index) ?? 0) as number
|
||||||
]}.svg#Layer_1"
|
]}.svg#Layer_1"
|
||||||
></use>
|
></use>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="weather-temp-max flex min-w-[65px] justify-center rounded-t p-1 text-sm"
|
class="weather-temp-max flex min-w-[65px] justify-center rounded-t p-1 text-sm"
|
||||||
style={`background-color: ${getColor(wd.daily.temperature_2m_max.values(index).toFixed(0), $params.temperature_unit)}; color: ${wd.daily.temperature_2m_min.values(index) < ($params.temperature_unit === 'celsius' ? 4 : 7) || wd.daily.temperature_2m_min.values(index) >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
|
style={`background-color: ${getColor((wd.daily.temperature_2m_max.values(index) ?? 0).toFixed(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'}`}
|
||||||
>
|
>
|
||||||
{wd.daily.temperature_2m_max.values(index).toFixed(1)}
|
{wd.daily.temperature_2m_max.values(index)?.toFixed(1)}
|
||||||
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="weather-temp-min flex min-w-[65px] justify-center rounded-b p-1 text-sm"
|
class="weather-temp-min flex min-w-[65px] justify-center rounded-b p-1 text-sm"
|
||||||
style={`background: ${getColor(wd.daily.temperature_2m_min.values(index).toFixed(0), $params.temperature_unit)}; color: ${wd.daily.temperature_2m_min.values(index) < ($params.temperature_unit === 'celsius' ? 4 : 7) || wd.daily.temperature_2m_min.values(index) >= ($params.temperature_unit === 'celsius' ? 30 : 104) ? 'white' : 'black'}`}
|
style={`background: ${getColor((wd.daily.temperature_2m_min.values(index) ?? 0).toFixed(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'}`}
|
||||||
>
|
>
|
||||||
{wd.daily.temperature_2m_min.values(index).toFixed(1)}
|
{wd.daily.temperature_2m_min.values(index)?.toFixed(1)}
|
||||||
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
{$params.temperature_unit === 'celsius' ? '°C' : '°F'}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-2 flex items-center justify-center gap-1">
|
<div class="mt-2 flex items-center justify-center gap-1">
|
||||||
@@ -369,7 +373,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{Number(wd.daily.sunshine_duration.values(index) / 3600).toFixed(0)}h
|
{Number((wd.daily.sunshine_duration.values(index) ?? 0) / 3600).toFixed(0)}h
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1 flex items-center justify-center">
|
<div class="mt-1 flex items-center justify-center">
|
||||||
<div class="relative flex h-6 w-6 items-center justify-center">
|
<div class="relative flex h-6 w-6 items-center justify-center">
|
||||||
@@ -442,8 +446,9 @@
|
|||||||
data-time={weather.hourlyTime[index].getHours() + ':00'}
|
data-time={weather.hourlyTime[index].getHours() + ':00'}
|
||||||
style="font-size: 11px; position: absolute; bottom: {188 +
|
style="font-size: 11px; position: absolute; bottom: {188 +
|
||||||
27 * entries}px; left:{111 +
|
27 * entries}px; left:{111 +
|
||||||
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
|
(5000 / (weather.entriesLength || 1)) * index}px; min-width: {5000 /
|
||||||
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
|
(weather.entriesLength || 1)}px; max-width: {5000 /
|
||||||
|
(weather.entriesLength || 1)}px;"
|
||||||
>{weather.hourlyTime[index].getHours() < 10 ? '0' : ''}{weather.hourlyTime[
|
>{weather.hourlyTime[index].getHours() < 10 ? '0' : ''}{weather.hourlyTime[
|
||||||
index
|
index
|
||||||
].getHours()}</td
|
].getHours()}</td
|
||||||
@@ -467,16 +472,17 @@
|
|||||||
0.8 * 200 -
|
0.8 * 200 -
|
||||||
0.54 *
|
0.54 *
|
||||||
200 *
|
200 *
|
||||||
((maxTemp - weather.entries[0].values[index]) / diffTemp)}px; left:{116 +
|
((maxTemp! - weather.entries[0].values![index]) / diffTemp!)}px; left:{116 +
|
||||||
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
|
(5000 / (weather.entriesLength || 1)) * index}px; min-width: {5000 /
|
||||||
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
|
(weather.entriesLength || 1)}px; max-width: {5000 /
|
||||||
|
(weather.entriesLength || 1)}px;"
|
||||||
><svg class="fill-foreground {now ? 'scale-125' : ''}" width="20px" height="20px">
|
><svg class="fill-foreground {now ? 'scale-125' : ''}" width="20px" height="20px">
|
||||||
<use
|
<use
|
||||||
class="stroke-2"
|
class="stroke-2"
|
||||||
xlink:href="/images/weather-icons/wi-{weather.hourlyTime[index].getHours() >
|
xlink:href="/images/weather-icons/wi-{weather.hourlyTime[index].getHours() >
|
||||||
6 && weather.hourlyTime[index].getHours() < 21
|
6 && weather.hourlyTime[index].getHours() < 21
|
||||||
? 'day'
|
? 'day'
|
||||||
: 'night'}-{weatherCodes[weatherCodesHourly[index]]}.svg#Layer_1"
|
: 'night'}-{weatherCodes[weatherCodesHourly![index] as number]}.svg#Layer_1"
|
||||||
></use>
|
></use>
|
||||||
</svg></td
|
</svg></td
|
||||||
>
|
>
|
||||||
@@ -491,9 +497,9 @@
|
|||||||
>Temp graph</th
|
>Temp graph</th
|
||||||
>
|
>
|
||||||
{#each weather.indexes as index, j (j)}
|
{#each weather.indexes as index, j (j)}
|
||||||
{@const temp = weather.entries[0].values[index]}
|
{@const temp = weather.entries?.[0]?.values?.[index]}
|
||||||
|
|
||||||
{#if !isNaN(temp)}
|
{#if temp !== undefined && !isNaN(temp)}
|
||||||
<td
|
<td
|
||||||
class={weather.hourlyTime[index].getDate() === today.getDate() &&
|
class={weather.hourlyTime[index].getDate() === today.getDate() &&
|
||||||
weather.hourlyTime[index].getHours() === today.getHours()
|
weather.hourlyTime[index].getHours() === today.getHours()
|
||||||
@@ -502,10 +508,10 @@
|
|||||||
style="position: absolute; bottom: {27.5 * entries -
|
style="position: absolute; bottom: {27.5 * entries -
|
||||||
49 +
|
49 +
|
||||||
0.8 * 200 -
|
0.8 * 200 -
|
||||||
0.55 * 200 * ((maxTemp - temp) / diffTemp)}px; left:{111 +
|
0.55 * 200 * ((maxTemp! - temp!) / diffTemp!)}px; left:{111 +
|
||||||
(5000 / weather.entriesLength) * index}px; min-width: {5000 /
|
(5000 / (weather.entriesLength || 1)) * index}px; min-width: {5000 /
|
||||||
weather.entriesLength}px; max-width: {5000 / weather.entriesLength}px;"
|
(weather.entriesLength || 1)}px; max-width: {5000 /
|
||||||
>{temp.toFixed(0)}</td
|
(weather.entriesLength || 1)}px;">{temp?.toFixed(0)}</td
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -520,49 +526,49 @@
|
|||||||
>
|
>
|
||||||
|
|
||||||
{#each weather.indexes as index, j (j)}
|
{#each weather.indexes as index, j (j)}
|
||||||
{#if !isNaN(entry.values[index])}
|
{#if entry.values && !isNaN(entry.values[index])}
|
||||||
<td
|
<td
|
||||||
class="border-r border-border {weather.hourlyTime[index].getDate() ===
|
class="border-r border-border {weather.hourlyTime[index].getDate() ===
|
||||||
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
|
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
|
||||||
? 'now'
|
? 'now'
|
||||||
: ''}"
|
: ''}"
|
||||||
style="min-width: {5000 / weather.entriesLength}px; max-width: {5000 /
|
style="min-width: {5000 / (weather.entriesLength || 1)}px; max-width: {5000 /
|
||||||
weather.entriesLength}px;
|
(weather.entriesLength || 1)}px;
|
||||||
{entry.name === 'temperature_2m'
|
{entry.name === 'temperature_2m'
|
||||||
? 'background: ' +
|
? 'background: ' +
|
||||||
getColor(
|
getColor(
|
||||||
weather.entries[0].values[index].toFixed(0),
|
weather.entries[0].values![index].toFixed(0),
|
||||||
$params.temperature_unit
|
$params.temperature_unit
|
||||||
)
|
)
|
||||||
: ''};
|
: ''};
|
||||||
{entry.name === 'temperature_2m'
|
{entry.name === 'temperature_2m'
|
||||||
? 'color: ' +
|
? 'color: ' +
|
||||||
(weather.entries[0].values[index] <
|
(weather.entries[0].values![index] <
|
||||||
($params.temperature_unit === 'celsius' ? -13 : 7) ||
|
($params.temperature_unit === 'celsius' ? -13 : 7) ||
|
||||||
weather.entries[0].values[index] >=
|
weather.entries[0].values![index] >=
|
||||||
($params.temperature_unit === 'celsius' ? 40 : 104)
|
($params.temperature_unit === 'celsius' ? 40 : 104)
|
||||||
? 'white'
|
? 'white'
|
||||||
: 'black')
|
: 'black')
|
||||||
: ''};
|
: ''};
|
||||||
{entry.name === 'precipitation_probability'
|
{entry.name === 'precipitation_probability'
|
||||||
? 'background: rgba(0, 0, 230,' +
|
? 'background: rgba(0, 0, 230,' +
|
||||||
weather.entries[2].values[index] / 120 +
|
weather.entries[2].values![index] / 120 +
|
||||||
')'
|
')'
|
||||||
: ''};
|
: ''};
|
||||||
{entry.name === 'precipitation_probability'
|
{entry.name === 'precipitation_probability'
|
||||||
? 'color: ' +
|
? 'color: ' +
|
||||||
(weather.entries[2].values[index] > 50
|
(weather.entries[2].values![index] > 50
|
||||||
? 'white'
|
? 'white'
|
||||||
: 'hsl(var(--foreground)')
|
: 'hsl(var(--foreground)')
|
||||||
: ''};
|
: ''};
|
||||||
{entry.name === 'relative_humidity_2m'
|
{entry.name === 'relative_humidity_2m'
|
||||||
? 'background: rgba(0, 240, 240,' +
|
? 'background: rgba(0, 240, 240,' +
|
||||||
weather.entries[4].values[index] ** 3.8 / 10 ** 8.2 +
|
weather.entries[4].values![index] ** 3.8 / 10 ** 8.2 +
|
||||||
')'
|
')'
|
||||||
: ''};"
|
: ''};"
|
||||||
>{entry.name === 'precipitation' || entry.name === 'temperature_2m'
|
>{entry.name === 'precipitation' || entry.name === 'temperature_2m'
|
||||||
? entry.values[index].toFixed(1)
|
? entry.values![index].toFixed(1)
|
||||||
: entry.values[index]}</td
|
: entry.values![index]}</td
|
||||||
>
|
>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
@@ -578,16 +584,16 @@
|
|||||||
>Wind Dir.</th
|
>Wind Dir.</th
|
||||||
>
|
>
|
||||||
{#each weather.indexes as index, j (j)}
|
{#each weather.indexes as index, j (j)}
|
||||||
{#if !isNaN(weather.windDirections[index])}
|
{#if weather.windDirections && !isNaN(weather.windDirections[index])}
|
||||||
<td
|
<td
|
||||||
class="border-r border-border {weather.hourlyTime[index].getDate() ===
|
class="border-r border-border {weather.hourlyTime[index].getDate() ===
|
||||||
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
|
today.getDate() && weather.hourlyTime[index].getHours() === today.getHours()
|
||||||
? 'now'
|
? 'now'
|
||||||
: ''}"
|
: ''}"
|
||||||
style="transform: rotate({weather.windDirections[
|
style="transform: rotate({weather.windDirections![
|
||||||
index
|
index
|
||||||
]}deg);min-width: {5000 / weather.entriesLength}px; max-width: {5000 /
|
]}deg);min-width: {5000 / (weather.entriesLength || 1)}px; max-width: {5000 /
|
||||||
weather.entriesLength}px;"
|
(weather.entriesLength || 1)}px;"
|
||||||
><svg class="fill-foreground" width="25px" height="25px">
|
><svg class="fill-foreground" width="25px" height="25px">
|
||||||
<use xlink:href="/images/weather-icons/wi-direction-down.svg#Layer_1"></use>
|
<use xlink:href="/images/weather-icons/wi-direction-down.svg#Layer_1"></use>
|
||||||
</svg></td
|
</svg></td
|
||||||
@@ -621,7 +627,18 @@
|
|||||||
<div>
|
<div>
|
||||||
<div class="mt-6 flex gap-6 md:mt-12">
|
<div class="mt-6 flex gap-6 md:mt-12">
|
||||||
<div class="relative w-1/2">
|
<div class="relative w-1/2">
|
||||||
<Select.Root name="model_selection" type="single" bind:value={$params.models}>
|
{#if $params.models && $params.models.length > 0}
|
||||||
|
{@const modelValue = $params.models[0]}
|
||||||
|
<Select.Root
|
||||||
|
name="model_selection"
|
||||||
|
type="single"
|
||||||
|
value={modelValue}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
if ($params.models && val) {
|
||||||
|
$params.models = [val];
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Select.Trigger
|
<Select.Trigger
|
||||||
aria-label="Forecast days input"
|
aria-label="Forecast days input"
|
||||||
class="h-12 cursor-pointer pt-6 [&_svg]:mb-3">{modelSelected?.label}</Select.Trigger
|
class="h-12 cursor-pointer pt-6 [&_svg]:mb-3">{modelSelected?.label}</Select.Trigger
|
||||||
@@ -635,6 +652,7 @@
|
|||||||
>Weather model</Label
|
>Weather model</Label
|
||||||
>
|
>
|
||||||
</Select.Root>
|
</Select.Root>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import { type GeoLocation, storedLocation } from '$lib/stores/settings';
|
|||||||
|
|
||||||
import { geoLocationNameToRoute } from '$lib/utils/meteo';
|
import { geoLocationNameToRoute } from '$lib/utils/meteo';
|
||||||
|
|
||||||
import type { PageLoad } from '$types';
|
import type { PageLoad } from './$types';
|
||||||
|
|
||||||
export const prerender = true;
|
export const prerender = true;
|
||||||
|
|
||||||
export const load = (async (event) => {
|
export const load: PageLoad = async (event) => {
|
||||||
const urlLocation = event.params.location;
|
const urlLocation = event.params.location;
|
||||||
let urlLocationSplit, urlLocationName, urlLocationId;
|
let urlLocationSplit, urlLocationName, urlLocationId;
|
||||||
|
|
||||||
@@ -31,14 +31,28 @@ export const load = (async (event) => {
|
|||||||
// lat, long coordinates
|
// lat, long coordinates
|
||||||
if (urlLocation.includes('N') && urlLocation.includes('E')) {
|
if (urlLocation.includes('N') && urlLocation.includes('E')) {
|
||||||
urlLocationSplit = urlLocation.split(/N|E/);
|
urlLocationSplit = urlLocation.split(/N|E/);
|
||||||
const latitude = urlLocationSplit[0];
|
const latitude = parseFloat(urlLocationSplit[0]);
|
||||||
const longitude = urlLocationSplit[1];
|
const longitude = parseFloat(urlLocationSplit[1]);
|
||||||
|
|
||||||
location = {
|
location = {
|
||||||
//id: undefined,
|
id: 0,
|
||||||
name: `${latitude}N° ${longitude}E°`,
|
name: `${latitude}N° ${longitude}E°`,
|
||||||
latitude: latitude,
|
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 {
|
} else {
|
||||||
if (urlLocationId) {
|
if (urlLocationId) {
|
||||||
@@ -74,4 +88,4 @@ export const load = (async (event) => {
|
|||||||
|
|
||||||
storedLocation.set(location);
|
storedLocation.set(location);
|
||||||
return { location: location };
|
return { location: location };
|
||||||
}) satisfies PageLoad;
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user