weather: now, the next 24 hours and the week
git clone https://git.lucas.co/cce-weather.git
src/glyph.rs (3.8K)
1 //! WMO weather codes: their wording, and the glyph for each — one of the
2 //! multicolour `weather-*` glyphs of the cce-icons set, drawn through
3 //! cce-ui's `PaintCtx::icon_untinted`.
4
5 use cce_ui::scene::layout::Rect;
6 use cce_ui::scene::paint::PaintCtx;
7
8 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
9 pub enum Sky {
10 Clear,
11 PartlyCloudy,
12 Cloudy,
13 Fog,
14 Drizzle,
15 Rain,
16 Snow,
17 Thunder,
18 }
19
20 /// WMO 4677 code as Open-Meteo reports it → (sky, words).
21 pub fn describe(code: u8) -> (Sky, &'static str) {
22 match code {
23 0 => (Sky::Clear, "Clear"),
24 1 => (Sky::Clear, "Mainly clear"),
25 2 => (Sky::PartlyCloudy, "Partly cloudy"),
26 3 => (Sky::Cloudy, "Overcast"),
27 45 => (Sky::Fog, "Fog"),
28 48 => (Sky::Fog, "Freezing fog"),
29 51 => (Sky::Drizzle, "Light drizzle"),
30 53 => (Sky::Drizzle, "Drizzle"),
31 55 => (Sky::Drizzle, "Heavy drizzle"),
32 56 | 57 => (Sky::Drizzle, "Freezing drizzle"),
33 61 => (Sky::Rain, "Light rain"),
34 63 => (Sky::Rain, "Rain"),
35 65 => (Sky::Rain, "Heavy rain"),
36 66 | 67 => (Sky::Rain, "Freezing rain"),
37 71 => (Sky::Snow, "Light snow"),
38 73 => (Sky::Snow, "Snow"),
39 75 => (Sky::Snow, "Heavy snow"),
40 77 => (Sky::Snow, "Snow grains"),
41 80 => (Sky::Rain, "Light showers"),
42 81 => (Sky::Rain, "Showers"),
43 82 => (Sky::Rain, "Violent showers"),
44 85 | 86 => (Sky::Snow, "Snow showers"),
45 95 => (Sky::Thunder, "Thunderstorm"),
46 96 | 99 => (Sky::Thunder, "Thunderstorm, hail"),
47 _ => (Sky::Cloudy, "Unknown"),
48 }
49 }
50
51 /// The cce-icons glyph for `sky`. `day` picks sun or moon for the clear and
52 /// partly-cloudy skies; the rest have one glyph for day and night alike.
53 pub fn icon_name(sky: Sky, day: bool) -> &'static str {
54 match (sky, day) {
55 (Sky::Clear, true) => "weather-clear-day",
56 (Sky::Clear, false) => "weather-clear-night",
57 (Sky::PartlyCloudy, true) => "weather-partly-cloudy-day",
58 (Sky::PartlyCloudy, false) => "weather-partly-cloudy-night",
59 (Sky::Cloudy, _) => "weather-cloudy",
60 (Sky::Fog, _) => "weather-fog",
61 (Sky::Drizzle, _) => "weather-drizzle",
62 (Sky::Rain, _) => "weather-rain",
63 (Sky::Snow, _) => "weather-snow",
64 (Sky::Thunder, _) => "weather-thunder",
65 }
66 }
67
68 /// Draw the glyph for `code` filling the square `rect`. The weather glyphs
69 /// carry their own colours, so they are drawn untinted.
70 pub fn draw(pc: &mut PaintCtx, rect: Rect, code: u8, day: bool) {
71 let (sky, _) = describe(code);
72 pc.icon_untinted(icon_name(sky, day), rect, 1.0);
73 }
74
75 #[cfg(test)]
76 mod tests {
77 use super::*;
78
79 #[test]
80 fn every_documented_code_has_words() {
81 for code in [0, 1, 2, 3, 45, 48, 51, 53, 55, 56, 57, 61, 63, 65, 66, 67, 71, 73, 75, 77, 80, 81, 82, 85, 86, 95, 96, 99] {
82 assert_ne!(describe(code).1, "Unknown", "code {code}");
83 }
84 assert_eq!(describe(42).1, "Unknown");
85 }
86
87 /// Every glyph named is one the cce-icons set ships.
88 #[test]
89 fn every_sky_names_a_shipped_glyph() {
90 let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../cce-icons/svg");
91 if !dir.is_dir() {
92 eprintln!("skipping: no cce-icons checkout beside this crate");
93 return;
94 }
95 let skies = [Sky::Clear, Sky::PartlyCloudy, Sky::Cloudy, Sky::Fog, Sky::Drizzle, Sky::Rain, Sky::Snow, Sky::Thunder];
96 for sky in skies {
97 for day in [true, false] {
98 let name = icon_name(sky, day);
99 assert!(dir.join(format!("{name}.svg")).is_file(), "{sky:?} day={day}: no {name}.svg");
100 }
101 }
102 assert_ne!(icon_name(Sky::Clear, true), icon_name(Sky::Clear, false));
103 assert_ne!(icon_name(Sky::PartlyCloudy, true), icon_name(Sky::PartlyCloudy, false));
104 }
105 }