module Data.Wmo.AerodromeReport
( module Data.Icao.Location
, module Data.Icao.Time
, ReportType(..)
, SpeedUnit(..)
, WindVariableDirection(wvdLeft, wvdRight)
, WindSpeed(wsUnit, wsValue)
, Wind(wDirection, wSpeed, wGust, wVariation)
, VisibilityTendency(..)
, VisibilityDistance(vdUnit, vdValue, vdFraction)
, RunwayDesignator
, ExtremeRvr(..)
, RunwayVisualRange(rvrDesignator, rvrMeanVisibility,
rvrOutsideMeasuringRange, rvrVisibilityTendency)
, CompassPoint(..)
, Visibility(vPrevailing, vLowest, vLowestDirection, vRunways)
, WeatherQualifier(..)
, WeatherDescriptor(..)
, WeatherPhenomenon(..)
, Weather(wQualifier, wDescriptor, wPhenomenon)
, CloudType(..)
, CloudAmountType(..)
, CloudAmount(caHeight, caType, clType)
, VerticalVisibility(vvExtent)
, NoCloudObserved(..)
, Clouds(..)
, Pressure(pUnit, pValue)
, ReportModifiers(reportCorrected, reportAuto, reportMissed)
, AerodromeReport(reportType, reportStation, reportDate, reportWind,
reportVisibility, reportWeather, reportClouds, reportTemperature,
reportDewPoint, reportPressure, reportRemarks)
, cavok
, metar
, speci
, with
, withModifiers
, withWindDirection
, withWindSpeed
, withWindVariation
, withPrevailingVisibility
, withFaaPrevailingVisibility
, withLowestVisibility
, withRunwayVisualRange
, withFaaRunwayVisualRange
, withWeather
, withCloudAmount
, withObscuredSky
, withNoCloudObserved
, withTemperature
, withPressure
, withFaaPressure
, Parser
, Error(message, column)
, parser
, parse
) where
import Control.Monad ((<=<))
import Control.Monad.Fail
import Data.Aeromess.Parser
import Data.Char (isDigit)
import Data.Icao.Lang
import Data.Icao.Location
import Data.Icao.Time
import Data.Maybe
import Prelude hiding (fail)
data ReportType
= METAR
| SPECI
deriving (Bounded, Enum, Eq, Read, Show)
data SpeedUnit
= KT
| MPS
| KMH
deriving (Bounded, Enum, Eq, Read, Show)
data LengthUnit
= Metre
| Foot
| Mile
deriving (Eq, Show)
data WindVariableDirection = WindVariableDirection
{ wvdLeft :: Int
, wvdRight :: Int
} deriving (Eq, Show)
data WindSpeed = WindSpeed
{ wsUnit :: SpeedUnit
, wsValue :: Int
} deriving (Eq, Show)
data Wind = Wind
{ wDirection :: Maybe Int
, wSpeed :: WindSpeed
, wGust :: Maybe WindSpeed
, wVariation :: Maybe WindVariableDirection
} deriving (Eq, Show)
data VisibilityTendency
= Down
| Up
| NoChange
deriving (Eq, Show)
data VisibilityDistance = VisibilityDistance
{ vdUnit :: LengthUnit
, vdValue :: Int
, vdFraction :: Maybe (Int, Int)
} deriving (Eq, Show)
newtype RunwayDesignator =
RunwayDesignator String
deriving (Eq, Show)
data ExtremeRvr
= Lower
| Higher
deriving (Eq, Show)
data RunwayVisualRange = RunwayVisualRange
{ rvrDesignator :: RunwayDesignator
, rvrMeanVisibility :: VisibilityDistance
, rvrOutsideMeasuringRange :: Maybe ExtremeRvr
, rvrVisibilityTendency :: Maybe VisibilityTendency
} deriving (Eq, Show)
data CompassPoint
= North
| NorthEast
| East
| SouthEast
| South
| SouthWest
| West
| NorthWest
deriving (Eq, Show)
data Visibility = Visibility
{ vPrevailing :: VisibilityDistance
, vLowest :: Maybe VisibilityDistance
, vLowestDirection :: Maybe CompassPoint
, vRunways :: [RunwayVisualRange]
} deriving (Eq, Show)
data WeatherQualifier
= LightWeather
| HeavyWeather
| InVicinityWeather
deriving (Eq, Show)
data WeatherDescriptor
= Shallow
| Patches
| Partial
| Drifting
| Blowing
| Showers
| Thunderstorm
| Freezing
deriving (Eq, Show)
data WeatherPhenomenon
= Drizzle
| Rain
| Snow
| SnowGrains
| IceCrystals
| IcePellets
| Hail
| SmallHail
| UnknownPrecipitation
| Mist
| Fog
| Smoke
| VolcanicAsh
| WidespreadDust
| Sand
| Haze
| Spray
| Dust
| Squall
| FunnelCloud
| Sandstorm
| DustStorm
deriving (Eq, Show)
data Weather = Weather
{ wQualifier :: Maybe WeatherQualifier
, wDescriptor :: Maybe WeatherDescriptor
, wPhenomenon :: [WeatherPhenomenon]
} deriving (Eq, Show)
data CloudType
= Cumulonimbus
| ToweringCumulus
deriving (Eq, Show)
data CloudAmountType
= Few
| Scattered
| Broken
| Overcast
deriving (Eq, Show)
data CloudAmount = CloudAmount
{ caType :: CloudAmountType
, caHeight :: Maybe Int
, clType :: Maybe CloudType
} deriving (Eq, Show)
newtype VerticalVisibility = VerticalVisibility
{ vvExtent :: Maybe Int
} deriving (Eq, Show)
data NoCloudObserved
= NoCloudBelow1500
| NoCloudBelow3600
| SkyClear
deriving (Eq, Show)
data Clouds
= CloudAmounts [CloudAmount]
| ObscuredSky VerticalVisibility
| NoneObserved NoCloudObserved
deriving (Eq, Show)
data PressureUnit
= Hpa
| InchesHg
deriving (Enum, Eq, Show)
data Pressure = Pressure
{ pUnit :: PressureUnit
, pValue :: Int
} deriving (Eq, Show)
data ReportModifiers = ReportModifiers
{ reportCorrected :: Bool
, reportAuto :: Bool
, reportMissed :: Bool
} deriving (Eq, Show)
data AerodromeReport = AerodromeReport
{ reportType :: ReportType
, reportModifiers :: ReportModifiers
, reportStation :: Aerodrome
, reportDate :: DayTime
, reportWind :: Maybe Wind
, reportVisibility :: Maybe Visibility
, reportWeather :: [Weather]
, reportClouds :: Maybe Clouds
, reportTemperature :: Int
, reportDewPoint :: Int
, reportPressure :: Pressure
, reportRemarks :: Maybe FreeText
} deriving (Eq, Show)
data CompassPointCode
= NE
| SE
| SW
| NW
| N
| E
| S
| W
deriving (Bounded, Enum, Eq, Read, Show)
data WeatherDescriptorCode
= MI
| BC
| PR
| DR
| BL
| SH
| TS
| FZ
deriving (Bounded, Enum, Eq, Read, Show)
data WeatherPhenomenonCode
= DZ
| RA
| SN
| SG
| IC
| PL
| GR
| GS
| UP
| BR
| FG
| FU
| VA
| DU
| SA
| HZ
| PY
| PO
| SQ
| FC
| SS
| DS
deriving (Bounded, Enum, Eq, Read, Show)
cavok :: AerodromeReport -> Bool
cavok m = isNothing (reportVisibility m) && null (reportWeather m) && isNothing (reportClouds m)
metar
:: (MonadFail m)
=> String -> (Int, Int, Int) -> [AerodromeReport -> m AerodromeReport] -> m AerodromeReport
metar st dt setters = defaultReport METAR st dt >>= with setters
speci
:: (MonadFail m)
=> String -> (Int, Int, Int) -> [AerodromeReport -> m AerodromeReport] -> m AerodromeReport
speci st dt setters = defaultReport SPECI st dt >>= with setters
with
:: (MonadFail m)
=> [AerodromeReport -> m AerodromeReport] -> AerodromeReport -> m AerodromeReport
with = foldl (<=<) return
withModifiers
:: (MonadFail m)
=> (Bool, Bool, Bool) -> AerodromeReport -> m AerodromeReport
withModifiers modifs report = do
let (cor, aut, mis) = modifs
return report {reportModifiers = ReportModifiers cor aut mis}
withWindDirection
:: (MonadFail m)
=> Int -> AerodromeReport -> m AerodromeReport
withWindDirection dir report = do
_dir <- mkWindDirection dir
return report {reportWind = windWithDirection _dir (reportWind report)}
withWindSpeed
:: (MonadFail m)
=> Int -> Maybe Int -> SpeedUnit -> AerodromeReport -> m AerodromeReport
withWindSpeed spd gst ut report = do
_spd <- mkWindSpeed spd ut
_gst <-
case gst of
Nothing -> return Nothing
Just s -> fmap Just (mkWindSpeed s ut)
return report {reportWind = windWithSpeed _spd _gst (reportWind report)}
withWindVariation
:: (MonadFail m)
=> Int -> Int -> AerodromeReport -> m AerodromeReport
withWindVariation lft rgt report = do
_lft <- mkWindDirection lft
_rgt <- mkWindDirection rgt
return
report
{reportWind = windWithVariation (WindVariableDirection _lft _rgt) (reportWind report)}
withPrevailingVisibility
:: (MonadFail m)
=> Int -> AerodromeReport -> m AerodromeReport
withPrevailingVisibility dst report = do
_dst <- mkVisibilityDistanceMetre dst
return report {reportVisibility = visiblityWithPrevailing _dst (reportVisibility report)}
withFaaPrevailingVisibility
:: (MonadFail m)
=> Maybe Int -> Maybe (Int, Int) -> AerodromeReport -> m AerodromeReport
withFaaPrevailingVisibility m f report = do
_dst <- mkVisibilityDistanceMile m f
return report {reportVisibility = visiblityWithPrevailing _dst (reportVisibility report)}
withLowestVisibility
:: (MonadFail m)
=> Int -> Maybe CompassPoint -> AerodromeReport -> m AerodromeReport
withLowestVisibility dst cp report = do
_dst <- mkVisibilityDistanceMetre dst
return report {reportVisibility = visiblityWithLowest _dst cp (reportVisibility report)}
withRunwayVisualRange
:: (MonadFail m)
=> String
-> Int
-> Maybe ExtremeRvr
-> Maybe VisibilityTendency
-> AerodromeReport
-> m AerodromeReport
withRunwayVisualRange rwy dst ext tdc report = do
_rwy <- mkRunwayDesignator rwy
_dst <- mkVisibilityDistanceMetre dst
return
report
{ reportVisibility =
visiblityWithRvr (RunwayVisualRange _rwy _dst ext tdc) (reportVisibility report)
}
withFaaRunwayVisualRange
:: (MonadFail m)
=> String
-> Int
-> Maybe ExtremeRvr
-> Maybe VisibilityTendency
-> AerodromeReport
-> m AerodromeReport
withFaaRunwayVisualRange rwy dst ext tdc report = do
_rwy <- mkRunwayDesignator rwy
_dst <- mkVisibilityDistanceFeet dst
return
report
{ reportVisibility =
visiblityWithRvr (RunwayVisualRange _rwy _dst ext tdc) (reportVisibility report)
}
withWeather
:: (MonadFail m)
=> Maybe WeatherQualifier
-> Maybe WeatherDescriptor
-> [WeatherPhenomenon]
-> AerodromeReport
-> m AerodromeReport
withWeather q d p report = return v
where
v = reportWithWeather (Weather q d p) report
withCloudAmount
:: (MonadFail m)
=> CloudAmountType -> Maybe Int -> Maybe CloudType -> AerodromeReport -> m AerodromeReport
withCloudAmount cat h ct report = do
ca <- mkCloudAmount cat h ct
return (reportWithCloudAmount ca report)
withObscuredSky
:: (MonadFail m)
=> Maybe Int -> AerodromeReport -> m AerodromeReport
withObscuredSky h report = do
os <- mkObscuredSky h
return (reportWithSkyCondition os report)
withNoCloudObserved
:: (MonadFail m)
=> NoCloudObserved -> AerodromeReport -> m AerodromeReport
withNoCloudObserved nco report = return (reportWithSkyCondition (NoneObserved nco) report)
withTemperature
:: (MonadFail m)
=> Int -> Int -> AerodromeReport -> m AerodromeReport
withTemperature t d report = do
_t <- mkTemperature t
_d <- mkTemperature d
return report {reportTemperature = _t, reportDewPoint = _d}
withPressure
:: (MonadFail m)
=> Int -> AerodromeReport -> m AerodromeReport
withPressure p report = do
_p <- mkPressure p Hpa
return report {reportPressure = _p}
withFaaPressure
:: (MonadFail m)
=> Int -> AerodromeReport -> m AerodromeReport
withFaaPressure p report = do
_p <- mkPressure p InchesHg
return report {reportPressure = _p}
parser :: Parser AerodromeReport
parser = do
rt <- enumeration :: Parser ReportType
cor1 <- fmap isJust (optional (try (string " COR")))
_ <- space
st <- aerodromeParser
_ <- space
dt <- dayTimeParser
_ <- char 'Z'
ms <- fmap isJust (optional (try (string " NIL")))
au <- fmap isJust (optional (try (string " AUTO")))
cor2 <- fmap isJust (optional (try (string " COR")))
_ <- space
wd <- calmParser <|> windParser
_ <- space
vwc <- vwcParser
t <- temperatureParser
_ <- slash
d <- temperatureParser
_ <- space
p <- pressureParser
let (vs, we, cl) = fromMaybe (Nothing, [], Nothing) vwc
let m = ReportModifiers (cor1 || cor2) au ms
return (AerodromeReport rt m st dt wd vs we cl t d p Nothing)
parse :: String -> Either Error AerodromeReport
parse = runParser parser
noModifiers :: ReportModifiers
noModifiers = ReportModifiers False False False
mkRunwayDesignator
:: (MonadFail m)
=> String -> m RunwayDesignator
mkRunwayDesignator s
| length s /= 2 && length s /= 3 = fail ("invalid runway designator=" ++ s)
| not (all isDigit (take 2 s)) = fail ("invalid runway designator=" ++ s)
| length s == 3 && (last s /= 'C' && last s /= 'R' && last s /= 'L') =
fail ("invalid runway designator=" ++ s)
| otherwise = return (RunwayDesignator s)
mkWindSpeed
:: (MonadFail m)
=> Int -> SpeedUnit -> m WindSpeed
mkWindSpeed s u
| s < 0 || s > 99 = fail ("invalid wind speed [" ++ show u ++ "]=" ++ show s)
| otherwise = return (WindSpeed u s)
mkWindDirection
:: (MonadFail m)
=> Int -> m Int
mkWindDirection d
| d < 0 || d > 359 = fail ("invalid wind direction [degrees]=" ++ show d)
| otherwise = return d
mkVisibilityDistanceMetre
:: (MonadFail m)
=> Int -> m VisibilityDistance
mkVisibilityDistanceMetre m
| m < 0 || m > 9999 = fail ("invalid visibility distance [metre]=" ++ show m)
| otherwise = return (VisibilityDistance Metre m Nothing)
mkVisibilityDistanceMile
:: (MonadFail m)
=> Maybe Int -> Maybe (Int, Int) -> m VisibilityDistance
mkVisibilityDistanceMile Nothing Nothing = fail "invalid visibility distance [mile]"
mkVisibilityDistanceMile m f
| maybe False (< 0) m || maybe False (> 99) m =
fail ("invalid visibility distance [mile]=" ++ show m)
| maybe False (< 0) (fmap fst f) || maybe False (> 9) (fmap fst f) =
fail ("invalid visibility distance [fraction]=" ++ show f)
| maybe False (< 0) (fmap snd f) || maybe False (> 9) (fmap snd f) =
fail ("invalid visibility distance [fraction]=" ++ show f)
| otherwise = return (VisibilityDistance Mile (fromMaybe 0 m) f)
mkVisibilityDistanceFeet
:: (MonadFail m)
=> Int -> m VisibilityDistance
mkVisibilityDistanceFeet m
| m < 0 || m > 9999 = fail ("invalid visibility distance [feet]=" ++ show m)
| otherwise = return (VisibilityDistance Foot m Nothing)
mkCloudAmount
:: (MonadFail m)
=> CloudAmountType -> Maybe Int -> Maybe CloudType -> m CloudAmount
mkCloudAmount ca Nothing ct = return (CloudAmount ca Nothing ct)
mkCloudAmount ca (Just ft) ct
| ft < 0 || ft > 999 = fail ("invalid cloud amount height [hundreds feet]=" ++ show ft)
| otherwise = return (CloudAmount ca (Just ft) ct)
mkObscuredSky
:: (MonadFail m)
=> Maybe Int -> m Clouds
mkObscuredSky Nothing = return (ObscuredSky (VerticalVisibility Nothing))
mkObscuredSky (Just ft)
| ft < 0 || ft > 999 = fail ("invalid vertical visibility [hundreds feet]=" ++ show ft)
| otherwise = return (ObscuredSky (VerticalVisibility (Just ft)))
mkTemperature :: (MonadFail m) => Int -> m Int
mkTemperature t
| t < -99 || t > 99 = fail ("invalid temperature [celsius]=" ++ show t)
| otherwise = return t
mkPressure:: (MonadFail m) => Int -> PressureUnit -> m Pressure
mkPressure p u
| p < 0 || p > 9999 = fail ("invalid pressure [" ++ show u ++ "]=" ++ show p)
| otherwise = return (Pressure u p)
isaTemperature :: Int
isaTemperature = 15
isaPression :: Pressure
isaPression = Pressure Hpa 1013
defaultReport
:: (MonadFail m)
=> ReportType -> String -> (Int, Int, Int) -> m AerodromeReport
defaultReport t st (d, h, m) = do
_st <- mkAerodrome st
_dt <- mkDayTime d h m
return
(AerodromeReport
t
noModifiers
_st
_dt
Nothing
Nothing
[]
Nothing
isaTemperature
isaTemperature
isaPression
Nothing)
windWithDirection :: Int -> Maybe Wind -> Maybe Wind
windWithDirection dir Nothing = Just (Wind (Just dir) (WindSpeed KT 0) Nothing Nothing)
windWithDirection dir (Just wd) = Just (wd {wDirection = Just dir})
windWithSpeed :: WindSpeed -> Maybe WindSpeed -> Maybe Wind -> Maybe Wind
windWithSpeed spd gst Nothing = Just (Wind Nothing spd gst Nothing)
windWithSpeed spd gst (Just wd) = Just (wd {wSpeed = spd, wGust = gst})
windWithVariation :: WindVariableDirection -> Maybe Wind -> Maybe Wind
windWithVariation var Nothing = Just (Wind Nothing (WindSpeed KT 0) Nothing (Just var))
windWithVariation var (Just wd) = Just (wd {wVariation = Just var})
visiblityWithPrevailing :: VisibilityDistance -> Maybe Visibility -> Maybe Visibility
visiblityWithPrevailing dst Nothing = Just (Visibility dst Nothing Nothing [])
visiblityWithPrevailing dst (Just v) = Just (v {vPrevailing = dst})
visiblityWithLowest :: VisibilityDistance
-> Maybe CompassPoint
-> Maybe Visibility
-> Maybe Visibility
visiblityWithLowest dst cp Nothing =
Just (Visibility (VisibilityDistance Metre 0 Nothing) (Just dst) cp [])
visiblityWithLowest dst cp (Just v) = Just (v {vLowest = Just dst, vLowestDirection = cp})
visiblityWithRvr :: RunwayVisualRange -> Maybe Visibility -> Maybe Visibility
visiblityWithRvr rvr Nothing =
Just (Visibility (VisibilityDistance Metre 0 Nothing) Nothing Nothing [rvr])
visiblityWithRvr rvr (Just v) = Just (v {vRunways = rvr : vRunways v})
reportWithWeather :: Weather -> AerodromeReport -> AerodromeReport
reportWithWeather w r = r {reportWeather = w : reportWeather r}
reportWithCloudAmount :: CloudAmount -> AerodromeReport -> AerodromeReport
reportWithCloudAmount ca r =
case reportClouds r of
(Just (CloudAmounts cur)) -> r {reportClouds = Just (CloudAmounts (ca : cur))}
_ -> r {reportClouds = Just (CloudAmounts [ca])}
reportWithSkyCondition :: Clouds -> AerodromeReport -> AerodromeReport
reportWithSkyCondition sc r = r {reportClouds = Just sc}
wdParser :: Parser Int
wdParser = natural 3 >>= mkWindDirection
variableDirectionParser :: Parser WindVariableDirection
variableDirectionParser = do
_ <- space
l <- wdParser
_ <- char 'V'
r <- wdParser
return (WindVariableDirection l r)
windDirectionParser :: Parser (Maybe Int)
windDirectionParser = do
var <- fmap isJust (optional (string "VRB"))
if var
then return Nothing
else fmap Just wdParser
calmParser :: Parser (Maybe Wind)
calmParser = do
_ <- try (string "00000")
_ <- enumeration :: Parser SpeedUnit
return Nothing
windParser :: Parser (Maybe Wind)
windParser = do
d <- windDirectionParser
s <- natural 2
g <- optional (char 'G' >> natural 2)
u <- enumeration :: Parser SpeedUnit
v <- optional (try variableDirectionParser)
return (Just (Wind d (WindSpeed u s) (fmap (WindSpeed u) g) v))
rvrParser :: Parser RunwayVisualRange
rvrParser = do
r <- string "R" >> stringTill '/' >>= mkRunwayDesignator
_o <- optional (oneOf "MP")
o <-
case _o of
Just 'M' -> return (Just Lower)
Just 'P' -> return (Just Higher)
_ -> return Nothing
_d <- natural 4
u <- optional (string "FT")
d <-
case u of
Just _ -> return (VisibilityDistance Foot _d Nothing)
_ -> return (VisibilityDistance Metre _d Nothing)
_t <- optional (oneOf "UDN")
t <-
case _t of
Just 'U' -> return (Just Up)
Just 'D' -> return (Just Down)
Just 'N' -> return (Just NoChange)
_ -> return Nothing
_ <- space
return (RunwayVisualRange r d o t)
rvrsParser :: Parser [RunwayVisualRange]
rvrsParser = many (try rvrParser)
compassPointParser :: Parser CompassPoint
compassPointParser = do
c <- enumeration :: Parser CompassPointCode
case c of
N -> return North
NE -> return NorthEast
E -> return East
SE -> return SouthEast
S -> return South
SW -> return SouthWest
W -> return West
NW -> return NorthWest
wmoVisibilityParser :: Parser (VisibilityDistance, Maybe VisibilityDistance, Maybe CompassPoint)
wmoVisibilityParser = do
v <- natural 4
_ <- space
l <- optional (try (natural 4))
d <-
case l of
Nothing -> return Nothing
Just _ -> fmap Just (compassPointParser <* space)
return
(VisibilityDistance Metre v Nothing, fmap (\lv -> VisibilityDistance Metre lv Nothing) l, d)
mileFractionParser :: Parser (Int, Int)
mileFractionParser = do
_ <- optional space
n <- natural 1
_ <- slash
d <- natural 1
_ <- string "SM"
return (n, d)
faaVisibilityParser :: Parser (VisibilityDistance, Maybe VisibilityDistance, Maybe CompassPoint)
faaVisibilityParser = do
unitOnly <- optional (try ((natural 2 <|> natural 1) <* string "SM"))
dm <-
case unitOnly of
Just _ -> mkVisibilityDistanceMile unitOnly Nothing
Nothing -> do
u <- optional (try ((natural 2 <|> natural 1) <* string " "))
f <- mileFractionParser
mkVisibilityDistanceMile u (Just f)
_ <- space
return (dm, Nothing, Nothing)
visibilityParser :: Parser Visibility
visibilityParser = do
(v, l, d) <- try wmoVisibilityParser <|> faaVisibilityParser
r <- try rvrsParser
_ <- optional space
return (Visibility v l d r)
wQualifierParser :: Parser WeatherQualifier
wQualifierParser = do
q <- choice [string "-", string "+", string "VC"]
case q of
"-" -> return LightWeather
"+" -> return HeavyWeather
_ -> return InVicinityWeather
wDescriptorParser :: Parser WeatherDescriptor
wDescriptorParser = do
d <- enumeration :: Parser WeatherDescriptorCode
case d of
MI -> return Shallow
BC -> return Patches
PR -> return Partial
DR -> return Drifting
BL -> return Blowing
SH -> return Showers
TS -> return Thunderstorm
FZ -> return Freezing
wPhenomenonParser :: Parser WeatherPhenomenon
wPhenomenonParser = do
p <- enumeration :: Parser WeatherPhenomenonCode
case p of
DZ -> return Drizzle
RA -> return Rain
SN -> return Snow
SG -> return SnowGrains
IC -> return IceCrystals
PL -> return IcePellets
GR -> return Hail
GS -> return SmallHail
UP -> return UnknownPrecipitation
BR -> return Mist
FG -> return Fog
FU -> return Smoke
VA -> return VolcanicAsh
DU -> return WidespreadDust
SA -> return Sand
HZ -> return Haze
PY -> return Spray
PO -> return Dust
SQ -> return Squall
FC -> return FunnelCloud
SS -> return Sandstorm
DS -> return DustStorm
weatherParser :: Parser Weather
weatherParser = do
q <- optional wQualifierParser
d <- optional wDescriptorParser
p <- manyTillSpace wPhenomenonParser
return (Weather q d p)
weathersParser :: Parser [Weather]
weathersParser = many (try weatherParser)
clearishSkyParser :: Parser (Maybe Clouds)
clearishSkyParser = do
key <- choice [try (string "NSC"), string "NCD", string "CLR", string "SKC"]
_ <- space
case key of
"NSC" -> return (Just (NoneObserved NoCloudBelow1500))
"NCD" -> return (Just (NoneObserved SkyClear))
"CLR" -> return (Just (NoneObserved NoCloudBelow3600))
"SKC" -> return (Just (NoneObserved SkyClear))
_ -> unexpected "cloud key"
heightParser :: Parser (Maybe Int)
heightParser = do
h <- optional (natural 3)
case h of
Nothing -> do
_ <- string "///"
return Nothing
Just _ -> return h
cloudAmountParser :: Parser CloudAmount
cloudAmountParser = do
catk <- choice [string "FEW", string "SCT", string "BKN", string "OVC"]
cat <-
case catk of
"FEW" -> return Few
"SCT" -> return Scattered
"BKN" -> return Broken
"OVC" -> return Overcast
_ -> unexpected "Cloud amount"
vv <- heightParser
ctk <- optional (choice [string "CB", string "TBU"])
ct <-
case ctk of
Just "CB" -> return (Just Cumulonimbus)
Just "TCU" -> return (Just ToweringCumulus)
_ -> return Nothing
ca <- mkCloudAmount cat vv ct
_ <- space
return ca
cloudAmountsParser :: Parser (Maybe Clouds)
cloudAmountsParser = do
cas <- some cloudAmountParser
return (Just (CloudAmounts cas))
verticalVisibilityParser :: Parser (Maybe Clouds)
verticalVisibilityParser = do
_ <- string "VV"
vv <- heightParser
os <- mkObscuredSky vv
_ <- space
return (Just os)
cloudsParser :: Parser (Maybe Clouds)
cloudsParser =
choice [clearishSkyParser, cloudAmountsParser, verticalVisibilityParser, return Nothing]
vwcParser :: Parser (Maybe (Maybe Visibility, [Weather], Maybe Clouds))
vwcParser = do
ok <- fmap isJust (optional (string "CAVOK "))
if ok
then return Nothing
else do
vs <- visibilityParser
we <- weathersParser
cs <- cloudsParser
return (Just (Just vs, we, cs))
temperatureParser :: Parser Int
temperatureParser = do
neg <- optional (char 'M')
t <- natural 2
if isJust neg then
mkTemperature (negate t)
else
mkTemperature t
pressureParser :: Parser Pressure
pressureParser = do
u <- char 'A' <|> char 'Q'
p <- natural 4
case u of
'A' -> mkPressure p InchesHg
'Q' -> mkPressure p Hpa
_ -> unexpected "pression"