Remove abbreviations to make decoded reports easier to read

This commit is contained in:
Elara 2023-08-22 17:34:26 -07:00
parent 6116c1d9b7
commit 35c5c70b25
5 changed files with 250 additions and 121 deletions

154
convert.go Normal file
View File

@ -0,0 +1,154 @@
package taf
func convertSkyConditionType(s string) SkyConditionType {
switch s {
case "FEW":
return Few
case "SCT":
return Scattered
case "BKN":
return Broken
case "OVC":
return Overcast
case "VV":
return VerticalVisibility
case "SKC":
return SkyClear
default:
return ""
}
}
func convertCloudType(s string) CloudType {
switch s {
case "CB":
return CumuloNimbus
case "TCU":
return ToweringCumulus
default:
return ""
}
}
func convertModifier(s string) Modifier {
switch s {
case "+":
return Heavy
case "-":
return Light
default:
return ""
}
}
func convertDescriptor(s string) Descriptor {
switch s {
case "MI":
return Shallow
case "BC":
return Patches
case "DC":
return LowDrifting
case "BL":
return Blowing
case "SH":
return Showers
case "TS":
return Thunderstorm
case "FZ":
return Freezing
case "PR":
return Partial
default:
return ""
}
}
func convertPrecipitation(s string) Precipitation {
switch s {
case "DZ":
return Drizzle
case "RA":
return Rain
case "SN":
return Snow
case "SG":
return SnowGrains
case "IC":
return IceCrystals
case "PL":
return IcePellets
case "GR":
return Hail
case "GS":
return SmallHail
case "UP":
return Unknown
default:
return ""
}
}
func convertObscuration(s string) Obscuration {
switch s {
case "BR":
return Mist
case "FG":
return Fog
case "FU":
return Smoke
case "DU":
return Dust
case "SA":
return Sand
case "HZ":
return Haze
case "PY":
return Spray
case "VA":
return VolcanicAsh
default:
return ""
}
}
func convertPhenomenon(s string) Phenomenon {
switch s {
case "PO":
return Whirls
case "SQ":
return Squalls
case "FC":
return FunnelCloud
case "SS":
return Sandstorm
case "DS":
return Duststorm
default:
return ""
}
}
func convertTemperatureType(s string) TemperatureType {
switch s {
case "TX":
return High
case "TN":
return Low
default:
return ""
}
}
func convertChangeType(s string) ChangeType {
switch s {
case "FM":
return From
case "BECMG":
return Becoming
case "TEMPO":
return Temporary
default:
return ""
}
}

34
taf.go
View File

