diff --git a/backends/worldweatheronline.com.go b/backends/worldweatheronline.com.go index 43d8a24..e73d443 100644 --- a/backends/worldweatheronline.com.go +++ b/backends/worldweatheronline.com.go @@ -134,7 +134,10 @@ func wwoParseCond(cond wwoCond, date time.Time) (ret iface.Cond) { } ret.FeelsLikeC = cond.FeelsLikeC - ret.PrecipMM = cond.PrecipMM + if cond.PrecipMM != nil { + ret.PrecipM = new(float32) + *ret.PrecipM = *cond.PrecipMM / 1000 + } ret.Time = date if cond.TmpTime != nil { @@ -143,7 +146,10 @@ func wwoParseCond(cond wwoCond, date time.Time) (ret iface.Cond) { ret.Time = time.Date(year, month, day, hour, min, 0, 0, time.UTC) } - ret.VisibleDistKM = cond.VisibleDistKM + if cond.VisibleDistKM != nil { + ret.VisibleDistM = new(float32) + *ret.VisibleDistM = *cond.VisibleDistKM * 1000 + } if cond.WinddirDegree != nil && *cond.WinddirDegree >= 0 { ret.WinddirDegree = new(int) diff --git a/frontends/ascii-art-table.go b/frontends/ascii-art-table.go index f8946c9..7d59923 100644 --- a/frontends/ascii-art-table.go +++ b/frontends/ascii-art-table.go @@ -1,7 +1,6 @@ package frontends import ( - "flag" "fmt" "log" "math" @@ -16,7 +15,7 @@ import ( ) type aatConfig struct { - imperial bool + unit iface.UnitSystem } //TODO: replace s parameter with printf interface? @@ -41,10 +40,6 @@ func aatPad(s string, mustLen int) (ret string) { } func (c *aatConfig) formatTemp(cond iface.Cond) string { - unit := map[bool]string{ - false: "C", - true: "F", - } color := func(temp float32) string { colmap := []struct { maxtemp float32 @@ -63,34 +58,29 @@ func (c *aatConfig) formatTemp(cond iface.Cond) string { break } } - - if c.imperial { - temp = (temp*18 + 320) / 10 - } - return fmt.Sprintf("\033[38;5;%03dm%d\033[0m", col, int(temp)) + t, _ := c.unit.Temp(temp) + return fmt.Sprintf("\033[38;5;%03dm%d\033[0m", col, int(t)) } + _, u := c.unit.Temp(0.0) + if cond.TempC == nil { - return aatPad(fmt.Sprintf("? °%s", unit[c.imperial]), 15) + return aatPad(fmt.Sprintf("? %s", u), 15) } t := *cond.TempC if cond.FeelsLikeC != nil { fl := *cond.FeelsLikeC if fl < t { - return aatPad(fmt.Sprintf("%s – %s °%s", color(fl), color(t), unit[c.imperial]), 15) + return aatPad(fmt.Sprintf("%s – %s %s", color(fl), color(t), u), 15) } else if fl > t { - return aatPad(fmt.Sprintf("%s – %s °%s", color(t), color(fl), unit[c.imperial]), 15) + return aatPad(fmt.Sprintf("%s – %s %s", color(t), color(fl), u), 15) } } - return aatPad(fmt.Sprintf("%s °%s", color(t), unit[c.imperial]), 15) + return aatPad(fmt.Sprintf("%s %s", color(t), u), 15) } func (c *aatConfig) formatWind(cond iface.Cond) string { - unit := map[bool]string{ - false: "km/h", - true: "mph", - } windDir := func(deg *int) string { if deg == nil { return "?" @@ -115,12 +105,12 @@ func (c *aatConfig) formatWind(cond iface.Cond) string { } } - if c.imperial { - spdKmph = (spdKmph * 1000) / 1609 - } - return fmt.Sprintf("\033[38;5;%03dm%d\033[0m", col, int(spdKmph)) + s, _ := c.unit.Speed(spdKmph) + return fmt.Sprintf("\033[38;5;%03dm%d\033[0m", col, int(s)) } + _, u := c.unit.Speed(0.0) + if cond.WindspeedKmph == nil { return aatPad(windDir(cond.WinddirDegree), 15) } @@ -128,44 +118,28 @@ func (c *aatConfig) formatWind(cond iface.Cond) string { if cond.WindGustKmph != nil { if g := *cond.WindGustKmph; g > s { - return aatPad(fmt.Sprintf("%s %s – %s %s", windDir(cond.WinddirDegree), color(s), color(g), unit[c.imperial]), 15) + return aatPad(fmt.Sprintf("%s %s – %s %s", windDir(cond.WinddirDegree), color(s), color(g), u), 15) } } - return aatPad(fmt.Sprintf("%s %s %s", windDir(cond.WinddirDegree), color(s), unit[c.imperial]), 15) + return aatPad(fmt.Sprintf("%s %s %s", windDir(cond.WinddirDegree), color(s), u), 15) } func (c *aatConfig) formatVisibility(cond iface.Cond) string { - unit := map[bool]string{ - false: "km", - true: "mi", - } - if cond.VisibleDistKM == nil { + if cond.VisibleDistM == nil { return aatPad("", 15) } - v := *cond.VisibleDistKM - - if c.imperial { - v = (v * 621) / 1000 - } - return aatPad(fmt.Sprintf("%d %s", int(v), unit[c.imperial]), 15) + v, u := c.unit.Distance(*cond.VisibleDistM) + return aatPad(fmt.Sprintf("%d %s", int(v), u), 15) } func (c *aatConfig) formatRain(cond iface.Cond) string { - unit := map[bool]string{ - false: "mm", - true: "in", - } - if cond.PrecipMM != nil { - a := *cond.PrecipMM - if c.imperial { - a *= 0.039 - } - + if cond.PrecipM != nil { + v, u := c.unit.Distance(*cond.PrecipM) if cond.ChanceOfRainPercent != nil { - return aatPad(fmt.Sprintf("%.1f %s | %d%%", a, unit[c.imperial], *cond.ChanceOfRainPercent), 15) + return aatPad(fmt.Sprintf("%.1f %s | %d%%", v, u, *cond.ChanceOfRainPercent), 15) } - return aatPad(fmt.Sprintf("%.1f %s", a, unit[c.imperial]), 15) + return aatPad(fmt.Sprintf("%.1f %s", v, u), 15) } else if cond.ChanceOfRainPercent != nil { return aatPad(fmt.Sprintf("%d%%", *cond.ChanceOfRainPercent), 15) } @@ -371,10 +345,11 @@ func (c *aatConfig) printDay(day iface.Day) (ret []string) { } func (c *aatConfig) Setup() { - flag.BoolVar(&c.imperial, "aat-imperial", false, "aat frontend: use imperial units for output") } -func (c *aatConfig) Render(r iface.Data) { +func (c *aatConfig) Render(r iface.Data, unitSystem iface.UnitSystem) { + c.unit = unitSystem + fmt.Printf("Weather for %s\n\n", r.Location) stdout := colorable.NewColorableStdout() diff --git a/iface/iface.go b/iface/iface.go index 2282f4c..9b5e076 100644 --- a/iface/iface.go +++ b/iface/iface.go @@ -2,6 +2,7 @@ package iface import ( "time" + "log" ) type WeatherCode int @@ -51,11 +52,11 @@ type Cond struct { // range [0, 100]. ChanceOfRainPercent *int - // PrecipMM is the precipitation amount. It must be >= 0. - PrecipMM *float32 + // PrecipM is the precipitation amount in meters(!). It must be >= 0. + PrecipM *float32 - // VisibleDistKM is the visibility range in kilometers. It must be >= 0. - VisibleDistKM *float32 + // VisibleDistM is the visibility range in meters(!). It must be >= 0. + VisibleDistM *float32 // WindspeedKmph is the average wind speed in kilometers per second. WindspeedKmph *float32 @@ -103,6 +104,61 @@ type Data struct { Location string } +type UnitSystem int + +const ( + UnitsMetric UnitSystem = iota + UnitsImperial + UnitsSi +) + +func (u UnitSystem) Temp(tempC float32) (res float32, unit string) { + if u == UnitsMetric { + return tempC, "°C" + } else if u == UnitsImperial { + return tempC*1.8 + 32, "°F" + } else if u == UnitsSi { + return tempC + 273.16, "°K" + } + log.Fatalln("Unknown unit system:", u) + return +} + +func (u UnitSystem) Speed(spdKmph float32) (res float32, unit string) { + if u == UnitsMetric { + return spdKmph, "km/h" + } else if u == UnitsImperial { + return spdKmph/1.609, "mph" + } else if u == UnitsSi { + return spdKmph/3.6, "m/s" + } + log.Fatalln("Unknown unit system:", u) + return +} + +func (u UnitSystem) Distance(distM float32) (res float32, unit string) { + if u == UnitsMetric || u == UnitsSi { + if distM < 1 { + return distM*1000, "mm" + } else if distM < 1000 { + return distM, "m" + } else { + return distM/1000, "km" + } + } else if u == UnitsImperial { + res, unit = distM/0.0254, "in" + if res < 3 * 12 { // 1yd = 3ft, 1ft = 12in + return + } else if res < 8 * 10 * 22 * 36 { //1mi = 8fur, 1fur = 10ch, 1ch = 22yd + return res / 36, "yd" + } else { + return res / 8 / 10 / 22 / 36, "mi" + } + } + log.Fatalln("Unknown unit system:", u) + return +} + type Backend interface { Setup() Fetch(location string, numdays int) Data @@ -110,7 +166,7 @@ type Backend interface { type Frontend interface { Setup() - Render(weather Data) + Render(weather Data, unitSystem UnitSystem) } var ( diff --git a/main.go b/main.go index f6bce4a..b25d544 100644 --- a/main.go +++ b/main.go @@ -35,6 +35,7 @@ func main() { // initialize global flags and default config numdays := flag.Int("days", 3, "`NUMBER` of days of weather forecast to be displayed") location := flag.String("city", "New York", "`LOCATION` to be queried") + unitSystem := flag.String("units", "metric", "`UNITSYSTEM` to use for output.\n \tChoices are: metric, imperial, si") selectedBackend := flag.String("backend", "worldweatheronline.com", "`BACKEND` to be used") selectedFrontend := flag.String("frontend", "ascii-art-table", "`FRONTEND` to be used") @@ -57,10 +58,18 @@ func main() { } r := be.Fetch(*location, *numdays) + // set unit system + unit := iface.UnitsMetric + if *unitSystem == "imperial" { + unit = iface.UnitsImperial + } else if *unitSystem == "si" { + unit = iface.UnitsSi + } + // get selected frontend and render the weather data with it fe, ok := iface.AllFrontends[*selectedFrontend] if !ok { log.Fatalf("Could not find selected frontend \"%s\"", *selectedFrontend) } - fe.Render(r) + fe.Render(r, unit) }