aboutsummaryrefslogtreecommitdiff
path: root/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'main.go')
-rw-r--r--main.go223
1 files changed, 223 insertions, 0 deletions
diff --git a/main.go b/main.go
new file mode 100644
index 0000000..d191c72
--- /dev/null
+++ b/main.go
@@ -0,0 +1,223 @@
+package main
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+
+ "charm.land/lipgloss/v2"
+ "github.com/NimbleMarkets/ntcharts/linechart/timeserieslinechart"
+)
+
+var block = lipgloss.NewStyle().
+ Padding(1).
+ BorderStyle(lipgloss.NormalBorder()).
+ BorderForeground(lipgloss.Magenta)
+var bold = lipgloss.NewStyle().
+ Bold(true)
+var tStyle = bold
+
+// TODO Faint should render based on light/dark terminal theme
+var faint = lipgloss.NewStyle().
+ Foreground(lipgloss.BrightWhite)
+
+var temperature_colors = lipgloss.Blend1D(20,
+ lipgloss.Color("#94006f"),
+ lipgloss.Blue,
+ lipgloss.Blue,
+ lipgloss.Green,
+ lipgloss.Color("#ff5500"),
+ lipgloss.Red,
+)
+
+// TODO Handle light theme
+var rain_colors = lipgloss.Blend1D(10,
+ lipgloss.White,
+ lipgloss.Blue,
+)
+
+func main() {
+ weather, coords := GetWeather()
+ header := fmt.Sprintf("%s, %s (%s, %s)\n",
+ bold.Render(coords.Name),
+ bold.Render(coords.State),
+ faint.Render(ff(weather.Lat)),
+ faint.Render(ff(weather.Lon)),
+ )
+ current := fmtCurrent(weather)
+ minutely := fmtMinutely(weather)
+ hourly := fmtHourly(weather)
+
+ lipgloss.Println(lipgloss.JoinVertical(lipgloss.Center,
+ header,
+ current,
+ minutely,
+ hourly,
+ ))
+}
+
+func fmtHourly(w WeatherResponse) string {
+ hours := make([]string, len(w.Hourly))
+ // TODO Make this not ugly and include units
+ for i, v := range w.Hourly {
+ // For now only show next 8
+ // 48 available later
+ if i > 7 {
+ break
+ }
+ temperature_color := int(v.Temp / 5)
+ tStyle = tStyle.Foreground(temperature_colors[temperature_color])
+ rain_color := int(v.Pop * 9)
+ rStyle := tStyle.Foreground(rain_colors[rain_color])
+ now := time.Unix(v.Dt, 0).Local()
+ hours[i] = block.Render(lipgloss.JoinVertical(lipgloss.Center,
+ now.Format(time.DateOnly),
+ now.Format(time.Kitchen),
+ tStyle.Render(fmt.Sprintf("%g°F", v.Temp)),
+ rStyle.Render(fmt.Sprintf("雨%d%%", int(v.Pop*100))),
+ ))
+ }
+
+ return lipgloss.JoinHorizontal(lipgloss.Center, hours...)
+}
+
+/*
+func fmtHourlyGraph(w WeatherResponse) string {
+ dataset := make([]timeserieslinechart.TimePoint, len(w.Hourly))
+
+ for i, v := range w.Hourly {
+ fmt.Println(linechart.DefaultLabelFormatter()(i, float64(v.Dt)))
+ dataset[i] = timeserieslinechart.TimePoint{
+ Time: time.Unix(v.Dt, 0).Local(),
+ Value: v.Temp,
+ }
+ }
+ chart := timeserieslinechart.New(
+ 80,
+ 10,
+ timeserieslinechart.WithTimeSeries(dataset),
+ timeserieslinechart.WithXLabelFormatter(func(i int, _ float64) string {
+ res := time.Unix(w.Hourly[0].Dt, 0).Add(time.Hour * time.Duration(i))
+ return res.Local().Format(time.Kitchen)
+ }),
+ )
+ chart.DrawBraille()
+
+ return block.Render(chart.View())
+}
+*/
+
+func fmtMinutely(w WeatherResponse) string {
+ dataset := make([]timeserieslinechart.TimePoint, len(w.Minutely))
+
+ empty := true
+ for i, v := range w.Minutely {
+ now := time.Unix(v.Dt, 0).Local()
+ if v.Precipitation != 0 {
+ empty = false
+ }
+ dataset[i] = timeserieslinechart.TimePoint{
+ Time: now,
+ Value: v.Precipitation,
+ }
+ }
+
+ if empty {
+ return ""
+ }
+
+ tslc := timeserieslinechart.New(
+ 80,
+ 10,
+ timeserieslinechart.WithXLabelFormatter(func(_ int, d float64) string {
+ res := (int64(d) - w.Minutely[0].Dt) / 60
+ return strconv.Itoa(int(res))
+ }),
+ timeserieslinechart.WithTimeSeries(dataset),
+ )
+
+ tslc.DrawBraille()
+
+ return block.BorderForeground(lipgloss.Blue).Render(
+ lipgloss.JoinVertical(lipgloss.Center,
+ bold.Render("Precipitation"),
+ lipgloss.JoinHorizontal(lipgloss.Center,
+ "mm/h",
+ tslc.View(),
+ ),
+ "minutes",
+ ),
+ )
+}
+
+func fmtCurrent(w WeatherResponse) string {
+ // TODO This color gradient only handles 0-100
+ temperature_color := int(w.Current.Temp / 5)
+ tStyle = tStyle.Foreground(temperature_colors[temperature_color])
+
+ var b strings.Builder
+
+ // Temperature
+ fmt.Fprintf(&b, "%s°F (%s°F)",
+ tStyle.Render(ff(w.Current.Temp)),
+ tStyle.Render(ff(w.Current.FeelsLike)),
+ )
+ temp := block.Render(b.String())
+ b.Reset()
+
+ fmt.Fprintf(&b, "Humidity: %s%%",
+ bold.Render(strconv.Itoa(w.Current.Humidity)),
+ )
+ humidity := block.Render(b.String())
+ b.Reset()
+
+ fmt.Fprintf(&b, "Clouds: %s%%",
+ bold.Render(strconv.Itoa(w.Current.Clouds)),
+ )
+ clouds := block.Render(b.String())
+ b.Reset()
+
+ fmt.Fprintf(&b, "%s° at %s MPH",
+ bold.Render(strconv.Itoa(w.Current.WindDeg)),
+ bold.Render(ff(w.Current.WindSpeed)),
+ )
+ wind := block.Render(b.String())
+ b.Reset()
+
+ var rain string
+ if w.Current.Rain != nil {
+ rain = block.
+ BorderForeground(lipgloss.Blue).
+ Render(fmt.Sprintf("Rain:\n%v mm/h", w.Current.Rain.Hour))
+ b.Reset()
+ }
+
+ var snow string
+ if w.Current.Snow != nil {
+ snow = block.
+ BorderForeground(lipgloss.Blue).
+ Render(fmt.Sprintf("Snow:\n%v mm/h", w.Current.Snow.Hour))
+ b.Reset()
+ }
+
+ now := time.Unix(w.Current.Dt, 0).Local()
+ subheader := fmt.Sprintf("%s with %s", now.Format(time.Kitchen), w.Current.Weather[0].Description)
+
+ return lipgloss.JoinVertical(lipgloss.Center,
+ subheader,
+ lipgloss.JoinHorizontal(lipgloss.Top,
+ temp,
+ humidity,
+ clouds,
+ wind,
+ rain,
+ snow,
+ ),
+ )
+}
+
+// [f]ormat [f]loat
+func ff(f float64) string {
+ return strconv.FormatFloat(f, 'f', -1, 64)
+}