|
33 | 33 | let showTimeSelector = $state(true);
|
34 | 34 |
|
35 | 35 | import '../styles.css';
|
| 36 | + import { SvelteDate } from 'svelte/reactivity'; |
36 | 37 |
|
37 | 38 | let darkMode = $derived(mode.current);
|
38 | 39 | let timeSliderApi: { setDisabled: (d: boolean) => void };
|
|
163 | 164 | let url: URL;
|
164 | 165 | let params: URLSearchParams;
|
165 | 166 |
|
166 |
| - let domain: Domain = $state({ value: 'meteoswiss_icon_ch1', label: 'DWD ICON D2' }); |
| 167 | + let domain: Domain = $state({ |
| 168 | + value: 'meteoswiss_icon_ch1', |
| 169 | + label: 'DWD ICON D2', |
| 170 | + model_interval: 3 |
| 171 | + }); |
167 | 172 | let variable: Variable = $state({ value: 'temperature_2m', label: 'Temperature 2m' });
|
168 | 173 | let timeSelected = $state(new Date());
|
169 | 174 | let modelRunSelected = $state(new Date());
|
|
239 | 244 | domain = domains.find((dm) => dm.value === import.meta.env.VITE_DOMAIN) ?? domains[0];
|
240 | 245 | }
|
241 | 246 |
|
242 |
| - let urlModelTime = params.get('model_time'); |
| 247 | + let urlModelTime = params.get('model'); |
243 | 248 | if (urlModelTime && urlModelTime.length == 15) {
|
244 | 249 | const year = parseInt(urlModelTime.slice(0, 4));
|
245 | 250 | const month = parseInt(urlModelTime.slice(5, 7)) - 1; // zero-based
|
|
467 | 472 | `https://openmeteo.s3.amazonaws.com/data_spatial/${domain.value}/${latest ? 'latest' : 'in-progress'}.json`
|
468 | 473 | ).then(async (result) => {
|
469 | 474 | const json = await result.json();
|
470 |
| - const referenceTime = json.reference_time; |
471 |
| - modelRunSelected = new Date(referenceTime); |
| 475 | + if (latest) { |
| 476 | + const referenceTime = json.reference_time; |
| 477 | + modelRunSelected = new Date(referenceTime); |
472 | 478 |
|
473 |
| - if (modelRunSelected - timeSelected > 0) { |
474 |
| - timeSelected = new Date(referenceTime); |
| 479 | + if (modelRunSelected - timeSelected > 0) { |
| 480 | + timeSelected = new Date(referenceTime); |
| 481 | + } |
475 | 482 | }
|
476 | 483 |
|
477 | 484 | resolve(json);
|
|
509 | 516 | });
|
510 | 517 |
|
511 | 518 | let colors = $derived(colorScale.colors.reverse());
|
| 519 | +
|
| 520 | + let modelRuns = $derived.by(() => { |
| 521 | + if (latest) { |
| 522 | + let referenceTime = new Date(latest.reference_time); |
| 523 | + let returnArray = [ |
| 524 | + ...Array(Math.round(referenceTime.getUTCHours() / domain.model_interval + 1)) |
| 525 | + ].map((_, i) => { |
| 526 | + let d = new Date(); |
| 527 | + d.setUTCHours(i * domain.model_interval, 0, 0, 0); |
| 528 | + return d; |
| 529 | + }); |
| 530 | + return returnArray; |
| 531 | + } else { |
| 532 | + return []; |
| 533 | + } |
| 534 | + }); |
512 | 535 | </script>
|
513 | 536 |
|
514 | 537 | <div class="map" id="#map_container" bind:this={mapContainer}></div>
|
|
566 | 589 | </Sheet.Root>
|
567 | 590 |
|
568 | 591 | <Drawer.Root bind:open={drawerOpen}>
|
569 |
| - <Drawer.Content class=" h-1/3 "> |
570 |
| - <div class="flex flex-col items-center overflow-y-scroll"> |
| 592 | + <Drawer.Content class="h-1/3"> |
| 593 | + <div class="flex flex-col items-center overflow-y-scroll pb-12"> |
571 | 594 | <div class="container mx-auto px-3">
|
572 | 595 | <div class="mt-3 flex w-full flex-col flex-wrap gap-6 sm:flex-row sm:gap-0">
|
573 |
| - <div class="flex flex-col gap-3 sm:w-1/2 md:w-1/4 md:pr-3"> |
| 596 | + <div class="flex flex-col gap-3 sm:w-1/2 md:w-1/3 md:pr-3"> |
574 | 597 | <h2 class="text-lg font-bold">Domains</h2>
|
575 | 598 | <div class="relative">
|
576 | 599 | <Select.Root
|
|
608 | 631 | </div>
|
609 | 632 |
|
610 | 633 | {#await latestRequest}
|
611 |
| - <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/4 md:px-3"> |
| 634 | + <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/3 md:px-3"> |
612 | 635 | <h2 class="mb-2 text-lg font-bold">Model runs</h2>
|
613 | 636 | Loading latest model runs...
|
614 | 637 | </div>
|
615 | 638 |
|
616 |
| - <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/4 md:pl-3"> |
| 639 | + <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/3 md:pl-3"> |
617 | 640 | <h2 class="mb-2 text-lg font-bold">Variables</h2>
|
618 | 641 | Loading domain variables...
|
619 | 642 | </div>
|
620 | 643 | {:then latest}
|
621 |
| - <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/4 md:px-3"> |
| 644 | + <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/3 md:px-3"> |
622 | 645 | <h2 class="mb-2 text-lg font-bold">Model runs</h2>
|
623 |
| - {#each [modelRunSelected] as vt, i (i)} |
624 |
| - {@const mr = new Date(vt)} |
| 646 | + {#each modelRuns as mr, i (i)} |
625 | 647 | <Button
|
626 | 648 | class="cursor-pointer bg-blue-200 hover:bg-blue-600 {mr.getTime() ===
|
627 | 649 | modelRunSelected.getTime()
|
628 | 650 | ? 'bg-blue-400'
|
629 | 651 | : ''}"
|
630 | 652 | onclick={() => {
|
| 653 | + modelRunSelected = mr; |
| 654 | + url.searchParams.set( |
| 655 | + 'model', |
| 656 | + mr.toISOString().replace(/[:Z]/g, '').slice(0, 15) |
| 657 | + ); |
| 658 | + pushState(url + map._hash.getHashString(), {}); |
631 | 659 | toast(
|
632 | 660 | 'Model run set to: ' +
|
633 | 661 | mr.getUTCFullYear() +
|
|
664 | 692 | ? 'bg-blue-400'
|
665 | 693 | : ''}"
|
666 | 694 | onclick={() => {
|
| 695 | + modelRunSelected = ip; |
| 696 | + url.searchParams.set( |
| 697 | + 'model', |
| 698 | + ip.toISOString().replace(/[:Z]/g, '').slice(0, 15) |
| 699 | + ); |
| 700 | + pushState(url + map._hash.getHashString(), {}); |
667 | 701 | toast(
|
668 | 702 | 'Model run set to: ' +
|
669 | 703 | ip.getUTCFullYear() +
|
|
692 | 726 | {/await}
|
693 | 727 | </div>
|
694 | 728 | {#if timeValid}
|
695 |
| - <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/4 md:pl-3"> |
| 729 | + <div class="flex flex-col gap-1 sm:w-1/2 md:w-1/3 md:pl-3"> |
696 | 730 | <h2 class="mb-2 text-lg font-bold">Variables</h2>
|
697 | 731 |
|
698 | 732 | <div class="relative">
|
|
0 commit comments