diff --git a/src/lib/stores/settings.ts b/src/lib/stores/settings.ts index 766f4c1..284e7ed 100644 --- a/src/lib/stores/settings.ts +++ b/src/lib/stores/settings.ts @@ -78,3 +78,6 @@ export const defaultVariablePrefs: VariablePrefs = { }; export const storedVariablePrefs = persisted('variable_prefs', defaultVariablePrefs); + +/** Selected ensemble model for the 14-day spread forecast. */ +export const storedEnsembleModel = persisted('ensemble_model', 'ncep_gefs_seamless'); diff --git a/src/routes/+layout.server.ts b/src/routes/+layout.server.ts index 189f71e..deb9ffd 100644 --- a/src/routes/+layout.server.ts +++ b/src/routes/+layout.server.ts @@ -1 +1,5 @@ export const prerender = true; + +// Static hosting: emit every page as /index.html so plain file servers +// resolve URLs like /weather/week/ without pretty-URL rewrites. +export const trailingSlash = 'always'; diff --git a/src/routes/weather/14-day/[location]/+page.svelte b/src/routes/weather/14-day/[location]/+page.svelte index 64752b1..9f25cef 100644 --- a/src/routes/weather/14-day/[location]/+page.svelte +++ b/src/routes/weather/14-day/[location]/+page.svelte @@ -1,7 +1,8 @@ + + +
+
+ {location.country +
+

+ {location.name} +

+

+ {#if location.admin1}{location.admin1}, + {/if}{location.country ?? ''} + · + 14-day ensemble forecast +

+
+
+ + { + params.models = [model]; + storedEnsembleModel.set(model); + }} + /> +
+ {#if loadError} diff --git a/src/routes/weather/options.ts b/src/routes/weather/options.ts index 04f3cbd..0197874 100644 --- a/src/routes/weather/options.ts +++ b/src/routes/weather/options.ts @@ -351,3 +351,131 @@ export const hourly = [ { value: 'temperature_180m', label: 'Temperature 180m' } ] ]; + +/** + * Ensemble models for the 14-day spread forecast, synced from the + * open-meteo/website project (src/routes/en/docs/ensemble-api/options.ts). + * Resolutions/update cadences from weather-map-layer domains.ts where known. + */ +export const ensembleModelGroups: WeatherModelGroup[] = [ + { + value: 'dwd', + label: 'DWD Germany', + models: [ + { + value: 'icon_seamless_eps', + label: 'DWD ICON EPS Seamless', + resolution: '2-25 km', + update: 'every 6 h' + }, + { + value: 'icon_global_eps', + label: 'DWD ICON EPS Global', + resolution: '25 km', + update: 'every 12 h' + }, + { value: 'icon_eu_eps', label: 'DWD ICON EPS EU', resolution: '13 km', update: 'every 6 h' }, + { value: 'icon_d2_eps', label: 'DWD ICON EPS D2', resolution: '2 km', update: 'every 6 h' } + ] + }, + { + value: 'ncep', + label: 'NOAA U.S.', + models: [ + { + value: 'ncep_gefs_seamless', + label: 'GFS Ensemble Seamless', + resolution: '25-50 km', + update: 'every 6 h' + }, + { + value: 'ncep_gefs025', + label: 'GFS Ensemble 0.25°', + resolution: '25 km', + update: 'every 6 h' + }, + { + value: 'ncep_gefs05', + label: 'GFS Ensemble 0.5°', + resolution: '50 km', + update: 'every 6 h' + }, + { value: 'ncep_aigefs025', label: 'AIGEFS 0.25°', resolution: '25 km', update: 'every 6 h' } + ] + }, + { + value: 'ecmwf', + label: 'ECMWF', + models: [ + { + value: 'ecmwf_ifs025_ensemble', + label: 'ECMWF IFS 0.25° Ensemble', + resolution: '25 km', + update: 'every 6 h' + }, + { + value: 'ecmwf_aifs025_ensemble', + label: 'ECMWF AIFS 0.25° Ensemble', + resolution: '25 km', + update: 'every 6 h' + } + ] + }, + { + value: 'cmc_gem', + label: 'GEM Canada', + models: [ + { + value: 'gem_global_ensemble', + label: 'GEM Global Ensemble', + resolution: '50 km', + update: 'every 12 h' + } + ] + }, + { + value: 'bom', + label: 'BOM Australia', + models: [{ value: 'bom_access_global_ensemble', label: 'BOM ACCESS Global' }] + }, + { + value: 'ukmo', + label: 'UK Met Office', + models: [ + { + value: 'ukmo_global_ensemble_20km', + label: 'UK MetOffice Global 20km', + resolution: '20 km' + }, + { value: 'ukmo_uk_ensemble_2km', label: 'UK MetOffice UK 2km', resolution: '2 km' } + ] + }, + { + value: 'meteoswiss', + label: 'MeteoSwiss', + models: [ + { + value: 'meteoswiss_icon_ch1_ensemble', + label: 'MeteoSwiss ICON CH1', + resolution: '1 km', + update: 'every 12 h' + }, + { + value: 'meteoswiss_icon_ch2_ensemble', + label: 'MeteoSwiss ICON CH2', + resolution: '2 km', + update: 'every 12 h' + } + ] + }, + { + value: 'google', + label: 'Google', + models: [{ value: 'google_weathernext2_ensemble', label: 'Google WeatherNext 2 Ensemble' }] + } +]; + +export const ensembleModels: WeatherModel[] = ensembleModelGroups.flatMap((group) => group.models); + +export const findEnsembleModel = (value: string): WeatherModel | undefined => + ensembleModels.find((model) => model.value === value); diff --git a/src/routes/weather/week/[location]/ModelSelector.svelte b/src/routes/weather/week/[location]/ModelSelector.svelte index 35d623b..864f8ec 100644 --- a/src/routes/weather/week/[location]/ModelSelector.svelte +++ b/src/routes/weather/week/[location]/ModelSelector.svelte @@ -1,22 +1,35 @@
- Weather model + {label} {modelLabel} - - {modelMeta} - + {#if modelMeta} + + {modelMeta} + + {/if}
- {#each modelGroups as group (group.value)} + {#each groups as group (group.value)} {mo.label} {#if mo.resolution && mo.resolution !== 'varies'} - {mo.resolution} · updated {mo.update} + {mo.resolution}{mo.update ? ` · updated ${mo.update}` : ''} - {:else} + {:else if mo.value === 'best_match'} Automatic selection {/if}