diff --git a/src/render/ansi.zig b/src/render/ansi.zig index 7be834a..c031686 100644 --- a/src/render/ansi.zig +++ b/src/render/ansi.zig @@ -1,5 +1,6 @@ const std = @import("std"); const types = @import("../weather/types.zig"); +const utils = @import("utils.zig"); pub const Format = enum { plain_text, @@ -55,7 +56,7 @@ fn renderCurrent(w: *std.Io.Writer, current: types.CurrentCondition, options: Re if (options.format == .plain_text) { try w.print("{s} {s}\n", .{ art[0], current.condition }); try w.print("{s} {c}{d:.0}({c}{d:.0}) {s}\n", .{ art[1], sign, abs_temp, fl_sign, abs_fl, temp_unit }); - try w.print("{s} {s} {d:.0} {s}\n", .{ art[2], current.wind_dir, wind_speed, wind_unit }); + try w.print("{s} {s} {d:.0} {s}\n", .{ art[2], utils.degreeToDirection(current.wind_deg), wind_speed, wind_unit }); try w.print("{s} {s}\n", .{ art[3], visibility }); try w.print("{s} {d:.1} {s}\n", .{ art[4], precip, precip_unit }); } else { @@ -66,7 +67,7 @@ fn renderCurrent(w: *std.Io.Writer, current: types.CurrentCondition, options: Re try w.print("{s}{s}{s} {s}\n", .{ cloud_color, art[0], reset, current.condition }); try w.print("{s}{s}{s} \x1b[38;5;{d}m{c}{d:.0}({c}{d:.0}){s} {s}\n", .{ cloud_color, art[1], reset, temp_color_code, sign, abs_temp, fl_sign, abs_fl, reset, temp_unit }); - try w.print("{s}{s}{s} {s} \x1b[38;5;{d}m{d:.0}{s} {s}\n", .{ cloud_color, art[2], reset, current.wind_dir, wind_color_code, wind_speed, reset, wind_unit }); + try w.print("{s}{s}{s} {s} \x1b[38;5;{d}m{d:.0}{s} {s}\n", .{ cloud_color, art[2], reset, utils.degreeToDirection(current.wind_deg), wind_color_code, wind_speed, reset, wind_unit }); try w.print("{s}{s}{s} {s}\n", .{ cloud_color, art[3], reset, visibility }); try w.print("{s}{s}{s} {d:.1} {s}\n", .{ cloud_color, art[4], reset, precip, precip_unit }); } @@ -327,7 +328,7 @@ test "render with imperial units" { .weather_code = .clear, .humidity = 60, .wind_kph = 16.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, @@ -354,7 +355,7 @@ test "clear weather art" { .weather_code = .clear, .humidity = 50, .wind_kph = 10.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, @@ -379,7 +380,7 @@ test "partly cloudy weather art" { .weather_code = .clouds_few, .humidity = 55, .wind_kph = 12.0, - .wind_dir = "NE", + .wind_deg = 45.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, @@ -404,7 +405,7 @@ test "cloudy weather art" { .weather_code = .clouds_overcast, .humidity = 70, .wind_kph = 15.0, - .wind_dir = "E", + .wind_deg = 90.0, .pressure_mb = 1010.0, .precip_mm = 0.0, }, @@ -429,7 +430,7 @@ test "rain weather art" { .weather_code = .rain_moderate, .humidity = 85, .wind_kph = 20.0, - .wind_dir = "SE", + .wind_deg = 135.0, .pressure_mb = 1005.0, .precip_mm = 5.0, }, @@ -453,7 +454,7 @@ test "thunderstorm weather art" { .weather_code = .thunderstorm, .humidity = 90, .wind_kph = 30.0, - .wind_dir = "S", + .wind_deg = 180.0, .pressure_mb = 1000.0, .precip_mm = 10.0, }, @@ -477,7 +478,7 @@ test "snow weather art" { .weather_code = .snow, .humidity = 80, .wind_kph = 18.0, - .wind_dir = "SW", + .wind_deg = 225.0, .pressure_mb = 1008.0, .precip_mm = 3.0, }, @@ -501,7 +502,7 @@ test "sleet weather art" { .weather_code = .sleet, .humidity = 75, .wind_kph = 22.0, - .wind_dir = "W", + .wind_deg = 270.0, .pressure_mb = 1007.0, .precip_mm = 2.0, }, @@ -525,7 +526,7 @@ test "fog weather art" { .weather_code = .fog, .humidity = 95, .wind_kph = 5.0, - .wind_dir = "NW", + .wind_deg = 315.0, .pressure_mb = 1012.0, .precip_mm = 0.0, }, @@ -549,7 +550,7 @@ test "unknown weather code art" { .weather_code = .unknown, .humidity = 60, .wind_kph = 10.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, @@ -576,7 +577,7 @@ test "temperature matches between ansi and custom format" { .weather_code = .clear, .humidity = 60, .wind_kph = 10.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, diff --git a/src/render/custom.zig b/src/render/custom.zig index e5445d0..a820f4a 100644 --- a/src/render/custom.zig +++ b/src/render/custom.zig @@ -1,6 +1,7 @@ const std = @import("std"); const types = @import("../weather/types.zig"); const emoji = @import("emoji.zig"); +const utils = @import("utils.zig"); pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format: []const u8, use_imperial: bool) ![]const u8 { var output: std.ArrayList(u8) = .empty; @@ -38,7 +39,7 @@ pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, format: 'w' => { const wind = if (use_imperial) weather.current.wind_kph * 0.621371 else weather.current.wind_kph; const unit = if (use_imperial) "mph" else "km/h"; - try writer.print("{d:.0} {s} {s}", .{ wind, unit, weather.current.wind_dir }); + try writer.print("{d:.0} {s} {s}", .{ wind, unit, utils.degreeToDirection(weather.current.wind_deg) }); }, 'l' => try writer.print("{s}", .{weather.location}), 'p' => { @@ -87,7 +88,7 @@ test "render custom format with location and temp" { .weather_code = .clouds_overcast, .humidity = 76, .wind_kph = 11.0, - .wind_dir = "NNE", + .wind_deg = 22.5, .pressure_mb = 1019.0, .precip_mm = 0.0, }, @@ -114,7 +115,7 @@ test "render custom format with newline" { .weather_code = .clear, .humidity = 65, .wind_kph = 8.0, - .wind_dir = "E", + .wind_deg = 90.0, .pressure_mb = 1020.0, .precip_mm = 0.0, }, @@ -140,7 +141,7 @@ test "render custom format with humidity and pressure" { .weather_code = .clouds_overcast, .humidity = 85, .wind_kph = 12.0, - .wind_dir = "W", + .wind_deg = 270.0, .pressure_mb = 1012.0, .precip_mm = 0.2, }, @@ -167,7 +168,7 @@ test "render custom format with imperial units" { .weather_code = .clear, .humidity = 60, .wind_kph = 16.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 2.5, }, diff --git a/src/render/json.zig b/src/render/json.zig index 4c6b5c4..8c97055 100644 --- a/src/render/json.zig +++ b/src/render/json.zig @@ -9,7 +9,7 @@ pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData) ![]const .weatherDesc = .{.{ .value = weather.current.condition }}, .humidity = weather.current.humidity, .windspeedKmph = weather.current.wind_kph, - .winddirDegree = weather.current.wind_dir, + .winddirDegree = weather.current.wind_deg, .pressure = weather.current.pressure_mb, .precipMM = weather.current.precip_mm, }, @@ -31,7 +31,7 @@ test "render json format" { .weather_code = .clouds_few, .humidity = 72, .wind_kph = 13.0, - .wind_dir = "SW", + .wind_deg = 225.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, diff --git a/src/render/line.zig b/src/render/line.zig index ec1b50f..3705093 100644 --- a/src/render/line.zig +++ b/src/render/line.zig @@ -1,6 +1,7 @@ const std = @import("std"); const types = @import("../weather/types.zig"); const emoji = @import("emoji.zig"); +const utils = @import("utils.zig"); pub fn render(allocator: std.mem.Allocator, data: types.WeatherData, format: []const u8, use_imperial: bool) ![]const u8 { if (std.mem.eql(u8, format, "1")) { @@ -23,7 +24,7 @@ pub fn render(allocator: std.mem.Allocator, data: types.WeatherData, format: []c temp, unit, "🌬️", - data.current.wind_dir, + utils.degreeToDirection(data.current.wind_deg), wind, wind_unit, }); @@ -38,7 +39,7 @@ pub fn render(allocator: std.mem.Allocator, data: types.WeatherData, format: []c temp, unit, "🌬️", - data.current.wind_dir, + utils.degreeToDirection(data.current.wind_deg), wind, wind_unit, "💧", @@ -55,7 +56,7 @@ pub fn render(allocator: std.mem.Allocator, data: types.WeatherData, format: []c temp, unit, "🌬️", - data.current.wind_dir, + utils.degreeToDirection(data.current.wind_deg), wind, wind_unit, "💧", @@ -90,7 +91,7 @@ fn renderCustom(allocator: std.mem.Allocator, data: types.WeatherData, format: [ 'w' => { const wind = if (use_imperial) data.current.wind_kph * 0.621371 else data.current.wind_kph; const wind_unit = if (use_imperial) "mph" else "km/h"; - try output.writer(allocator).print("{s}{d:.0}{s}", .{ data.current.wind_dir, wind, wind_unit }); + try output.writer(allocator).print("{s}{d:.0}{s}", .{ utils.degreeToDirection(data.current.wind_deg), wind, wind_unit }); }, 'l' => try output.appendSlice(allocator, data.location), 'p' => { @@ -127,7 +128,7 @@ test "format 1" { .weather_code = .clear, .humidity = 65, .wind_kph = 10.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, @@ -151,7 +152,7 @@ test "custom format" { .weather_code = .clear, .humidity = 65, .wind_kph = 10.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, @@ -175,7 +176,7 @@ test "format 2 with imperial units" { .weather_code = .clouds_overcast, .humidity = 70, .wind_kph = 20.0, - .wind_dir = "SE", + .wind_deg = 135.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, diff --git a/src/render/utils.zig b/src/render/utils.zig new file mode 100644 index 0000000..4df44ad --- /dev/null +++ b/src/render/utils.zig @@ -0,0 +1,19 @@ +const std = @import("std"); + +pub fn degreeToDirection(deg: f32) []const u8 { + const normalized = @mod(deg + 22.5, 360.0); + const idx: usize = @intFromFloat(normalized / 45.0); + const directions = [_][]const u8{ "N", "NE", "E", "SE", "S", "SW", "W", "NW" }; + return directions[@min(idx, 7)]; +} + +test "degreeToDirection" { + try std.testing.expectEqualStrings("N", degreeToDirection(0)); + try std.testing.expectEqualStrings("NE", degreeToDirection(45)); + try std.testing.expectEqualStrings("E", degreeToDirection(90)); + try std.testing.expectEqualStrings("SE", degreeToDirection(135)); + try std.testing.expectEqualStrings("S", degreeToDirection(180)); + try std.testing.expectEqualStrings("SW", degreeToDirection(225)); + try std.testing.expectEqualStrings("W", degreeToDirection(270)); + try std.testing.expectEqualStrings("NW", degreeToDirection(315)); +} diff --git a/src/render/v2.zig b/src/render/v2.zig index 3594ac2..79f7208 100644 --- a/src/render/v2.zig +++ b/src/render/v2.zig @@ -1,5 +1,6 @@ const std = @import("std"); const types = @import("../weather/types.zig"); +const utils = @import("utils.zig"); pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, use_imperial: bool) ![]const u8 { var output: std.ArrayList(u8) = .empty; @@ -23,9 +24,9 @@ pub fn render(allocator: std.mem.Allocator, weather: types.WeatherData, use_impe try writer.writeAll(" 🌬️ "); if (use_imperial) { const wind_mph = weather.current.wind_kph * 0.621371; - try writer.print("{d:.1} mph {s}\n", .{ wind_mph, weather.current.wind_dir }); + try writer.print("{d:.1} mph {s}\n", .{ wind_mph, utils.degreeToDirection(weather.current.wind_deg) }); } else { - try writer.print("{d:.1} km/h {s}\n", .{ weather.current.wind_kph, weather.current.wind_dir }); + try writer.print("{d:.1} km/h {s}\n", .{ weather.current.wind_kph, utils.degreeToDirection(weather.current.wind_deg) }); } try writer.writeAll(" 🔽 "); if (use_imperial) { @@ -77,7 +78,7 @@ test "render v2 format" { .weather_code = .clouds_overcast, .humidity = 80, .wind_kph = 15.0, - .wind_dir = "NW", + .wind_deg = 315.0, .pressure_mb = 1015.0, .precip_mm = 0.5, }, @@ -106,7 +107,7 @@ test "render v2 format with imperial units" { .weather_code = .clear, .humidity = 65, .wind_kph = 16.0, - .wind_dir = "N", + .wind_deg = 0.0, .pressure_mb = 1013.0, .precip_mm = 0.0, }, diff --git a/src/weather/MetNo.zig b/src/weather/MetNo.zig index 611b665..b24ef8d 100644 --- a/src/weather/MetNo.zig +++ b/src/weather/MetNo.zig @@ -172,11 +172,10 @@ fn parseMetNoResponse(allocator: std.mem.Allocator, coords: Coordinates, json: s "clearsky_day"; // Get wind direction - const wind_from_deg = details.object.get("wind_from_direction"); - const wind_dir = if (wind_from_deg) |deg| - degreeToDirection(@as(f32, @floatCast(deg.float))) + const wind_deg = if (details.object.get("wind_from_direction")) |deg| + @as(f32, @floatCast(deg.float)) else - "N"; + 0.0; // Parse forecast days from timeseries const forecast = try parseForecastDays(allocator, timeseries.array.items); @@ -192,7 +191,7 @@ fn parseMetNoResponse(allocator: std.mem.Allocator, coords: Coordinates, json: s .weather_code = symbolCodeToWeatherCode(symbol_code), .humidity = humidity, .wind_kph = wind_kph, - .wind_dir = try allocator.dupe(u8, wind_dir), + .wind_deg = wind_deg, .pressure_mb = pressure_mb, .precip_mm = 0.0, }, @@ -430,24 +429,6 @@ fn symbolCodeToCondition(symbol: []const u8) []const u8 { return "Unknown"; } -fn degreeToDirection(deg: f32) []const u8 { - const normalized = @mod(deg + 22.5, 360.0); - const idx: usize = @intFromFloat(normalized / 45.0); - const directions = [_][]const u8{ "N", "NE", "E", "SE", "S", "SW", "W", "NW" }; - return directions[@min(idx, 7)]; -} - -test "degreeToDirection" { - try std.testing.expectEqualStrings("N", degreeToDirection(0)); - try std.testing.expectEqualStrings("NE", degreeToDirection(45)); - try std.testing.expectEqualStrings("E", degreeToDirection(90)); - try std.testing.expectEqualStrings("SE", degreeToDirection(135)); - try std.testing.expectEqualStrings("S", degreeToDirection(180)); - try std.testing.expectEqualStrings("SW", degreeToDirection(225)); - try std.testing.expectEqualStrings("W", degreeToDirection(270)); - try std.testing.expectEqualStrings("NW", degreeToDirection(315)); -} - test "symbolCodeToWeatherCode" { try std.testing.expectEqual(types.WeatherCode.clear, symbolCodeToWeatherCode("clearsky_day")); try std.testing.expectEqual(types.WeatherCode.clouds_few, symbolCodeToWeatherCode("fair_night")); diff --git a/src/weather/types.zig b/src/weather/types.zig index 9efb915..59744ac 100644 --- a/src/weather/types.zig +++ b/src/weather/types.zig @@ -90,7 +90,6 @@ pub const WeatherData = struct { pub fn deinit(self: WeatherData) void { self.allocator.free(self.location); self.allocator.free(self.current.condition); - self.allocator.free(self.current.wind_dir); for (self.forecast) |day| { self.allocator.free(day.date); self.allocator.free(day.condition); @@ -114,7 +113,7 @@ pub const CurrentCondition = struct { weather_code: WeatherCode, humidity: u8, wind_kph: f32, - wind_dir: []const u8, + wind_deg: f32, pressure_mb: f32, precip_mm: f32,