SoftTraderBackend/ServiceHandler/WeatherService.cs
2026-03-21 09:16:59 -04:00

110 lines
3.3 KiB
C#

using System.Text.Json;
namespace Backend.ServiceHandler
{
public sealed class WeatherService
{
private static readonly HttpClient Http = new()
{
DefaultRequestHeaders = { { "User-Agent", "SoftTrader/1.0" } }
};
public async Task<(string City, double Latitude, double Longitude)?> GeocodeAsync(
string city,
CancellationToken cancellationToken = default)
{
string encoded = Uri.EscapeDataString(city.Trim());
string url = $"https://geocoding-api.open-meteo.com/v1/search?name={encoded}&count=1&language=en";
string json = await Http.GetStringAsync(url, cancellationToken);
using var doc = JsonDocument.Parse(json);
if (!doc.RootElement.TryGetProperty("results", out var results) ||
results.GetArrayLength() == 0)
return null;
var first = results[0];
string name = first.GetProperty("name").GetString() ?? city;
double lat = first.GetProperty("latitude").GetDouble();
double lng = first.GetProperty("longitude").GetDouble();
return (name, lat, lng);
}
public async Task<WeatherResult?> GetCurrentWeatherAsync(
double latitude,
double longitude,
CancellationToken cancellationToken = default)
{
string url =
$"https://api.open-meteo.com/v1/forecast" +
$"?latitude={latitude}&longitude={longitude}" +
$"&current=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m" +
$"&temperature_unit=fahrenheit" +
$"&wind_speed_unit=mph";
string json = await Http.GetStringAsync(url, cancellationToken);
using var doc = JsonDocument.Parse(json);
if (!doc.RootElement.TryGetProperty("current", out var current))
return null;
return new WeatherResult
{
TemperatureF = current.GetProperty("temperature_2m").GetDouble(),
HumidityPercent = current.GetProperty("relative_humidity_2m").GetDouble(),
WeatherCode = current.GetProperty("weather_code").GetInt32(),
WindSpeedMph = current.GetProperty("wind_speed_10m").GetDouble()
};
}
}
public sealed class WeatherResult
{
public double TemperatureF { get; init; }
public double HumidityPercent { get; init; }
public int WeatherCode { get; init; }
public double WindSpeedMph { get; init; }
public string Description => WeatherCode switch
{
0 => "Clear sky",
1 => "Mainly clear",
2 => "Partly cloudy",
3 => "Overcast",
45 or 48 => "Foggy",
51 or 53 or 55 => "Drizzle",
61 or 63 or 65 => "Rain",
66 or 67 => "Freezing rain",
71 or 73 or 75 => "Snowfall",
77 => "Snow grains",
80 or 81 or 82 => "Rain showers",
85 or 86 => "Snow showers",
95 => "Thunderstorm",
96 or 99 => "Thunderstorm with hail",
_ => "Unknown"
};
}
public sealed class CurrentWeatherResponse
{
public string City { get; init; } = string.Empty;
public double TemperatureF { get; init; }
public double HumidityPercent { get; init; }
public double WindSpeedMph { get; init; }
public int WeatherCode { get; init; }
public string Description { get; init; } = string.Empty;
public static CurrentWeatherResponse From(
Backend.DatabaseHandler.UserLocationRecord location, WeatherResult weather) => new()
{
City = location.City,
TemperatureF = weather.TemperatureF,
HumidityPercent = weather.HumidityPercent,
WindSpeedMph = weather.WindSpeedMph,
WeatherCode = weather.WeatherCode,
Description = weather.Description
};
}
}