{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Data.Aviation.WX(
weatherParser
, Weather(..)
, HasWeather(..)
, AsWeather(..)
, Date(..)
, HasDate(..)
, Station(..)
, Flag(..)
, HasFlag(..)
, AsFlag(..)
, Wind(..)
, HasWind(..)
, Visibility(..)
, HasVisibility(..)
, AsVisibility(..)
, Runway(..)
, HasRunway(..)
, AsRunway(..)
, VisTrend(..)
, HasVisTrend(..)
, AsVisTrend(..)
, RunwayCondition(..)
, HasRunwayCondition(..)
, AsRunwayCondition(..)
, WeatherPhenomenon(..)
, HasWeatherPhenomenon(..)
, Cloud(..)
, HasCloud(..)
, AsCloud(..)
, Pressure(..)
, HasPressure(..)
, AsPressure(..)
, Trend(..)
, HasTrend(..)
, AsTrend(..)
, WPDesc(..)
, HasWPDesc(..)
, AsWPDesc(..)
, WPPrecipitation(..)
, HasWPPrecipitation(..)
, AsWPPrecipitation(..)
, WPObfuscation(..)
, HasWPObfuscation(..)
, AsWPObfuscation(..)
, WPOther(..)
, HasWPOther(..)
, AsWPOther(..)
, Distance(..)
, HasDistance(..)
, AsDistance(..)
, Direction(..)
, HasDirection(..)
, AsDirection(..)
, RwyCoverType(..)
, HasRwyCoverType(..)
, AsRwyCoverType(..)
, RunwayBraking(..)
, HasRunwayBraking(..)
, AsRunwayBraking(..)
, Vertical(..)
, HasVertical(..)
, AsVertical(..)
, WindDirection(..)
, HasWindDirection(..)
, AsWindDirection(..)
, Cover(..)
, HasCover(..)
, AsCover(..)
, CloudType(..)
, HasCloudType(..)
, AsCloudType(..)
, WPIntensity(..)
, HasWPIntensity(..)
, AsWPIntensity(..)
, Transition(..)
, HasTransition(..)
, AsTransition(..)
, Unit(..)
, HasUnit(..)
, AsUnit(..)
, ReportType(..)
) where
import Control.Applicative(Alternative((<|>), some, many), optional)
import Control.Lens(makeClassy, makeClassyPrisms, makeWrapped)
import Control.Monad(when, void)
import Data.Maybe(isNothing, catMaybes)
import Data.Text(Text, pack)
import Text.Parser.Char(CharParsing, space, spaces, char, satisfy, text, digit, anyChar)
import Text.Parser.Combinators(try, option, choice, sepBy, sepBy1, count, unexpected)
takeChars ::
CharParsing f =>
Int
-> f Text
takeChars n =
pack <$> count n anyChar
data ReportType
= MetarReport | TafReport
deriving (Eq, Show, Enum)
data Weather
=
METAR
{
_reporttype :: ReportType
,
_metardate :: Date
,
_station :: Station
,
_flags :: [Flag]
,
_metarwind :: Maybe Wind
,
_metarvisibility :: [Visibility]
,
_runwayvis :: [(Runway, [Visibility], Maybe VisTrend)]
,
_runwaycond :: [RunwayCondition]
,
_wx :: [WeatherPhenomenon]
,
_clouds :: [Cloud]
,
_metarpressure :: Maybe Pressure
,
_temperature :: Maybe Int
,
_dewPoint :: Maybe Int
,
_weathertrend :: [Trend]
,
_remark :: Maybe Text
,
_maintenance :: Bool }
|
ATIS
|
SPECI
|
TAF
{
_reporttype :: ReportType
,
_tafissuedat :: Date
,
_flags :: [Flag]
,
_station :: Station
,
_tafvalidfrom :: Date
,
_tafvaliduntil :: Date
,
_tafinitialconditions :: [Transition]
,
_tafchanges :: [Trend] }
|
AIRMET
|
SIGMET
|
GAMET
deriving (Eq, Show)
data Flag
=
COR
|
AMD
|
AUTO
deriving (Eq, Show)
data Trend
=
BECMG
{ _becmgStart :: Maybe Date
, _becmgFinished :: Maybe Date
, _becmgTransitions :: [Transition] }
|
TEMPO
{ _tempoFrom :: Maybe Date
, _tempoTo :: Maybe Date
, _tempoTransitions :: [Transition] }
|
PROB Int Trend
|
NOSIG
|
NOTAVAIL
deriving (Eq, Show)
data Transition
=
TransWind Wind
|
TransVis [Visibility]
|
TransRunwayVis [(Runway, [Visibility], Maybe VisTrend)]
|
TransWX [WeatherPhenomenon]
|
TransClouds [Cloud]
|
TransPressure [Pressure]
deriving (Eq, Show)
data VisTrend
=
VisTrendUpward
|
VisTrendDownward
|
VisTrendNoDistinctTendency
deriving (Eq, Show)
data Pressure
=
QNH Int
|
Altimeter Int
|
QFE Int
|
QFF Int
deriving (Eq, Show)
data WeatherPhenomenon
= Phenomenon
{
_intensity :: WPIntensity
,
_desc :: Maybe WPDesc
,
_prec :: Maybe WPPrecipitation
,
_obfus :: Maybe WPObfuscation
,
_other :: Maybe WPOther }
deriving (Eq, Show)
data WPIntensity
=
Light
|
Moderate
|
Heavy
|
Vicinity
|
Recent
deriving (Enum, Eq, Show)
data WPDesc
=
Shallow
|
Patches
|
WXPartial
|
LowDrifting
|
Blowing
|
Shower
|
Thunderstorm
|
Freezing
deriving (Enum, Eq, Ord, Show)
data WPPrecipitation
=
Drizzle
|
Rain
|
Snow
|
ShowGrains
|
IceCrystals
|
IcePellets
|
Hail
|
SnowPellets
|
NoPrecipitationDetected
|
UnknownPrecipitation
deriving (Enum, Eq, Ord, Show)
data WPObfuscation
=
Mist
|
Fog
|
Smoke
|
VolcanicAsh
|
Dust
|
Sand
|
Haze
deriving (Enum, Eq, Ord, Show)
data WPOther
=
DustOrSandwhirls
|
Squalls
|
Tornado
|
Sandstorm
|
Duststorm
deriving (Enum, Eq, Ord, Show)
data Distance
=
Metres Int
|
KM Int
|
SM Int
|
NM Int
deriving (Eq, Show)
data Visibility
=
TenOrMore
|
FiftyMetresOrLess
|
TwoOrMore
|
SpecificVisibility Distance (Maybe Direction)
deriving (Eq, Show)
data Direction
=
North
|
South
|
East
|
West
|
NorthWest
|
NorthEast
|
SouthWest
|
SouthEast
|
NDV
|
RWYLeft
|
RWYRight
|
RWYCenter
deriving (Eq, Show)
data Runway
=
AllRunways
|
SpecificRunway
{
_runwayQFU :: Int
,
_runwayDirection :: Maybe Direction }
deriving (Eq, Show)
data RwyCoverType
=
RCTDry
|
RCTMoist
|
RCTWet
|
RCTRime
|
RCTDrySnow
|
RCTWetSnow
|
RCTSlush
|
RCTIce
|
RCTFZRut
|
RCTUnknown
deriving (Eq, Show, Enum)
data RunwayCondition
=
SpecificRunwayCondition
{
_rwcondRunway :: Runway
,
_rwcondCover :: RwyCoverType
,
_rwcondSpread :: Maybe Int
,
_rwcondCoverHeight :: Maybe Int
,
_rwcondBrkCoeff :: RunwayBraking }
|
RwyClosed
{
_rwclosedRunway :: Runway }
|
ADClosed
deriving (Eq, Show)
data RunwayBraking
=
BrakingFriction Int
|
BrakingEffect Int
deriving (Eq, Show)
data Date
= Date {
_dayOfMonth :: Int
, _hour :: Int
, _minute :: Int
} deriving (Eq, Show)
newtype Station
=
ICAO Text
deriving (Eq, Show)
data Vertical
=
Height Int
|
Altitude Int
|
FlightLevel Int
|
VertNotSpec
deriving (Eq, Show)
data Wind
= Wind
{
_winddirection :: Maybe WindDirection
,
_velocity :: Maybe Unit
,
_gusts :: Maybe Int
} deriving (Eq, Show)
data WindDirection
=
Variable
|
Degrees Int
|
Varying
{
_windmean :: Int
,
_windfrom :: Int
,
_windto :: Int
} deriving (Eq, Show)
data Unit
=
Knots Int
|
Miles Int
|
MPS Int
|
KMH Int
deriving (Eq, Show)
data Cloud
=
VVis (Maybe Int)
|
ObservedCloud Cover Vertical CloudType
deriving (Eq, Show)
data CloudType
=
Cumulonimbus
|
ToweringCumulus
|
Stratus
|
Cumulus
|
Stratocumulus
|
Altostratus
|
Altocumulus
|
Cirrostratus
|
Cirrus
|
Unclassified
deriving (Enum, Eq, Show)
data Cover
=
FEW
|
SCT
|
BKN
|
OVC
|
CoverNotSpecified
deriving (Enum, Eq, Ord, Show)
makeClassy ''Weather
makeClassyPrisms ''Weather
makeClassy ''Flag
makeClassyPrisms ''Flag
makeClassy ''Trend
makeClassyPrisms ''Trend
makeClassy ''Transition
makeClassyPrisms ''Transition
makeClassy ''VisTrend
makeClassyPrisms ''VisTrend
makeClassy ''Pressure
makeClassyPrisms ''Pressure
makeClassy ''WeatherPhenomenon
makeClassy ''WPIntensity
makeClassyPrisms ''WPIntensity
makeClassy ''WPDesc
makeClassyPrisms ''WPDesc
makeClassy ''WPPrecipitation
makeClassyPrisms ''WPPrecipitation
makeClassy ''WPObfuscation
makeClassyPrisms ''WPObfuscation
makeClassy ''WPOther
makeClassyPrisms ''WPOther
makeClassy ''Distance
makeClassyPrisms ''Distance
makeClassy ''Visibility
makeClassyPrisms ''Visibility
makeClassy ''Direction
makeClassyPrisms ''Direction
makeClassy ''Runway
makeClassyPrisms ''Runway
makeClassy ''RwyCoverType
makeClassyPrisms ''RwyCoverType
makeClassy ''RunwayCondition
makeClassyPrisms ''RunwayCondition
makeClassy ''RunwayBraking
makeClassyPrisms ''RunwayBraking
makeClassy ''Date
makeWrapped ''Station
makeClassy ''Vertical
makeClassyPrisms ''Vertical
makeClassy ''Wind
makeClassy ''WindDirection
makeClassyPrisms ''WindDirection
makeClassy ''Unit
makeClassyPrisms ''Unit
makeClassy ''Cloud
makeClassyPrisms ''Cloud
makeClassy ''CloudType
makeClassyPrisms ''CloudType
makeClassy ''Cover
makeClassyPrisms ''Cover
instance HasWPIntensity WeatherPhenomenon where
wPIntensity =
intensity . wPIntensity
stationParser :: CharParsing f => f Station
stationParser = ICAO <$> takeChars 4
dateParser :: CharParsing f => f Date
dateParser = Date <$> twin <*> twin <*> (twin <* text "Z")
where twin = (\a b -> read [a, b]) <$> digit <*> digit
dateParserSansZulu :: CharParsing f => f Date
dateParserSansZulu = Date <$> twin <*> twin <*> twin
where twin = (\a b -> read [a, b]) <$> digit <*> digit
briefDateParser :: CharParsing f => f Date
briefDateParser = Date <$> twin <*> twin <*> pure 0
where twin = (\a b -> read [a, b]) <$> digit <*> digit
variableWindParser :: (Monad f, CharParsing f) => Maybe WindDirection -> f WindDirection
variableWindParser (Just (Degrees meanWind)) = try $ do
dir1 <- (\a b c -> read [a, b, c]) <$> digit <*> digit <*> digit
_ <- char 'V'
dir2 <- (\a b c -> read [a, b, c]) <$> digit <*> digit <*> digit
return $ Varying meanWind dir1 dir2
variableWindParser _ = unexpected "Erroneous parameters"
windParser :: (Monad f, CharParsing f) => f Wind
windParser = do
dir <- choice [Just <$> readwinddir, Just <$> variablewind, text "///" >> return Nothing]
str <- choice [Just <$> readwindstr, text "//" >> return Nothing]
gustsies <- option Nothing readgusts
unit' <- readunit
dir2 <- option dir (Just <$> (char ' ' >> variableWindParser dir))
return $ Wind dir2 (unit' <$> str) gustsies
where
variablewind = "VRB" `means` Variable
readwinddir = (\a b c -> Degrees . read $ [a, b, c]) <$> digit <*> digit <*> digit
readwindstr = (\a b -> read [a, b]) <$> digit <*> digit
readunit = choice [ "KT" `means` Knots
, "MPH" `means` Miles
, "MPS" `means` MPS
, "KM" `means` KMH]
readgusts = (\_ b c -> Just . read $ [b, c]) <$> char 'G' <*> digit <*> digit
pressureParser :: CharParsing f => f Pressure
pressureParser = choice [qnha, mmhg, qnh]
where
qnh = (\_ a b c d -> QNH $ read [a, b, c, d]) <$> char 'Q' <*> digit <*> digit <*> digit <*> digit
qnha = (\_ a b c d _ -> Altimeter $ read [a, b, c, d]) <$> text "QNH" <*> digit <*> digit <*> digit <*> digit <*> text "INS"
mmhg = (\_ a b c d -> Altimeter $ read [a, b, c, d]) <$> char 'A' <*> digit <*> digit <*> digit <*> digit
wxParser :: (Monad f, CharParsing f) => f WeatherPhenomenon
wxParser = do
spaces
intsy <- intensityParser
dsc <- perhaps descParser
prc <- perhaps precipitationParser
obfs <- perhaps obfuscationParser
othr <- perhaps otherParser
when ( (== 0) . Prelude.length . Prelude.filter not $
[ isNothing dsc, isNothing prc
, isNothing obfs, isNothing othr ] ) $ unexpected ""
return $ Phenomenon intsy dsc prc obfs othr
perhaps :: Alternative m => m a -> m (Maybe a)
perhaps parser = option Nothing $ Just <$> parser
perhaps_ :: Alternative f => f a -> f ()
perhaps_ parser = void $ perhaps parser
callsfor :: (CharParsing m, Monad m) => Text -> m b -> m b
a `callsfor` b = text a >> b
means :: (CharParsing m, Monad m) => Text -> b -> m b
a `means` b = text a >> return b
means' :: (CharParsing m, Monad m) => Text -> a -> m a
a `means'` b = try $ spaces >> text a >> spaces >> return b
descParser :: (Monad f, CharParsing f) => f WPDesc
descParser = choice
[ "MI" `means` Shallow
, "BC" `means` Patches
, "PR" `means` WXPartial
, "DR" `means` LowDrifting
, "BL" `means` Blowing
, "SH" `means` Shower
, "TS" `means` Thunderstorm
, "FZ" `means` Freezing ]
precipitationParser :: (Monad f, CharParsing f) => f WPPrecipitation
precipitationParser = choice
[ "DZ" `means` Drizzle
, "RA" `means` Rain
, "SN" `means` Snow
, "SG" `means` ShowGrains
, "IC" `means` IceCrystals
, "PL" `means` IcePellets
, "GR" `means` Hail
, "GS" `means` SnowPellets
, "// " `means` NoPrecipitationDetected
, "UP" `means` UnknownPrecipitation ]
obfuscationParser :: (Monad f, CharParsing f) => f WPObfuscation
obfuscationParser = choice
[ "BR" `means` Mist
, "FG" `means` Fog
, "FU" `means` Smoke
, "VA" `means` VolcanicAsh
, "DU" `means` Dust
, "SA" `means` Sand
, "HZ" `means` Haze ]
otherParser :: (Monad f, CharParsing f) => f WPOther
otherParser = choice
[ "PO" `means` DustOrSandwhirls
, "SQ" `means` Squalls
, "FC" `means` Tornado
, "SS" `means` Sandstorm
, "DS" `means` Duststorm ]
intensityParser :: (Monad f, CharParsing f) => f WPIntensity
intensityParser = option Moderate $ choice
[ char '-' >> return Light
, char '+' >> return Heavy
, "VC" `means` Vicinity
, "RE" `means` Recent ]
visibilityParser :: (Monad f, CharParsing f) => f Visibility
visibilityParser = spaces >> choice [ tenormorendv, tenormore, sixmilesormore, arb, arb1, metres ]
where
tenormorendv = text "9999NDV" >> return TenOrMore
tenormore = text "9999" >> return TenOrMore
sixmilesormore = text "P6SM" >> return TenOrMore
metres = (\a b c d dir -> SpecificVisibility (visunit $ read [a,b,c,d]) dir) <$> digit <*> digit <*> digit <*> digit <*> directionParser
visunit :: Int -> Distance
visunit n = if n > 5000
then KM (n `quot` 1000)
else Metres n
arb = (\a b unit' -> SpecificVisibility (unit' $ read [a,b])) <$> digit <*> digit <*> distanceUnitParser <*> directionParser
arb1 = (\a unit' -> SpecificVisibility (unit' $ read ['0', a])) <$> digit <*> distanceUnitParser <*> directionParser
directionParser :: (Monad f, CharParsing f) => f (Maybe Direction)
directionParser = Nothing `option` (Just <$> choice
[ "NE" `means` NorthEast, "NW" `means` NorthWest
, "SE" `means` SouthEast, "SW" `means` SouthWest
, "NDV" `means` NDV
, "N" `means` North, "S" `means` South
, "E" `means` East, "W" `means` West ])
distanceUnitParser :: (Monad f, CharParsing f) => f (Int -> Distance)
distanceUnitParser = choice
[ "KM" `means` KM
, "SM" `means` SM
, "NM" `means` NM ]
cloudParser :: (Monad f, CharParsing f) => f [Cloud]
cloudParser = choice [ (:[]) <$> vvisParser, cavok
, catMaybes <$> sepBy1 (choice [ Just <$> clds, noclouds ]) (char ' ')]
where
clds = do
perhaps_ space
intsy <- cloudIntensityParser
height <- choice
[ "///" `means` VertNotSpec
, (\a b c -> Height $ (* 100) $ read [a, b, c]) <$> digit <*> digit <*> digit ]
cloudType' <- cloudTypeParser
return $ ObservedCloud intsy height cloudType'
cavok = spaces >> "CAVOK" `means` []
noclouds = choice [ clr, nsc
, ncd, skc, nsw, nowx, ncd2 ]
nsc = "NSC " `means` Nothing
clr = "CLR " `means` Nothing
skc = "SKC " `means` Nothing
nsw = "NSW " `means` Nothing
ncd = "NCD " `means` Nothing
nowx = "// " `means` Nothing
ncd2 = "////// " `means` Nothing
vvisParser :: (Monad f, CharParsing f) => f Cloud
vvisParser = do
_ <- text "VV"
choice
[ "///" `means` VVis Nothing
, (\a b c -> VVis . Just . read $ [a,b,c]) <$> digit <*> digit <*> digit ]
cloudIntensityParser :: (Monad f, CharParsing f) => f Cover
cloudIntensityParser = choice
[ "FEW" `means` FEW
, "SCT" `means` SCT
, "BKN" `means` BKN
, "OVC" `means` OVC
, "///" `means` CoverNotSpecified ]
cloudTypeParser :: (Monad f, CharParsing f) => f CloudType
cloudTypeParser = option Unclassified $ choice
[ "CB" `means` Cumulonimbus
, "TCU" `means` ToweringCumulus
, "ST" `means` Stratus
, "CU" `means` Cumulus
, "SC" `means` Stratocumulus
, "AS" `means` Altostratus
, "AC" `means` Altocumulus
, "CS" `means` Cirrostratus
, "CI" `means` Cirrus
, "///" `means` Unclassified]
perhapsMinus :: (Monad f, CharParsing f) => f String
perhapsMinus = "" `option` (char 'M' >> return "-")
tdParser :: (Monad f, CharParsing f) => f (Maybe Int, Maybe Int)
tdParser = do
tmpr <- choice
[ text "//" >> return Nothing
, Just <$> tmpParser ]
_ <- char '/'
dewpoint <- choice
[ text "//" >> return Nothing
, Just <$> tmpParser ]
return (tmpr, dewpoint)
where
tmpParser = (\pm a b -> read (pm ++ [a, b]) :: Int) <$> perhapsMinus <*> digit <*> digit
flagsParser :: (Monad f, CharParsing f) => f [Flag]
flagsParser = many $ choice
[ "COR" `means'` COR
, "AMD" `means'` AMD
, "AUTO" `means'` AUTO ]
runwayvisParser :: (Monad f, CharParsing f) => f (Runway, [Visibility], Maybe VisTrend)
runwayvisParser = do
runway' <- runwayDesignationParser
_ <- char '/'
vis <- parseRwyVis
vistrend <- Nothing `option` (Just <$> choice
[ "D" `means` VisTrendDownward
, "N" `means` VisTrendNoDistinctTendency
, "U" `means` VisTrendUpward ] )
return (runway', vis, vistrend)
where
parseRwyVis = do
worstvis <- Nothing `option` (Just <$> choice visspec <* text "V")
vis <- Just <$> choice visspec
return $ catMaybes [worstvis, vis]
visspec =
[ "M0050" `means` FiftyMetresOrLess
, "P2000" `means` TwoOrMore
, fourDigits >>= \a -> return $ SpecificVisibility (Metres a) Nothing
, trieDigits >>= \a -> return $ SpecificVisibility (Metres a) Nothing ]
runwayconditionParser :: (Monad f, CharParsing f) => f RunwayCondition
runwayconditionParser = do
runway' <- runwayDesignationParser
_ <- char '/'
choice
[ "SNOCLO" `means` ADClosed
, rwycond runway' ]
where
rwycond runway' = do
cover' <- RCTUnknown `option` ((toEnum . read . (:[])) <$> digit)
spread <- choice
[ char '/' >> return Nothing
, (Just . read . (:[])) <$> digit ]
spreadheight <- choice
[ text "//" >> return Nothing
, Just <$> tuhDigits ]
rkorbw <- tuhDigits
let coff = if rkorbw <= 90
then BrakingFriction rkorbw
else BrakingEffect rkorbw
return $ SpecificRunwayCondition runway' cover' spread spreadheight coff
fourDigits :: CharParsing f => f Int
fourDigits = (\a b c d -> read [a,b,c,d]) <$> digit <*> digit <*> digit <*> digit
trieDigits :: CharParsing f => f Int
trieDigits = (\a b c -> read [a,b,c]) <$> digit <*> digit <*> digit
tuhDigits :: CharParsing f => f Int
tuhDigits = (\a b -> read [a,b]) <$> digit <*> digit
runwayDesignationParser :: (Monad f, CharParsing f) => f Runway
runwayDesignationParser = choice ["R88" `means` AllRunways, oneRunway]
where
oneRunway = do
_ <- char 'R'
magheading <- (\a b -> read [a,b]) <$> digit <*> digit
dir <- Nothing `option` (Just <$> choice
[ "L" `means` RWYLeft
, "R" `means` RWYRight
, "C" `means` RWYCenter ])
return $ SpecificRunway magheading dir
trendParser :: (Monad f, CharParsing f) => f [Trend]
trendParser = choice
[ "NOSIG" `means` [NOSIG]
, changesParser ]
changesParser :: (Monad f, CharParsing f) => f [Trend]
changesParser = some $ spaces >> transitionTypeParser
where
transitionTypeParser = choice
[ "TEMPO" `callsfor` (TEMPO <$> parseFrom <*> parseTo <*> transitionParser)
, "BECMG" `callsfor` (BECMG <$> parseFrom <*> parseTo <*> transitionParser)
, "FM" `callsfor` (BECMG <$> parseFromFM <*> pure Nothing <*> transitionParser)
, "PROB" `callsfor` (PROB <$> twoDigits <*> (head <$> changesParser)) ]
transitionParser = sepBy1 oneTransition (char ' ')
parseFromFM = do
fromDate <- dateParserSansZulu
void $ text " "
return $ Just fromDate
parseFrom = Nothing `option` do
spaces
fromDate <- briefDateParser
void $ text "/"
return $ Just fromDate
parseTo = Nothing `option` (Just <$> briefDateParser)
oneTransition = do
spaces
choice . map try $
[ TransClouds <$> cloudParser
, TransWind <$> windParser
, TransVis <$> some visibilityParser
, TransWX <$> many wxParser
, TransPressure . (:[]) <$> pressureParser
, TransRunwayVis <$> sepBy runwayvisParser (char ' ') ]
twoDigits :: CharParsing f => f Int
twoDigits = (\a b -> read [a,b]) <$> digit <*> digit
tafParser :: (Monad f, CharParsing f) => f Weather
tafParser = do
_ <- text "TAF"
tafflags <- flagsParser
identifier <- spaces >> stationParser
issuedate <- spaces >> dateParser
validFrom <- spaces >> briefDateParser
validTo <- text "/" >> briefDateParser
predictedWind <- Nothing `option` (spaces >> Just <$> windParser)
spaces
predictedVisibility <- [TenOrMore] `option` some visibilityParser
spaces
predictedRunwayvis <- sepBy runwayvisParser (char ' ')
predictedWx <- many wxParser
predictedClouds <- [] `option` (spaces >> cloudParser)
predictedQnh <- many $ spaces >> pressureParser
let initialConditions = catMaybes
[ TransWind <$> predictedWind
, Just $ TransVis predictedVisibility
, Just $ TransRunwayVis predictedRunwayvis
, Just $ TransWX predictedWx
, Just $ TransClouds predictedClouds
, Just $ TransPressure predictedQnh ]
changes <- [] `option` changesParser
return TAF
{ _reporttype=TafReport
, _tafissuedat=issuedate
, _flags=tafflags
, _station=identifier
, _tafvalidfrom=validFrom
, _tafvaliduntil=validTo
, _tafinitialconditions=initialConditions
, _tafchanges=changes}
metarParser :: (Monad f, CharParsing f) => f Weather
metarParser = do
_ <- text "METAR"
reportflags <- flagsParser
identifier <- spaces >> stationParser
reportdate <- spaces >> dateParser
reportflags2 <- flagsParser
reportwind <- Nothing `option` (spaces >> Just <$> windParser)
spaces
reportvis <- [TenOrMore] `option` some visibilityParser
spaces
reportrunwaycond <- sepBy runwayconditionParser (char ' ')
reportrunwayvis <- sepBy runwayvisParser (char ' ')
reportwx <- many wxParser
reportclouds <- [] `option` (spaces >> cloudParser)
(reporttemp, reportdewpoint) <- (Nothing, Nothing) `option` (spaces >> tdParser)
reportpressure <- Nothing `option` (spaces >> Just <$> pressureParser)
void $ many $ spaces >> pressureParser
spaces
reporttrend <- [] `option` trendParser
reportrmk <- maybeRMK
spaces
maintenance' <- or <$> optional (True <$ char '$' <|> False <$ char '=')
return $ METAR MetarReport reportdate identifier (reportflags ++ reportflags2)
reportwind reportvis reportrunwayvis reportrunwaycond reportwx
reportclouds reportpressure reporttemp reportdewpoint
reporttrend reportrmk maintenance'
maybeRMK :: (Monad f, CharParsing f) => f (Maybe Text)
maybeRMK = Nothing `option` do
void $ choice [ text "RMK ", text " RMK " ]
Just . pack <$> some (satisfy (`notElem` ("$=" :: String)))
weatherParser :: (Monad f, CharParsing f) => f Weather
weatherParser = choice [ metarParser, tafParser ]