wind direction -> wind degree

This commit is contained in:
Emil Lerch 2026-01-03 16:06:10 -08:00
parent 025cc8ce86
commit bd6678da08
Signed by: lobo
GPG key ID: A7B62D657EF764F8
8 changed files with 59 additions and 56 deletions

View file

@ -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,
},

View file

@ -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,
},

View file

@ -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,
},

View file

@ -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,
},

19
src/render/utils.zig Normal file
View file

@ -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));
}

View file

@ -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,
},

View file

@ -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"));

View file

@ -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,