fix: type errors (#3)
Co-authored-by: terraputix <terraputix@mailbox.org> Reviewed-on: useweb/open-meteo-blue#3
This commit was merged in pull request #3.
This commit is contained in:
@@ -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';
|
||||
@@ -16,14 +15,15 @@
|
||||
|
||||
let node: HTMLElement;
|
||||
let chart: any;
|
||||
let Highcharts = $state(null);
|
||||
let Highcharts = $state<typeof import('highcharts') | null>(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,
|
||||
@@ -41,221 +41,230 @@
|
||||
if (dev) {
|
||||
// const HighchartsDebugger = await import('highcharts/modules/debugger');
|
||||
// HighchartsDebugger.default(Highcharts);
|
||||
const Debugger = (await import('highcharts/es-modules/Extensions/Debugger/Debugger.js'))
|
||||
.default;
|
||||
const ErrorMessages = (
|
||||
await import('highcharts/es-modules/Extensions/Debugger/ErrorMessages.js')
|
||||
const Debugger = (
|
||||
await import('highcharts/es-modules/Extensions/Debugger/Debugger.js' as any)
|
||||
).default;
|
||||
Highcharts.errorMessages = ErrorMessages;
|
||||
Debugger.compose(Highcharts.Chart);
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$effect(async () => {
|
||||
count = 0;
|
||||
if (Highcharts) {
|
||||
node.replaceChildren([]);
|
||||
$effect(() => {
|
||||
const loadData = async () => {
|
||||
count = 0;
|
||||
if (Highcharts) {
|
||||
node.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();
|
||||
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
|
||||
};
|
||||
});
|
||||
}
|
||||
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'
|
||||
},
|
||||
new Highcharts!.Chart({
|
||||
chart: {
|
||||
renderTo: chartDiv,
|
||||
height: showLegend ? '400px' : '300px',
|
||||
styledMode: true,
|
||||
marginLeft: 50,
|
||||
marginRight: 0
|
||||
},
|
||||
|
||||
series: series,
|
||||
credits: {
|
||||
text: count === (params.hourly?.length || 0) - 1 ? 'Open-Meteo.com' : '',
|
||||
href: 'http://open-meteo.com'
|
||||
},
|
||||
|
||||
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 as any,
|
||||
|
||||
responsive: {
|
||||
rules: [
|
||||
{
|
||||
condition: {
|
||||
maxWidth: 800
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
tooltip: {
|
||||
shared: true,
|
||||
animation: false
|
||||
}
|
||||
});
|
||||
|
||||
count++;
|
||||
node.appendChild(chartDiv);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
loadData();
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
@@ -266,7 +275,10 @@
|
||||
</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
|
||||
@@ -299,7 +311,7 @@
|
||||
name="Show legend"
|
||||
bind:checked={showLegend}
|
||||
onCheckedChange={() => {
|
||||
$params.hourly = $params.hourly;
|
||||
params.hourly = params.hourly;
|
||||
}}
|
||||
/>
|
||||
<Label for="show_legend" class="mb-[2px] cursor-pointer text-lg">Show legend</Label>
|
||||
@@ -310,7 +322,7 @@
|
||||
name="Average only"
|
||||
bind:checked={averageOnly}
|
||||
onCheckedChange={() => {
|
||||
$params.hourly = $params.hourly;
|
||||
params.hourly = params.hourly;
|
||||
}}
|
||||
/>
|
||||
<Label for="average_only" class="mb-[2px] cursor-pointer text-lg">Average only</Label>
|
||||
|
||||
Reference in New Issue
Block a user