make metno conditions more exhaustive

This commit is contained in:
Emil Lerch 2025-12-19 10:14:24 -08:00
parent 52de6f0f9b
commit 048aca4ece
Signed by: lobo
GPG key ID: A7B62D657EF764F8
3 changed files with 75 additions and 23 deletions

View file

@ -376,7 +376,7 @@ test "unknown weather code art" {
.temp_c = 16.0,
.temp_f = 61.0,
.condition = "Unknown",
.weather_code = @enumFromInt(999),
.weather_code = .unknown,
.humidity = 60,
.wind_kph = 10.0,
.wind_dir = "N",

View file

@ -5,6 +5,61 @@ const types = @import("types.zig");
const MetNo = @This();
const MetNoOpenWeatherEntry = struct { []const u8, types.WeatherCode };
// symbol codes: https://github.com/metno/weathericons/tree/main/weather
// they also have _day, _night and _polartwilight variants
//
// Openweathermap weather condition codes:
// https://openweathermap.org/weather-conditions
const weather_code_entries = [_]MetNoOpenWeatherEntry{
// zig fmt: off
.{ "clearsky", .clear },
.{ "cloudy", .clouds_overcast },
.{ "fair", .clouds_few },
.{ "fog", .fog },
.{ "heavyrain", .rain_heavy },
.{ "heavyrainandthunder", .thunderstorm_heavy_rain },
.{ "heavyrainshowers", .thunderstorm_drizzle },
.{ "heavyrainshowersandthunder", .thunderstorm_heavy_drizzle },
.{ "heavysleet", .sleet },
.{ "heavysleetandthunder", .thunderstorm_heavy_rain },
.{ "heavysleetshowers", .sleet_shower },
.{ "heavysleetshowersandthunder", .thunderstorm_heavy_drizzle },
.{ "heavysnow", .snow_heavy },
.{ "heavysnowandthunder", .thunderstorm_heavy },
.{ "heavysnowshowers", .snow_shower_heavy },
.{ "heavysnowshowersandthunder", .thunderstorm_heavy },
.{ "lightrain", .rain_light },
.{ "lightrainandthunder", .thunderstorm_light_rain },
.{ "lightrainshowers", .rain_shower_light },
.{ "lightrainshowersandthunder", .thunderstorm_light_rain },
.{ "lightsleet", .sleet_shower_light },
.{ "lightsleetandthunder", .thunderstorm_light },
.{ "lightsleetshowers", .sleet_shower_light },
.{ "lightsnow", .snow_light },
.{ "lightsnowandthunder", .thunderstorm_light },
.{ "lightsnowshowers", .snow_shower_light },
.{ "lightssleetshowersandthunder", .thunderstorm_light },
.{ "lightssnowshowersandthunder", .thunderstorm_light },
.{ "partlycloudy", .clouds_few },
.{ "rain", .rain_moderate },
.{ "rainandthunder", .thunderstorm_rain },
.{ "rainshowers", .rain_shower },
.{ "rainshowersandthunder", .thunderstorm_drizzle },
.{ "sleet", .sleet },
.{ "sleetandthunder", .thunderstorm },
.{ "sleetshowers", .sleet_shower },
.{ "sleetshowersandthunder", .thunderstorm_drizzle },
.{ "snow", .snow },
.{ "snowandthunder", .thunderstorm },
.{ "snowshowers", .snow_shower },
.{ "snowshowersandthunder", .thunderstorm },
// zig fmt: on
};
const WeatherCodeMap = std.StaticStringMap(types.WeatherCode);
const weather_code_map = WeatherCodeMap.initComptime(weather_code_entries);
allocator: std.mem.Allocator,
pub fn init(allocator: std.mem.Allocator) !MetNo {
@ -127,29 +182,26 @@ fn parseMetNoResponse(allocator: std.mem.Allocator, coords: Coordinates, json: s
}
fn symbolCodeToWeatherCode(symbol: []const u8) types.WeatherCode {
if (std.mem.indexOf(u8, symbol, "clearsky")) |_| return .clear;
if (std.mem.indexOf(u8, symbol, "partlycloudy")) |_| return .clouds_scattered;
if (std.mem.indexOf(u8, symbol, "fair")) |_| return .clouds_few;
if (std.mem.indexOf(u8, symbol, "cloudy")) |_| return .clouds_overcast;
if (std.mem.indexOf(u8, symbol, "fog")) |_| return .fog;
if (std.mem.indexOf(u8, symbol, "thunder")) |_| return .thunderstorm;
if (std.mem.indexOf(u8, symbol, "rain")) |_| return .rain_moderate;
if (std.mem.indexOf(u8, symbol, "sleet")) |_| return .sleet;
if (std.mem.indexOf(u8, symbol, "snow")) |_| return .snow;
return .clear;
var it = std.mem.splitScalar(u8, symbol, '_');
const metno_weather_code = it.next().?;
const variant = it.next();
_ = variant; // discard day/night/polar twilight for now
return weather_code_map.get(metno_weather_code) orelse .unknown;
}
fn symbolCodeToCondition(symbol: []const u8) []const u8 {
if (std.mem.indexOf(u8, symbol, "clearsky")) |_| return "Clear";
if (std.mem.indexOf(u8, symbol, "partlycloudy")) |_| return "Partly cloudy";
if (std.mem.indexOf(u8, symbol, "fair")) |_| return "Fair";
if (std.mem.indexOf(u8, symbol, "cloudy")) |_| return "Cloudy";
if (std.mem.indexOf(u8, symbol, "fog")) |_| return "Fog";
if (std.mem.indexOf(u8, symbol, "thunder")) |_| return "Thunderstorm";
if (std.mem.indexOf(u8, symbol, "rain")) |_| return "Rain";
if (std.mem.indexOf(u8, symbol, "sleet")) |_| return "Sleet";
if (std.mem.indexOf(u8, symbol, "snow")) |_| return "Snow";
return "Clear";
var it = std.mem.splitScalar(u8, symbol, '_');
const metno_weather_code = it.next().?;
if (std.mem.eql(u8, metno_weather_code, "clearsky")) return "Clear";
if (std.mem.eql(u8, metno_weather_code, "partlycloudy")) return "Partly cloudy";
if (std.mem.eql(u8, metno_weather_code, "fair")) return "Fair";
if (std.mem.eql(u8, metno_weather_code, "cloudy")) return "Cloudy";
if (std.mem.eql(u8, metno_weather_code, "fog")) return "Fog";
if (std.mem.eql(u8, metno_weather_code, "thunder")) return "Thunderstorm";
if (std.mem.eql(u8, metno_weather_code, "rain")) return "Rain";
if (std.mem.eql(u8, metno_weather_code, "sleet")) return "Sleet";
if (std.mem.eql(u8, metno_weather_code, "snow")) return "Snow";
return "Unknown";
}
fn degreeToDirection(deg: f32) []const u8 {
@ -174,6 +226,6 @@ test "symbolCodeToWeatherCode" {
try std.testing.expectEqual(types.WeatherCode.clear, symbolCodeToWeatherCode("clearsky_day"));
try std.testing.expectEqual(types.WeatherCode.clouds_few, symbolCodeToWeatherCode("fair_night"));
try std.testing.expectEqual(types.WeatherCode.clouds_overcast, symbolCodeToWeatherCode("cloudy"));
try std.testing.expectEqual(types.WeatherCode.rain_moderate, symbolCodeToWeatherCode("lightrain"));
try std.testing.expectEqual(types.WeatherCode.rain_light, symbolCodeToWeatherCode("lightrain"));
try std.testing.expectEqual(types.WeatherCode.snow, symbolCodeToWeatherCode("snow"));
}

View file

@ -72,7 +72,7 @@ pub const WeatherCode = enum(u16) {
clouds_broken = 803, // 51-84%
clouds_overcast = 804, // 85-100%
_,
unknown = 999,
};
pub const WeatherError = error{