summaryrefslogtreecommitdiff
path: root/internal/openmeteo/openmeteo.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/openmeteo/openmeteo.go')
-rw-r--r--internal/openmeteo/openmeteo.go49
1 files changed, 49 insertions, 0 deletions
diff --git a/internal/openmeteo/openmeteo.go b/internal/openmeteo/openmeteo.go
index b5220f4..b1596e0 100644
--- a/internal/openmeteo/openmeteo.go
+++ b/internal/openmeteo/openmeteo.go
@@ -383,3 +383,52 @@ func presentSlice(v any) []bool {
}
return out
}
+
+// AirHourly fetches per-hour values for air-quality fields, keyed by the local
+// timestamp the API reports ("2006-01-02T15:04") and then by field.
+//
+// Keyed by time rather than by index because this is a different endpoint from
+// the forecast: nothing guarantees the two arrays start at the same hour, and
+// merging by position would silently shift a column by an hour.
+//
+// A field the API withholds is absent rather than zero, so a column shows blank
+// instead of a confident wrong number.
+func AirHourly(lat, lon float64, hours int, fields []string) (map[string]map[string]float64, error) {
+ out := map[string]map[string]float64{}
+ if len(fields) == 0 {
+ return out, nil
+ }
+ days := hours/24 + 2
+ if days > MaxAirDays {
+ days = MaxAirDays
+ }
+ var r struct {
+ Hourly map[string]any `json:"hourly"`
+ }
+ err := get(airURL, url.Values{
+ "latitude": {strconv.FormatFloat(lat, 'f', 4, 64)},
+ "longitude": {strconv.FormatFloat(lon, 'f', 4, 64)},
+ "hourly": {strings.Join(fields, ",")},
+ "forecast_days": {strconv.Itoa(days)},
+ "timezone": {"auto"},
+ }, &r)
+ if err != nil {
+ return nil, err
+ }
+
+ times := stringSlice(r.Hourly["time"])
+ for _, f := range fields {
+ vals := floatSlice(r.Hourly[f])
+ have := presentSlice(r.Hourly[f])
+ for i, t := range times {
+ if i >= len(vals) || (i < len(have) && !have[i]) {
+ continue
+ }
+ if out[t] == nil {
+ out[t] = map[string]float64{}
+ }
+ out[t][f] = vals[i]
+ }
+ }
+ return out, nil
+}