Skip to content

Instantly share code, notes, and snippets.

@LunaticMuch
Created February 13, 2022 20:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LunaticMuch/275e10e11087302467d7f2faf5263944 to your computer and use it in GitHub Desktop.
Save LunaticMuch/275e10e11087302467d7f2faf5263944 to your computer and use it in GitHub Desktop.
Decode metar weather string
import Foundation
extension String {
func index(from: Int) -> Index {
return self.index(startIndex, offsetBy: from)
}
func substring(from: Int) -> String {
let fromIndex = index(from: from)
return String(self[fromIndex...])
}
func substring(to: Int) -> String {
let toIndex = index(from: to)
return String(self[..<toIndex])
}
func substring(with r: Range<Int>) -> String {
let startIndex = index(from: r.lowerBound)
let endIndex = index(from: r.upperBound)
return String(self[startIndex..<endIndex])
}
}
// An example of a full length metar
let fullMetar: String = "FM2000 30015G25KT 1SM SHRA OVC15CB PROB40 2022 1/2SM TSRA +FZFG OVC008CB -RA RERA"
// The pattern extracts only the weather part, ie. winds, clouds, etc are discarded
let pattern = #"[+-]?\S{0,2}(?:RA|SN|SG|IC|PL|GR|GS|UP|FG|FU|VA|DU|SA|HZ|PY|SQ|FC|SS|SS)"#
let regex = try NSRegularExpression(pattern: pattern, options: [])
let regularExpression = try! NSRegularExpression(pattern: pattern, options: [])
let matchesArray = regularExpression.matches(in: fullMetar, options: [], range: NSRange(location: 0, length: fullMetar.utf16.count))
for match in matchesArray {
for i in 0..<match.numberOfRanges {
let range = match.range(at: i)
if range.location == NSNotFound {continue}
print(readWeather((fullMetar as NSString).substring(with: range)))
}
}
// The function parses the weather string which can be either 2,3,4 or five chars
// It composes a strict just adding each decoded part
func readWeather (_ codedMetar:String) -> String {
var decoded: String = ""
switch codedMetar.count {
case 5: decoded += decodeWeather(codedMetar.substring(with: 0..<1)) + " "
decoded += decodeWeather(codedMetar.substring(with: 1..<3)) + " "
decoded += decodeWeather(codedMetar.substring(with: 3..<5))
case 4: decoded += decodeWeather(codedMetar.substring(with: 0..<2)) + " "
decoded += decodeWeather(codedMetar.substring(with: 2..<4))
case 3:
decoded += decodeWeather(codedMetar.substring(with: 0..<1)) + " "
decoded += decodeWeather(codedMetar.substring(with: 1..<3))
case 2:
decoded += decodeWeather(codedMetar.substring(with: 0..<2))
default: decoded = ""
}
return decoded
}
// The decoder of weather codes
func decodeWeather (_ code:String) -> String {
switch code {
case "VC": return "In the vicinity"
case "MI": return "Shallow"
case "PR": return "Partial"
case "BC": return "Patches"
case "DR": return "Low Drifting"
case "BL": return "Blowing"
case "SH": return "Shower"
case "TS": return "Thunderstorm"
case "FZ": return "Freezing"
case "DZ": return "Drizzle"
case "RA": return "Rain"
case "SN": return "Snow"
case "SG": return "Snow Grains"
case "IC": return "Ice Crystals"
case "PL": return "Ice Pellets"
case "GR": return "Hail"
case "GS": return "Small Hail"
case "UP": return "Unknown Precipitation"
case "BR": return "Mist"
case "FG": return "Fog"
case "FU": return "Smoke"
case "VA": return "Volcanic Ash"
case "DU": return "Widespread Dust"
case "SA": return "Sand"
case "HZ": return "Haze"
case "PY": return "Spray"
case "PO": return "Well- Developed Dust/Sand Whirls"
case "SQ": return "Squalls"
case "FC": return "Funnel Cloud Tornado Waterspout"
case "SS": return "Sandstorm"
case "SS": return "Duststorm"
case "RE": return "Recent"
case "+": return "Heavy"
case "-": return "Light"
default: return ""
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment