Updated model and normalizers to map provider “feels like” fields into a single ApparentTemperatureC field.
This commit is contained in:
@@ -103,8 +103,6 @@ type WeatherForecastPeriod struct {
|
|||||||
BarometricPressurePa *float64 `json:"barometricPressurePa,omitempty"`
|
BarometricPressurePa *float64 `json:"barometricPressurePa,omitempty"`
|
||||||
VisibilityMeters *float64 `json:"visibilityMeters,omitempty"`
|
VisibilityMeters *float64 `json:"visibilityMeters,omitempty"`
|
||||||
ApparentTemperatureC *float64 `json:"apparentTemperatureC,omitempty"`
|
ApparentTemperatureC *float64 `json:"apparentTemperatureC,omitempty"`
|
||||||
WindChillC *float64 `json:"windChillC,omitempty"`
|
|
||||||
HeatIndexC *float64 `json:"heatIndexC,omitempty"`
|
|
||||||
CloudCoverPercent *float64 `json:"cloudCoverPercent,omitempty"`
|
CloudCoverPercent *float64 `json:"cloudCoverPercent,omitempty"`
|
||||||
|
|
||||||
// Precipitation (forecast-specific). Keep these generic and provider-independent.
|
// Precipitation (forecast-specific). Keep these generic and provider-independent.
|
||||||
|
|||||||
@@ -36,8 +36,7 @@ type WeatherObservation struct {
|
|||||||
VisibilityMeters *float64 `json:"visibilityMeters,omitempty"`
|
VisibilityMeters *float64 `json:"visibilityMeters,omitempty"`
|
||||||
|
|
||||||
RelativeHumidityPercent *float64 `json:"relativeHumidityPercent,omitempty"`
|
RelativeHumidityPercent *float64 `json:"relativeHumidityPercent,omitempty"`
|
||||||
WindChillC *float64 `json:"windChillC,omitempty"`
|
ApparentTemperatureC *float64 `json:"apparentTemperatureC,omitempty"`
|
||||||
HeatIndexC *float64 `json:"heatIndexC,omitempty"`
|
|
||||||
|
|
||||||
ElevationMeters *float64 `json:"elevationMeters,omitempty"`
|
ElevationMeters *float64 `json:"elevationMeters,omitempty"`
|
||||||
RawMessage string `json:"rawMessage,omitempty"`
|
RawMessage string `json:"rawMessage,omitempty"`
|
||||||
|
|||||||
@@ -80,6 +80,14 @@ func buildObservation(parsed nwsObservationResponse) (model.WeatherObservation,
|
|||||||
// NWS observation responses typically do not include a day/night flag -> nil.
|
// NWS observation responses typically do not include a day/night flag -> nil.
|
||||||
canonicalText := standards.WMOText(wmo, nil)
|
canonicalText := standards.WMOText(wmo, nil)
|
||||||
|
|
||||||
|
// Apparent temperature: prefer wind chill when both are supplied.
|
||||||
|
var apparentC *float64
|
||||||
|
if parsed.Properties.WindChill.Value != nil {
|
||||||
|
apparentC = parsed.Properties.WindChill.Value
|
||||||
|
} else if parsed.Properties.HeatIndex.Value != nil {
|
||||||
|
apparentC = parsed.Properties.HeatIndex.Value
|
||||||
|
}
|
||||||
|
|
||||||
obs := model.WeatherObservation{
|
obs := model.WeatherObservation{
|
||||||
StationID: parsed.Properties.StationID,
|
StationID: parsed.Properties.StationID,
|
||||||
StationName: parsed.Properties.StationName,
|
StationName: parsed.Properties.StationName,
|
||||||
@@ -108,8 +116,7 @@ func buildObservation(parsed nwsObservationResponse) (model.WeatherObservation,
|
|||||||
VisibilityMeters: parsed.Properties.Visibility.Value,
|
VisibilityMeters: parsed.Properties.Visibility.Value,
|
||||||
|
|
||||||
RelativeHumidityPercent: parsed.Properties.RelativeHumidity.Value,
|
RelativeHumidityPercent: parsed.Properties.RelativeHumidity.Value,
|
||||||
WindChillC: parsed.Properties.WindChill.Value,
|
ApparentTemperatureC: apparentC,
|
||||||
HeatIndexC: parsed.Properties.HeatIndex.Value,
|
|
||||||
|
|
||||||
ElevationMeters: parsed.Properties.Elevation.Value,
|
ElevationMeters: parsed.Properties.Elevation.Value,
|
||||||
RawMessage: parsed.Properties.RawMessage,
|
RawMessage: parsed.Properties.RawMessage,
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import (
|
|||||||
// Event.EmittedAt when present, otherwise the first hourly time.
|
// Event.EmittedAt when present, otherwise the first hourly time.
|
||||||
// - Hourly payloads are array-oriented; missing fields are treated as nil per-period.
|
// - Hourly payloads are array-oriented; missing fields are treated as nil per-period.
|
||||||
// - Snowfall is provided in centimeters and is converted to millimeters.
|
// - Snowfall is provided in centimeters and is converted to millimeters.
|
||||||
// - apparent_temperature is ignored (no canonical "feels like" field).
|
// - apparent_temperature is mapped to ApparentTemperatureC when present.
|
||||||
type ForecastNormalizer struct{}
|
type ForecastNormalizer struct{}
|
||||||
|
|
||||||
func (ForecastNormalizer) Match(e event.Event) bool {
|
func (ForecastNormalizer) Match(e event.Event) bool {
|
||||||
@@ -121,6 +121,9 @@ func buildForecast(parsed omForecastResponse, fallbackIssued time.Time) (model.W
|
|||||||
if v := floatAt(parsed.Hourly.Temperature2m, i); v != nil {
|
if v := floatAt(parsed.Hourly.Temperature2m, i); v != nil {
|
||||||
period.TemperatureC = v
|
period.TemperatureC = v
|
||||||
}
|
}
|
||||||
|
if v := floatAt(parsed.Hourly.ApparentTemp, i); v != nil {
|
||||||
|
period.ApparentTemperatureC = v
|
||||||
|
}
|
||||||
if v := floatAt(parsed.Hourly.DewPoint2m, i); v != nil {
|
if v := floatAt(parsed.Hourly.DewPoint2m, i); v != nil {
|
||||||
period.DewpointC = v
|
period.DewpointC = v
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,6 +107,10 @@ func buildObservation(parsed omResponse) (model.WeatherObservation, time.Time, e
|
|||||||
v := *parsed.Current.Temperature2m
|
v := *parsed.Current.Temperature2m
|
||||||
obs.TemperatureC = &v
|
obs.TemperatureC = &v
|
||||||
}
|
}
|
||||||
|
if parsed.Current.ApparentTemperature != nil {
|
||||||
|
v := *parsed.Current.ApparentTemperature
|
||||||
|
obs.ApparentTemperatureC = &v
|
||||||
|
}
|
||||||
|
|
||||||
if parsed.Current.RelativeHumidity2m != nil {
|
if parsed.Current.RelativeHumidity2m != nil {
|
||||||
v := *parsed.Current.RelativeHumidity2m
|
v := *parsed.Current.RelativeHumidity2m
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ type omCurrent struct {
|
|||||||
Time string `json:"time"` // e.g. "2026-01-10T12:30" (often no timezone suffix)
|
Time string `json:"time"` // e.g. "2026-01-10T12:30" (often no timezone suffix)
|
||||||
|
|
||||||
Temperature2m *float64 `json:"temperature_2m"`
|
Temperature2m *float64 `json:"temperature_2m"`
|
||||||
|
ApparentTemperature *float64 `json:"apparent_temperature"`
|
||||||
RelativeHumidity2m *float64 `json:"relative_humidity_2m"`
|
RelativeHumidity2m *float64 `json:"relative_humidity_2m"`
|
||||||
WeatherCode *int `json:"weather_code"`
|
WeatherCode *int `json:"weather_code"`
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,11 @@ func buildObservation(parsed owmResponse) (model.WeatherObservation, time.Time,
|
|||||||
// - wind speed is m/s -> km/h conversion
|
// - wind speed is m/s -> km/h conversion
|
||||||
tempC := parsed.Main.Temp
|
tempC := parsed.Main.Temp
|
||||||
rh := parsed.Main.Humidity
|
rh := parsed.Main.Humidity
|
||||||
|
var apparentC *float64
|
||||||
|
if parsed.Main.FeelsLike != nil {
|
||||||
|
v := *parsed.Main.FeelsLike
|
||||||
|
apparentC = &v
|
||||||
|
}
|
||||||
|
|
||||||
surfacePa := normcommon.PressurePaFromHPa(parsed.Main.Pressure)
|
surfacePa := normcommon.PressurePaFromHPa(parsed.Main.Pressure)
|
||||||
var seaLevelPa *float64
|
var seaLevelPa *float64
|
||||||
@@ -117,6 +122,7 @@ func buildObservation(parsed owmResponse) (model.WeatherObservation, time.Time,
|
|||||||
IconURL: iconURL,
|
IconURL: iconURL,
|
||||||
|
|
||||||
TemperatureC: &tempC,
|
TemperatureC: &tempC,
|
||||||
|
ApparentTemperatureC: apparentC,
|
||||||
|
|
||||||
WindDirectionDegrees: parsed.Wind.Deg,
|
WindDirectionDegrees: parsed.Wind.Deg,
|
||||||
WindSpeedKmh: &wsKmh,
|
WindSpeedKmh: &wsKmh,
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type owmResponse struct {
|
|||||||
|
|
||||||
Main struct {
|
Main struct {
|
||||||
Temp float64 `json:"temp"` // °C when units=metric (enforced by source)
|
Temp float64 `json:"temp"` // °C when units=metric (enforced by source)
|
||||||
|
FeelsLike *float64 `json:"feels_like"` // °C when units=metric (enforced by source)
|
||||||
Pressure float64 `json:"pressure"` // hPa
|
Pressure float64 `json:"pressure"` // hPa
|
||||||
Humidity float64 `json:"humidity"` // %
|
Humidity float64 `json:"humidity"` // %
|
||||||
SeaLevel *float64 `json:"sea_level"`
|
SeaLevel *float64 `json:"sea_level"`
|
||||||
|
|||||||
Reference in New Issue
Block a user