feat: add hour lines and sun/moon rise/set times
This commit is contained in:
19
src/dwd.rs
19
src/dwd.rs
@@ -3,14 +3,14 @@ use std::collections::{BTreeMap, HashMap};
|
||||
use reqwest::Url;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::data::Data;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DwdCache {}
|
||||
|
||||
pub type Forecast = HashMap<String, ForecastDatas>;
|
||||
#[derive(Deserialize)]
|
||||
pub struct ForecastDatas {
|
||||
#[serde(default)]
|
||||
pub days: Vec<ForecastDay>,
|
||||
forecast0: Option<ForecastData>,
|
||||
forecast1: Option<ForecastData>,
|
||||
forecast2: Option<ForecastData>,
|
||||
@@ -22,6 +22,21 @@ pub struct ForecastDatas {
|
||||
forecast8: Option<ForecastData>,
|
||||
forecast9: Option<ForecastData>,
|
||||
}
|
||||
#[derive(Deserialize)]
|
||||
pub struct ForecastDay {
|
||||
#[serde(rename = "dayDate")]
|
||||
pub date: String,
|
||||
// #[serde(rename = "moonPhase")]
|
||||
// pub moon_phase: Option<i64>,
|
||||
// #[serde(rename = "moonriseOnThisDay")]
|
||||
pub moonrise: Option<f64>,
|
||||
// #[serde(rename = "moonsetOnThisDay")]
|
||||
pub moonset: Option<f64>,
|
||||
// #[serde(rename = "sunriseOnThisDay")]
|
||||
pub sunrise: Option<f64>,
|
||||
// #[serde(rename = "sunsetOnThisDay")]
|
||||
pub sunset: Option<f64>,
|
||||
}
|
||||
impl ForecastDatas {
|
||||
pub fn forecasts(&self) -> impl Iterator<Item = &ForecastData> {
|
||||
[
|
||||
|
||||
67
src/main.rs
67
src/main.rs
@@ -247,6 +247,22 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
|
||||
i += 1;
|
||||
i
|
||||
};
|
||||
let additional = |date| {
|
||||
forecast
|
||||
.values()
|
||||
.flat_map(|forecast| forecast.days.iter())
|
||||
.find(|day| day.date == date)
|
||||
.map(|day| {
|
||||
(
|
||||
day.sunrise
|
||||
.and_then(|rise| day.sunset.map(|set| (rise, set)))
|
||||
.map(|(rise, set)| (rise / 1000.0, set / 1000.0)),
|
||||
day.moonrise
|
||||
.and_then(|rise| day.moonset.map(|set| (rise, set)))
|
||||
.map(|(rise, set)| (rise / 1000.0, set / 1000.0)),
|
||||
)
|
||||
})
|
||||
};
|
||||
diagram_head(&mut body);
|
||||
diagram(
|
||||
("Temperature", "°C", 5.0),
|
||||
@@ -261,6 +277,7 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
|
||||
avg(forecasts(&forecast)
|
||||
.flat_map(move |forecast| forecast.temperature(time)))
|
||||
},
|
||||
additional,
|
||||
timer.clone(),
|
||||
&mut body,
|
||||
);
|
||||
@@ -272,6 +289,7 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
|
||||
avg(forecasts(&forecast)
|
||||
.flat_map(move |forecast| forecast.precipitation(time)))
|
||||
},
|
||||
additional,
|
||||
timer.clone(),
|
||||
&mut body,
|
||||
);
|
||||
@@ -283,6 +301,7 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
|
||||
avg(forecasts(&forecast)
|
||||
.flat_map(move |forecast| forecast.sunshine(time)))
|
||||
},
|
||||
additional,
|
||||
timer.clone(),
|
||||
&mut body,
|
||||
);
|
||||
@@ -294,6 +313,7 @@ async fn weather_overview(q: &str, data: &State, accept: &Accept) -> Page {
|
||||
avg(forecasts(&forecast)
|
||||
.flat_map(move |forecast| forecast.humidity(time)))
|
||||
},
|
||||
additional,
|
||||
timer.clone(),
|
||||
&mut body,
|
||||
);
|
||||
@@ -329,7 +349,7 @@ a {
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.diagram {
|
||||
height: 10em;
|
||||
height: 12em;
|
||||
margin: 0px;
|
||||
border: 0px;
|
||||
padding: 0px;
|
||||
@@ -347,6 +367,7 @@ fn diagram(
|
||||
(dindex, fmin, fmax): (usize, Option<f64>, Option<f64>),
|
||||
gradient: &[(f64, &str)],
|
||||
f: impl Fn(f64) -> Option<f64>,
|
||||
additional: impl Fn(String) -> Option<(Option<(f64, f64)>, Option<(f64, f64)>)>,
|
||||
mut timer: TimeStepper,
|
||||
body: &mut String,
|
||||
) {
|
||||
@@ -394,7 +415,7 @@ fn diagram(
|
||||
let scale_value = |v: f64| (max - v) * 1000.0 / (max - min);
|
||||
let scaled_end = scale_time(end);
|
||||
*body += &format!(
|
||||
r##"<svg class="diagram" xmlns="http://www.w3.org/2000/svg" viewBox="-500 -50 {} 1200">"##,
|
||||
r##"<svg class="diagram" xmlns="http://www.w3.org/2000/svg" viewBox="-500 -150 {} 1300">"##,
|
||||
scaled_end + 500.0,
|
||||
);
|
||||
let shape_head = format!(r##"<path stroke="url(#gra{dindex})" fill="transparent" d=""##);
|
||||
@@ -411,23 +432,53 @@ fn diagram(
|
||||
.with_timezone(&Local)
|
||||
.date_naive();
|
||||
loop {
|
||||
let day_start = date
|
||||
.and_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap())
|
||||
fn unix_day_start(date: NaiveDate) -> f64 {
|
||||
date.and_time(NaiveTime::from_hms_opt(0, 0, 0).unwrap())
|
||||
.and_local_timezone(Local)
|
||||
.earliest()
|
||||
.unwrap()
|
||||
.signed_duration_since(DateTime::UNIX_EPOCH)
|
||||
.as_seconds_f64();
|
||||
if let Some(succ) = date.succ_opt() {
|
||||
date = succ;
|
||||
.as_seconds_f64()
|
||||
}
|
||||
let day_start = unix_day_start(date);
|
||||
let (date, day_end) = if let Some(succ) = date.succ_opt() {
|
||||
(std::mem::replace(&mut date, succ), unix_day_start(succ))
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
};
|
||||
if day_start > end {
|
||||
break;
|
||||
} else if day_start < start {
|
||||
continue;
|
||||
}
|
||||
if let Some((sun, moon)) = additional(date.to_string()) {
|
||||
for (risenset, color1, color2, y) in
|
||||
[(sun, "#FF08", "#FB08", 150), (moon, "#88F8", "#00F6", 130)]
|
||||
{
|
||||
if let Some((rise, set)) = risenset {
|
||||
for (t, color) in [(rise, color1), (set, color2)] {
|
||||
let x = scale_time(t);
|
||||
*body += &format!(
|
||||
r#"<line x1="{x}" x2="{x}" y1="-{y}" y2="-100" stroke="{color}"/>"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (i, x) in (0..)
|
||||
.map(|i| (i, day_end + (i as f64 - 24.0) * 3600.0))
|
||||
.skip_while(|(_, t)| *t <= day_start)
|
||||
.take_while(|(_, t)| *t < day_end && *t <= end)
|
||||
.map(|(i, t)| (i, scale_time(t)))
|
||||
{
|
||||
*body += &format!(
|
||||
r##"<line x1="{x}" x2="{x}" y1="-100" y2="-{}" stroke="#FFF8"/>"##,
|
||||
match i {
|
||||
6 | 12 | 18 => "50",
|
||||
_ => "80",
|
||||
}
|
||||
);
|
||||
}
|
||||
let x = scale_time(day_start);
|
||||
*body += &format!(r#"<line x1="{x}" x2="{x}" y1="1040" y2="1120" stroke="white"/>"#);
|
||||
let x = scale_time(day_start + 3.0 * 3600.0);
|
||||
|
||||
Reference in New Issue
Block a user