feat: local time #7
@@ -387,7 +387,7 @@ export async function fetchWeekForecast(params: WeekForecastParams): Promise<Wee
|
||||
export async function fetchModelComparison(
|
||||
params: ModelCompareParams
|
||||
): Promise<ModelCompareResult> {
|
||||
const forecastApiParams: Record<string, string> = {
|
||||
const forecastApiParams: Record<string, string | number | undefined> = {
|
||||
latitude: String(params.latitude),
|
||||
longitude: String(params.longitude),
|
||||
hourly: params.hourlyVariables.join(','),
|
||||
@@ -395,7 +395,8 @@ export async function fetchModelComparison(
|
||||
daily: 'sunrise,sunset',
|
||||
temperature_unit: params.temperature_unit ?? 'celsius',
|
||||
wind_speed_unit: params.wind_speed_unit ?? 'kmh',
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm'
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm',
|
||||
timezone: params.timezone
|
||||
};
|
||||
|
||||
const responses = await fetchWeatherApi(FORECAST_URL, forecastApiParams);
|
||||
@@ -493,7 +494,7 @@ export async function fetchEnsembleForecast(
|
||||
): Promise<EnsembleForecastResult> {
|
||||
const forecastDays = params.forecast_days ?? 14;
|
||||
|
||||
const ensembleParams: Record<string, string> = {
|
||||
const ensembleParams: Record<string, string | number | undefined> = {
|
||||
latitude: String(params.latitude),
|
||||
longitude: String(params.longitude),
|
||||
hourly: params.hourlyVariables.join(','),
|
||||
@@ -501,7 +502,8 @@ export async function fetchEnsembleForecast(
|
||||
forecast_days: String(forecastDays),
|
||||
temperature_unit: params.temperature_unit ?? 'celsius',
|
||||
wind_speed_unit: params.wind_speed_unit ?? 'kmh',
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm'
|
||||
precipitation_unit: params.precipitation_unit ?? 'mm',
|
||||
timezone: params.timezone
|
||||
};
|
||||
|
||||
const dailyParams: Record<string, string> = {
|
||||
|
||||
@@ -131,7 +131,7 @@ export function buildTooltip(
|
||||
colors?: ThemeColors
|
||||
): Record<string, unknown> {
|
||||
const c = colors ?? getThemeColors();
|
||||
const { unit } = options;
|
||||
const { unit, timezone } = options;
|
||||
|
||||
return {
|
||||
trigger: 'axis',
|
||||
@@ -142,7 +142,15 @@ export function buildTooltip(
|
||||
backgroundColor: c.tooltipBg,
|
||||
color: c.text,
|
||||
borderColor: c.tooltipBorder,
|
||||
borderWidth: 1
|
||||
borderWidth: 1,
|
||||
formatter: timezone
|
||||
? (params: { axisDimension: string; value: number }) => {
|
||||
if (params.axisDimension === 'x') {
|
||||
return formatZoned(new Date(params.value), timezone, 'EEE d MMM HH:mm');
|
||||
}
|
||||
return params.value.toFixed(1);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
},
|
||||
backgroundColor: c.tooltipBg,
|
||||
@@ -150,6 +158,27 @@ export function buildTooltip(
|
||||
textStyle: {
|
||||
color: c.text
|
||||
},
|
||||
formatter: timezone
|
||||
? (
|
||||
params: Array<{
|
||||
axisValue: number;
|
||||
seriesName: string;
|
||||
marker: string;
|
||||
value: number | number[] | null;
|
||||
}>
|
||||
) => {
|
||||
if (!params || params.length === 0) return '';
|
||||
const date = new Date(params[0].axisValue);
|
||||
let html = `<b>${formatZoned(date, timezone, 'EEE d MMM HH:mm')}</b><br/>`;
|
||||
params.forEach((item) => {
|
||||
if (item.seriesName === 'Daylight' || item.seriesName === 'Current Time') return;
|
||||
const val = Array.isArray(item.value) ? item.value[1] : item.value;
|
||||
if (val === null || val === undefined) return;
|
||||
html += `${item.marker} ${item.seriesName}: <b>${val.toFixed(1)} ${unit}</b><br/>`;
|
||||
});
|
||||
return html;
|
||||
}
|
||||
: undefined,
|
||||
valueFormatter: (value: number) => {
|
||||
if (value === null || value === undefined) return '-';
|
||||
return value.toFixed(1) + ' ' + unit;
|
||||
@@ -197,6 +226,14 @@ export function buildTimeXAxis(timezone?: string, colors?: ThemeColors): Record<
|
||||
color: c.axisLine
|
||||
}
|
||||
},
|
||||
axisPointer: {
|
||||
label: {
|
||||
formatter: timezone
|
||||
? (params: { value: number }) =>
|
||||
formatZoned(new Date(params.value), timezone, 'EEE d MMM HH:mm')
|
||||
: undefined
|
||||
}
|
||||
},
|
||||
axisLabel: {
|
||||
color: c.text,
|
||||
hideOverlap: true,
|
||||
|
||||
@@ -53,13 +53,21 @@
|
||||
|
||||
let params = $state({
|
||||
...defaultParameters,
|
||||
hourly: ['temperature_2m', 'rain', 'relative_humidity_2m'],
|
||||
hourly: [
|
||||
'temperature_2m',
|
||||
'rain',
|
||||
'relative_humidity_2m',
|
||||
'wind_speed_10m',
|
||||
'wind_direction_10m'
|
||||
],
|
||||
models: [
|
||||
'ecmwf_ifs',
|
||||
'ecmwf_ifs025',
|
||||
'meteofrance_seamless',
|
||||
'ukmo_seamless',
|
||||
'icon_seamless',
|
||||
'gem_seamless'
|
||||
'gem_seamless',
|
||||
'gfs_seamless'
|
||||
]
|
||||
});
|
||||
|
||||
|
||||
@@ -7,16 +7,52 @@ 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: 'ecmwf_ifs', label: 'ECMWF IFS' },
|
||||
{ value: 'ecmwf_ifs025', label: 'ECMWF IFS 0.25' },
|
||||
{ value: 'ecmwf_aifs025_single', label: 'ECMWF AIFS 0.25 Single' },
|
||||
{ value: 'cma_grapes_global', label: 'CMA GRAPES Global' },
|
||||
{ value: 'bom_access_global', label: 'BOM ACCESS Global' },
|
||||
{ value: 'kma_seamless', label: 'KMA Seamless' },
|
||||
{ value: 'icon_seamless', label: 'DWD ICON Seamless' },
|
||||
{ value: 'kma_ldps', label: 'KMA LDPS' },
|
||||
{ value: 'kma_gdps', label: 'KMA GDPS' },
|
||||
{ value: 'meteofrance_seamless', label: 'Meteo-France Seamless' },
|
||||
{ value: 'meteofrance_arpege_world', label: 'Meteo-France ARPEGE World' },
|
||||
{ value: 'meteofrance_arpege_europe', label: 'Meteo-France ARPEGE Europe' },
|
||||
{ value: 'meteofrance_arome_france', label: 'Meteo-France AROME France' },
|
||||
{ value: 'meteofrance_arome_france_hd', label: 'Meteo-France AROME France HD' },
|
||||
{ value: 'knmi_seamless', label: 'KNMI Seamless' },
|
||||
{ value: 'knmi_harmonie_arome_europe', label: 'KNMI Harmonie Arome Europe' },
|
||||
{ value: 'knmi_harmonie_arome_netherlands', label: 'KNMI Harmonie Arome Netherlands' },
|
||||
{ value: 'dmi_seamless', label: 'DMI Seamless' },
|
||||
{ value: 'dmi_harmonie_arome_europe', label: 'DMI Harmonie Arome Europe' },
|
||||
{ value: 'ukmo_seamless', label: 'UKMO Seamless' },
|
||||
{ value: 'ukmo_global_deterministic_10km', label: 'UKMO Global Deterministic 10km' },
|
||||
{ value: 'ukmo_uk_deterministic_2km', label: 'UKMO UK Deterministic 2km' },
|
||||
{ value: 'meteoswiss_icon_seamless', label: 'MeteoSwiss ICON Seamless' },
|
||||
{ value: 'meteoswiss_icon_ch2', label: 'MeteoSwiss ICON CH2' },
|
||||
{ value: 'meteoswiss_icon_ch1', label: 'MeteoSwiss ICON CH1' },
|
||||
{ value: 'metno_nordic', label: 'MET Norway Nordic' },
|
||||
{ value: 'metno_seamless', label: 'MET Norway Seamless' },
|
||||
{ value: 'gem_hrdps_west', label: 'GEM HRDPS West' },
|
||||
{ value: 'gem_regional', label: 'GEM Regional' },
|
||||
{ value: 'gem_global', label: 'GEM Global' },
|
||||
{ value: 'gem_seamless', label: 'GEM Seamless' },
|
||||
{ value: 'meteofrance_seamless', label: 'Météo-France 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: 'jma_seamless', label: 'JMA Seamless' },
|
||||
{ value: 'jma_msm', label: 'JMA MSM' },
|
||||
{ value: 'jma_gsm', label: 'JMA GSM' },
|
||||
{ value: 'gfs_seamless', label: 'GFS Seamless' },
|
||||
{ value: 'gfs_global', label: 'GFS Global' },
|
||||
{ value: 'gfs_hrrr', label: 'GFS HRRR' },
|
||||
{ value: 'gfs_graphcast025', label: 'GFS Graphcast 0.25' },
|
||||
{ value: 'ncep_nbm_conus', label: 'NCEP NBM CONUS' },
|
||||
{ value: 'ncep_nam_conus', label: 'NCEP NAM CONUS' },
|
||||
{ value: 'ncep_aigfs025', label: 'NCEP AIGFS 0.25' },
|
||||
{ value: 'ncep_hgefs025_ensemble_mean', label: 'NCEP HG-EFS 0.25 Ensemble Mean' },
|
||||
{ value: 'icon_seamless', label: 'ICON Seamless (DWD)' },
|
||||
{ value: 'icon_global', label: 'ICON Global' },
|
||||
{ value: 'icon_eu', label: 'ICON EU' },
|
||||
{ value: 'icon_d2', label: 'ICON-D2' },
|
||||
{ value: 'italia_meteo_arpae_icon_2i', label: 'Italia Meteo ARPAE ICON 2i' }
|
||||
];
|
||||
|
||||
export const hourly = [
|
||||
|
||||
@@ -171,7 +171,14 @@
|
||||
});
|
||||
|
||||
const tooltipBase = (
|
||||
formatter: (params: Record<string, unknown>[]) => string
|
||||
formatter: (
|
||||
params: Array<{
|
||||
axisValue: number;
|
||||
seriesName: string;
|
||||
marker: string;
|
||||
value: number | number[] | null;
|
||||
}>
|
||||
) => string
|
||||
): Record<string, unknown> => ({
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
@@ -181,7 +188,13 @@
|
||||
backgroundColor: colors.tooltipBg,
|
||||
color: colors.text,
|
||||
borderColor: colors.tooltipBorder,
|
||||
borderWidth: 1
|
||||
borderWidth: 1,
|
||||
formatter: (params: { axisDimension: string; value: number }) => {
|
||||
if (params.axisDimension === 'x') {
|
||||
return formatZoned(new Date(params.value), data.timezone, 'EEE d MMM HH:mm');
|
||||
}
|
||||
return params.value.toFixed(1);
|
||||
}
|
||||
}
|
||||
},
|
||||
backgroundColor: colors.tooltipBg,
|
||||
|
||||
Reference in New Issue
Block a user