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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -48
  3. data/Rakefile +2 -1
  4. data/lib/metar/data/base.rb +16 -10
  5. data/lib/metar/data/density_altitude.rb +16 -10
  6. data/lib/metar/data/direction.rb +10 -4
  7. data/lib/metar/data/distance.rb +27 -20
  8. data/lib/metar/data/lightning.rb +69 -60
  9. data/lib/metar/data/observer.rb +26 -20
  10. data/lib/metar/data/pressure.rb +28 -22
  11. data/lib/metar/data/remark.rb +146 -130
  12. data/lib/metar/data/runway_visible_range.rb +98 -78
  13. data/lib/metar/data/sky_condition.rb +68 -57
  14. data/lib/metar/data/speed.rb +21 -14
  15. data/lib/metar/data/station_code.rb +8 -4
  16. data/lib/metar/data/temperature.rb +21 -14
  17. data/lib/metar/data/temperature_and_dew_point.rb +22 -16
  18. data/lib/metar/data/time.rb +57 -47
  19. data/lib/metar/data/variable_wind.rb +30 -19
  20. data/lib/metar/data/vertical_visibility.rb +27 -21
  21. data/lib/metar/data/visibility.rb +91 -79
  22. data/lib/metar/data/visibility_remark.rb +16 -5
  23. data/lib/metar/data/weather_phenomenon.rb +92 -74
  24. data/lib/metar/data/wind.rb +105 -93
  25. data/lib/metar/data.rb +25 -23
  26. data/lib/metar/i18n.rb +5 -2
  27. data/lib/metar/parser.rb +46 -21
  28. data/lib/metar/raw.rb +32 -44
  29. data/lib/metar/report.rb +31 -20
  30. data/lib/metar/station.rb +28 -19
  31. data/lib/metar/version.rb +3 -1
  32. data/lib/metar.rb +2 -1
  33. data/locales/de.yml +1 -0
  34. data/locales/en.yml +1 -0
  35. data/locales/it.yml +1 -0
  36. data/locales/pt-BR.yml +1 -0
  37. data/spec/data/density_altitude_spec.rb +2 -1
  38. data/spec/data/distance_spec.rb +2 -1
  39. data/spec/data/lightning_spec.rb +26 -9
  40. data/spec/data/pressure_spec.rb +2 -0
  41. data/spec/data/remark_spec.rb +26 -9
  42. data/spec/data/runway_visible_range_spec.rb +71 -35
  43. data/spec/data/sky_condition_spec.rb +63 -19
  44. data/spec/data/speed_spec.rb +2 -0
  45. data/spec/data/temperature_spec.rb +2 -1
  46. data/spec/data/variable_wind_spec.rb +2 -0
  47. data/spec/data/vertical_visibility_spec.rb +4 -4
  48. data/spec/data/visibility_remark_spec.rb +2 -1
  49. data/spec/data/visibility_spec.rb +46 -25
  50. data/spec/data/weather_phenomenon_spec.rb +79 -24
  51. data/spec/data/wind_spec.rb +156 -38
  52. data/spec/i18n_spec.rb +2 -0
  53. data/spec/parser_spec.rb +192 -64
  54. data/spec/raw_spec.rb +40 -68
  55. data/spec/report_spec.rb +27 -25
  56. data/spec/spec_helper.rb +5 -6
  57. data/spec/station_spec.rb +43 -44
  58. metadata +56 -42
@@ -1,136 +1,152 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "i18n"
2
4
  require "m9t"
3
5
 