@ -94,17 +94,17 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
setField(out, "Valid", vp) setField(out, "Valid", vp)
case item.Weather != nil: case item.Weather != nil:
appendField(out, "Weather", Weather{ appendField(out, "Weather", Weather{
Modifier: Modifier(item.Weather.Modifier), Modifier: convertModifier(item.Weather.Modifier),
Descriptor: Descriptor(item.Weather.Descriptor), Descriptor: convertDescriptor(item.Weather.Descriptor),
Precipitation: Precipitation(item.Weather.Precipitation), Precipitation: convertPrecipitation(item.Weather.Precipitation),
Obscuration: Obscuration(item.Weather.Obscuration), Obscuration: convertObscuration(item.Weather.Obscuration),
Phenomenon: Phenomenon(item.Weather.Other), Phenomenon: convertPhenomenon(item.Weather.Other),
}) })
case item.Vicinity != nil: case item.Vicinity != nil:
appendField(out, "Weather", Weather{ appendField(out, "Weather", Weather{
Vicinity: true, Vicinity: true,
Descriptor: Descriptor(item.Vicinity.Descriptor), Descriptor: convertDescriptor(item.Vicinity.Descriptor),
Precipitation: Precipitation(item.Vicinity.Precipitation), Precipitation: convertPrecipitation(item.Vicinity.Precipitation),
}) })
case item.SkyCondition != nil: case item.SkyCondition != nil:
var altitude int var altitude int
@ -117,8 +117,8 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
appendField(out, "SkyCondition", SkyCondition{ appendField(out, "SkyCondition", SkyCondition{
Altitude: altitude * 100, // Scale factor for altitude is 100 Altitude: altitude * 100, // Scale factor for altitude is 100
Type: SkyConditionType(item.SkyCondition.Type), Type: convertSkyConditionType(item.SkyCondition.Type),
CloudType: CloudType(item.SkyCondition.CloudType), CloudType: convertCloudType(item.SkyCondition.CloudType),
}) })
case item.Temperature != nil: case item.Temperature != nil:
vt, err := parseValidTime(item.Temperature.Time) vt, err := parseValidTime(item.Temperature.Time)
@ -132,7 +132,7 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
} }
appendField(out, "Temperature", Temperature{ appendField(out, "Temperature", Temperature{
Type: TemperatureType(item.Temperature.Type), Type: convertTemperatureType(item.Temperature.Type),
Time: vt, Time: vt,
Value: val, Value: val,
}) })
@ -173,7 +173,11 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
item.Visibility.Unit = "M" item.Visibility.Unit = "M"
} }
unit := units.Distance(item.Visibility.Unit) unit, ok := units.ParseDistance(item.Visibility.Unit)
if !ok {
return nil, participle.Errorf(item.Visibility.Pos, "visibility: invalid unit %q", item.Visibility.Unit)
}
val, _ := ratNum.Float64() val, _ := ratNum.Float64()
if opts.DistanceUnit != "" { if opts.DistanceUnit != "" {
@ -234,7 +238,11 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
} }
} }
unit := units.Speed(item.WindSpeed.Unit) unit, ok := units.ParseSpeed(item.WindSpeed.Unit)
if !ok {
return nil, participle.Errorf(item.WindSpeed.Pos, "wind: invalid unit %q", item.Visibility.Unit)
}
if opts.SpeedUnit != "" { if opts.SpeedUnit != "" {
speed = unit.Convert(opts.SpeedUnit, speed) speed = unit.Convert(opts.SpeedUnit, speed)
if gusts != 0 { if gusts != 0 {
@ -260,7 +268,7 @@ func ParseWithOptions(r io.Reader, opts Options) (*Forecast, error) {
} }
case item.Change != nil: case item.Change != nil:
ch := &Change{ ch := &Change{
Type: ChangeType(item.Change.Type), Type: convertChangeType(item.Change.Type),
} }
// FM changes don't have a valid pair, they only come with a single time string // FM changes don't have a valid pair, they only come with a single time string

View File

@ -28,7 +28,7 @@ func TestKLAX(t *testing.T) {
Visibility: Visibility{ Visibility: Visibility{
Plus: true, Plus: true,
Value: 6, Value: 6,
Unit: units.StatuteMiles, Unit: units.Miles,
}, },
Wind: Wind{ Wind: Wind{
Direction: Direction{ Direction: Direction{
@ -60,7 +60,7 @@ func TestKLAX(t *testing.T) {
Visibility: Visibility{ Visibility: Visibility{
Plus: true, Plus: true,
Value: 6, Value: 6,
Unit: units.StatuteMiles, Unit: units.Miles,
}, },
Wind: Wind{ Wind: Wind{
Direction: Direction{ Direction: Direction{
@ -84,7 +84,7 @@ func TestKLAX(t *testing.T) {
Visibility: Visibility{ Visibility: Visibility{
Plus: true, Plus: true,
Value: 6, Value: 6,
Unit: units.StatuteMiles, Unit: units.Miles,
}, },
Wind: Wind{ Wind: Wind{
Direction: Direction{ Direction: Direction{
@ -108,7 +108,7 @@ func TestKLAX(t *testing.T) {
Visibility: Visibility{ Visibility: Visibility{
Plus: true, Plus: true,
Value: 6, Value: 6,
Unit: units.StatuteMiles, Unit: units.Miles,
}, },
Wind: Wind{ Wind: Wind{
Direction: Direction{ Direction: Direction{
@ -132,7 +132,7 @@ func TestKLAX(t *testing.T) {
Visibility: Visibility{ Visibility: Visibility{
Plus: true, Plus: true,
Value: 6, Value: 6,
Unit: units.StatuteMiles, Unit: units.Miles,
}, },
Wind: Wind{ Wind: Wind{
Direction: Direction{ Direction: Direction{
@ -156,7 +156,7 @@ func TestKLAX(t *testing.T) {
Visibility: Visibility{ Visibility: Visibility{
Plus: true, Plus: true,
Value: 6, Value: 6,
Unit: units.StatuteMiles, Unit: units.Miles,
}, },
Wind: Wind{ Wind: Wind{
Direction: Direction{ Direction: Direction{

View File

@ -60,19 +60,19 @@ type Visibility struct {
type SkyConditionType string type SkyConditionType string
const ( const (
Few SkyConditionType = "FEW" Few SkyConditionType = "Few"
Scattered SkyConditionType = "SCT" Scattered SkyConditionType = "Scattered"
Broken SkyConditionType = "BKN" Broken SkyConditionType = "Broken"
Overcast SkyConditionType = "OVC" Overcast SkyConditionType = "Overcast"
VerticalVisibility SkyConditionType = "VV" VerticalVisibility SkyConditionType = "VerticalVisibility"
SkyClear SkyConditionType = "SKC" SkyClear SkyConditionType = "SkyClear"
) )
type CloudType string type CloudType string
const ( const (
CumuloNimbus CloudType = "CB" CumuloNimbus CloudType = "CumuloNumbus"
ToweringCumulus CloudType = "TCU" ToweringCumulus CloudType = "ToweringCumulus"
) )
type SkyCondition struct { type SkyCondition struct {
@ -97,58 +97,58 @@ type Direction struct {
type Modifier string type Modifier string
const ( const (
Heavy Modifier = "+" Heavy Modifier = "Heavy"
Light Modifier = "-" Light Modifier = "Light"
) )
type Descriptor string type Descriptor string
const ( const (
Shallow Descriptor = "MI" Shallow Descriptor = "Shallow"
Patches Descriptor = "BC" Patches Descriptor = "Patches"
LowDrifting Descriptor = "DC" LowDrifting Descriptor = "LowDrifting"
Blowing Descriptor = "BL" Blowing Descriptor = "Blowing"
Showers Descriptor = "SH" Showers Descriptor = "Showers"
Thunderstorm Descriptor = "TS" Thunderstorm Descriptor = "Thunderstorm"
Freezing Descriptor = "FZ" Freezing Descriptor = "Freezing"
Partial Descriptor = "PR" Partial Descriptor = "Partial"
) )
type Precipitation string type Precipitation string
const ( const (
Drizzle Precipitation = "DZ" Drizzle Precipitation = "Drizzle"
Rain Precipitation = "RA" Rain Precipitation = "Rain"
Snow Precipitation = "SN" Snow Precipitation = "Snow"
SnowGrains Precipitation = "SG" SnowGrains Precipitation = "SnowGrains"
IceCrystals Precipitation = "IC" IceCrystals Precipitation = "IceCrystals"
IcePellets Precipitation = "PL" IcePellets Precipitation = "IcePellets"
Hail Precipitation = "GR" Hail Precipitation = "Hail"
SmallHail Precipitation = "GS" SmallHail Precipitation = "SmallHail"
Unknown Precipitation = "UP" Unknown Precipitation = "Unknown"
) )
type Obscuration string type Obscuration string
const ( const (
Mist Obscuration = "BR" Mist Obscuration = "Mist"
Fog Obscuration = "FG" Fog Obscuration = "Fog"
Smoke Obscuration = "FU" Smoke Obscuration = "Smoke"
Dust Obscuration = "DU" Dust Obscuration = "Dust"
Sand Obscuration = "SA" Sand Obscuration = "Sand"
Haze Obscuration = "HZ" Haze Obscuration = "Haze"
Spray Obscuration = "PY" Spray Obscuration = "Spray"
VolcanicAsh Obscuration = "VA" VolcanicAsh Obscuration = "VolcanicAsh"
) )
type Phenomenon string type Phenomenon string
const ( const (
Whirls Phenomenon = "PO" Whirls Phenomenon = "Whirls"
Squalls Phenomenon = "SQ" Squalls Phenomenon = "Squalls"
FunnelCloud Phenomenon = "FC" FunnelCloud Phenomenon = "FunnelCloud"
SandStorm Phenomenon = "SS" Sandstorm Phenomenon = "Sandstorm"
DustStorm Phenomenon = "DS" Duststorm Phenomenon = "Duststorm"
) )
type Weather struct { type Weather struct {
@ -163,8 +163,8 @@ type Weather struct {
type TemperatureType string type TemperatureType string
const ( const (
High TemperatureType = "TX" High TemperatureType = "High"
Low TemperatureType = "TN" Low TemperatureType = "Low"
) )
type Temperature struct { type Temperature struct {
@ -176,13 +176,13 @@ type Temperature struct {
type ChangeType string type ChangeType string
const ( const (
From ChangeType = "FM" From ChangeType = "From"
Becoming ChangeType = "BECMG" Becoming ChangeType = "Becoming"
Temporary ChangeType = "TEMPO" Temporary ChangeType = "Temporary"
) )
type Flag string type Flag string
const ( const (
CeilingAndVisibilityOK Flag = "CAVOK" CeilingAndVisibilityOK Flag = "CeilingAndVisibilityOK"
) )

View File

@ -9,25 +9,11 @@ type Speed string
// Speed units // Speed units
const ( const (
MetersPerSecond Speed = "MPS" MetersPerSecond Speed = "MetersPerSecond"
KilometersPerHour Speed = "KMH" KilometersPerHour Speed = "KilometersPerHour"
Knots Speed = "KT" Knots Speed = "Knots"
) )
var speedNames = map[Speed]string{
MetersPerSecond: "m/s",
KilometersPerHour: "kph",
Knots: "kts",
}
func (su Speed) String() string {
name, ok := speedNames[su]
if !ok {
return "<unknown>"
}
return name
}
// Convert converts a value from one unit to another // Convert converts a value from one unit to another
func (sf Speed) Convert(st Speed, val int) int { func (sf Speed) Convert(st Speed, val int) int {
switch { switch {
@ -52,18 +38,16 @@ func (sf Speed) Convert(st Speed, val int) int {
// mps, m/s, kmh, kph, kt, and kts. // mps, m/s, kmh, kph, kt, and kts.
// This function is case-insensitive. // This function is case-insensitive.
func ParseSpeed(s string) (Speed, bool) { func ParseSpeed(s string) (Speed, bool) {
if _, ok := speedNames[Speed(s)]; ok { switch strings.ToLower(s) {
return Speed(s), true case "m/s", "mps", "meterspersecond", "meters per second":
} return MetersPerSecond, true
case "kmh", "kph", "km/h", "kilometersperhour", "kilometers per hour":
for su, name := range speedNames { return KilometersPerHour, true
if strings.EqualFold(s, name) || case "kt", "kts", "knot", "knots":
strings.EqualFold(s, string(su)) { return Knots, true
return su, true default:
}
}
return "", false return "", false
}
} }
// Distance represents a unit of distance // Distance represents a unit of distance
@ -71,29 +55,16 @@ type Distance string
// Distance units // Distance units
const ( const (
StatuteMiles Distance = "SM" Miles Distance = "Miles"
Meters Distance = "M" Meters Distance = "Meters"
) )
var distanceNames = map[Distance]string{
StatuteMiles: "mi",
Meters: "m",
}
func (du Distance) String() string {
name, ok := distanceNames[du]
if !ok {
return "<unknown>"
}
return name
}
// Convert converts a value from one unit to another // Convert converts a value from one unit to another
func (df Distance) Convert(dt Distance, val float64) float64 { func (df Distance) Convert(dt Distance, val float64) float64 {
switch { switch {
case df == StatuteMiles && dt == Meters: case df == Miles && dt == Meters:
return val * 1609 return val * 1609
case df == Meters && dt == StatuteMiles: case df == Meters && dt == Miles:
return val / 1609 return val / 1609
default: default:
return val return val
@ -103,16 +74,12 @@ func (df Distance) Convert(dt Distance, val float64) float64 {
// ParseDistance parses a speed value. Valid inputs include: // ParseDistance parses a speed value. Valid inputs include:
// sm, mi, and m. This function is case-insensitive. // sm, mi, and m. This function is case-insensitive.
func ParseDistance(s string) (Distance, bool) { func ParseDistance(s string) (Distance, bool) {
if _, ok := distanceNames[Distance(s)]; ok { switch strings.ToLower(s) {
return Distance(s), true case "sm", "mi", "mile", "miles":
} return Miles, true
case "m", "meter", "meters":
for d, name := range distanceNames { return Meters, true
if strings.EqualFold(s, name) || default:
strings.EqualFold(s, string(d)) {
return d, true
}
}
return "", false return "", false
}
} }