1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// SPDX-License-Identifier: MIT OR Apache-2.0
//! WMO weather codes and compass direction mappings.
use serde::{Deserialize, Serialize};
/// WMO weather condition mapped from numeric weathercode.
///
/// Core returns the enum variant, frontends match it to produce translated strings.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum WeatherCondition {
/// WMO code 0.
ClearSky,
/// WMO code 1.
MainlyClear,
/// WMO code 2.
PartlyCloudy,
/// WMO code 3.
Overcast,
/// WMO codes 45, 48.
Foggy,
/// WMO codes 51, 53, 55.
Drizzle,
/// WMO codes 56, 57.
FreezingDrizzle,
/// WMO codes 61, 63, 65.
Rain,
/// WMO codes 66, 67.
FreezingRain,
/// WMO codes 71, 73, 75.
Snow,
/// WMO code 77.
SnowGrains,
/// WMO codes 80-82.
RainShowers,
/// WMO codes 85, 86.
SnowShowers,
/// WMO code 95.
Thunderstorm,
/// WMO codes 96, 99.
ThunderstormHail,
/// Anything outside the WMO range.
Unknown,
}
impl WeatherCondition {
/// Maps a WMO weather code to a condition variant.
pub fn from_code(code: i32) -> Self {
match code {
0 => Self::ClearSky,
1 => Self::MainlyClear,
2 => Self::PartlyCloudy,
3 => Self::Overcast,
45 | 48 => Self::Foggy,
51 | 53 | 55 => Self::Drizzle,
56 | 57 => Self::FreezingDrizzle,
61 | 63 | 65 => Self::Rain,
66 | 67 => Self::FreezingRain,
71 | 73 | 75 => Self::Snow,
77 => Self::SnowGrains,
80..=82 => Self::RainShowers,
85 | 86 => Self::SnowShowers,
95 => Self::Thunderstorm,
96 | 99 => Self::ThunderstormHail,
_ => Self::Unknown,
}
}
/// Returns the freedesktop icon name for this condition.
/// Uses the -symbolic suffix for proper icon lookup across themes.
pub fn icon_name(&self, is_night: bool) -> &'static str {
match self {
Self::ClearSky => {
if is_night {
"weather-clear-night-symbolic"
} else {
"weather-clear-symbolic"
}
}
Self::MainlyClear | Self::PartlyCloudy => {
if is_night {
"weather-few-clouds-night-symbolic"
} else {
"weather-few-clouds-symbolic"
}
}
Self::Overcast => "weather-overcast-symbolic",
Self::Foggy => "weather-fog-symbolic",
Self::Drizzle | Self::FreezingDrizzle => "weather-showers-scattered-symbolic",
Self::Rain | Self::FreezingRain | Self::RainShowers => "weather-showers-symbolic",
Self::Snow | Self::SnowGrains | Self::SnowShowers => "weather-snow-symbolic",
Self::Thunderstorm | Self::ThunderstormHail => "weather-storm-symbolic",
Self::Unknown => "weather-severe-alert-symbolic",
}
}
}
/// Cardinal/intercardinal compass direction from wind bearing.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum CompassDirection {
/// North (338-360, 0-22 degrees).
N,
/// Northeast (23-67 degrees).
NE,
/// East (68-112 degrees).
E,
/// Southeast (113-157 degrees).
SE,
/// South (158-202 degrees).
S,
/// Southwest (203-247 degrees).
SW,
/// West (248-292 degrees).
W,
/// Northwest (293-337 degrees).
NW,
}
impl CompassDirection {
/// Converts degrees to the nearest compass direction.
/// Normalizes to 0-359 first, so negative values and >360 are handled.
pub fn from_degrees(degrees: i32) -> Self {
match degrees.rem_euclid(360) {
0..=22 | 338..=359 => Self::N,
23..=67 => Self::NE,
68..=112 => Self::E,
113..=157 => Self::SE,
158..=202 => Self::S,
203..=247 => Self::SW,
248..=292 => Self::W,
293..=337 => Self::NW,
_ => unreachable!("rem_euclid(360) always produces 0..=359"),
}
}
/// Returns the short compass label (e.g. "NE", "SW").
pub fn as_str(&self) -> &'static str {
match self {
Self::N => "N",
Self::NE => "NE",
Self::E => "E",
Self::SE => "SE",
Self::S => "S",
Self::SW => "SW",
Self::W => "W",
Self::NW => "NW",
}
}
}