aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Stendahl <jakob.stendahl@outlook.com>2022-04-28 01:32:54 +0200
committerJakob Stendahl <jakob.stendahl@outlook.com>2022-04-28 01:33:08 +0200
commit55cd53f4e6b1e13d2866a84a9631be8f89651cf2 (patch)
tree5b77ffc3311b490d4f979055e97823011280aace
parentae52909d00cdcda30d6ed07e302c0a50abe70b75 (diff)
downloadAurora-data-55cd53f4e6b1e13d2866a84a9631be8f89651cf2.tar.gz
Aurora-data-55cd53f4e6b1e13d2866a84a9631be8f89651cf2.zip
Add one hour forecast
-rw-r--r--src/components/Basic/Bar.svelte48
-rw-r--r--src/components/Forecast/ForecastDrawer.svelte108
-rw-r--r--src/components/Forecast/OneHourForecast/OneHourForecast.svelte76
-rw-r--r--src/components/Forecast/OneHourForecast/PredictionItem.svelte91
-rw-r--r--src/components/Forecast/ThreeDayForecast/PredictionItem.svelte (renamed from src/components/PredictedSpaceWeatherThing.svelte)2
-rw-r--r--src/components/Forecast/ThreeDayForecast/ThreeDayForecast.svelte (renamed from src/components/PredictedSpaceWeather.svelte)78
-rw-r--r--src/routes/index.svelte4
-rw-r--r--src/stores.ts8
8 files changed, 338 insertions, 77 deletions
diff --git a/src/components/Basic/Bar.svelte b/src/components/Basic/Bar.svelte
new file mode 100644
index 0000000..7b17c3e
--- /dev/null
+++ b/src/components/Basic/Bar.svelte
@@ -0,0 +1,48 @@
+<script lang="ts">
+ export let percentage = 0.7;
+ export let vertical = false;
+</script>
+
+<style>
+ :root {
+ --progress: 100%;
+ }
+
+ .wrapper {
+ position: relative;
+ background-color: #c6c6c6;
+ border-radius:15px;
+ width: 100%;
+ height: 100%;
+ }
+
+ .bar-wrapper {
+ overflow: hidden;
+ position: relative;
+ border-radius: 15px;
+ height: 100%;
+ width: 100%;
+ top: calc(100% - var(--progress));
+ }
+
+ .bar {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+ bottom: calc(100% - var(--progress));
+ overflow: hidden;
+ background: linear-gradient(0deg,
+ rgba(217,217,217,1) 0%,
+ rgba(164,255,177,1) 70%,
+ rgba(255,136,240,1) 100%);
+ border-radius: 15px;
+ }
+</style>
+
+<div>
+ <div class="wrapper" class:vertical style="--progress: {percentage*100}%">
+ <div class="bar-wrapper">
+ <div class="bar"></div>
+ </div>
+ </div>
+</div>
diff --git a/src/components/Forecast/ForecastDrawer.svelte b/src/components/Forecast/ForecastDrawer.svelte
new file mode 100644
index 0000000..b47a810
--- /dev/null
+++ b/src/components/Forecast/ForecastDrawer.svelte
@@ -0,0 +1,108 @@
+<script lang="ts">
+ import OneHourForecast from "./OneHourForecast/OneHourForecast.svelte";
+ import ThreeDayForecast from "./ThreeDayForecast/ThreeDayForecast.svelte";
+
+ import { earth_weather, space_weather } from "../../stores.ts";
+
+ let selected_version = ThreeDayForecast;
+
+</script>
+
+<style>
+ .drawer {
+ border-top-left-radius: 2rem;
+ border-top-right-radius: 2rem;
+ --bg-opacity: 1;
+ background-color: #f7fafc;
+ background-color: rgba(247, 250, 252, var(--bg-opacity));
+ padding: 1.5rem;
+ --text-opacity: 1;
+ color: #1a202c;
+ color: rgba(26, 32, 44, var(--text-opacity));
+ height: 100%;
+ overflow: hidden;
+ align-self: stretch;
+ box-shadow: 0px -6px 7px 0px black;
+ }
+
+ @media (min-width: 640px), (min-height: 720px) {
+ .drawer {
+ padding: 2rem;
+ padding-top: 1.5rem;
+ }
+ }
+
+ @media (min-width: 640px) {
+ .drawer {
+ border-bottom-right-radius: 1rem;
+ border-bottom-left-radius: 1rem;
+ }
+ }
+
+ .drawer .header {
+ display: flex;
+ align-items: flex-start;
+ }
+
+ .drawer h2 {
+ text-transform: uppercase;
+ font-size: 0.875rem;
+ letter-spacing: 0.1em;
+ font-weight: 700;
+ margin-top: 0.25rem;
+ margin-bottom: 0.5rem;
+ margin-right: auto;
+ }
+
+ .version-picker {
+ display: flex;
+ font-size: 0.875rem;
+ gap: 0.5rem;
+ align-items: center;
+ color: #616161;
+ }
+ .version-picker > * {
+ transition: all 0.1s ease;
+ }
+
+ .version-picker .selected {
+ background-color: #c2c2c2;
+ padding: 3px 5px;
+ box-sizing: border-box;
+ border-radius: 10px;
+ color: black;
+ }
+
+ .no-data {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ text-align: center;
+ color: grey;
+
+ }
+
+ .no-data {
+ margin-top: 1rem;
+ }
+</style>
+
+<div class="drawer">
+ {#if !$earth_weather.updating && !$space_weather.updating}
+ <div class="header">
+ <h2>Forecast</h2>
+ <div class="version-picker">
+ <div class:selected={selected_version == OneHourForecast} on:click={() => selected_version = OneHourForecast}>hour</div>
+ <div class:selected={selected_version == ThreeDayForecast} on:click={() => selected_version = ThreeDayForecast}>3 day</div>
+ </div>
+ </div>
+ <svelte:component this={selected_version} />
+ {:else}
+ <div class="no-data">
+ <i class="fas fa-7x fa-exclamation-triangle"></i>
+ <p>No prediction data</p>
+ </div>
+ {/if}
+</div>
diff --git a/src/components/Forecast/OneHourForecast/OneHourForecast.svelte b/src/components/Forecast/OneHourForecast/OneHourForecast.svelte
new file mode 100644
index 0000000..3222dc8
--- /dev/null
+++ b/src/components/Forecast/OneHourForecast/OneHourForecast.svelte
@@ -0,0 +1,76 @@
+<script lang="ts">
+ import PredictionItem from './PredictionItem.svelte';
+
+ import { onMount } from 'svelte';
+ import { earth_weather, space_weather } from '../../../stores';
+
+ const monthNames = ["January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ ];
+
+ let predictions;
+
+ space_weather.subscribe(assembleWeatherData);
+ earth_weather.subscribe(assembleWeatherData);
+
+ async function assembleWeatherData() {
+ if ($space_weather.updating || $earth_weather.updating) {
+ predictions = null;
+ return;
+ }
+
+ // First just reorganize the space_weather data
+ let forecast = $space_weather.usnoaa_data_raw.geospace_pred_est_kp_1_hour.filter(x => x.model_prediction_time >= new Date());
+ predictions = forecast.map(
+ pred => ({
+ "time": pred.model_prediction_time,
+ "kp": Math.round(pred.k),
+ "temp": null,
+ "clouds": null,
+ "hasNOMETData": $earth_weather.available
+ })
+ );
+
+ // Add earth weather data if it is available
+ if ($earth_weather.available) {
+ predictions.forEach((pred, i) => {
+ let closestDate = new Date(0,0,0);
+ let temp;
+ let clouds;
+
+ $earth_weather.yr_data_raw.properties.timeseries.forEach((earth_pred, i) => {
+ let predDate = new Date(earth_pred.time);
+ if (Math.abs(predDate.getTime() - pred.time.getTime()) < Math.abs(closestDate.getTime() - pred.time.getTime())) {
+ closestDate = predDate;
+ temp = (earth_pred["data"]["instant"]["details"]["air_temperature"]);
+ clouds = earth_pred["data"]["instant"]["details"]["cloud_area_fraction"];
+ }
+ });
+
+ predictions[i] = {
+ ...predictions[i], "temp": temp, "clouds": clouds
+ }
+ });
+ }
+
+ }
+
+</script>
+
+<style>
+ .prediction-table {
+ height: 100%;
+ overflow-y: scroll;
+ padding-bottom: 1rem;
+ }
+
+ .prediction-table::-webkit-scrollbar {
+ display: none;
+ }
+</style>
+
+<div class="prediction-table">
+ {#each predictions as prediction, i}
+ <PredictionItem {prediction}/>
+ {/each}
+</div>
diff --git a/src/components/Forecast/OneHourForecast/PredictionItem.svelte b/src/components/Forecast/OneHourForecast/PredictionItem.svelte
new file mode 100644
index 0000000..201c4f5
--- /dev/null
+++ b/src/components/Forecast/OneHourForecast/PredictionItem.svelte
@@ -0,0 +1,91 @@
+<script>
+ import Chip from "../../Basic/Chip.svelte";
+
+ export let prediction;
+
+ const monthNames = ["January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ ];
+
+ function zpad(value, n=2) {
+ let r = value;
+ for (let i = 0; i < n - value.length; i++) {
+ r = "0" + r;
+ }
+ return r;
+ }
+
+ let kp = prediction["kp"];
+ let date = prediction["time"].getDate() + ". " + monthNames[prediction["time"].getMonth()];
+ let time = zpad(prediction["time"].getHours().toString()) + ":" + zpad(prediction["time"].getMinutes().toString());
+ let temp = prediction["temp"];
+ let clouds = prediction["clouds"];
+ let hasNOMETData = prediction["hasNOMETData"];
+</script>
+
+<style>
+ .prediction-details {
+ display: flex;
+ border-bottom-width: 1px;
+ padding-top: 0.5rem;
+ padding-bottom: 0.5rem;
+ font-size: 0.75rem;
+ letter-spacing: 0.05em;
+ align-items: center;
+ }
+
+ .prediction-details:last-of-type {
+ border-width: 0;
+ padding-bottom: 0;
+ }
+
+ .prediction-details > * {
+ margin: 5px;
+ }
+
+ .prediction-details div:last-child {
+ margin-left: auto;
+ }
+
+ .prediction-details h3 {
+ font-size: 0.875rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-right: 0.5rem;
+ }
+
+ .prediction-details .data {
+ display: flex;
+ flex-direction: row;
+ }
+
+ .prediction-details .data h2 {
+ margin-right: 1rem;
+ font-size: 1.5rem;
+ }
+ .prediction-details .data p {
+ min-width: 3.3rem;
+ }
+ .prediction-details .data i {
+ width: 1rem;
+ text-align: center;
+ }
+
+</style>
+
+<div class="prediction-details">
+ <div>
+ <h3>{time}</h3>
+ <p>{date}</p>
+ </div>
+ <div class="data">
+ <h2>{kp}</h2>
+ {#if hasNOMETData}
+ <div>
+ <p><i class="fas fa-thermometer-half"></i> {Math.round(temp)}°C</p>
+ <p><i class="fas fa-cloud"></i> {Math.round(clouds)}%</p>
+ </div>
+ {/if}
+ </div>
+</div>
diff --git a/src/components/PredictedSpaceWeatherThing.svelte b/src/components/Forecast/ThreeDayForecast/PredictionItem.svelte
index 13bf1c2..0d4a94d 100644
--- a/src/components/PredictedSpaceWeatherThing.svelte
+++ b/src/components/Forecast/ThreeDayForecast/PredictionItem.svelte
@@ -1,5 +1,5 @@
<script>
- import Chip from "./Basic/Chip.svelte";
+ import Chip from "../../Basic/Chip.svelte";
export let prediction;
diff --git a/src/components/PredictedSpaceWeather.svelte b/src/components/Forecast/ThreeDayForecast/ThreeDayForecast.svelte
index 2282a40..7e13c2b 100644
--- a/src/components/PredictedSpaceWeather.svelte
+++ b/src/components/Forecast/ThreeDayForecast/ThreeDayForecast.svelte
@@ -1,8 +1,8 @@
<script lang="ts">
- import PredictedSpaceWeatherThing from './PredictedSpaceWeatherThing.svelte';
+ import PredictionItem from './PredictionItem.svelte';
import { onMount } from 'svelte';
- import { earth_weather, space_weather } from '../stores';
+ import { earth_weather, space_weather } from '../../../stores';
const monthNames = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
@@ -59,45 +59,6 @@
</script>
<style>
- .predicted-weather {
- border-top-left-radius: 2rem;
- border-top-right-radius: 2rem;
- --bg-opacity: 1;
- background-color: #f7fafc;
- background-color: rgba(247, 250, 252, var(--bg-opacity));
- padding: 1.5rem;
- --text-opacity: 1;
- color: #1a202c;
- color: rgba(26, 32, 44, var(--text-opacity));
- height: 100%;
- overflow: hidden;
- align-self: stretch;
- box-shadow: 0px -6px 7px 0px black;
- }
-
- @media (min-width: 640px), (min-height: 720px) {
- .predicted-weather {
- padding: 2rem;
- padding-top: 1.5rem;
- }
- }
-
- @media (min-width: 640px) {
- .predicted-weather {
- border-bottom-right-radius: 1rem;
- border-bottom-left-radius: 1rem;
- }
- }
-
- .predicted-weather h2 {
- text-transform: uppercase;
- font-size: 0.875rem;
- letter-spacing: 0.1em;
- font-weight: 700;
- margin-top: 0.25rem;
- margin-bottom: 0.5rem;
- }
-
.prediction-table {
height: 100%;
overflow-y: scroll;
@@ -107,37 +68,10 @@
.prediction-table::-webkit-scrollbar {
display: none;
}
-
- .no-data {
- height: 100%;
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- text-align: center;
- color: grey;
-
- }
-
- .no-data {
- margin-top: 1rem;
- }
</style>
-<div class="predicted-weather">
- {#if predictions}
- <div className="flex flex-row justify-between items-top">
- <h2>Predicted</h2>
- </div>
- <div class="prediction-table">
- {#each predictions as prediction, i}
- <PredictedSpaceWeatherThing {prediction}/>
- {/each}
- </div>
- {:else}
- <div class="no-data">
- <i class="fas fa-7x fa-exclamation-triangle"></i>
- <p>No prediction data</p>
- </div>
- {/if}
+<div class="prediction-table">
+ {#each predictions as prediction, i}
+ <PredictionItem {prediction}/>
+ {/each}
</div>
diff --git a/src/routes/index.svelte b/src/routes/index.svelte
index 7efda23..64bcce9 100644
--- a/src/routes/index.svelte
+++ b/src/routes/index.svelte
@@ -1,6 +1,6 @@
<script lang="ts">
import WeatherCurrent from '../components/WeatherCurrent.svelte';
- import PredictedSpaceWeather from '../components/PredictedSpaceWeather.svelte';
+ import ForecastDrawer from '../components/Forecast/ForecastDrawer.svelte';
//import { theme, fetchingData, fetchDataError, weatherData } from "../stores.ts";
//weatherData.subscribe(console.log);
@@ -27,5 +27,5 @@
<div class="homescreen">
<WeatherCurrent/>
- <PredictedSpaceWeather />
+ <ForecastDrawer/>
</div>
diff --git a/src/stores.ts b/src/stores.ts
index 05da19b..2e91c25 100644
--- a/src/stores.ts
+++ b/src/stores.ts
@@ -134,7 +134,8 @@ async function getSpaceWeather() {
},
"usnoaa_data_raw": {
"solar_wind_mag_field": false,
- "noaa_planetary_k_index_forecast": false
+ "noaa_planetary_k_index_forecast": false,
+ "geospace_pred_est_kp_1_hour": false
}
};
@@ -144,8 +145,11 @@ async function getSpaceWeather() {
ret.now.bz = ret.usnoaa_data_raw.solar_wind_mag_field["Bz"];
ret.now.bt = ret.usnoaa_data_raw.solar_wind_mag_field["Bt"];
+ res = await fetch("https://services.swpc.noaa.gov/json/geospace/geospace_pred_est_kp_1_hour.json");
+ ret.usnoaa_data_raw.geospace_pred_est_kp_1_hour = (await res.json()).map(x => ({...x, "model_prediction_time": new Date(x.model_prediction_time)}));
+
res = await fetch("https://services.swpc.noaa.gov/products/noaa-planetary-k-index-forecast.json")
- ret.usnoaa_data_raw.noaa_planetary_k_index_forecast = await res.json()
+ ret.usnoaa_data_raw.noaa_planetary_k_index_forecast = await res.json();
ret.usnoaa_data_raw.noaa_planetary_k_index_forecast.shift();
let cDate = new Date();