Add Kontula weather info
This commit is contained in:
parent
469bfc5640
commit
7c8f0ce2dc
5 changed files with 1412 additions and 238 deletions
1206
Cargo.lock
generated
1206
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
10
Cargo.toml
10
Cargo.toml
|
@ -8,16 +8,18 @@ authors = ["Tanguy Gérôme <tanguy@juustodiilerit.fi>"]
|
|||
|
||||
[dependencies]
|
||||
# leptos = { version = "0.7", features = ["nightly"] }
|
||||
leptos = { version = "0.7", features = ["csr"] }
|
||||
leptos = { version = "0.8", features = ["csr"] }
|
||||
# leptos_router = { version = "0.7", features = ["nightly"] }
|
||||
leptos_router = { version = "0.7" }
|
||||
leptos_meta = { version = "0.7" }
|
||||
leptos_router = { version = "0.8" }
|
||||
leptos_meta = { version = "0.8" }
|
||||
console_log = "1"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1"
|
||||
chrono = { version = "0.4.40", features = ["unstable-locales"] }
|
||||
calendrier = "1.0.0"
|
||||
leptos-use = "0.15.7"
|
||||
leptos-use = "0.16"
|
||||
reqwest = { version = "0.12.20", features = ["json"] }
|
||||
serde_json = "1.0.140"
|
||||
|
||||
# utils
|
||||
# strum = { version = "0.25", features = ["derive", "strum_macros"] }
|
||||
|
|
|
@ -90,9 +90,61 @@ body {
|
|||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 32px;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
gap: 32px;
|
||||
|
||||
.datetime {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.weather {
|
||||
.forecast-per-hours {
|
||||
// background: white;
|
||||
// color: black;
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
padding: 8px;
|
||||
overflow-x: auto;
|
||||
|
||||
table {
|
||||
text-align: center;
|
||||
table-layout: auto;
|
||||
border-collapse: collapse;
|
||||
|
||||
th,td {
|
||||
border: grey dashed 1px;
|
||||
width: fit-content;
|
||||
min-width: fit-content;
|
||||
padding: 0px 2px;
|
||||
}
|
||||
|
||||
.weather-code {
|
||||
td {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
img {
|
||||
position: relative;
|
||||
top: 2px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: radial-gradient(closest-side, #BBB, white);
|
||||
@media all and (prefers-color-scheme: dark) {
|
||||
background: radial-gradient(closest-side, #AAA, black);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.links {
|
||||
|
|
94
src/lib.rs
94
src/lib.rs
|
@ -1,8 +1,95 @@
|
|||
use leptos::prelude::*;
|
||||
use leptos_meta::*;
|
||||
use leptos_use::{use_timestamp};
|
||||
use chrono::prelude::{DateTime, Local, Timelike};
|
||||
use chrono::{prelude::{DateTime, Local, Timelike}, NaiveDateTime};
|
||||
use calendrier::{DateTime as FRDateTime, Timestamp as FRTimestamp};
|
||||
use serde_json::Value;
|
||||
|
||||
pub mod wmo;
|
||||
|
||||
#[component]
|
||||
pub fn Weather() -> impl IntoView {
|
||||
let weather_json = LocalResource::new(async move || {
|
||||
let json_response = reqwest::get("https://api.open-meteo.com/v1/forecast?latitude=60.236610&longitude=25.083630&timezone=auto&forecast_hours=16&wind_speed_unit=kn&hourly=temperature_2m,wind_speed_10m,precipitation,precipitation_probability,weather_code,is_day").await
|
||||
.and_then(|response| Ok(response.json::<Value>()));
|
||||
match json_response {
|
||||
Ok(result) => Some(result.await.ok()),
|
||||
_ => None
|
||||
}.flatten()
|
||||
});
|
||||
let times = move || {
|
||||
Some(weather_json.get().flatten()?["hourly"]["time"].as_array()?.into_iter().map(|timestamp| {
|
||||
let datetime = NaiveDateTime::parse_from_str(timestamp.as_str().unwrap_or_default(), "%Y-%m-%dT%H:%M");
|
||||
view! {<td>{format!("{}", datetime.unwrap_or_default().hour())}</td>}
|
||||
}).collect::<Vec<_>>())
|
||||
};
|
||||
let weather_codes = move || {
|
||||
let json = weather_json.get().flatten()?;
|
||||
let is_day = json["hourly"]["is_day"].as_array()?.into_iter();
|
||||
let codes = json["hourly"]["weather_code"].as_array()?.into_iter();
|
||||
Some(is_day.zip(codes).map(move |(is_day, code)| {
|
||||
let wmo_codes = crate::wmo::get_codes();
|
||||
let image = wmo_codes[code.to_string()][if *is_day == Value::Number(1.into()) {"day"} else {"night"}]["image"].as_str().unwrap_or_default();
|
||||
let description = wmo_codes[code.to_string()][if *is_day == Value::Number(1.into()) {"day"} else {"night"}]["description"].as_str().unwrap_or_default();
|
||||
view! { <td><img src={image.to_string()} alt={description.to_string()}/></td> }
|
||||
}).collect::<Vec<_>>())
|
||||
};
|
||||
let temperatures = move || {
|
||||
Some(weather_json.get().flatten()?["hourly"]["temperature_2m"].as_array()?.into_iter().map(|temperature| {
|
||||
view! { <td>{format!("{}°C", temperature.to_string())}</td> }
|
||||
}).collect::<Vec<_>>())
|
||||
};
|
||||
let wind = move || {
|
||||
Some(weather_json.get().flatten()?["hourly"]["wind_speed_10m"].as_array()?.into_iter().map(|wind_speed| {
|
||||
view! { <td>{format!("{}kn", wind_speed.to_string())}</td> }
|
||||
}).collect::<Vec<_>>())
|
||||
};
|
||||
let precipitation = move || {
|
||||
let json = weather_json.get().flatten()?;
|
||||
let amounts = json["hourly"]["precipitation"].as_array()?.into_iter();
|
||||
let probabilities = json["hourly"]["precipitation"].as_array()?.into_iter();
|
||||
Some(amounts.zip(probabilities).map(|(amount, probability_float)| {
|
||||
let probability = (probability_float.as_f64()? * 100.0).round() as i64;
|
||||
Some(view! {
|
||||
<td>
|
||||
<span>{format!("{}mm", amount.to_string())}</span>
|
||||
<br/>
|
||||
<span>{format!("{}%", probability)}</span>
|
||||
</td>
|
||||
})
|
||||
}).collect::<Vec<_>>())
|
||||
};
|
||||
view! {
|
||||
<div class="weather">
|
||||
<Suspense fallback=move || view! { <p>"Loading..."</p> }>
|
||||
<div class="forecast-per-hours">
|
||||
<table class="forecast-per-hours">
|
||||
<tr class="time">
|
||||
<th>klo</th>
|
||||
{times}
|
||||
</tr>
|
||||
<tr class="weather-code">
|
||||
<th>sää</th>
|
||||
{weather_codes}
|
||||
</tr>
|
||||
<tr class="temperature">
|
||||
<th>lämpö</th>
|
||||
{temperatures}
|
||||
</tr>
|
||||
<tr class="wind">
|
||||
<th>tuuli</th>
|
||||
{wind}
|
||||
</tr>
|
||||
<tr class="temperature">
|
||||
<th>sade&<br/>mahd.</th>
|
||||
{precipitation}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</Suspense>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Times() -> impl IntoView {
|
||||
|
@ -19,12 +106,12 @@ pub fn Times() -> impl IntoView {
|
|||
let fr_seconds_from_hour = fr_seconds_from_midnight - (100 * 100 * fr_hours);
|
||||
let fr_minutes = fr_seconds_from_hour / 100;
|
||||
let fr_seconds = fr_seconds_from_hour - (100 * fr_minutes);
|
||||
let fr_time = format!("{}:{}:{}", fr_hours, fr_minutes, fr_seconds);
|
||||
let fr_time = format!("{:02}:{:02}:{:02}", fr_hours, fr_minutes, fr_seconds);
|
||||
fr_time
|
||||
});
|
||||
let fr_date_time = move || format!("{}, {}", fr_date.get().to_string_default(), fr_time.get());
|
||||
|
||||
let fi_date_time = move || local_now.get().format_localized("%A %e %B %Y, %T", chrono::Locale::fi_FI).to_string();
|
||||
let fi_date_time = move || local_now.get().format_localized("%A %e %B %Y, %H.%M.%S", chrono::Locale::fi_FI).to_string();
|
||||
|
||||
view! {
|
||||
<div class="datetime">
|
||||
|
@ -58,6 +145,7 @@ pub fn App() -> impl IntoView {
|
|||
</header>
|
||||
<main class="main-width">
|
||||
<Times/>
|
||||
<Weather/>
|
||||
<ul class="links">
|
||||
<li>
|
||||
<a href="https://forgejo.juustodiilerit.fi">git (forgejo)</a>
|
||||
|
|
286
src/wmo.rs
Normal file
286
src/wmo.rs
Normal file
|
@ -0,0 +1,286 @@
|
|||
use serde_json::{Value, json};
|
||||
|
||||
pub fn get_codes() -> Value {
|
||||
json!({
|
||||
"0":{
|
||||
"day":{
|
||||
"description":"Sunny",
|
||||
"image":"http://openweathermap.org/img/wn/01d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Clear",
|
||||
"image":"http://openweathermap.org/img/wn/01n@2x.png"
|
||||
}
|
||||
},
|
||||
"1":{
|
||||
"day":{
|
||||
"description":"Mainly Sunny",
|
||||
"image":"http://openweathermap.org/img/wn/01d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Mainly Clear",
|
||||
"image":"http://openweathermap.org/img/wn/01n@2x.png"
|
||||
}
|
||||
},
|
||||
"2":{
|
||||
"day":{
|
||||
"description":"Partly Cloudy",
|
||||
"image":"http://openweathermap.org/img/wn/02d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Partly Cloudy",
|
||||
"image":"http://openweathermap.org/img/wn/02n@2x.png"
|
||||
}
|
||||
},
|
||||
"3":{
|
||||
"day":{
|
||||
"description":"Cloudy",
|
||||
"image":"http://openweathermap.org/img/wn/03d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Cloudy",
|
||||
"image":"http://openweathermap.org/img/wn/03n@2x.png"
|
||||
}
|
||||
},
|
||||
"45":{
|
||||
"day":{
|
||||
"description":"Foggy",
|
||||
"image":"http://openweathermap.org/img/wn/50d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Foggy",
|
||||
"image":"http://openweathermap.org/img/wn/50n@2x.png"
|
||||
}
|
||||
},
|
||||
"48":{
|
||||
"day":{
|
||||
"description":"Rime Fog",
|
||||
"image":"http://openweathermap.org/img/wn/50d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Rime Fog",
|
||||
"image":"http://openweathermap.org/img/wn/50n@2x.png"
|
||||
}
|
||||
},
|
||||
"51":{
|
||||
"day":{
|
||||
"description":"Light Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"53":{
|
||||
"day":{
|
||||
"description":"Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"55":{
|
||||
"day":{
|
||||
"description":"Heavy Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Heavy Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"56":{
|
||||
"day":{
|
||||
"description":"Light Freezing Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Freezing Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"57":{
|
||||
"day":{
|
||||
"description":"Freezing Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Freezing Drizzle",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"61":{
|
||||
"day":{
|
||||
"description":"Light Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10n@2x.png"
|
||||
}
|
||||
},
|
||||
"63":{
|
||||
"day":{
|
||||
"description":"Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10n@2x.png"
|
||||
}
|
||||
},
|
||||
"65":{
|
||||
"day":{
|
||||
"description":"Heavy Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Heavy Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10n@2x.png"
|
||||
}
|
||||
},
|
||||
"66":{
|
||||
"day":{
|
||||
"description":"Light Freezing Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Freezing Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10n@2x.png"
|
||||
}
|
||||
},
|
||||
"67":{
|
||||
"day":{
|
||||
"description":"Freezing Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Freezing Rain",
|
||||
"image":"http://openweathermap.org/img/wn/10n@2x.png"
|
||||
}
|
||||
},
|
||||
"71":{
|
||||
"day":{
|
||||
"description":"Light Snow",
|
||||
"image":"http://openweathermap.org/img/wn/13d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Snow",
|
||||
"image":"http://openweathermap.org/img/wn/13n@2x.png"
|
||||
}
|
||||
},
|
||||
"73":{
|
||||
"day":{
|
||||
"description":"Snow",
|
||||
"image":"http://openweathermap.org/img/wn/13d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Snow",
|
||||
"image":"http://openweathermap.org/img/wn/13n@2x.png"
|
||||
}
|
||||
},
|
||||
"75":{
|
||||
"day":{
|
||||
"description":"Heavy Snow",
|
||||
"image":"http://openweathermap.org/img/wn/13d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Heavy Snow",
|
||||
"image":"http://openweathermap.org/img/wn/13n@2x.png"
|
||||
}
|
||||
},
|
||||
"77":{
|
||||
"day":{
|
||||
"description":"Snow Grains",
|
||||
"image":"http://openweathermap.org/img/wn/13d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Snow Grains",
|
||||
"image":"http://openweathermap.org/img/wn/13n@2x.png"
|
||||
}
|
||||
},
|
||||
"80":{
|
||||
"day":{
|
||||
"description":"Light Showers",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Showers",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"81":{
|
||||
"day":{
|
||||
"description":"Showers",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Showers",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"82":{
|
||||
"day":{
|
||||
"description":"Heavy Showers",
|
||||
"image":"http://openweathermap.org/img/wn/09d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Heavy Showers",
|
||||
"image":"http://openweathermap.org/img/wn/09n@2x.png"
|
||||
}
|
||||
},
|
||||
"85":{
|
||||
"day":{
|
||||
"description":"Light Snow Showers",
|
||||
"image":"http://openweathermap.org/img/wn/13d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Snow Showers",
|
||||
"image":"http://openweathermap.org/img/wn/13n@2x.png"
|
||||
}
|
||||
},
|
||||
"86":{
|
||||
"day":{
|
||||
"description":"Snow Showers",
|
||||
"image":"http://openweathermap.org/img/wn/13d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Snow Showers",
|
||||
"image":"http://openweathermap.org/img/wn/13n@2x.png"
|
||||
}
|
||||
},
|
||||
"95":{
|
||||
"day":{
|
||||
"description":"Thunderstorm",
|
||||
"image":"http://openweathermap.org/img/wn/11d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Thunderstorm",
|
||||
"image":"http://openweathermap.org/img/wn/11n@2x.png"
|
||||
}
|
||||
},
|
||||
"96":{
|
||||
"day":{
|
||||
"description":"Light Thunderstorms With Hail",
|
||||
"image":"http://openweathermap.org/img/wn/11d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Light Thunderstorms With Hail",
|
||||
"image":"http://openweathermap.org/img/wn/11n@2x.png"
|
||||
}
|
||||
},
|
||||
"99":{
|
||||
"day":{
|
||||
"description":"Thunderstorm With Hail",
|
||||
"image":"http://openweathermap.org/img/wn/11d@2x.png"
|
||||
},
|
||||
"night":{
|
||||
"description":"Thunderstorm With Hail",
|
||||
"image":"http://openweathermap.org/img/wn/11n@2x.png"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
Loading…
Add table
Reference in a new issue