metar-parser 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +29 -20
- 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 +92 -52
- metadata +53 -39
@@ -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
|