metar-parser 1.4.2 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +7 -49
- data/Rakefile +2 -1
- data/lib/metar/data/base.rb +16 -10
- data/lib/metar/data/density_altitude.rb +16 -10
- data/lib/metar/data/direction.rb +10 -4
- data/lib/metar/data/distance.rb +27 -20
- data/lib/metar/data/lightning.rb +69 -60
- data/lib/metar/data/observer.rb +26 -20
- data/lib/metar/data/pressure.rb +28 -22
- data/lib/metar/data/remark.rb +146 -130
- data/lib/metar/data/runway_visible_range.rb +98 -78
- data/lib/metar/data/sky_condition.rb +68 -57
- data/lib/metar/data/speed.rb +21 -14
- data/lib/metar/data/station_code.rb +8 -4
- data/lib/metar/data/temperature.rb +21 -13
- data/lib/metar/data/temperature_and_dew_point.rb +22 -16
- data/lib/metar/data/time.rb +57 -47
- data/lib/metar/data/variable_wind.rb +30 -19
- data/lib/metar/data/vertical_visibility.rb +27 -21
- data/lib/metar/data/visibility.rb +91 -79
- data/lib/metar/data/visibility_remark.rb +16 -5
- data/lib/metar/data/weather_phenomenon.rb +92 -74
- data/lib/metar/data/wind.rb +106 -87
- data/lib/metar/data.rb +25 -23
- data/lib/metar/i18n.rb +5 -2
- data/lib/metar/parser.rb +47 -22
- data/lib/metar/raw.rb +32 -44
- data/lib/metar/report.rb +31 -20
- data/lib/metar/station.rb +28 -19
- data/lib/metar/version.rb +4 -2
- data/lib/metar.rb +2 -1
- data/locales/de.yml +1 -0
- data/locales/en.yml +1 -0
- data/locales/it.yml +1 -0
- data/locales/pt-BR.yml +1 -0
- data/spec/data/density_altitude_spec.rb +2 -1
- data/spec/data/distance_spec.rb +2 -1
- data/spec/data/lightning_spec.rb +26 -9
- data/spec/data/pressure_spec.rb +2 -0
- data/spec/data/remark_spec.rb +26 -9
- data/spec/data/runway_visible_range_spec.rb +71 -35
- data/spec/data/sky_condition_spec.rb +63 -19
- data/spec/data/speed_spec.rb +2 -0
- data/spec/data/temperature_spec.rb +2 -1
- data/spec/data/variable_wind_spec.rb +2 -0
- data/spec/data/vertical_visibility_spec.rb +4 -4
- data/spec/data/visibility_remark_spec.rb +2 -1
- data/spec/data/visibility_spec.rb +46 -25
- data/spec/data/weather_phenomenon_spec.rb +79 -24
- data/spec/data/wind_spec.rb +156 -38
- data/spec/i18n_spec.rb +2 -0
- data/spec/parser_spec.rb +224 -64
- data/spec/raw_spec.rb +40 -68
- data/spec/report_spec.rb +27 -25
- data/spec/spec_helper.rb +5 -6
- data/spec/station_spec.rb +43 -44
- metadata +56 -43
@@ -1,86 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "metar/i18n"
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
module Metar
|
6
|
+
module Data
|
7
|
+
class WeatherPhenomenon < Metar::Data::Base
|
8
|
+
MODIFIERS = {
|
9
|
+
'+' => 'heavy',
|
10
|
+
'-' => 'light',
|
11
|
+
'VC' => 'nearby',
|
12
|
+
'-VC' => 'nearby light',
|
13
|
+
'+VC' => 'nearby heavy'
|
14
|
+
}.freeze
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
DESCRIPTORS = {
|
17
|
+
'BC' => 'patches of',
|
18
|
+
'BL' => 'blowing',
|
19
|
+
'DR' => 'low drifting',
|
20
|
+
'FZ' => 'freezing',
|
21
|
+
'MI' => 'shallow',
|
22
|
+
'PR' => 'partial',
|
23
|
+
'SH' => 'shower of',
|
24
|
+
'TS' => 'thunderstorm and'
|
25
|
+
}.freeze
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
27
|
+
PHENOMENA = {
|
28
|
+
'BR' => 'mist',
|
29
|
+
'DU' => 'dust',
|
30
|
+
'DZ' => 'drizzle',
|
31
|
+
'FG' => 'fog',
|
32
|
+
'FU' => 'smoke',
|
33
|
+
'GR' => 'hail',
|
34
|
+
'GS' => 'small hail',
|
35
|
+
'HZ' => 'haze',
|
36
|
+
'IC' => 'ice crystals',
|
37
|
+
'PL' => 'ice pellets',
|
38
|
+
'PO' => 'dust whirls',
|
39
|
+
'PY' => 'spray', # US only
|
40
|
+
'RA' => 'rain',
|
41
|
+
'SA' => 'sand',
|
42
|
+
'SH' => 'shower',
|
43
|
+
'SN' => 'snow',
|
44
|
+
'SG' => 'snow grains',
|
45
|
+
'SQ' => 'squall',
|
46
|
+
'UP' => 'unknown phenomenon', # => AUTO
|
47
|
+
'VA' => 'volcanic ash',
|
48
|
+
'FC' => 'funnel cloud',
|
49
|
+
'SS' => 'sand storm',
|
50
|
+
'DS' => 'dust storm',
|
51
|
+
'TS' => 'thunderstorm'
|
52
|
+
}.freeze
|
49
53
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
modifiers = Modifiers.keys.join('|')
|
55
|
-
modifiers.gsub!(/([\+\-])/) { |m| "\\#{m}" }
|
56
|
-
rxp = Regexp.new("^(RE)?(#{modifiers})?(#{descriptors})?((?:#{phenomena}){1,2})$")
|
57
|
-
m = rxp.match(raw)
|
58
|
-
return nil if m.nil?
|
54
|
+
# Accepts all standard (and some non-standard) present weather codes
|
55
|
+
def self.parse(raw)
|
56
|
+
modifiers = MODIFIERS.keys.join('|')
|
57
|
+
modifiers.gsub!(/([\+\-])/) { |m| "\\#{m}" }
|
59
58
|
|
60
|
-
|
61
|
-
modifier_code = m[2]
|
62
|
-
descriptor_code = m[3]
|
63
|
-
phenomena_codes = m[4].scan(/../)
|
64
|
-
phenomena_phrase = phenomena_codes.map { |c| Phenomena[c] }.join(' and ')
|
59
|
+
descriptors = DESCRIPTORS.keys.join('|')
|
65
60
|
|
66
|
-
|
67
|
-
raw,
|
68
|
-
phenomenon: phenomena_phrase,
|
69
|
-
modifier: Modifiers[modifier_code],
|
70
|
-
descriptor: Descriptors[descriptor_code]
|
71
|
-
)
|
72
|
-
end
|
61
|
+
phenomena = PHENOMENA.keys.join('|')
|
73
62
|
|
74
|
-
|
63
|
+
rxp = Regexp.new(
|
64
|
+
"^(RE)?(#{modifiers})?(#{descriptors})?((?:#{phenomena}){1,2})$"
|
65
|
+
)
|
75
66
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
67
|
+
m = rxp.match(raw)
|
68
|
+
return nil if m.nil?
|
69
|
+
|
70
|
+
recent = m[1] == "RE"
|
71
|
+
modifier_code = m[2]
|
72
|
+
descriptor_code = m[3]
|
73
|
+
phenomena_codes = m[4].scan(/../)
|
74
|
+
phenomena = phenomena_codes.map { |c| PHENOMENA[c] }
|
75
|
+
phenomena_phrase = phenomena.join(' and ')
|
76
|
+
|
77
|
+
new(
|
78
|
+
raw,
|
79
|
+
phenomenon: phenomena_phrase,
|
80
|
+
modifier: MODIFIERS[modifier_code],
|
81
|
+
descriptor: DESCRIPTORS[descriptor_code],
|
82
|
+
recent: recent
|
83
|
+
)
|
84
|
+
end
|
85
|
+
|
86
|
+
attr_reader :phenomenon, :modifier, :descriptor, :recent
|
87
|
+
|
88
|
+
def initialize(
|
89
|
+
raw, phenomenon:, modifier: nil, descriptor: nil, recent: false
|
90
|
+
)
|
91
|
+
@raw = raw
|
92
|
+
@phenomenon = phenomenon
|
93
|
+
@modifier = modifier
|
94
|
+
@descriptor = descriptor
|
95
|
+
@recent = recent
|
96
|
+
end
|
81
97
|
|
82
|
-
|
83
|
-
|
84
|
-
|
98
|
+
def to_s
|
99
|
+
key = [modifier, descriptor, phenomenon].compact.join(' ')
|
100
|
+
I18n.t("metar.present_weather.#{key}")
|
101
|
+
end
|
102
|
+
end
|
85
103
|
end
|
86
104
|
end
|
data/lib/metar/data/wind.rb
CHANGED
@@ -1,101 +1,120 @@
|
|
1
|
-
|
2
|
-
def self.parse(raw)
|
3
|
-
return nil if raw.nil?
|
4
|
-
|
5
|
-
m1 = raw.match(/^(\d{3})(\d{2}(|MPS|KMH|KT))$/)
|
6
|
-
if m1
|
7
|
-
return nil if m1[1].to_i > 360
|
8
|
-
return new(
|
9
|
-
raw,
|
10
|
-
direction: Metar::Data::Direction.new(m1[1]),
|
11
|
-
speed: Metar::Data::Speed.parse(m1[2])
|
12
|
-
)
|
13
|
-
end
|
1
|
+
# frozen_string_literal: true
|
14
2
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
raw
|
20
|
-
direction: Metar::Data::Direction.new(m2[1]),
|
21
|
-
speed: Metar::Data::Speed.parse(m2[2] + m2[4]),
|
22
|
-
gusts: Metar::Data::Speed.parse(m2[3])
|
23
|
-
)
|
24
|
-
end
|
3
|
+
module Metar
|
4
|
+
module Data
|
5
|
+
class Wind < Metar::Data::Base
|
6
|
+
def self.parse(raw, strict: false)
|
7
|
+
return nil if raw.nil?
|
25
8
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
direction: :variable_direction,
|
33
|
-
speed: Metar::Data::Speed.parse(speed),
|
34
|
-
gusts: Metar::Data::Speed.parse(gusts)
|
35
|
-
)
|
36
|
-
end
|
9
|
+
plain_match =
|
10
|
+
if strict
|
11
|
+
/^(\d{3})(\d{2}(|MPS|KMH|KT))$/
|
12
|
+
else
|
13
|
+
/^(\d{3})(\d{2,3}(|MPS|KMH|KT))$/
|
14
|
+
end
|
37
15
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
return new(raw, direction: :variable_direction, speed: speed)
|
42
|
-
end
|
16
|
+
m1 = raw.match(plain_match)
|
17
|
+
if m1
|
18
|
+
return nil if m1[1].to_i > 360
|
43
19
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
20
|
+
return new(
|
21
|
+
raw,
|
22
|
+
direction: Metar::Data::Direction.new(m1[1]),
|
23
|
+
speed: Metar::Data::Speed.parse(m1[2])
|
24
|
+
)
|
25
|
+
end
|
49
26
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
27
|
+
m2 = raw.match(/^(\d{3})(\d{2})G(\d{2,3}(|MPS|KMH|KT))$/)
|
28
|
+
if m2
|
29
|
+
return nil if m2[1].to_i > 360
|
54
30
|
|
55
|
-
|
56
|
-
|
31
|
+
return new(
|
32
|
+
raw,
|
33
|
+
direction: Metar::Data::Direction.new(m2[1]),
|
34
|
+
speed: Metar::Data::Speed.parse(m2[2] + m2[4]),
|
35
|
+
gusts: Metar::Data::Speed.parse(m2[3])
|
36
|
+
)
|
37
|
+
end
|
57
38
|
|
58
|
-
|
39
|
+
m3 = raw.match(/^VRB(\d{2})G(\d{2,3})(|MPS|KMH|KT)$/)
|
40
|
+
if m3
|
41
|
+
speed = m3[1] + m3[3]
|
42
|
+
gusts = m3[2] + m3[3]
|
43
|
+
return new(
|
44
|
+
raw,
|
45
|
+
direction: :variable_direction,
|
46
|
+
speed: Metar::Data::Speed.parse(speed),
|
47
|
+
gusts: Metar::Data::Speed.parse(gusts)
|
48
|
+
)
|
49
|
+
end
|
59
50
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
51
|
+
m4 = raw.match(/^VRB(\d{2}(|MPS|KMH|KT))$/)
|
52
|
+
if m4
|
53
|
+
speed = Metar::Data::Speed.parse(m4[1])
|
54
|
+
return new(raw, direction: :variable_direction, speed: speed)
|
55
|
+
end
|
64
56
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
57
|
+
m5 = raw.match(%r{^/{3}(\d{2}(|MPS|KMH|KT))$})
|
58
|
+
if m5
|
59
|
+
speed = Metar::Data::Speed.parse(m5[1])
|
60
|
+
return new(raw, direction: :unknown_direction, speed: speed)
|
61
|
+
end
|
62
|
+
|
63
|
+
m6 = raw.match(%r{^/////(|MPS|KMH|KT)$})
|
64
|
+
if m6
|
65
|
+
return new(raw, direction: :unknown_direction, speed: :unknown_speed)
|
66
|
+
end
|
67
|
+
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :direction, :speed, :gusts
|
72
|
+
|
73
|
+
def initialize(raw, direction:, speed:, gusts: nil)
|
74
|
+
@raw = raw
|
75
|
+
@direction = direction
|
76
|
+
@speed = speed
|
77
|
+
@gusts = gusts
|
80
78
|
end
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
79
|
+
|
80
|
+
def to_s(options = {})
|
81
|
+
options = {
|
82
|
+
direction_units: :compass,
|
83
|
+
speed_units: :kilometers_per_hour
|
84
|
+
}.merge(options)
|
85
|
+
speed =
|
86
|
+
case @speed
|
87
|
+
when :unknown_speed
|
88
|
+
I18n.t('metar.wind.unknown_speed')
|
89
|
+
else
|
90
|
+
@speed.to_s(
|
91
|
+
abbreviated: true,
|
92
|
+
precision: 0,
|
93
|
+
units: options[:speed_units]
|
94
|
+
)
|
95
|
+
end
|
96
|
+
direction =
|
97
|
+
case @direction
|
98
|
+
when :variable_direction
|
99
|
+
I18n.t('metar.wind.variable_direction')
|
100
|
+
when :unknown_direction
|
101
|
+
I18n.t('metar.wind.unknown_direction')
|
102
|
+
else
|
103
|
+
@direction.to_s(units: options[:direction_units])
|
104
|
+
end
|
105
|
+
s = "#{speed} #{direction}"
|
106
|
+
|
107
|
+
if !@gusts.nil?
|
108
|
+
g = @gusts.to_s(
|
109
|
+
abbreviated: true,
|
110
|
+
precision: 0,
|
111
|
+
units: options[:speed_units]
|
112
|
+
)
|
113
|
+
s += " #{I18n.t('metar.wind.gusts')} #{g}"
|
114
|
+
end
|
115
|
+
|
116
|
+
s
|
89
117
|
end
|
90
|
-
s = "#{speed} #{direction}"
|
91
|
-
if not @gusts.nil?
|
92
|
-
g = @gusts.to_s(
|
93
|
-
abbreviated: true,
|
94
|
-
precision: 0,
|
95
|
-
units: options[:speed_units]
|
96
|
-
)
|
97
|
-
s += " #{I18n.t('metar.wind.gusts')} #{g}"
|
98
118
|
end
|
99
|
-
s
|
100
119
|
end
|
101
120
|
end
|
data/lib/metar/data.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
module Metar
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
3
|
+
module Metar
|
4
|
+
module Data
|
5
|
+
autoload :Base, "metar/data/base"
|
6
|
+
autoload :DensityAltitude, "metar/data/density_altitude"
|
7
|
+
autoload :Direction, "metar/data/direction"
|
8
|
+
autoload :Distance, "metar/data/distance"
|
9
|
+
autoload :Lightning, "metar/data/lightning"
|
10
|
+
autoload :Observer, "metar/data/observer"
|
11
|
+
autoload :Pressure, "metar/data/pressure"
|
12
|
+
autoload :Remark, "metar/data/remark"
|
13
|
+
autoload :RunwayVisibleRange, "metar/data/runway_visible_range"
|
14
|
+
autoload :SkyCondition, "metar/data/sky_condition"
|
15
|
+
autoload :Speed, "metar/data/speed"
|
16
|
+
autoload :StationCode, "metar/data/station_code"
|
17
|
+
autoload :Temperature, "metar/data/temperature"
|
18
|
+
autoload :TemperatureAndDewPoint, "metar/data/temperature_and_dew_point"
|
19
|
+
autoload :Time, "metar/data/time"
|
20
|
+
autoload :VariableWind, "metar/data/variable_wind"
|
21
|
+
autoload :VerticalVisibility, "metar/data/vertical_visibility"
|
22
|
+
autoload :Visibility, "metar/data/visibility"
|
23
|
+
autoload :VisibilityRemark, "metar/data/visibility_remark"
|
24
|
+
autoload :WeatherPhenomenon, "metar/data/weather_phenomenon"
|
25
|
+
autoload :Wind, "metar/data/wind"
|
26
|
+
end
|
25
27
|
end
|
data/lib/metar/i18n.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "i18n"
|
2
4
|
|
3
|
-
locales_path = File.expand_path(
|
5
|
+
locales_path = File.expand_path(
|
6
|
+
File.join(File.dirname(__FILE__), "..", "..", "locales")
|
7
|
+
)
|
4
8
|
I18n.load_path += Dir.glob("#{locales_path}/*.yml")
|
5
9
|
I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks)
|
6
|
-
|