update for 4 hour rain forecast

This commit is contained in:
2026-04-06 18:32:33 +10:00
parent fb50c8ed71
commit 3a7309b2cf
20 changed files with 716 additions and 132 deletions
+49 -19
View File
@@ -51,11 +51,14 @@ type RainPredictionPoint struct {
GeneratedAt time.Time `json:"generated_at"`
ModelName string `json:"model_name"`
ModelVersion string `json:"model_version"`
HorizonHours int `json:"horizon_hours"`
Threshold float64 `json:"threshold"`
Probability float64 `json:"probability"`
PredictRain bool `json:"predict_rain"`
RainNext1hMM *float64 `json:"rain_next_1h_mm_actual,omitempty"`
RainNext1hActual *bool `json:"rain_next_1h_actual,omitempty"`
RainNextMM *float64 `json:"rain_next_mm_actual,omitempty"`
RainNextActual *bool `json:"rain_next_actual,omitempty"`
RainNext1hMM *float64 `json:"rain_next_1h_mm_actual,omitempty"` // backward-compatible alias
RainNext1hActual *bool `json:"rain_next_1h_actual,omitempty"` // backward-compatible alias
EvaluatedAt *time.Time `json:"evaluated_at,omitempty"`
}
@@ -365,8 +368,24 @@ func (d *DB) ForecastSeriesRange(ctx context.Context, site, model string, start,
}, nil
}
func (d *DB) LatestRainPrediction(ctx context.Context, site, modelName string) (*RainPredictionPoint, error) {
query := `
func predictionStorageForHorizon(horizonHours int) (table string, mmCol string, flagCol string, err error) {
switch horizonHours {
case 1:
return "predictions_rain_1h", "rain_next_1h_mm_actual", "rain_next_1h_actual", nil
case 4:
return "predictions_rain_4h", "rain_next_4h_mm_actual", "rain_next_4h_actual", nil
default:
return "", "", "", fmt.Errorf("unsupported rain prediction horizon: %d", horizonHours)
}
}
func (d *DB) LatestRainPrediction(ctx context.Context, site, modelName string, horizonHours int) (*RainPredictionPoint, error) {
table, mmCol, flagCol, err := predictionStorageForHorizon(horizonHours)
if err != nil {
return nil, err
}
query := fmt.Sprintf(`
SELECT
ts,
generated_at,
@@ -375,15 +394,15 @@ func (d *DB) LatestRainPrediction(ctx context.Context, site, modelName string) (
threshold,
probability,
predict_rain,
rain_next_1h_mm_actual,
rain_next_1h_actual,
%s,
%s,
evaluated_at
FROM predictions_rain_1h
FROM %s
WHERE site = $1
AND model_name = $2
ORDER BY ts DESC, generated_at DESC
LIMIT 1
`
`, mmCol, flagCol, table)
var (
p RainPredictionPoint
@@ -393,7 +412,7 @@ func (d *DB) LatestRainPrediction(ctx context.Context, site, modelName string) (
predictRain sql.NullBool
)
err := d.Pool.QueryRow(ctx, query, site, modelName).Scan(
err = d.Pool.QueryRow(ctx, query, site, modelName).Scan(
&p.TS,
&p.GeneratedAt,
&p.ModelName,
@@ -421,14 +440,22 @@ func (d *DB) LatestRainPrediction(ctx context.Context, site, modelName string) (
if predictRain.Valid {
p.PredictRain = predictRain.Bool
}
p.RainNext1hMM = nullFloatPtr(rainMM)
p.RainNext1hActual = nullBoolPtr(rainActual)
p.HorizonHours = horizonHours
p.RainNextMM = nullFloatPtr(rainMM)
p.RainNextActual = nullBoolPtr(rainActual)
p.RainNext1hMM = p.RainNextMM
p.RainNext1hActual = p.RainNextActual
p.EvaluatedAt = nullTimePtr(evaluatedAt)
return &p, nil
}
func (d *DB) RainPredictionSeriesRange(ctx context.Context, site, modelName string, start, end time.Time) ([]RainPredictionPoint, error) {
query := `
func (d *DB) RainPredictionSeriesRange(ctx context.Context, site, modelName string, horizonHours int, start, end time.Time) ([]RainPredictionPoint, error) {
table, mmCol, flagCol, err := predictionStorageForHorizon(horizonHours)
if err != nil {
return nil, err
}
query := fmt.Sprintf(`
SELECT DISTINCT ON (ts)
ts,
generated_at,
@@ -437,16 +464,16 @@ func (d *DB) RainPredictionSeriesRange(ctx context.Context, site, modelName stri
threshold,
probability,
predict_rain,
rain_next_1h_mm_actual,
rain_next_1h_actual,
%s,
%s,
evaluated_at
FROM predictions_rain_1h
FROM %s
WHERE site = $1
AND model_name = $2
AND ts >= $3
AND ts <= $4
ORDER BY ts ASC, generated_at DESC
`
`, mmCol, flagCol, table)
rows, err := d.Pool.Query(ctx, query, site, modelName, start, end)
if err != nil {
@@ -486,8 +513,11 @@ func (d *DB) RainPredictionSeriesRange(ctx context.Context, site, modelName stri
if predictRain.Valid {
p.PredictRain = predictRain.Bool
}
p.RainNext1hMM = nullFloatPtr(rainMM)
p.RainNext1hActual = nullBoolPtr(rainActual)
p.HorizonHours = horizonHours
p.RainNextMM = nullFloatPtr(rainMM)
p.RainNextActual = nullBoolPtr(rainActual)
p.RainNext1hMM = p.RainNextMM
p.RainNext1hActual = p.RainNextActual
p.EvaluatedAt = nullTimePtr(evaluatedAt)
points = append(points, p)
}