Update wind direction and precipitation window presentation

This commit is contained in:
2026-06-10 09:24:06 -05:00
parent c3da3af2f4
commit d1d0df11a8
10 changed files with 284 additions and 45 deletions

View File

@@ -47,6 +47,8 @@ type TimedValue struct {
Time time.Time `json:"time"`
}
const DefaultPrecipWindowProbabilityThreshold = 40
type Indicators struct {
Snow bool `json:"snow,omitempty"`
Ice bool `json:"ice,omitempty"`
@@ -66,34 +68,101 @@ type AlertOverlap struct {
}
type PrecipTiming struct {
MaxPrecipitationProbability *TimedValue `json:"maxPrecipitationProbability,omitempty"`
FirstPrecipitation *TimedValue `json:"firstPrecipitation,omitempty"`
LastPrecipitation *TimedValue `json:"lastPrecipitation,omitempty"`
ThunderMentioned bool `json:"thunderMentioned,omitempty"`
MaxPrecipitationProbability *TimedValue `json:"maxPrecipitationProbability,omitempty"`
FirstPrecipitation *TimedValue `json:"firstPrecipitation,omitempty"`
LastPrecipitation *TimedValue `json:"lastPrecipitation,omitempty"`
ProbabilityThreshold float64 `json:"probabilityThreshold"`
PrecipitationWindows []PrecipitationWindow `json:"precipitationWindows,omitempty"`
ThunderMentioned bool `json:"thunderMentioned,omitempty"`
}
type PrecipitationWindow struct {
Start time.Time `json:"start"`
End *time.Time `json:"end,omitempty"`
MaxPrecipitationProbability TimedValue `json:"maxPrecipitationProbability"`
ProbabilityThreshold float64 `json:"probabilityThreshold"`
}
func BuildPrecipTiming(periods []weatherdata.ForecastPeriod) PrecipTiming {
var timing PrecipTiming
for _, forecastPeriod := range periods {
setMaxTimedValue(&timing.MaxPrecipitationProbability, forecastPeriod.ProbabilityOfPrecipitationPercent, forecastPeriod.StartTime)
if forecastPeriod.ProbabilityOfPrecipitationPercent != nil && *forecastPeriod.ProbabilityOfPrecipitationPercent > 0 {
value := TimedValue{
Value: *forecastPeriod.ProbabilityOfPrecipitationPercent,
return buildPrecipTimingWithThreshold(periods, DefaultPrecipWindowProbabilityThreshold)
}
func buildPrecipTimingWithThreshold(periods []weatherdata.ForecastPeriod, threshold float64) PrecipTiming {
timing := PrecipTiming{ProbabilityThreshold: threshold}
sorted := append([]weatherdata.ForecastPeriod(nil), periods...)
sort.SliceStable(sorted, func(i int, j int) bool {
return sorted[i].StartTime.Before(sorted[j].StartTime)
})
var active *PrecipitationWindow
var activeLastEnd time.Time
closeActive := func() {
if active == nil {
return
}
end := activeLastEnd
active.End = &end
timing.PrecipitationWindows = append(timing.PrecipitationWindows, *active)
active = nil
}
startActive := func(forecastPeriod weatherdata.ForecastPeriod, probability float64) {
active = &PrecipitationWindow{
Start: forecastPeriod.StartTime,
MaxPrecipitationProbability: TimedValue{
Value: probability,
Time: forecastPeriod.StartTime,
},
ProbabilityThreshold: threshold,
}
activeLastEnd = forecastPeriod.EndTime
if timing.FirstPrecipitation == nil {
timing.FirstPrecipitation = &TimedValue{
Value: probability,
Time: forecastPeriod.StartTime,
}
if timing.FirstPrecipitation == nil || value.Time.Before(timing.FirstPrecipitation.Time) {
copied := value
timing.FirstPrecipitation = &copied
}
if timing.LastPrecipitation == nil || value.Time.After(timing.LastPrecipitation.Time) {
copied := value
timing.LastPrecipitation = &copied
}
}
for _, forecastPeriod := range sorted {
setMaxTimedValue(&timing.MaxPrecipitationProbability, forecastPeriod.ProbabilityOfPrecipitationPercent, forecastPeriod.StartTime)
if forecastPeriod.ProbabilityOfPrecipitationPercent == nil || *forecastPeriod.ProbabilityOfPrecipitationPercent < threshold {
closeActive()
} else {
probability := *forecastPeriod.ProbabilityOfPrecipitationPercent
if active == nil {
startActive(forecastPeriod, probability)
} else {
if forecastPeriod.StartTime.After(activeLastEnd) {
closeActive()
startActive(forecastPeriod, probability)
}
if probability > active.MaxPrecipitationProbability.Value {
active.MaxPrecipitationProbability = TimedValue{
Value: probability,
Time: forecastPeriod.StartTime,
}
}
if forecastPeriod.EndTime.After(activeLastEnd) {
activeLastEnd = forecastPeriod.EndTime
}
}
}
if mentionsThunder(forecastPeriod.TextDescription) {
timing.ThunderMentioned = true
}
}
if active != nil {
timing.PrecipitationWindows = append(timing.PrecipitationWindows, *active)
}
if len(timing.PrecipitationWindows) > 0 {
final := timing.PrecipitationWindows[len(timing.PrecipitationWindows)-1]
if final.End != nil {
timing.LastPrecipitation = &TimedValue{
Value: final.MaxPrecipitationProbability.Value,
Time: *final.End,
}
}
}
return timing
}