4
- class Metar::Data::Remark
5
- PRESSURE_CHANGE_CHARACTER = [
6
- :increasing_then_decreasing, # 0
7
- :increasing_then_steady, # 1
8
- :increasing, # 2
9
- :decreasing_or_steady_then_increasing, # 3
10
- :steady, # 4
11
- :decreasing_then_increasing, # 5
12
- :decreasing_then_steady, # 6
13
- :decreasing, # 7
14
- :steady_then_decreasing, # 8
15
- ]
16
-
17
- INDICATOR_TYPE = {
18
- 'TS' => :thunderstorm_information,
19
- 'PWI' => :precipitation_identifier,
20
- 'P' => :precipitation_amount,
21
- }
22
-
23
- COLOR_CODE = ['RED', 'AMB', 'YLO', 'GRN', 'WHT', 'BLU']
24
-
25
- def self.parse(raw)
26
- if !raw
27
- return nil
28
- end
29
-
30
- m1 = raw.match(/^([12])([01])(\d{3})$/)
31
- if m1
32
- extreme = {'1' => :maximum, '2' => :minimum}[m1[1]]
33
- value = sign(m1[2]) * tenths(m1[3])
34
- return Metar::Data::TemperatureExtreme.new(raw, extreme, value)
35
- end
36
-
37
- m2 = raw.match(/^4([01])(\d{3})([01])(\d{3})$/)
38
- if m2
39
- return [
40
- Metar::Data::TemperatureExtreme.new(raw, :maximum, sign(m2[1]) * tenths(m2[2])),
41
- Metar::Data::TemperatureExtreme.new(raw, :minimum, sign(m2[3]) * tenths(m2[4])),
42
- ]
43
- end
44
-
45
- m3 = raw.match(/^5([0-8])(\d{3})$/)
46
- if m3
47
- character = PRESSURE_CHANGE_CHARACTER[m3[1].to_i]
48
- return Metar::Data::PressureTendency.new(raw, character, tenths(m3[2]))
49
- end
50
-
51
- m4 = raw.match(/^6(\d{4})$/)
52
- if m4
53
- return Metar::Data::Precipitation.new(raw, 3, Metar::Data::Distance.new(inches_to_meters(m4[1]))) # actually 3 or 6 depending on reporting time
54
- end
55
-
56
- m5 = raw.match(/^7(\d{4})$/)
57
- if m5
58
- return Metar::Data::Precipitation.new(raw, 24, Metar::Data::Distance.new(inches_to_meters(m5[1])))
59
- end
60
-
61
- m6 = raw.match(/^A[0O]([12])$/)
62
- if m6
63
- type = [:with_precipitation_discriminator, :without_precipitation_discriminator][m6[1].to_i - 1]
64
- return Metar::Data::AutomatedStationType.new(raw, type)
65
- end
66
-
67
- m7 = raw.match(/^P(\d{4})$/)
68
- if m7
69
- return Metar::Data::Precipitation.new(raw, 1, Metar::Data::Distance.new(inches_to_meters(m7[1])))
70
- end
71
-
72
- m8 = raw.match(/^T([01])(\d{3})([01])(\d{3})$/)
73
- if m8
74
- temperature = Metar::Data::Temperature.new(sign(m8[1]) * tenths(m8[2]))
75
- dew_point = Metar::Data::Temperature.new(sign(m8[3]) * tenths(m8[4]))
76
- return Metar::Data::HourlyTemperatureAndDewPoint.new(raw, temperature, dew_point)
77
- end
78
-
79
- m9 = raw.match(/^SLP(\d{3})$/)
80
- if m9
81
- return Metar::Data::SeaLevelPressure.new(raw, M9t::Pressure.hectopascals(tenths(m9[1])))
6
+ module Metar
7
+ module Data
8
+ TemperatureExtreme = Struct.new(:raw, :extreme, :value)
9
+ PressureTendency = Struct.new(:raw, :character, :value)
10
+ Precipitation = Struct.new(:raw, :period, :amount)
11
+ AutomatedStationType = Struct.new(:raw, :type)
12
+ HourlyTemperatureAndDewPoint = Struct.new(:raw, :temperature, :dew_point)
13
+ SeaLevelPressure = Struct.new(:raw, :pressure)
14
+ SensorStatusIndicator = Struct.new(:raw, :type, :state)
15
+ ColorCode = Struct.new(:raw, :code)
16
+ MaintenanceNeeded = Struct.new(:raw)
17
+
18
+ class Remark
19
+ PRESSURE_CHANGE_CHARACTER = [
20
+ :increasing_then_decreasing, # 0
21
+ :increasing_then_steady, # 1
22
+ :increasing, # 2
23
+ :decreasing_or_steady_then_increasing, # 3
24
+ :steady, # 4
25
+ :decreasing_then_increasing, # 5
26
+ :decreasing_then_steady, # 6
27
+ :decreasing, # 7
28
+ :steady_then_decreasing # 8
29
+ ].freeze
30
+
31
+ INDICATOR_TYPE = {
32
+ 'TS' => :thunderstorm_information,
33
+ 'PWI' => :precipitation_identifier,
34
+ 'P' => :precipitation_amount
35
+ }.freeze
36
+
37
+ COLOR_CODE = %w(RED AMB YLO GRN WHT BLU).freeze
38
+
39
+ def self.parse(raw)
40
+ return nil if !raw
41
+
42
+ m1 = raw.match(/^([12])([01])(\d{3})$/)
43
+ if m1
44
+ extreme = {'1' => :maximum, '2' => :minimum}[m1[1]]
45
+ value = sign(m1[2]) * tenths(m1[3])
46
+ return Metar::Data::TemperatureExtreme.new(raw, extreme, value)
47
+ end
48
+
49
+ m2 = raw.match(/^4([01])(\d{3})([01])(\d{3})$/)
50
+ if m2
51
+ v1 = sign(m2[1]) * tenths(m2[2])
52
+ v2 = sign(m2[3]) * tenths(m2[4])
53
+ return [
54
+ Metar::Data::TemperatureExtreme.new(raw, :maximum, v1),
55
+ Metar::Data::TemperatureExtreme.new(raw, :minimum, v2)
56
+ ]
57
+ end
58
+
59
+ m3 = raw.match(/^5([0-8])(\d{3})$/)
60
+ if m3
61
+ character = PRESSURE_CHANGE_CHARACTER[m3[1].to_i]
62
+ return Metar::Data::PressureTendency.new(
63
+ raw, character, tenths(m3[2])
64
+ )
65
+ end
66
+
67
+ m4 = raw.match(/^6(\d{4})$/)
68
+ if m4
69
+ d = Metar::Data::Distance.new(inches_to_meters(m4[1]))
70
+ period = 3 # actually 3 or 6 depending on reporting time
71
+ return Metar::Data::Precipitation.new(raw, period, d)
72
+ end
73
+
74
+ m5 = raw.match(/^7(\d{4})$/)
75
+ if m5
76
+ d = Metar::Data::Distance.new(inches_to_meters(m5[1]))
77
+ return Metar::Data::Precipitation.new(raw, 24, d)
78
+ end
79
+
80
+ m6 = raw.match(/^A[0O]([12])$/)
81
+ if m6
82
+ index = m6[1].to_i - 1
83
+ type = %i(
84
+ with_precipitation_discriminator without_precipitation_discriminator
85
+ )[index]
86
+ return Metar::Data::AutomatedStationType.new(raw, type)
87
+ end
88
+
89
+ m7 = raw.match(/^P(\d{4})$/)
90
+ if m7
91
+ d = Metar::Data::Distance.new(inches_to_meters(m7[1]))
92
+ return Metar::Data::Precipitation.new(raw, 1, d)
93
+ end
94
+
95
+ m8 = raw.match(/^T([01])(\d{3})([01])(\d{3})$/)
96
+ if m8
97
+ temperature = Metar::Data::Temperature.new(
98
+ sign(m8[1]) * tenths(m8[2])
99
+ )
100
+ dew_point = Metar::Data::Temperature.new(
101
+ sign(m8[3]) * tenths(m8[4])
102
+ )
103
+ return Metar::Data::HourlyTemperatureAndDewPoint.new(
104
+ raw, temperature, dew_point
105
+ )
106
+ end
107
+
108
+ m9 = raw.match(/^SLP(\d{3})$/)
109
+ if m9
110
+ pressure = M9t::Pressure.hectopascals(tenths(m9[1]))
111
+ return Metar::Data::SeaLevelPressure.new(raw, pressure)
112
+ end
113
+
114
+ m10 = raw.match(/^(#{INDICATOR_TYPE.keys.join('|')})NO$/)
115
+ if m10
116
+ type = INDICATOR_TYPE[m10[1]]
117
+ return Metar::Data::SensorStatusIndicator.new(
118
+ raw, type, :not_available
119
+ )
120
+ end
121
+
122
+ m11 = raw.match(/^(#{COLOR_CODE.join('|')})$/)
123
+ return Metar::Data::ColorCode.new(raw, m11[1]) if m11
124
+
125
+ return Metar::Data::SkyCondition.new(raw) if raw == 'SKC'
126
+
127
+ return Metar::Data::MaintenanceNeeded.new(raw) if raw == '$'
128
+
129
+ nil
130
+ end
131
+
132
+ def self.sign(digit)
133
+ case digit
134
+ when '0'
135
+ 1.0
136
+ when '1'
137
+ -1.0
138
+ else
139
+ raise "Unexpected sign: #{digit}"
140
+ end
141
+ end
142
+
143
+ def self.tenths(digits)
144
+ digits.to_f / 10.0
145
+ end
146
+
147
+ def self.inches_to_meters(digits)
148
+ digits.to_f * 0.000254
149
+ end
82
150
  end
83
-
84
- m10 = raw.match(/^(#{INDICATOR_TYPE.keys.join('|')})NO$/)
85
- if m10
86
- type = INDICATOR_TYPE[m10[1]]
87
- return Metar::Data::SensorStatusIndicator.new(raw, type, :not_available)
88
- end
89
-
90
- m11 = raw.match(/^(#{COLOR_CODE.join('|')})$/)
91
- if m11
92
- return Metar::Data::ColorCode.new(raw, m11[1])
93
- end
94
-
95
- if raw == 'SKC'
96
- return Metar::Data::SkyCondition.new(raw)
97
- end
98
-
99
- if raw == '$'
100
- Metar::Data::MaintenanceNeeded.new(raw)
101
- end
102
-
103
- nil
104
151
  end
105
-
106
- def self.sign(digit)
107
- case digit
108
- when '0'
109
- 1.0
110
- when '1'
111
- -1.0
112
- else
113
- raise "Unexpected sign: #{digit}"
114
- end
115
- end
116
-
117
- def self.tenths(digits)
118
- digits.to_f / 10.0
119
- end
120
-
121
- def self.inches_to_meters(digits)
122
- digits.to_f * 0.000254
123
- end
124
- end
125
-
126
- module Metar::Data
127
- TemperatureExtreme = Struct.new(:raw, :extreme, :value)
128
- PressureTendency = Struct.new(:raw, :character, :value)
129
- Precipitation = Struct.new(:raw, :period, :amount)
130
- AutomatedStationType = Struct.new(:raw, :type)
131
- HourlyTemperatureAndDewPoint = Struct.new(:raw, :temperature, :dew_point)
132
- SeaLevelPressure = Struct.new(:raw, :pressure)
133
- SensorStatusIndicator = Struct.new(:raw, :type, :state)
134
- ColorCode = Struct.new(:raw, :code)
135
- MaintenanceNeeded = Struct.new(:raw)
136
152
  end
@@ -1,90 +1,110 @@
1
- class Metar::Data::RunwayVisibleRange < Metar::Data::Base
2
- TENDENCY = {'' => nil, 'N' => :no_change, 'U' => :improving, 'D' => :worsening}
3
- COMPARATOR = {'' => nil, 'P' => :more_than, 'M' => :less_than}
4
- UNITS = {'' => :meters, 'FT' => :feet}
1
+ # frozen_string_literal: true
5
2
 
6
- def self.parse(raw)
7
- return nil if raw.nil?
3
+ module Metar
4
+ module Data
5
+ class RunwayVisibleRange < Metar::Data::Base
6
+ TENDENCY = {
7
+ '' => nil,
8
+ 'N' => :no_change,
9
+ 'U' => :improving,
10
+ 'D' => :worsening
11
+ }.freeze
8
12
 
9
- m1 = raw.match(/^R(\d+[RLC]?)\/(P|M|)(\d{4})(FT|)\/?(N|U|D|)$/)
10
- if m1
11
- designator = m1[1]
12
- comparator = COMPARATOR[m1[2]]
13
- count = m1[3].to_f
14
- units = UNITS[m1[4]]
15
- tendency = TENDENCY[m1[5]]
16
- distance = Metar::Data::Distance.send(units, count)
17
- visibility = Metar::Data::Visibility.new(
18
- nil, distance: distance, comparator: comparator
19
- )
20
- return new(
21
- raw,
22
- designator: designator, visibility1: visibility, tendency: tendency
23
- )
24
- end
13
+ COMPARATOR = {'' => nil, 'P' => :more_than, 'M' => :less_than}.freeze
14
+ UNITS = {'' => :meters, 'FT' => :feet}.freeze
25
15
 
26
- m2 = raw.match(/^R(\d+[RLC]?)\/(P|M|)(\d{4})V(P|M|)(\d{4})(FT|)\/?(N|U|D)?$/)
27
- if m2
28
- designator = m2[1]
29
- comparator1 = COMPARATOR[m2[2]]
30
- count1 = m2[3].to_f
31
- comparator2 = COMPARATOR[m2[4]]
32
- count2 = m2[5].to_f
33
- units = UNITS[m2[6]]
34
- tendency = TENDENCY[m2[7]]
35
- distance1 = Metar::Data::Distance.send(units, count1)
36
- distance2 = Metar::Data::Distance.send(units, count2)
37
- visibility1 = Metar::Data::Visibility.new(
38
- nil, distance: distance1, comparator: comparator1
39
- )
40
- visibility2 = Metar::Data::Visibility.new(
41
- nil, distance: distance2, comparator: comparator2
42
- )
43
- return new(
44
- raw,
45
- designator: designator,
46
- visibility1: visibility1, visibility2: visibility2,
47
- tendency: tendency, units: units
48
- )
49
- end
16
+ def self.parse(raw)
17
+ return nil if raw.nil?
50
18
 
51
- nil
52
- end
19
+ m1 = raw.match(%r{^R(\d+[RLC]?)/(P|M|)(\d{4})(FT|)/?(N|U|D|)$})
20
+ if m1
21
+ designator = m1[1]
22
+ comparator = COMPARATOR[m1[2]]
23
+ count = m1[3].to_f
24
+ units = UNITS[m1[4]]
25
+ tendency = TENDENCY[m1[5]]
26
+ distance = Metar::Data::Distance.send(units, count)
27
+ visibility = Metar::Data::Visibility.new(
28
+ nil, distance: distance, comparator: comparator
29
+ )
30
+ return new(
31
+ raw,
32
+ designator: designator, visibility1: visibility, tendency: tendency
33
+ )
34
+ end
53
35
 
54
- attr_reader :designator, :visibility1, :visibility2, :tendency
36
+ m2 = raw.match(
37
+ %r{^R(\d+[RLC]?)/(P|M|)(\d{4})V(P|M|)(\d{4})(FT|)/?(N|U|D)?$}
38
+ )
39
+ if m2
40
+ designator = m2[1]
41
+ comparator1 = COMPARATOR[m2[2]]
42
+ count1 = m2[3].to_f
43
+ comparator2 = COMPARATOR[m2[4]]
44
+ count2 = m2[5].to_f
45
+ units = UNITS[m2[6]]
46
+ tendency = TENDENCY[m2[7]]
47
+ distance1 = Metar::Data::Distance.send(units, count1)
48
+ distance2 = Metar::Data::Distance.send(units, count2)
49
+ visibility1 = Metar::Data::Visibility.new(
50
+ nil, distance: distance1, comparator: comparator1
51
+ )
52
+ visibility2 = Metar::Data::Visibility.new(
53
+ nil, distance: distance2, comparator: comparator2
54
+ )
55
+ return new(
56
+ raw,
57
+ designator: designator,
58
+ visibility1: visibility1, visibility2: visibility2,
59
+ tendency: tendency, units: units
60
+ )
61
+ end
55
62
 
56
- def initialize(
57
- raw,
58
- designator:, visibility1:, visibility2: nil, tendency: nil, units: :meters
59
- )
60
- @raw = raw
61
- @designator, @visibility1, @visibility2, @tendency, @units = designator, visibility1, visibility2, tendency, units
62
- end
63
+ nil
64
+ end
65
+
66
+ attr_reader :designator, :visibility1, :visibility2, :tendency
63
67
 
64
- def to_s
65
- distance_options = {
66
- abbreviated: true,
67
- precision: 0,
68
- units: @units,
69
- }
70
- s =
71
- if @visibility2.nil?
72
- I18n.t('metar.runway_visible_range.runway') +
73
- ' ' + @designator +
74
- ': ' + @visibility1.to_s(distance_options)
75
- else
76
- I18n.t('metar.runway_visible_range.runway') +
77
- ' ' + @designator +
78
- ': ' + I18n.t('metar.runway_visible_range.from') +
79
- ' ' + @visibility1.to_s(distance_options) +
80
- ' ' + I18n.t('metar.runway_visible_range.to') +
81
- ' ' + @visibility2.to_s(distance_options)
68
+ def initialize(
69
+ raw,
70
+ designator:,
71
+ visibility1:,
72
+ visibility2: nil,
73
+ tendency: nil,
74
+ units: :meters
75
+ )
76
+ @raw = raw
77
+ @designator = designator
78
+ @visibility1 = visibility1
79
+ @visibility2 = visibility2
80
+ @tendency = tendency
81
+ @units = units
82
82
  end
83
83
 
84
- if ! tendency.nil?
85
- s += ' ' + I18n.t("tendency.#{tendency}")
86
- end
84
+ def to_s
85
+ distance_options = {
86
+ abbreviated: true,
87
+ precision: 0,
88
+ units: @units
89
+ }
90
+ s =
91
+ if @visibility2.nil?
92
+ I18n.t('metar.runway_visible_range.runway') +
93
+ ' ' + @designator +
94
+ ': ' + @visibility1.to_s(distance_options)
95
+ else
96
+ I18n.t('metar.runway_visible_range.runway') +
97
+ ' ' + @designator +
98
+ ': ' + I18n.t('metar.runway_visible_range.from') +
99
+ ' ' + @visibility1.to_s(distance_options) +
100
+ ' ' + I18n.t('metar.runway_visible_range.to') +
101
+ ' ' + @visibility2.to_s(distance_options)
102
+ end
103
+
104
+ s += ' ' + I18n.t("tendency.#{tendency}") if !tendency.nil?
87
105
 
88
- s
106
+ s
107
+ end
108
+ end
89
109
  end
90
110
  end
@@ -1,70 +1,81 @@
1
- class Metar::Data::SkyCondition < Metar::Data::Base
2
- QUANTITY = {'BKN' => 'broken', 'FEW' => 'few', 'OVC' => 'overcast', 'SCT' => 'scattered'}
3
- CONDITION = {
4
- 'CB' => 'cumulonimbus',
5
- 'TCU' => 'towering cumulus',
6
- '///' => nil, # cloud type unknown as observed by automatic system (15.9.1.7)
7
- '' => nil,
8
- }
9
- CLEAR_SKIES = [
10
- 'NSC', # WMO
11
- 'NCD', # WMO
12
- 'CLR',
13
- 'SKC',
14
- ]
1
+ # frozen_string_literal: true
15
2
 
16
- def self.parse(raw)
17
- if !raw
18
- return nil
19
- end
3
+ module Metar
4
+ module Data
5
+ class SkyCondition < Metar::Data::Base
6
+ QUANTITY = {
7
+ 'BKN' => 'broken',
8
+ 'FEW' => 'few',
9
+ 'OVC' => 'overcast',
10
+ 'SCT' => 'scattered'
11
+ }.freeze
20
12
 
21
- if CLEAR_SKIES.include?(raw)
22
- return new(raw)
23
- end
13
+ CONDITION = {
14
+ 'CB' => 'cumulonimbus',
15
+ 'TCU' => 'towering cumulus',
16
+ # /// - cloud type unknown as observed by automatic system (15.9.1.7)
17
+ '///' => nil,
18
+ '' => nil
19
+ }.freeze
20
+ CLEAR_SKIES = [
21
+ 'NSC', # WMO
22
+ 'NCD', # WMO
23
+ 'CLR',
24
+ 'SKC'
25
+ ].freeze
24
26
 
25
- m1 = raw.match(/^(BKN|FEW|OVC|SCT)(\d+|\/{3})(CB|TCU|\/{3}|)?$/)
26
- if m1
27
- quantity = QUANTITY[m1[1]]
28
- height =
29
- if m1[2] == '///'
30
- nil
31
- else
32
- Metar::Data::Distance.new(m1[2].to_i * 30.48)
27
+ def self.parse(raw)
28
+ return nil if !raw
29
+
30
+ return new(raw) if CLEAR_SKIES.include?(raw)
31
+
32
+ m1 = raw.match(%r{^(BKN|FEW|OVC|SCT)(\d+|/{3})(CB|TCU|/{3}|)?$})
33
+ if m1
34
+ quantity = QUANTITY[m1[1]]
35
+ height =
36
+ if m1[2] == '///'
37
+ nil
38
+ else
39
+ Metar::Data::Distance.new(m1[2].to_i * 30.48)
40
+ end
41
+ type = CONDITION[m1[3]]
42
+ return new(raw, quantity: quantity, height: height, type: type)
33
43
  end
34
- type = CONDITION[m1[3]]
35
- return new(raw, quantity: quantity, height: height, type: type)
36
- end
37
44
 
38
- m2 = raw.match(/^(CB|TCU)$/)
39
- if m2
40
- type = CONDITION[m2[1]]
41
- return new(raw, type: type)
42
- end
45
+ m2 = raw.match(/^(CB|TCU)$/)
46
+ if m2
47
+ type = CONDITION[m2[1]]
48
+ return new(raw, type: type)
49
+ end
43
50
 
44
- nil
45
- end
51
+ nil
52
+ end
46
53
 
47
- attr_reader :quantity, :height, :type
54
+ attr_reader :quantity, :height, :type
48
55
 
49
- def initialize(raw, quantity: nil, height: nil, type: nil)
50
- @raw = raw
51
- @quantity, @height, @type = quantity, height, type
52
- end
56
+ def initialize(raw, quantity: nil, height: nil, type: nil)
57
+ @raw = raw
58
+ @quantity = quantity
59
+ @height = height
60
+ @type = type
61
+ end
53
62
 
54
- def to_s
55
- if @height.nil?
56
- to_summary
57
- else
58
- to_summary + ' ' + I18n.t('metar.altitude.at') + ' ' + height.to_s
59
- end
60
- end
63
+ def to_s
64
+ if @height.nil?
65
+ to_summary
66
+ else
67
+ to_summary + ' ' + I18n.t('metar.altitude.at') + ' ' + height.to_s
68
+ end
69
+ end
61
70
 
62
- def to_summary
63
- if @quantity == nil and @height == nil and @type == nil
64
- I18n.t('metar.sky_conditions.clear skies')
65
- else
66
- type = @type ? ' ' + @type : ''
67
- I18n.t("metar.sky_conditions.#{@quantity}#{type}")
71
+ def to_summary
72
+ if @quantity.nil? && @height.nil? && @type.nil?
73
+ I18n.t('metar.sky_conditions.clear skies')
74
+ else
75
+ type = @type ? ' ' + @type : ''
76
+ I18n.t("metar.sky_conditions.#{@quantity}#{type}")
77
+ end
78
+ end
68
79
  end
69
80
  end
70
81
  end
@@ -1,20 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "i18n"
2
4
  require "m9t"
3
5
 
4
- # Adds a parse method to the M9t base class
5
- class Metar::Data::Speed < M9t::Speed
6
- METAR_UNITS = {
7
- "" => :kilometers_per_hour,
8
- "KMH" => :kilometers_per_hour,
9
- "MPS" => :meters_per_second,
10
- "KT" => :knots,
11
- }
6
+ module Metar
7
+ module Data
8
+ class Speed < M9t::Speed
9
+ METAR_UNITS = {
10
+ "" => :kilometers_per_hour,
11
+ "KMH" => :kilometers_per_hour,
12
+ "MPS" => :meters_per_second,
13
+ "KT" => :knots
14
+ }.freeze
15
+
16
+ def self.parse(raw)
17
+ return nil if raw.nil?
18
+
19
+ m = raw.match(/^(\d+)(|KT|MPS|KMH)$/)
20
+ return nil if m.nil?
12
21
 
13
- def self.parse(raw)
14
- return nil if raw.nil?
15
- m = raw.match(/^(\d+)(|KT|MPS|KMH)$/)
16
- return nil if m.nil?
17
- # Call the appropriate factory method for the supplied units
18
- return send(METAR_UNITS[m[2]], m[1].to_i)
22
+ # Call the appropriate factory method for the supplied units
23
+ send(METAR_UNITS[m[2]], m[1].to_i)
24
+ end
25
+ end
19
26
  end
20
27
  end
@@ -1,7 +1,11 @@
1
- class Metar::Data::StationCode < Metar::Data::Base
2
- def self.parse(raw)
3
- if raw =~ /^[A-Z][A-Z0-9]{3}$/
4
- new(raw)
1
+ # frozen_string_literal: true
2
+
3
+ module Metar
4
+ module Data
5
+ class StationCode < Metar::Data::Base
6
+ def self.parse(raw)
7
+ new(raw) if raw =~ /^[A-Z][A-Z0-9]{3}$/
8
+ end
5
9
  end
6
10
  end
7
11
  end