aboutsummaryrefslogtreecommitdiff
path: root/cmd/Print.hs
blob: e27fa90cf90407d456c9b361ab5720f605285847 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
module Print
  ( printCurrectWeather
  , printForecastWeather
  ) where

import Data.List (intercalate)
import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
import Data.Time.LocalTime (TimeZone, minutesToTimeZone, utcToZonedTime)
import Text.Printf (printf)

import Web.OpenWeatherMap.Formulas (absoluteHumidity)
import qualified Web.OpenWeatherMap.Types.City as City
import qualified Web.OpenWeatherMap.Types.Coord as Coord
import qualified Web.OpenWeatherMap.Types.CurrentWeather as CW
import qualified Web.OpenWeatherMap.Types.Forecast as FC
import qualified Web.OpenWeatherMap.Types.ForecastWeather as FW
import qualified Web.OpenWeatherMap.Types.Main as Main
import qualified Web.OpenWeatherMap.Types.Sys as Sys
import qualified Web.OpenWeatherMap.Types.Weather as Weather
import qualified Web.OpenWeatherMap.Types.Wind as Wind

printCurrectWeather :: CW.CurrentWeather -> IO ()
printCurrectWeather cw =
  putStrLn
    (place ++
     ", " ++
     time ++
     ": " ++
     intercalate
       ",  "
       [ w
       , showHumidity mainw
       , showAbsoluteHumidity mainw
       , showPressure mainw
       , showTemp mainw
       , showWind wind
       ])
  where
    tz = secondsToTimeZone (CW.timezone cw)
    time = showLocalTime tz (CW.dt cw)
    w = showWeather $ CW.weather cw
    place = showLocation (CW.name cw) (Sys.country . CW.sys $ cw) (CW.coord cw)
    mainw = CW.main cw
    wind = CW.wind cw

printForecastWeather :: FW.ForecastWeather -> IO ()
printForecastWeather fw = do
  let c = FW.city fw
      tz = secondsToTimeZone (City.timezone c)
      place = showLocation (City.name c) (City.country c) (City.coord c)
  putStrLn place
  mapM_ putStrLn (showForecast tz <$> FW.list fw)

secondsToTimeZone :: Int -> TimeZone
secondsToTimeZone s = minutesToTimeZone (s `div` 60)

showForecast :: TimeZone -> FC.Forecast -> String
showForecast tz fc =
  showLocalTime tz (FC.dt fc) ++
  ": " ++
  intercalate
    ", "
    [ showWeather (FC.weather fc)
    , showHumidity mainw
    , showAbsoluteHumidity mainw
    , showPressure mainw
    , showTemp mainw
    , showWind (FC.wind fc)
    ]
  where
    mainw = FC.main fc

showLocalTime :: TimeZone -> Int -> String
showLocalTime tz =
  show . utcToZonedTime tz . posixSecondsToUTCTime . fromIntegral

showLocation :: String -> Maybe String -> Coord.Coord -> String
showLocation name country coord =
  name' ++ maybe "" ("," ++) country ++ " " ++ coords
  where
    coords = showCoord coord
    name' =
      if name /= ""
        then name
        else "<unknown>"

showCoord :: Coord.Coord -> String
showCoord coord =
  "(" ++
  maybe "?" show (Coord.lat coord) ++
  "°, " ++ maybe "?" show (Coord.lon coord) ++ "°)"

showWeather :: [Weather.Weather] -> String
showWeather w = intercalate "/" $ Weather.main <$> w

showHumidity :: Main.Main -> String
showHumidity m = "H " ++ show hm ++ " %"
  where
    hm :: Int
    hm = round . Main.humidity $ m

showAbsoluteHumidity :: Main.Main -> String
showAbsoluteHumidity m = "ρ " ++ rho ++ " g/m³"
  where
    r = absoluteHumidity m
    rho = maybe "??" (printf "%0.2f") r

-- https://en.wikipedia.org/wiki/Millimeter_of_mercury
showPressure :: Main.Main -> String
showPressure m = "P " ++ show p ++ " mmHg"
  where
    hPa2mmHg hpa = hpa * 0.750061561303
    p :: Int
    p = round . hPa2mmHg . Main.pressure $ m

-- https://stackoverflow.com/q/7490660/933161
showWind :: Wind.Wind -> String
showWind w = dir ++ " " ++ show speed ++ " m/s"
  where
    speed :: Int
    speed = round . Wind.speed $ w
    deg = Wind.deg w
    --     [ "N", "NE", "E", "SE", "S", "SW", "W", "NW" ]
    dirs = ["↓", "↙", "←", "↖", "↑", "↗", "→", "↘"]
    l = length dirs
    sector = round $ (deg * fromIntegral l) / 360.0
    dir = dirs !! (sector `rem` l)

showTemp :: Main.Main -> String
showTemp m = "T " ++ temp ++ " °C"
  where
    k2c k = k - 273.15 -- Kelvin to Celsius
    tmax :: Int
    tmin :: Int
    tmax = round . k2c . Main.temp_max $ m
    tmin = round . k2c . Main.temp_min $ m
    show' t =
      if t > 0
        then "+" ++ show t
        else show t
    temp =
      if tmax /= tmin
        then show' tmin ++ ".." ++ show' tmax
        else show' tmin