Fix weather plot, close #5

This commit is contained in:
Felix Suchert 2023-07-30 22:04:40 +02:00
parent e8f5f00386
commit d537c6f7be
Signed by: feliix42
GPG key ID: 24363525EA0E8A99
2 changed files with 130 additions and 22 deletions

View file

@ -28,7 +28,7 @@ pub struct WeatherData {
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
minutely: Vec<()>, minutely: Vec<()>,
/// Weather Information for the coming hours /// Weather Information for the coming hours
hourly: Vec<HourlyWeather>, pub hourly: Vec<HourlyWeather>,
/// Weather information for the next days _(unused at the moment)_ /// Weather information for the next days _(unused at the moment)_
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
daily: Vec<()>, daily: Vec<()>,
@ -95,6 +95,12 @@ pub struct HourlyWeather {
pub(self) pop: f32, pub(self) pop: f32,
} }
impl HourlyWeather {
pub fn temperature(&self) -> f32 {
self.data.temp
}
}
/// Common weather information shared among different forecast types. /// Common weather information shared among different forecast types.
/// ///
/// This struct is not instantiated by the API but merely introduced by the `Deserializer`. /// This struct is not instantiated by the API but merely introduced by the `Deserializer`.

View file

@ -1,22 +1,129 @@
use crate::error::display_no_data;
use embedded_graphics::mono_font::MonoTextStyleBuilder;
use embedded_graphics::{ use embedded_graphics::{
draw_target::DrawTarget, draw_target::DrawTarget, image::Image, mono_font::MonoTextStyle, prelude::*, text::Text,
image::Image,
mono_font::MonoTextStyle,
prelude::*,
text::Text
}; };
use embedded_plots::{axis::Scale, curve::Curve, single_plot::SinglePlot}; use embedded_plots::{
use profont::{ axis::{Axis, Placement, Scale},
PROFONT_14_POINT, PROFONT_24_POINT, PROFONT_7_POINT, PROFONT_9_POINT, curve::{Curve, PlotPoint},
single_plot::SinglePlot,
}; };
use profont::{PROFONT_14_POINT, PROFONT_24_POINT, PROFONT_7_POINT, PROFONT_9_POINT};
use ssd1675::Color; use ssd1675::Color;
use std::fmt::Debug; use std::fmt::Debug;
use crate::error::display_no_data; use std::ops::Range;
use time::OffsetDateTime;
mod api; mod api;
mod data; mod data;
mod secret;
mod icons; mod icons;
mod secret;
/// Draws the 24hr weather data in a graph with 2 x axes, one for today and one for tomorrow.
fn draw_weather_plot<D>(
wtr: &data::WeatherData,
display: &mut D,
top_left: Point,
bottom_right: Point,
) where
D: DrawTarget<Color = Color>,
D::Error: Debug,
{
let local_time =
OffsetDateTime::now_local().expect("Failed to retrieve current time from system clock");
let now_hour = local_time.hour();
// Split the plot data into two sets: one for today's data (time up to 23:00), one for tomorrow
// (0:00 onwards). This is necessary because if all data were plotted in one graph, it would
// show the delta in hours to the current time. If you replaced the delta with the actual hour,
// the plot would be re-ordered, leading to confusion.
// NOTE(feliix42): We're doing a +1 here to actually also take the 24 for a better overlap
let num_entries_today = 24 - now_hour + 1;
let num_entries_tomorrow = 24 - num_entries_today;
let plot_data = wtr.forecast_plot_data();
// X coordinate where to split between axis 1 and 2
let cutoff_point = top_left.x + ((bottom_right.x - top_left.x) / 24) * num_entries_today as i32;
// computation of scales. We want 5 (plus x origin) labels on the x axis. So we compute as
// follows: num_labels % 6 = num labels per section
// Note, that the origin of x2 will be printed anyways as well
let num_labels_in_x1: u8 = num_entries_today / 6;
let x1_labels = if num_labels_in_x1 > 0 {
num_labels_in_x1
} else {
1
};
let mut x2_labels = 5 - x1_labels;
if x2_labels == 0 {
x2_labels = 1;
}
let x1_scale = Scale::RangeFraction(x1_labels.into());
let x2_scale = Scale::RangeFraction(x2_labels.into());
let y_scale = Scale::RangeFraction(2);
let thickness = 2;
let axis_thickness = thickness;
let text_style = MonoTextStyleBuilder::new().text_color(Color::Black).build();
let curve = [(Curve::from_data(&plot_data), Color::Black)];
let x1_range: Range<i32> = (now_hour as i32)..((now_hour + num_entries_today) as i32);
let x2_range: Range<i32> = 0..(num_entries_tomorrow as i32);
let y_range = curve[0].0.y_range.clone();
Axis::new(x1_range)
.set_title("")
.set_scale(x1_scale)
.into_drawable_axis(Placement::X {
x1: top_left.x,
x2: cutoff_point,
y: bottom_right.y,
})
.set_color(Color::Black)
.set_text_style(text_style)
.set_tick_size(2)
.set_thickness(axis_thickness)
.draw(display)
.expect("Failed to draw the temperature plot");
Axis::new(x2_range)
.set_title("t")
.set_scale(x2_scale)
.into_drawable_axis(Placement::X {
x1: cutoff_point,
x2: bottom_right.x,
y: bottom_right.y,
})
.set_color(Color::Black)
.set_text_style(text_style)
.set_tick_size(2)
.set_thickness(axis_thickness)
.draw(display)
.expect("Failed to draw the temperature plot");
Axis::new(y_range)
.set_title("C")
.set_scale(y_scale)
.into_drawable_axis(Placement::Y {
y1: top_left.y,
y2: bottom_right.y,
x: top_left.x,
})
.set_color(Color::Black)
.set_text_style(text_style)
.set_tick_size(2)
.set_thickness(axis_thickness)
.draw(display)
.expect("Failed to draw the temperature plot");
curve[0]
.0
.into_drawable_curve(&top_left, &bottom_right)
.set_color(curve[0].1)
.set_thickness(thickness)
.draw(display)
.expect("Failed to draw the temperature plot");
}
pub fn get_weather<D>(display: &mut D) pub fn get_weather<D>(display: &mut D)
where where
@ -107,15 +214,10 @@ where
.expect("error drawing text"); .expect("error drawing text");
// Plot the forecast // Plot the forecast
// TODO(feliix42): Annotate the plot better?? draw_weather_plot(
let data_points = weather_data.forecast_plot_data(); &weather_data,
let curve = [(Curve::from_data(&data_points), Color::Black)]; display,
Point::new(100, 54),
let plot = SinglePlot::new(&curve, Scale::RangeFraction(4), Scale::RangeFraction(2)) Point::new(206, 94),
.into_drawable(Point::new(100, 54), Point::new(206, 94)) );
.set_color(Color::Black);
plot.draw(display)
.expect("Failed to draw the temperature plot");
} }