From b946d4670963a138b73ae8921ab53ac54772ebc0 Mon Sep 17 00:00:00 2001 From: Nick White Date: Thu, 22 Mar 2018 18:45:00 +0000 Subject: Add BBC as data source option --- weather.go | 176 ++++++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 145 insertions(+), 31 deletions(-) diff --git a/weather.go b/weather.go index 4eb5c13..0f46391 100644 --- a/weather.go +++ b/weather.go @@ -1,9 +1,11 @@ package main // TODO: allow free-text lookups of place names, rather than ids -// TODO: decode weather type number -// TODO: split out met office specific stuff to separate module -// TODO: add other weather providers +// TODO: convert metoffice windspeed to mph +// TODO: split output into days +// TODO: add -n flag to only output a certain number of days +// TODO: convert weather type number into a human-friendly string +// TODO: human friendly dates import ( "encoding/json" @@ -14,9 +16,50 @@ import ( "net/http" ) -const defid = "310118" +const metdefid = "310118" +const bbcdefid = "2640729" -type Response struct { +const meturl = "https://www.metoffice.gov.uk/public/data/PWSCache/BestForecast/Forecast/%s.json?concise=true" +const bbcurl = "https://weather-broker-cdn.api.bbci.co.uk/en/forecast/aggregated/%s" + +// BBC structures +type BBCResponse struct { + Forecasts []struct { + Detailed struct { + Reports []Report + } + } +} + +type Report struct { + EnhancedWeatherDescription string + ExtendedWeatherType int + FeelsLikeTemperatureC int + FeelsLikeTemperatureF int + GustSpeedKph int + GustSpeedMph int + Humidity int + LocalDate string + PrecipitationProbabilityInPercent int + PrecipitationProbabilityText string + Pressure int + TemperatureC int + TemperatureF int + Timeslot string + TimeslotLength int + Visibility string + WeatherType int + WeatherTypeText string + WindDescription string + WindDirection string + WindDirectionAbbreviation string + WindDirectionFull string + WindSpeedKph int + WindSpeedMph int +} + +// Met Office structures +type MetResponse struct { BestFcst struct { Forecast struct { Location struct { @@ -57,23 +100,106 @@ type WeatherParams struct { WT int // Weather Type } +// Our prefered struct +type Weather struct { + date string + time string + temperature float64 + precipitation int + weathertype int + windspeed float64 +} + var ( + bbc = flag.Bool("b", false, "use bbc as data source") numdays = flag.Int("n", 2, "number of days to show") verbose = flag.Bool("v", false, "verbose: show all weather details") ) +func processBBC(b []byte) []Weather { + var r BBCResponse + var weather []Weather + var w Weather + err := json.Unmarshal(b, &r) + if err != nil { + log.Fatal(err) + } + + for _, f := range r.Forecasts { + for _, report := range f.Detailed.Reports { + w.date = report.LocalDate + w.time = report.Timeslot + w.temperature = float64(report.TemperatureC) + w.precipitation = report.PrecipitationProbabilityInPercent + w.weathertype = report.WeatherType + w.windspeed = float64(report.WindSpeedMph) + weather = append(weather, w) + } + } + return weather +} + +func processMet(b []byte) []Weather { + var r MetResponse + var weather []Weather + var w Weather + err := json.Unmarshal(b, &r) + if err != nil { + log.Fatal(err) + } + + for _, d := range r.BestFcst.Forecast.Location.Days { + w.date = d.Date + w.time = "Day " + w.temperature = d.DayValues.WeatherParameters.T + w.precipitation = d.DayValues.WeatherParameters.PP + w.weathertype = d.DayValues.WeatherParameters.WT + w.windspeed = d.DayValues.WeatherParameters.WS + weather = append(weather, w) + + w.date = d.Date + w.time = "Night " + w.temperature = d.DayValues.WeatherParameters.T + w.precipitation = d.DayValues.WeatherParameters.PP + w.weathertype = d.DayValues.WeatherParameters.WT + w.windspeed = d.DayValues.WeatherParameters.WS + weather = append(weather, w) + + for _, t := range d.TimeSteps.TimeStep { + w.date = d.Date + w.time = t.Time + w.temperature = t.WeatherParameters.T + w.precipitation = t.WeatherParameters.PP + w.weathertype = t.WeatherParameters.WT + w.windspeed = t.WeatherParameters.WS + weather = append(weather, w) + } + } + return weather +} + func main() { - var r Response + var err error var id string + var resp *http.Response + var weather []Weather flag.Parse() if flag.NArg() > 0 { id = flag.Arg(0) } else { - id = defid + if *bbc { + id = bbcdefid + } else { + id = metdefid + } } - resp, err := http.Get("https://www.metoffice.gov.uk/public/data/PWSCache/BestForecast/Forecast/" + id + ".json?concise=true") + if *bbc { + resp, err = http.Get(fmt.Sprintf(bbcurl, id)) + } else { + resp, err = http.Get(fmt.Sprintf(meturl, id)) + } if err != nil { log.Fatal(err) } @@ -82,31 +208,19 @@ func main() { log.Fatalf("HTTP status code: %d\n", resp.StatusCode) } b, err := ioutil.ReadAll(resp.Body) - err = json.Unmarshal(b, &r) - if err != nil { - log.Fatal(err) - } - - for i, d := range r.BestFcst.Forecast.Location.Days { - if *numdays > 0 && i >= *numdays { - break - } - if i > 0 { - fmt.Printf("---------------------------------------------------------\n") - } - fmt.Printf("%s\n", d.Date) - prettyWeather(" Day", d.DayValues.WeatherParameters) - prettyWeather(" Night", d.NightValues.WeatherParameters) - for _, t := range d.TimeSteps.TimeStep { - prettyWeather(t.Time, t.WeatherParameters) - } + if *bbc { + weather = processBBC(b) + } else { + weather = processMet(b) } -} -func prettyWeather(t string, w WeatherParams) { - fmt.Printf("%s Type: %2d, Temp: %4.1f°C, Rain: %2d%%, Wind: %2.1fm/s\n", t, w.WT, w.T, w.PP, w.WS) - if *verbose { - fmt.Printf(" %+v\n", w) + for _, w := range weather { + fmt.Printf("%s %s ", w.date, w.time) + fmt.Printf("Type: %2d, Temp: %4.1f°C, ", w.weathertype, w.temperature) + fmt.Printf("Rain: %2d%%, Wind: %4.1fmph\n", w.precipitation, w.windspeed) + if *verbose { + fmt.Printf(" %+v\n", w) + } } } -- cgit v1.2.3