metar-parser 1.5.0 → 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 +6 -48
- 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 -14
- 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 +105 -93
- data/lib/metar/data.rb +25 -23
- data/lib/metar/i18n.rb +5 -2
- data/lib/metar/parser.rb +46 -21
- 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 +3 -1
- 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 +192 -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 -42
@@ -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,108 +1,120 @@
|
|
1
|
-
|
2
|
-
def self.parse(raw, strict: false)
|
3
|
-
return nil if raw.nil?
|
4
|
-
|
5
|
-
plain_match =
|
6
|
-
if strict
|
7
|
-
/^(\d{3})(\d{2}(|MPS|KMH|KT))$/
|
8
|
-
else
|
9
|
-
/^(\d{3})(\d{2,3}(|MPS|KMH|KT))$/
|
10
|
-
end
|
1
|
+
# frozen_string_literal: true
|
11
2
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
raw
|
17
|
-
direction: Metar::Data::Direction.new(m1[1]),
|
18
|
-
speed: Metar::Data::Speed.parse(m1[2])
|
19
|
-
)
|
20
|
-
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?
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
speed: Metar::Data::Speed.parse(m2[2] + m2[4]),
|
29
|
-
gusts: Metar::Data::Speed.parse(m2[3])
|
30
|
-
)
|
31
|
-
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
|
32
15
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
gusts = m3[2] + m3[3]
|
37
|
-
return new(
|
38
|
-
raw,
|
39
|
-
direction: :variable_direction,
|
40
|
-
speed: Metar::Data::Speed.parse(speed),
|
41
|
-
gusts: Metar::Data::Speed.parse(gusts)
|
42
|
-
)
|
43
|
-
end
|
16
|
+
m1 = raw.match(plain_match)
|
17
|
+
if m1
|
18
|
+
return nil if m1[1].to_i > 360
|
44
19
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
20
|
+
return new(
|
21
|
+
raw,
|
22
|
+
direction: Metar::Data::Direction.new(m1[1]),
|
23
|
+
speed: Metar::Data::Speed.parse(m1[2])
|
24
|
+
)
|
25
|
+
end
|
50
26
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
return new(raw, direction: :unknown_direction, speed: speed)
|
55
|
-
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
|
56
30
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
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
|
61
38
|
|
62
|
-
|
63
|
-
|
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
|
64
50
|
|
65
|
-
|
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
|
66
56
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
71
70
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
when :unknown_speed
|
80
|
-
I18n.t('metar.wind.unknown_speed')
|
81
|
-
else
|
82
|
-
@speed.to_s(
|
83
|
-
abbreviated: true,
|
84
|
-
precision: 0,
|
85
|
-
units: options[:speed_units]
|
86
|
-
)
|
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
|
87
78
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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
|
96
117
|
end
|
97
|
-
s = "#{speed} #{direction}"
|
98
|
-
if not @gusts.nil?
|
99
|
-
g = @gusts.to_s(
|
100
|
-
abbreviated: true,
|
101
|
-
precision: 0,
|
102
|
-
units: options[:speed_units]
|
103
|
-
)
|
104
|
-
s += " #{I18n.t('metar.wind.gusts')} #{g}"
|
105
118
|
end
|
106
|
-
s
|
107
119
|
end
|
108
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
|
-
|
data/lib/metar/parser.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "m9t"
|
2
4
|
|
3
5
|
require "metar/data"
|
@@ -13,7 +15,7 @@ module Metar
|
|
13
15
|
new(raw)
|
14
16
|
end
|
15
17
|
|
16
|
-
COMPLIANCE =
|
18
|
+
COMPLIANCE = %i(strict loose).freeze
|
17
19
|
|
18
20
|
def self.thread_attributes
|
19
21
|
Thread.current[:metar_parser] ||= {}
|
@@ -25,6 +27,7 @@ module Metar
|
|
25
27
|
|
26
28
|
def self.compliance=(compliance)
|
27
29
|
raise 'Unknown compliance' unless COMPLIANCE.find(compliance)
|
30
|
+
|
28
31
|
thread_attributes[:compliance] = compliance
|
29
32
|
end
|
30
33
|
|
@@ -65,11 +68,13 @@ module Metar
|
|
65
68
|
|
66
69
|
def temperature
|
67
70
|
return nil if @temperature_and_dew_point.nil?
|
71
|
+
|
68
72
|
@temperature_and_dew_point.temperature
|
69
73
|
end
|
70
74
|
|
71
75
|
def dew_point
|
72
76
|
return nil if @temperature_and_dew_point.nil?
|
77
|
+
|
73
78
|
@temperature_and_dew_point.dew_point
|
74
79
|
end
|
75
80
|
|
@@ -77,7 +82,7 @@ module Metar
|
|
77
82
|
attr = {
|
78
83
|
metar: metar,
|
79
84
|
datetime: @time.raw,
|
80
|
-
station_code: station_code
|
85
|
+
station_code: station_code
|
81
86
|
}
|
82
87
|
%i(
|
83
88
|
minimum_visibility
|
@@ -106,6 +111,7 @@ module Metar
|
|
106
111
|
value = send(attribute)
|
107
112
|
return hash if value.nil?
|
108
113
|
return hash if value.raw.nil?
|
114
|
+
|
109
115
|
hash[attribute] = value.raw
|
110
116
|
hash
|
111
117
|
end
|
@@ -113,7 +119,8 @@ module Metar
|
|
113
119
|
def add_raw_if_not_empty(hash, attribute)
|
114
120
|
values = send(attribute)
|
115
121
|
raws = values.map(&:raw).compact
|
116
|
-
return hash if raws.
|
122
|
+
return hash if raws.empty?
|
123
|
+
|
117
124
|
hash[attribute] = raws.join(" ")
|
118
125
|
hash
|
119
126
|
end
|
@@ -121,8 +128,8 @@ module Metar
|
|
121
128
|
def analyze
|
122
129
|
@chunks = @metar.split(' ')
|
123
130
|
# Strip final '='
|
124
|
-
if !strict?
|
125
|
-
@chunks[-1].gsub!(/\s?=$/, '')
|
131
|
+
if !strict?
|
132
|
+
@chunks[-1].gsub!(/\s?=$/, '') if !@chunks.empty?
|
126
133
|
end
|
127
134
|
|
128
135
|
@station_code = nil
|
@@ -167,7 +174,8 @@ module Metar
|
|
167
174
|
def seek_station_code
|
168
175
|
@station_code = Metar::Data::StationCode.parse(@chunks[0])
|
169
176
|
if @station_code.nil?
|
170
|
-
|
177
|
+
message = "Expecting location, found '#{@chunks[0]}' in #{@metar}"
|
178
|
+
raise ParseError, message
|
171
179
|
end
|
172
180
|
@chunks.shift
|
173
181
|
@station_code
|
@@ -178,9 +186,11 @@ module Metar
|
|
178
186
|
@time = Metar::Data::Time.parse(
|
179
187
|
datetime, year: raw.time.year, month: raw.time.month, strict: strict?
|
180
188
|
)
|
189
|
+
|
181
190
|
if !@time
|
182
|
-
raise ParseError
|
191
|
+
raise ParseError, "Expecting datetime, found '#{datetime}' in #{@metar}"
|
183
192
|
end
|
193
|
+
|
184
194
|
@time
|
185
195
|
end
|
186
196
|
|
@@ -228,17 +238,17 @@ module Metar
|
|
228
238
|
end
|
229
239
|
end
|
230
240
|
|
231
|
-
if @chunks[0] == '1'
|
232
|
-
@visibility = Metar::Data::Visibility.parse(
|
241
|
+
if @chunks[0] == '1' || @chunks[0] == '2'
|
242
|
+
@visibility = Metar::Data::Visibility.parse(
|
243
|
+
@chunks[0] + ' ' + @chunks[1]
|
244
|
+
)
|
233
245
|
if @visibility
|
234
246
|
@chunks.shift
|
235
247
|
@chunks.shift
|
236
248
|
end
|
237
249
|
else
|
238
250
|
@visibility = Metar::Data::Visibility.parse(@chunks[0])
|
239
|
-
if @visibility
|
240
|
-
@chunks.shift
|
241
|
-
end
|
251
|
+
@chunks.shift if @visibility
|
242
252
|
end
|
243
253
|
@visibility
|
244
254
|
end
|
@@ -254,6 +264,7 @@ module Metar
|
|
254
264
|
loop do
|
255
265
|
rvr = Metar::Data::RunwayVisibleRange.parse(@chunks[0])
|
256
266
|
break if rvr.nil?
|
267
|
+
|
257
268
|
@chunks.shift
|
258
269
|
@runway_visible_range << rvr
|
259
270
|
end
|
@@ -271,11 +282,21 @@ module Metar
|
|
271
282
|
end
|
272
283
|
end
|
273
284
|
|
285
|
+
if @chunks[0] == 'NSW'
|
286
|
+
@present_weather << Metar::Data::WeatherPhenomenon.new(
|
287
|
+
nil, phenomenon: "no significant weather"
|
288
|
+
)
|
289
|
+
@chunks.shift
|
290
|
+
return
|
291
|
+
end
|
292
|
+
|
274
293
|
loop do
|
275
|
-
break if @chunks.
|
294
|
+
break if @chunks.empty?
|
276
295
|
break if @chunks[0].start_with?("RE")
|
296
|
+
|
277
297
|
wtp = Metar::Data::WeatherPhenomenon.parse(@chunks[0])
|
278
298
|
break if wtp.nil?
|
299
|
+
|
279
300
|
@chunks.shift
|
280
301
|
@present_weather << wtp
|
281
302
|
end
|
@@ -284,7 +305,7 @@ module Metar
|
|
284
305
|
# Repeatable: 15.9.1.3
|
285
306
|
def seek_sky_conditions
|
286
307
|
if observer.value == :auto # WMO 15.4
|
287
|
-
if @chunks[0] == '///'
|
308
|
+
if @chunks[0] == '///' || @chunks[0] == '//////'
|
288
309
|
@chunks.shift # Simply dispose of it
|
289
310
|
return
|
290
311
|
end
|
@@ -293,6 +314,7 @@ module Metar
|
|
293
314
|
loop do
|
294
315
|
sky_condition = Metar::Data::SkyCondition.parse(@chunks[0])
|
295
316
|
break if sky_condition.nil?
|
317
|
+
|
296
318
|
@chunks.shift
|
297
319
|
@sky_conditions << sky_condition
|
298
320
|
end
|
@@ -321,10 +343,12 @@ module Metar
|
|
321
343
|
|
322
344
|
def seek_recent_weather
|
323
345
|
loop do
|
324
|
-
return if @chunks.
|
346
|
+
return if @chunks.empty?
|
325
347
|
break if !@chunks[0].start_with?("RE")
|
348
|
+
|
326
349
|
recent_weather = Metar::Data::WeatherPhenomenon.parse(@chunks[0])
|
327
350
|
break if recent_weather.nil?
|
351
|
+
|
328
352
|
@chunks.shift
|
329
353
|
@recent_weather << recent_weather
|
330
354
|
end
|
@@ -333,11 +357,11 @@ module Metar
|
|
333
357
|
|
334
358
|
def seek_to_remarks
|
335
359
|
if strict?
|
336
|
-
if
|
337
|
-
raise ParseError
|
360
|
+
if !@chunks.empty? && @chunks[0] != 'RMK'
|
361
|
+
raise ParseError, "Unparsable text found: '#{@chunks.join(' ')}'"
|
338
362
|
end
|
339
363
|
else
|
340
|
-
while
|
364
|
+
while !@chunks.empty? && @chunks[0] != 'RMK' do
|
341
365
|
@unparsed << @chunks.shift
|
342
366
|
end
|
343
367
|
end
|
@@ -345,13 +369,14 @@ module Metar
|
|
345
369
|
|
346
370
|
# WMO: 15.15
|
347
371
|
def seek_remarks
|
348
|
-
return if @chunks.
|
372
|
+
return if @chunks.empty?
|
349
373
|
raise 'seek_remarks called without remark' if @chunks[0] != 'RMK'
|
350
374
|
|
351
375
|
@chunks.shift # Drop 'RMK'
|
352
376
|
@remarks = []
|
353
377
|
loop do
|
354
|
-
break if @chunks.
|
378
|
+
break if @chunks.empty?
|
379
|
+
|
355
380
|
r = Metar::Data::Remark.parse(@chunks[0])
|
356
381
|
if r
|
357
382
|
if r.is_a?(Array)
|
@@ -362,7 +387,7 @@ module Metar
|
|
362
387
|
@chunks.shift
|
363
388
|
next
|
364
389
|
end
|
365
|
-
if @chunks[0] == 'VIS'
|
390
|
+
if @chunks[0] == 'VIS' && @chunks.size >= 3 && @chunks[1] == 'MIN'
|
366
391
|
@chunks.shift(2)
|
367
392
|
r = Metar::Data::VisibilityRemark.parse(@chunks[0])
|
368
393
|
@remarks << r
|