Skip to content

Instantly share code, notes, and snippets.

@hideshi
Created December 29, 2013 07:59
Show Gist options
  • Save hideshi/8168465 to your computer and use it in GitHub Desktop.
Save hideshi/8168465 to your computer and use it in GitHub Desktop.
This is a parser for SIGMET which is a format for weather information. See also: http://d.hatena.ne.jp/hideshi_o/20130105/1357367954
abstract sealed class Bulletin
case class SigmetBulletin (
header:Header
,firstLine:FirstLine
,mainBody:MainBody
) extends Bulletin
case class Header(
identificationMessage:String = ""
,icao:String = ""
,dateTimeGroup:String = ""
,correction:String = ""
) extends Bulletin
case class FirstLine(
icaoLocationIndicatorOfATS:String
,messageIdentifier:String
,dailySequenceNumber:String
,validityIndicator:String
,beginAndEndDateTimeGroup:String
,icaoLocationIndicatorOfMWO:String
) extends Bulletin
case class MainBody(
firuircta:FIRUIRCTA
,phenomenon:String
,obsfcst:OBSFCST
,locationOfThePhenomenon:String
,flightLevelAndExtent:FlightLevel
,movement:Movement
,expectedChanges:String
) extends Bulletin
case class FIRUIRCTA (
icao:String
,name:String
,kind:String
) extends Bulletin
case class OBSFCST (
observedOrFcst:String
,optionalTimeGroup:String = ""
) extends Bulletin
case class FlightLevel (
indication:String = ""
,level:String = ""
) extends Bulletin
case class Movement (
indication:String = ""
,direction:String = ""
,speed:String = ""
) extends Bulletin
import scala.util.parsing.combinator.RegexParsers
class SigmetParser extends RegexParsers {
def parse(data:String) = parseAll(all, data)
def all:Parser[Any] = {
opt(header)~firstline~mainBody ^^ {
case Some(header)~firstLine~mainBody =>
SigmetBulletin(header, firstLine, mainBody)
case None~firstLine~mainBody =>
SigmetBulletin(Header(), firstLine, mainBody)
}
}
def header:Parser[Header] = {
("(WC|WS|WV)[A-Z]{2}[0-9]{2}".r)~("[A-Z]{4}".r)~("[0-9]{6}".r)~("(CC[AB])?".r) ^^ {
case identificationMessage~icao~dateTimeGroup~correction =>
Header(identificationMessage,icao,dateTimeGroup,correction)
}
}
def firstline:Parser[FirstLine] = {
("[A-Z]{4}".r)~"SIGMET"~("(([A-Z]{1,2})?[0-9]?[0-9])".r)~"VALID"~("[0-9]{6}/[0-9]{6}".r)~("[A-Z]{4}-".r) ^^ {
case icaoLocationIndicatorOfATS~messageIdentifier~dailySequenceNumber~validityIndicator~beginAndEndDateTimeGroup~icaoLocationIndicatorOfMWO =>
FirstLine(icaoLocationIndicatorOfATS,messageIdentifier,dailySequenceNumber,validityIndicator,beginAndEndDateTimeGroup,icaoLocationIndicatorOfMWO)
}
}
def mainBody:Parser[MainBody] = {
firuircta~phenomenon~obsfcst~location~opt(flightLevel)~opt(movement)~opt(expectedChanges)<~(""".*""".r) ^^ {
case firuircta~phenomenon~obsfcst~location~Some(flightLevel)~Some(movement)~Some(expectedChanges) =>
MainBody(firuircta, phenomenon, obsfcst, location, flightLevel, movement, expectedChanges)
case firuircta~phenomenon~obsfcst~location~Some(flightLevel)~None~Some(expectedChanges) =>
MainBody(firuircta, phenomenon, obsfcst, location, flightLevel, Movement(), expectedChanges)
case firuircta~phenomenon~obsfcst~location~Some(flightLevel)~Some(movement)~None =>
MainBody(firuircta, phenomenon, obsfcst, location, flightLevel, movement, "")
case firuircta~phenomenon~obsfcst~location~Some(flightLevel)~None~None =>
MainBody(firuircta, phenomenon, obsfcst, location, flightLevel, Movement(), "")
case firuircta~phenomenon~obsfcst~location~None~Some(movement)~Some(expectedChanges) =>
MainBody(firuircta, phenomenon, obsfcst, location, FlightLevel(), movement, expectedChanges)
case firuircta~phenomenon~obsfcst~location~None~None~Some(expectedChanges) =>
MainBody(firuircta, phenomenon, obsfcst, location, FlightLevel(), Movement(), expectedChanges)
case firuircta~phenomenon~obsfcst~location~None~Some(movement)~None =>
MainBody(firuircta, phenomenon, obsfcst, location, FlightLevel(), movement, "")
case firuircta~phenomenon~obsfcst~location~None~None~None =>
MainBody(firuircta, phenomenon, obsfcst, location, FlightLevel(), Movement(), "")
}
}
def firuircta:Parser[FIRUIRCTA] = {
("""[A-Z]{4}""".r)~("""[A-Z\s]+?(FIR(/UIR)?|UIR|CTA)""".r) ^^ {
case icao~name =>
val xs = name.split("""\s""")
FIRUIRCTA(icao, xs.init.reduceLeft{_ + " " + _}, xs.last)
}
}
def phenomenon:Parser[String] = {
"""(OBSC|EMBD|FRQ|SQL|SEV|FZRA|HVY|VA CLD|RDOACT CLD)\s?(TSGR|TS|TURB|ICE|MTW)?""".r
}
def obsfcst:Parser[OBSFCST] = {
("""(OBS|FCST)""".r)~opt("""AT [0-9]{4,6}Z""".r) ^^ {
case observedOrFcst~Some(optionalTimeGroup) => OBSFCST(observedOrFcst, optionalTimeGroup)
case observedOrFcst~None => OBSFCST(observedOrFcst)
}
}
def location:Parser[String] = {
rep("""([NSEW]{1,3}\s|(WI(THIN)?\s)?(AREA\s)?([0-9]{2}\s?NM\s)?([NSEW]{1,3}\s)?)?(OF\s)?((LINE|ETNA)\s)?\(?([NSEW][0-9]{2,5}|-|AND)\)?""".r) ^^ {
case (xs:List[String]) if !(xs.isEmpty) =>
println(xs)
xs.reduceLeft{_ + " " + _}
case _ => ""
}
}
def flightLevel:Parser[FlightLevel] = {
("""(TOPS?\s)?(TOPS?\s|ABV\s|BLW\s|SFC\/)?FL[0-9]{3}((\/|-)[0-9]{3})?""".r) ^^ {
case flightLevel =>
val indication = flightLevel.split("FL")(0) + "FL"
val level = flightLevel.split("FL")(1)
FlightLevel(indication, level)
}
}
def movement:Parser[Movement] = {
("""MOV"""~("""[NSEW]{1,3}""".r)~opt("""[0-9]{2}\s?(KMH|KT)""".r) | """STNR""") ^^ {
case indication~direction~Some(speed) => Movement(indication.toString, direction.toString, speed.toString)
case indication~direction~None => Movement(indication.toString, direction.toString)
case indication => Movement(indication.toString)
}
}
def expectedChanges:Parser[String] = {
("""(INTSF|WKN|NC)""".r)<~("""\s?=""".r)
}
}
val bulletins = List(
"WSTH31 VTBS 121200 CCA VHHK SIGMET 4 VALID 031200/031600 VHHH- VHHK HONG KONG FIR EMBD TS FCST S OF N20 E OF E112 TOP FL360 STNR NC="
,"WSSR20 WSSS 091131 WSJC SIGMET 3 VALID 091140/091540 WSSS- WSJC SINGAPORE FIR EMBD TS OBS AT 1130Z N OF N01 E OF E106 W OF E114 STNR NC="
,"WSNT03 KKCI 032340 KZNY SIGMET C17 VALID 032345/040345 KKCI- KZNY NEW YORK OCEANIC FIR FRQ TS OBS WI AREA N2400 W05500 - N2300 W04930 - N1845 W05645 - N2100 W05800 - N2400 W05500 TOP FL450 MOV E 15KT INTSF="
,"WSVS31 VVGL 122305 VVTS SIGMET 9 VALID 122330/130230 VVGL- VVTS HOCHIMINH FIR EMBD TS OBS S OF LINE N1420 E10930 - N1000 E10400 TOP FL280 MOV W 10KMH WKN="
,"WSUK31 EGGY 121120 EGTT SIGMET 01 VALID 121125/121525 EGRR- EGTT LONDON FIR EMBD TSGR OBS AT 1115Z SE OF LINE N5130 E00200 - N5000 W00400 TOPS FL220 MOV NE 30KT NC="
,"WSAU21 AMMC 280546 YBBB SIGMET BS02 VALID 280600/281000 YMMC- YBBB BRISBANE FIR SEV TURB FCST AT 0600Z WI S3900 E15100 - S4300 E15100 - S4300 E16000 - S4100 E16300 - S3700 E16300 - S3900 E16000 FL260/370 MOV E 20 KT NC="
,"EDUU SIGMET 1 VALID 050200/050400 EDZF- EDUU RHEIN UIR SEV TURB FCST NE OF LINE N5415 E01130 - N5320 E01425 FL245/350 MOV NE NC="
,"EGTT SIGMET 04 VALID 042300/050300 EGRR- EGTT LONDON FIR SEV TURB FCST S OF LINE N5000 W00800 - N5030 W00700 - N2030 W00500 - N4930 W00400 BLW FL060 MOV E 45KT INTSF="
,"EGTT SIGMET 01 VALID 050300/050700 EGRR- EGTT LONDON FIR SEV TURB OBS S OF LINE N4900 W00800 - N5000 W00400 - N5100 W00300 - N5200 W00100 - N5130 E00100 - N5100 E00130 BLW FL060 MOV ENE 45KT NC="
,"EKDK SIGMET 1 VALID 050025/050425 EKMI- EKDK COPENHAGEN FIR SEV TURB FCST AT 0020Z WITHIN 40NM OF LINE N57 E00609 - N5606 E01041 - N5501 E01247 FL240-390 MOV NE 15KT NC="
,"LFFF SIGMET 3 VALID 050100/050400 LFPW- LFFF PARIS FIR/UIR SEV TURB FCST N OF N4930 AND W OF E00000 SFC/FL050 MOV E 30KT INTSF="
,"LFRR SIGMET 1 VALID 050100/050400 LFPW- LFRR BREST FIR/UIR SEV TURB FCST WI N4930 W00415 - N5000 W00200 - N5000 W00015 - N4930 W00015 - N4845 W00400 SFC/FL050 MOV E 30KT INTSF ="
,"LIRR SIGMET 01 VALID 050130/050530 LIMM- LIRR ROMA FIR VA CLD OBS AT 040430Z WI 15 NM ESE OF ETNA (N3745 E1500) FL090/130 MOV SE 15 KT="
)
val parser = new SigmetParser
bulletins.map(b => (b, parser.parse(b)))./*filter(r => r.successful).*/foreach{x =>
println()
println(x._1)
println(x._2)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment