gemini going wild on this

This commit is contained in:
terraputix
2026-02-15 14:51:15 +01:00
parent 298c8ef597
commit ba9834a3eb
28 changed files with 556 additions and 477 deletions
+1
View File
@@ -0,0 +1 @@
export const prerender = true;
+4 -2
View File
@@ -13,7 +13,7 @@
import { get } from 'svelte/store';
let mounted = $state(false);
let location = get(storedLocation);
let location = $derived($storedLocation);
onMount(() => {
mounted = true;
@@ -180,7 +180,9 @@
Current Location: {location.name}
</CardTitle>
<CardDescription class="text-center">
{location.country}{location.latitude.toFixed(2)}°, {location.longitude.toFixed(2)}°
{location.country}{location.latitude?.toFixed(2)}°, {location.longitude?.toFixed(
2
)}°
</CardDescription>
</CardHeader>
<CardContent class="text-center">
+2 -2
View File
@@ -83,8 +83,8 @@
{/if}{location.country}
</p>
<p class="text-sm text-gray-500 dark:text-gray-400">
{location.latitude.toFixed(2)}°N, {location.longitude.toFixed(2)}°E
{#if location.elevation}{location.elevation.toFixed(0)}m{/if}
{location.latitude?.toFixed(2)}°N, {location.longitude?.toFixed(2)}°E
{#if location.elevation}{location.elevation?.toFixed(0)}m{/if}
</p>
</div>
</div>
+1 -1
View File
@@ -2,7 +2,7 @@ import { get } from 'svelte/store';
import { storedLocation } from '$lib/stores/settings';
import type { LayoutLoad } from './$types';
import type { LayoutLoad } from '././$types';
const location = get(storedLocation);
+3 -3
View File
@@ -1,8 +1,8 @@
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from '$types';
import type { PageLoad } from './$types';
export const prerender = true;
export const load = (async () => {
export const load: PageLoad = async () => {
throw redirect(303, '/weather/week/');
}) satisfies PageLoad;
};
+1 -1
View File
@@ -2,7 +2,7 @@ import { get } from 'svelte/store';
import { storedLocation } from '$lib/stores/settings';
import type { LayoutLoad } from './$types';
import type { LayoutLoad } from '././$types';
const location = get(storedLocation);
+201 -186
View File
@@ -15,8 +15,9 @@
import { defaultParameters } from './options';
let node: HTMLElement;
let chart: any;
let Highcharts = $state(null);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let charts: any[] = [];
let Highcharts = $state<any>(null);
let showLegend = $state(false);
let averageOnly = $state(false);
@@ -36,237 +37,251 @@
/// 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(Highcharts);
(more as any)(Highcharts);
if (dev) {
// const HighchartsDebugger = await import('highcharts/modules/debugger');
// HighchartsDebugger.default(Highcharts);
const Debugger = (await import('highcharts/es-modules/Extensions/Debugger/Debugger.js'))
.default;
// @ts-ignore
const Debugger = (
(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')
(await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js')) as any
).default;
Highcharts.errorMessages = ErrorMessages;
Debugger.compose(Highcharts.Chart);
}
});
$effect(async () => {
$effect(() => {
count = 0;
if (Highcharts) {
node.replaceChildren([]);
charts.forEach((c) => c.destroy());
charts = [];
// eslint-disable-next-line svelte/no-dom-manipulating
(node as any).replaceChildren();
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();
(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`
);
const data = await dataReq.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`
);
const data = await dataReq.json();
let plotBands: any = [];
if ('daily' in wd && 'sunrise' in wd.daily && 'sunset' in wd.daily) {
let rise = wd.daily.sunrise;
let set = wd.daily.sunset;
plotBands = rise.map(function (r, i) {
return {
color: 'rgba(255, 255, 194, 0.5)',
from: (r + data.utc_offset_seconds) * 1000,
to: (set[i] + data.utc_offset_seconds) * 1000
};
});
}
// 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;
let set = wd.daily.sunset;
plotBands = rise.map(function (r: any, i: number) {
return {
color: 'rgba(255, 255, 194, 0.5)',
from: (r + data.utc_offset_seconds) * 1000,
to: (set[i] + data.utc_offset_seconds) * 1000
};
});
}
let minValues = new Array(data.hourly.time.length).fill(undefined);
let maxValues = 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);
for (let variable of $params.hourly) {
const chartDiv = document.createElement('div');
for (let variable of $params.hourly || []) {
const chartDiv = document.createElement('div');
let unit;
let unit;
let hourly_starttime = (data.hourly.time[0] + data.utc_offset_seconds) * 1000;
let pointInterval = (data.hourly.time[1] - data.hourly.time[0]) * 1000;
let hourly_starttime = (data.hourly.time[0] + data.utc_offset_seconds) * 1000;
let pointInterval = (data.hourly.time[1] - data.hourly.time[0]) * 1000;
const series = [];
let average = new Array(data.hourly.time.length).fill(0);
let averageCount = new Array(data.hourly.time.length).fill(0);
const series = [];
let average = new Array(data.hourly.time.length).fill(0);
let averageCount = new Array(data.hourly.time.length).fill(0);
for (let [model, values] of Object.entries(data.hourly)) {
if (model === 'time') {
continue;
}
if (model.startsWith(variable)) {
for (let [index, val] of values.entries()) {
if (val) {
let avVal = average[index];
average[index] = avVal + val;
averageCount[index]++;
for (let [model, values] of Object.entries(data.hourly)) {
if (model === 'time') {
continue;
}
if (model.startsWith(variable)) {
for (let [index, val] of (values as any[]).entries()) {
if (val) {
let avVal = average[index];
average[index] = avVal + val;
averageCount[index]++;
if (minValues[index] > val || minValues[index] === undefined) {
minValues[index] = val;
}
if (maxValues[index] < val || maxValues[index] === undefined) {
maxValues[index] = val;
if (minValues[index] > val || minValues[index] === undefined) {
minValues[index] = val;
}
if (maxValues[index] < val || maxValues[index] === undefined) {
maxValues[index] = val;
}
}
}
}
unit = data.hourly_units[model];
unit = data.hourly_units[model];
}
}
}
for (let [index, val] of average.entries()) {
average[index] = Math.round((val / averageCount[index]) * 10) / 10;
}
for (let [index, val] of average.entries()) {
average[index] = Math.round((val / averageCount[index]) * 10) / 10;
}
const minMax = [];
for (let [index, min] of minValues.entries()) {
minMax.push([min, maxValues[index]]);
}
const minMax = [];
for (let [index, min] of minValues.entries()) {
minMax.push([min, maxValues[index]]);
}
series.push({
name: 'temperature_2m_spread',
data: minMax,
type: 'arearange',
tooltip: {
valueSuffix: ' ' + unit
},
pointStart: hourly_starttime,
pointInterval: pointInterval,
className: 'highcharts-spread-series'
});
series.push({
name: 'temperature_2m_spread',
data: minMax,
type: 'arearange',
tooltip: {
valueSuffix: ' ' + unit
},
pointStart: hourly_starttime,
pointInterval: pointInterval,
className: 'highcharts-spread-series'
});
series.push({
name: variable + '_average',
data: average,
dashStyle: 'ShortDashDot',
color: '#5e5e5e',
type:
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²' ? 'column' : 'spline',
tooltip: {
valueSuffix: ' ' + unit
},
lineWidth: 4,
states: {
hover: {
lineWidth: 6
}
},
pointStart: hourly_starttime,
pointInterval: pointInterval,
className: 'highcharts-average-series'
});
new Highcharts.Chart(chartDiv, {
credits: {
text: count === $params.hourly.length - 1 ? 'Open-Meteo.com' : '',
href: 'http://open-meteo.com'
},
chart: {
height: showLegend ? '400px' : '300px',
styledMode: true,
marginLeft: '50',
marginRight: 0
},
lang: {
locale: 'en-GB'
},
title: {
text: count === 0 ? 'Model Spread' : '',
align: 'left'
},
subtitle: {
text:
count === 0
? `Compare <span class="font-bold">${$params.hourly.join(', ')}</span> in models: <span class="font-bold">` +
$params.models.join(', ') +
'</span>'
: '',
align: 'left'
},
yAxis: {
title: {
text: unit
}
},
xAxis: {
type: 'datetime',
plotLines: [
{
value: Date.now() + data.utc_offset_seconds * 1000,
color: 'red',
width: 2
}
],
plotBands: plotBands
},
plotOptions: {
spline: {
lineWidth: 2,
states: {
hover: {
lineWidth: 3
}
},
marker: {
enabled: false
series.push({
name: variable + '_average',
data: average,
dashStyle: 'ShortDashDot',
color: '#5e5e5e',
type:
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²'
? 'column'
: 'spline',
tooltip: {
valueSuffix: ' ' + unit
},
lineWidth: 4,
states: {
hover: {
lineWidth: 6
}
},
column: {
pointWidth: 5
}
},
pointStart: hourly_starttime,
pointInterval: pointInterval,
className: 'highcharts-average-series'
});
legend: {
enabled: showLegend,
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
},
const chart = new Highcharts.Chart(chartDiv, {
credits: {
text: count === ($params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '',
href: 'http://open-meteo.com'
},
series: series,
chart: {
height: showLegend ? '400px' : '300px',
styledMode: true,
marginLeft: '50',
marginRight: 0
},
responsive: {
rules: [
{
condition: {
maxWidth: 800
}
lang: {
locale: 'en-GB'
},
title: {
text: count === 0 ? 'Model Spread' : '',
align: 'left'
},
subtitle: {
text:
count === 0
? `Compare <span class="font-bold">${$params.hourly?.join(', ')}</span> in models: <span class="font-bold">` +
$params.models?.join(', ') +
'</span>'
: '',
align: 'left'
},
yAxis: {
title: {
text: unit
}
]
},
},
tooltip: {
shared: true,
animation: false
}
});
xAxis: {
type: 'datetime',
plotLines: [
{
value: Date.now() + data.utc_offset_seconds * 1000,
color: 'red',
width: 2
}
],
plotBands: plotBands
},
count++;
node.appendChild(chartDiv);
}
plotOptions: {
spline: {
lineWidth: 2,
states: {
hover: {
lineWidth: 3
}
},
marker: {
enabled: false
}
},
column: {
pointWidth: 5
}
},
legend: {
enabled: showLegend,
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
},
series: series,
responsive: {
rules: [
{
condition: {
maxWidth: 800
}
}
]
},
tooltip: {
shared: true,
animation: false
}
});
count++;
charts.push(chart);
// eslint-disable-next-line svelte/no-dom-manipulating
node.appendChild(chartDiv);
}
})();
}
});
onDestroy(() => {
if (chart) {
chart.destroy();
}
charts.forEach((c) => c.destroy());
});
</script>
<!-- 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
class="{count > 0
+1 -1
View File
@@ -2,7 +2,7 @@ import { get } from 'svelte/store';
import { storedLocation } from '$lib/stores/settings';
import type { LayoutLoad } from './$types';
import type { LayoutLoad } from '././$types';
const location = get(storedLocation);
+208 -193
View File
@@ -17,8 +17,9 @@
import { defaultParameters } from './options';
let node: HTMLElement;
let chart: any;
let Highcharts = $state();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let charts: any[] = [];
let Highcharts = $state<any>();
let showLegend = $state(false);
let averageOnly = $state(false);
@@ -47,224 +48,238 @@
if (dev) {
// const HighchartsDebugger = await import('highcharts/modules/debugger');
// HighchartsDebugger.default(Highcharts);
const Debugger = (await import('highcharts/es-modules/Extensions/Debugger/Debugger.js'))
.default;
// @ts-ignore
const Debugger = (
(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')
(await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js')) as any
).default;
Highcharts.errorMessages = ErrorMessages;
Debugger.compose(Highcharts.Chart);
}
});
$effect(async () => {
$effect(() => {
count = 0;
if (Highcharts) {
node.replaceChildren([]);
charts.forEach((c) => c.destroy());
charts = [];
// eslint-disable-next-line svelte/no-dom-manipulating
(node as any).replaceChildren();
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`
);
const data = await dataReq.json();
(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`
);
const data = await dataReq.json();
let dailyFirstModelKey = Object.keys(data.daily)[1].split('_');
dailyFirstModelKey.shift();
dailyFirstModelKey = dailyFirstModelKey.join('_');
let dailyFirstModelKeyParts = Object.keys(data.daily)[1].split('_');
dailyFirstModelKeyParts.shift();
const dailyFirstModelKey = dailyFirstModelKeyParts.join('_');
let plotBands: any = [];
if (
'daily' in data &&
'sunrise_' + dailyFirstModelKey in data.daily &&
'sunset_' + dailyFirstModelKey in data.daily
) {
let rise = data.daily['sunrise_' + dailyFirstModelKey];
let set = data.daily['sunset_' + dailyFirstModelKey];
plotBands = rise.map(function (r, i) {
return {
color: 'rgba(255, 255, 194, 0.5)',
from: (r + data.utc_offset_seconds) * 1000,
to: (set[i] + data.utc_offset_seconds) * 1000
};
});
}
for (let variable of $params.hourly) {
const chartDiv = document.createElement('div');
let unit;
let hourly_starttime = (data.hourly.time[0] + data.utc_offset_seconds) * 1000;
let pointInterval = (data.hourly.time[1] - data.hourly.time[0]) * 1000;
const series = [];
let average = new Array(data.hourly.time.length).fill(0);
let averageCount = new Array(data.hourly.time.length).fill(0);
for (let [model, values] of Object.entries(data.hourly)) {
if (model === 'time') {
continue;
}
if (model.startsWith(variable)) {
for (let [index, val] of values.entries()) {
if (val) {
let avVal = average[index];
average[index] = avVal + val;
averageCount[index]++;
}
}
unit = data.hourly_units[model];
if (!averageOnly) {
series.push({
name: model,
data: values,
type:
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²'
? 'column'
: 'spline',
tooltip: {
valueSuffix: ' ' + unit
},
pointStart: hourly_starttime,
pointInterval: pointInterval
});
}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
let plotBands: any = [];
if (
'daily' in data &&
'sunrise_' + dailyFirstModelKey in data.daily &&
'sunset_' + dailyFirstModelKey in data.daily
) {
let rise = data.daily['sunrise_' + dailyFirstModelKey];
let set = data.daily['sunset_' + dailyFirstModelKey];
plotBands = rise.map(function (r: any, i: number) {
return {
color: 'rgba(255, 255, 194, 0.5)',
from: (r + data.utc_offset_seconds) * 1000,
to: (set[i] + data.utc_offset_seconds) * 1000
};
});
}
for (let [index, val] of average.entries()) {
average[index] = Math.round((val / averageCount[index]) * 10) / 10;
}
for (let variable of $params.hourly || []) {
const chartDiv = document.createElement('div');
series.push({
name: variable + '_average',
data: average,
dashStyle: 'ShortDashDot',
color: '#5e5e5e',
type:
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²' ? 'column' : 'spline',
tooltip: {
valueSuffix: ' ' + unit
},
lineWidth: 4,
states: {
hover: {
lineWidth: 6
let unit;
let hourly_starttime = (data.hourly.time[0] + data.utc_offset_seconds) * 1000;
let pointInterval = (data.hourly.time[1] - data.hourly.time[0]) * 1000;
const series = [];
let average = new Array(data.hourly.time.length).fill(0);
let averageCount = new Array(data.hourly.time.length).fill(0);
for (let [model, values] of Object.entries(data.hourly)) {
if (model === 'time') {
continue;
}
},
pointStart: hourly_starttime,
pointInterval: pointInterval,
className: 'highcharts-average-series'
});
new Highcharts.Chart(chartDiv, {
credits: {
text: count === $params.hourly.length - 1 ? 'Open-Meteo.com' : '',
href: 'http://open-meteo.com'
},
chart: {
height: showLegend ? '400px' : '300px',
styledMode: true,
marginLeft: '50',
marginRight: 0
},
lang: {
locale: 'en-GB'
},
title: {
text: count === 0 ? 'Model Compare' : '',
align: 'left'
},
subtitle: {
text:
count === 0
? `Compare <span class="font-bold">${$params.hourly.join(', ')}</span> in models: <span class="font-bold">` +
$params.models.join(', ') +
'</span>'
: '',
align: 'left'
},
yAxis: {
title: {
text: unit
}
},
xAxis: {
type: 'datetime',
plotLines: [
{
value: Date.now() + data.utc_offset_seconds * 1000,
color: 'red',
width: 2
}
],
plotBands: plotBands
},
plotOptions: {
spline: {
lineWidth: 2,
states: {
hover: {
lineWidth: 3
if (model.startsWith(variable)) {
for (let [index, val] of (values as any[]).entries()) {
if (val) {
let avVal = average[index];
average[index] = avVal + val;
averageCount[index]++;
}
},
marker: {
enabled: false
}
unit = data.hourly_units[model];
if (!averageOnly) {
series.push({
name: model,
data: values,
type:
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²'
? 'column'
: 'spline',
tooltip: {
valueSuffix: ' ' + unit
},
pointStart: hourly_starttime,
pointInterval: pointInterval
});
}
}
}
for (let [index, val] of average.entries()) {
average[index] = Math.round((val / averageCount[index]) * 10) / 10;
}
series.push({
name: variable + '_average',
data: average,
dashStyle: 'ShortDashDot',
color: '#5e5e5e',
type:
unit == 'mm' || unit == 'cm' || unit == 'inch' || unit == 'MJ/m²'
? 'column'
: 'spline',
tooltip: {
valueSuffix: ' ' + unit
},
lineWidth: 4,
states: {
hover: {
lineWidth: 6
}
},
column: {
pointWidth: 5
}
},
pointStart: hourly_starttime,
pointInterval: pointInterval,
className: 'highcharts-average-series'
});
legend: {
enabled: showLegend,
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
},
const chart = new Highcharts.Chart(chartDiv, {
credits: {
text: count === ($params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '',
href: 'http://open-meteo.com'
},
series: series,
chart: {
height: showLegend ? '400px' : '300px',
styledMode: true,
marginLeft: '50',
marginRight: 0
},
responsive: {
rules: [
{
condition: {
maxWidth: 800
}
lang: {
locale: 'en-GB'
},
title: {
text: count === 0 ? 'Model Compare' : '',
align: 'left'
},
subtitle: {
text:
count === 0
? `Compare <span class="font-bold">${$params.hourly?.join(', ')}</span> in models: <span class="font-bold">` +
$params.models?.join(', ') +
'</span>'
: '',
align: 'left'
},
yAxis: {
title: {
text: unit
}
]
},
},
tooltip: {
shared: true,
animation: false
}
});
xAxis: {
type: 'datetime',
plotLines: [
{
value: Date.now() + data.utc_offset_seconds * 1000,
color: 'red',
width: 2
}
],
plotBands: plotBands
},
count++;
node.appendChild(chartDiv);
}
plotOptions: {
spline: {
lineWidth: 2,
states: {
hover: {
lineWidth: 3
}
},
marker: {
enabled: false
}
},
column: {
pointWidth: 5
}
},
legend: {
enabled: showLegend,
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
},
series: series,
responsive: {
rules: [
{
condition: {
maxWidth: 800
}
}
]
},
tooltip: {
shared: true,
animation: false
}
});
count++;
charts.push(chart);
// eslint-disable-next-line svelte/no-dom-manipulating
node.appendChild(chartDiv);
}
})();
}
});
onDestroy(() => {
if (chart) {
chart.destroy();
}
charts.forEach((c) => c.destroy());
});
</script>
<!-- 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
class="{count > 0
@@ -319,12 +334,12 @@
<div class="mt-4 md:mt-8">
<div class="flex">
<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
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}&nbsp;/&nbsp;{models.flat().length}
{$params.models?.length}&nbsp;/&nbsp;{models.flat().length}
</div>
</div>
{/if}
@@ -343,7 +358,7 @@
aria-labelledby="{value}_label"
onCheckedChange={() => {
if ($params.models?.includes(value)) {
$params.models = $params.models.filter((item) => {
$params.models = $params.models.filter((item: string) => {
return item !== value;
});
} else if ($params.models) {
@@ -371,12 +386,12 @@
Hourly Weather Variables
</h2></a
>
{#if $params.hourly.length > 0}
{#if $params.hourly && $params.hourly.length > 0}
<div transition:fade={{ duration: 200 }} class="relative mt-[5px]">
<div
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}&nbsp;/&nbsp;{hourly.flat().length}
{$params.hourly?.length}&nbsp;/&nbsp;{hourly.flat().length}
</div>
</div>
{/if}
@@ -397,7 +412,7 @@
aria-labelledby="{value}_label"
onCheckedChange={() => {
if ($params.hourly?.includes(value)) {
$params.hourly = $params.hourly.filter((item) => {
$params.hourly = $params.hourly.filter((item: string) => {
return item !== value;
});
} else if ($params.hourly) {
+14 -12
View File
@@ -6,18 +6,20 @@ 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 = [
+4 -4
View File
@@ -2,13 +2,13 @@ import { get } from 'svelte/store';
import { redirect } from '@sveltejs/kit';
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;
export const load = (async () => {
export const load: PageLoad = async () => {
const location = get(storedLocation);
const locationRoute = geoLocationNameToRoute(location.name);
const locationRoute = geoLocationNameToRoute(location.name || '');
throw redirect(
303,
'/weather/week/' +
@@ -18,4 +18,4 @@ export const load = (async () => {
: locationRoute + '_' + location.id
: locationRoute + '_' + location.id)
);
}) satisfies PageLoad;
};
@@ -21,7 +21,7 @@
import { getColor, textWhite } from '../../utils/colors';
import weatherCodes from '../../utils/weather-codes';
import { SvelteDate } from 'svelte/reactivity';
import { pad } from '$lib/utils';
import { pad } from '$lib/utils/index.js';
// --- Interfaces ---
@@ -63,9 +63,9 @@
// --- State Setup ---
const params = urlHashStore({
latitude: [$storedLocation.latitude],
longitude: [$storedLocation.longitude],
models: 'best_match',
latitude: [$storedLocation.latitude || 0],
longitude: [$storedLocation.longitude || 0],
models: ['best_match'],
...defaultParameters
});
@@ -89,7 +89,9 @@
const entries = 6;
const winddir = true;
let modelSelected = $derived(models.find((mo) => String(mo.value) === $params.models));
let modelSelected = $derived(
models.flat().find((mo) => $params.models?.includes(String(mo.value)))
);
// --- Logic ---
@@ -654,13 +656,20 @@
<div>
<div class="mt-6 flex gap-6 md:mt-12">
<div class="relative w-1/2">
<Select.Root name="model_selection" type="single" bind:value={$params.models}>
<Select.Root
name="model_selection"
type="single"
value={$params.models?.[0]}
onValueChange={(v) => {
$params.models = [v];
}}
>
<Select.Trigger
aria-label="Forecast days input"
class="h-12 cursor-pointer pt-6 [&_svg]:mb-3">{modelSelected?.label}</Select.Trigger
>
<Select.Content preventScroll={false} class="border-border">
{#each models as mo (mo.value)}
{#each models.flat() as mo (mo.value)}
<Select.Item class="cursor-pointer" value={mo.value}>{mo.label}</Select.Item>
{/each}
</Select.Content>
+6 -6
View File
@@ -4,11 +4,11 @@ import { type GeoLocation, 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;
export const load = (async (event) => {
export const load: PageLoad = async (event) => {
const urlLocation = event.params.location;
let urlLocationSplit, urlLocationName, urlLocationId;
@@ -31,8 +31,8 @@ export const load = (async (event) => {
// lat, long coordinates
if (urlLocation.includes('N') && urlLocation.includes('E')) {
urlLocationSplit = urlLocation.split(/N|E/);
const latitude = urlLocationSplit[0];
const longitude = urlLocationSplit[1];
const latitude = Number(urlLocationSplit[0]);
const longitude = Number(urlLocationSplit[1]);
location = {
//id: undefined,
@@ -58,7 +58,7 @@ export const load = (async (event) => {
}
}
const locationRoute = geoLocationNameToRoute(location.name);
const locationRoute = geoLocationNameToRoute(location.name || '');
if (location.population && location.population > 543000) {
// 1000 biggest cities
@@ -74,4 +74,4 @@ export const load = (async (event) => {
storedLocation.set(location);
return { location: location };
}) satisfies PageLoad;
};