From bf844972aad8839d430be88b14097fd4cdaec059 Mon Sep 17 00:00:00 2001
From: Igor Pashev <pashev.igor@gmail.com>
Date: Wed, 12 May 2021 16:34:03 +0200
Subject: Show absolute humidity

---
 cmd/Print.hs                       |  10 +++
 lib/Web/OpenWeatherMap/Formulas.hs |  37 +++++++++++
 openweathermap.cabal               | 123 ++++++++++++++++++++-----------------
 3 files changed, 112 insertions(+), 58 deletions(-)
 create mode 100644 lib/Web/OpenWeatherMap/Formulas.hs

diff --git a/cmd/Print.hs b/cmd/Print.hs
index dd97c45..7a87023 100644
--- a/cmd/Print.hs
+++ b/cmd/Print.hs
@@ -6,7 +6,9 @@ module Print
 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
@@ -26,6 +28,7 @@ printCurrectWeather cw =
        ",  "
        [ w
        , showHumidity mainw
+       , showAbsoluteHumidity mainw
        , showPressure mainw
        , showTemp mainw
        , showWind wind
@@ -52,6 +55,7 @@ showForecast tz fc =
     ", "
     [ showWeather (FC.weather fc)
     , showHumidity mainw
+    , showAbsoluteHumidity mainw
     , showPressure mainw
     , showTemp mainw
     , showWind (FC.wind fc)
@@ -86,6 +90,12 @@ showHumidity m = "H " ++ show hm ++ " %"
     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"
diff --git a/lib/Web/OpenWeatherMap/Formulas.hs b/lib/Web/OpenWeatherMap/Formulas.hs
new file mode 100644
index 0000000..f63bc9f
--- /dev/null
+++ b/lib/Web/OpenWeatherMap/Formulas.hs
@@ -0,0 +1,37 @@
+{-# LANGUAGE NamedFieldPuns #-}
+
+module Web.OpenWeatherMap.Formulas
+  ( absoluteHumidity
+  ) where
+
+import Web.OpenWeatherMap.Types.Main
+
+{-- | Calculate absolute humidity (g/m³)
+
+Returns 'Nothing' if the temperature is out of range (−30°C,  +35°C).
+
+-}
+absoluteHumidity :: Main -> Maybe Double
+absoluteHumidity Main {temp, humidity}
+  | tC > -30 && tC < 35 = Just $ ahBolton1980 temp humidity
+  | otherwise = Nothing
+  where
+    tC = k2c temp
+
+{-- | Calculate absolute humidity (g/m³)
+
+Ref.: Bolton D. "The Computation of Equivalent Potential Temperature",
+Monthly Weather Review, 1980, 108(7):1046–1053.
+
+-}
+ahBolton1980 ::
+     Double -- ^ Temperature in Kelvins
+  -> Double -- ^ Relative humidity in %
+  -> Double
+ahBolton1980 temp humidity =
+  13.25 * humidity * exp (17.67 * tC / (tC + 243.5)) / temp
+  where
+    tC = k2c temp
+
+k2c :: Double -> Double
+k2c t = t - 273.15
diff --git a/openweathermap.cabal b/openweathermap.cabal
index 2138576..f410d5e 100644
--- a/openweathermap.cabal
+++ b/openweathermap.cabal
@@ -1,67 +1,74 @@
-name: openweathermap
-version: 0.2.0
-synopsis: Access data at OpenWeatherMap
-description: Client library and command-line utility to access
-  OpenWeatherMap https://openweathermap.org
-license: PublicDomain
-license-file: LICENSE
-author: Igor Pashev
-maintainer: Igor Pashev <pashev.igor@gmail.com>
-copyright: 2017, Igor Pashev <pashev.igor@gmail.com>
-category: Web
-build-type: Simple
-extra-source-files: README.md ChangeLog.md
-cabal-version: 1.20
+cabal-version:      1.20
+name:               openweathermap
+version:            0.2.0
+license:            PublicDomain
+license-file:       LICENSE
+copyright:          2017, Igor Pashev <pashev.igor@gmail.com>
+maintainer:         Igor Pashev <pashev.igor@gmail.com>
+author:             Igor Pashev
+synopsis:           Access data at OpenWeatherMap
+description:
+    Client library and command-line utility to access
+    OpenWeatherMap https://openweathermap.org
+
+category:           Web
+build-type:         Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
 
 source-repository head
-  type: git
-  location: https://github.com/ip1981/openweathermap.git
+    type:     git
+    location: https://github.com/ip1981/openweathermap.git
 
 flag cmd
-  description: Build a command-line utility.
-  default: True
+    description: Build a command-line utility.
 
 library
-  default-language: Haskell2010
-  ghc-options: -Wall
-  hs-source-dirs: lib
-  build-depends:
-      base >= 4.9 && < 5
-    , aeson
-    , http-api-data
-    , http-client
-    , servant
-    , servant-client >= 0.16
-    , servant-client-core
-  exposed-modules:
-     Web.OpenWeatherMap.API
-     Web.OpenWeatherMap.Client
-     Web.OpenWeatherMap.Types.City
-     Web.OpenWeatherMap.Types.Clouds
-     Web.OpenWeatherMap.Types.Coord
-     Web.OpenWeatherMap.Types.CurrentWeather
-     Web.OpenWeatherMap.Types.Forecast
-     Web.OpenWeatherMap.Types.ForecastWeather
-     Web.OpenWeatherMap.Types.Location
-     Web.OpenWeatherMap.Types.Main
-     Web.OpenWeatherMap.Types.Sys
-     Web.OpenWeatherMap.Types.Weather
-     Web.OpenWeatherMap.Types.Wind
+    exposed-modules:
+        Web.OpenWeatherMap.API
+        Web.OpenWeatherMap.Client
+        Web.OpenWeatherMap.Formulas
+        Web.OpenWeatherMap.Types.City
+        Web.OpenWeatherMap.Types.Clouds
+        Web.OpenWeatherMap.Types.Coord
+        Web.OpenWeatherMap.Types.CurrentWeather
+        Web.OpenWeatherMap.Types.Forecast
+        Web.OpenWeatherMap.Types.ForecastWeather
+        Web.OpenWeatherMap.Types.Location
+        Web.OpenWeatherMap.Types.Main
+        Web.OpenWeatherMap.Types.Sys
+        Web.OpenWeatherMap.Types.Weather
+        Web.OpenWeatherMap.Types.Wind
 
-executable openweathermap
-  default-language: Haskell2010
-  ghc-options: -Wall -static
-  hs-source-dirs: cmd
-  main-is: Main.hs
-  other-modules: Print
-  if flag(cmd)
+    hs-source-dirs:   lib
+    default-language: Haskell2010
+    ghc-options:      -Wall
     build-depends:
-        base >= 4.9 && < 5
-      , directory
-      , openweathermap
-      , optparse-applicative >= 0.13.0.0
-      , time
-      , xdg-basedir
-  else
-    buildable: False
+        base >=4.9 && <5,
+        aeson -any,
+        http-api-data -any,
+        http-client -any,
+        servant -any,
+        servant-client >=0.16,
+        servant-client-core -any
+
+executable openweathermap
+    main-is:          Main.hs
+    hs-source-dirs:   cmd
+    other-modules:    Print
+    default-language: Haskell2010
+    ghc-options:      -Wall -static
+
+    if flag(cmd)
+        build-depends:
+            base >=4.9 && <5,
+            directory -any,
+            openweathermap -any,
+            optparse-applicative >=0.13.0.0,
+            time -any,
+            xdg-basedir -any
+
+    else
+        buildable: False
 
-- 
cgit v1.2.3