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,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::Temperature < M9t::Temperature
6
- def self.parse(raw)
7
- return nil if !raw
8
- m = raw.match(/^(M?)(\d+)$/)
9
- return nil if !m
10
- sign = m[1]
11
- value = m[2].to_i
12
- value *= -1 if sign == 'M'
13
- new(value)
14
- end
6
+ module Metar
7
+ module Data
8
+ class Temperature < M9t::Temperature
9
+ def self.parse(raw)
10
+ return nil if !raw
11
+
12
+ m = raw.match(/^(M?)(\d+)$/)
13
+ return nil if !m
14
+
15
+ sign = m[1]
16
+ value = m[2].to_i
17
+ value *= -1 if sign == 'M'
18
+ new(value)
19
+ end
15
20
 
16
- def to_s(options = {})
17
- options = {abbreviated: true, precision: 0}.merge(options)
18
- super(options)
21
+ def to_s(options = {})
22
+ options = {abbreviated: true, precision: 0}.merge(options)
23
+ super(options)
24
+ end
25
+ end
19
26
  end
20
27
  end
@@ -1,21 +1,27 @@
1
- class Metar::Data::TemperatureAndDewPoint < Metar::Data::Base
2
- def self.parse(raw)
3
- return nil if !raw
1
+ # frozen_string_literal: true
4
2
 
5
- m = raw.match(/^(M?\d+|XX|\/\/)\/(M?\d+|XX|\/\/)?$/)
6
- return nil if !m
3
+ module Metar
4
+ module Data
5
+ class TemperatureAndDewPoint < Metar::Data::Base
6
+ def self.parse(raw)
7
+ return nil if !raw
7
8
 
8
- temperature = Metar::Data::Temperature.parse(m[1])
9
- dew_point = Metar::Data::Temperature.parse(m[2])
10
- new(raw, temperature: temperature, dew_point: dew_point)
11
- end
9
+ m = raw.match(%r{^(M?\d+|XX|//)\/(M?\d+|XX|//)?$})
10
+ return nil if !m
11
+
12
+ temperature = Metar::Data::Temperature.parse(m[1])
13
+ dew_point = Metar::Data::Temperature.parse(m[2])
14
+ new(raw, temperature: temperature, dew_point: dew_point)
15
+ end
16
+
17
+ attr_reader :temperature
18
+ attr_reader :dew_point
12
19
 
13
- attr_reader :temperature
14
- attr_reader :dew_point
15
-
16
- def initialize(raw, temperature:, dew_point:)
17
- @raw = raw
18
- @temperature = temperature
19
- @dew_point = dew_point
20
+ def initialize(raw, temperature:, dew_point:)
21
+ @raw = raw
22
+ @temperature = temperature
23
+ @dew_point = dew_point
24
+ end
25
+ end
20
26
  end
21
27
  end
@@ -1,53 +1,63 @@
1
- class Metar::Data::Time < Metar::Data::Base
2
- def self.parse(raw, year: nil, month: nil, strict: true)
3
- year ||= DateTime.now.year
4
- month ||= DateTime.now.month
5
-
6
- date_matcher =
7
- if strict
8
- /^(\d{2})(\d{2})(\d{2})Z$/
9
- else
10
- /^(\d{1,2})(\d{2})(\d{2})Z$/
11
- end
1
+ # frozen_string_literal: true
12
2
 
13
- m1 = raw.match(date_matcher)
14
- if m1
15
- day, hour, minute = m1[1].to_i, m1[2].to_i, m1[3].to_i
16
- else
17
- return nil if strict
18
-
19
- m2 = raw.match(/^(\d{1,2})(\d{2})Z$/)
20
- return nil if !m2
21
- # The day is missing, use today's date
22
- day = Time.now.day
23
- hour, minute = m2[1].to_i, m2[2].to_i
24
- end
3
+ module Metar
4
+ module Data
5
+ class Time < Metar::Data::Base
6
+ def self.parse(raw, year: nil, month: nil, strict: true)
7
+ year ||= DateTime.now.year
8
+ month ||= DateTime.now.month
25
9
 
26
- new(
27
- raw,
28
- strict: strict,
29
- year: year, month: month, day: day, hour: hour, minute: minute
30
- )
31
- end
10
+ date_matcher =
11
+ if strict
12
+ /^(\d{2})(\d{2})(\d{2})Z$/
13
+ else
14
+ /^(\d{1,2})(\d{2})(\d{2})Z$/
15
+ end
32
16
 
33
- attr_reader :strict
34
- attr_reader :year
35
- attr_reader :month
36
- attr_reader :day
37
- attr_reader :hour
38
- attr_reader :minute
39
-
40
- def initialize(raw, strict:, year:, month:, day:, hour:, minute:)
41
- @raw = raw
42
- @strict = strict
43
- @year = year
44
- @month = month
45
- @day = day
46
- @hour = hour
47
- @minute = minute
48
- end
17
+ m1 = raw.match(date_matcher)
18
+ if m1
19
+ day = m1[1].to_i
20
+ hour = m1[2].to_i
21
+ minute = m1[3].to_i
22
+ else
23
+ return nil if strict
49
24
 
50
- def value
51
- Time.gm(year, month, day, hour, minute)
25
+ m2 = raw.match(/^(\d{1,2})(\d{2})Z$/)
26
+ return nil if !m2
27
+
28
+ # The day is missing, use today's date
29
+ day = ::Time.now.day
30
+ hour = m2[1].to_i
31
+ minute = m2[2].to_i
32
+ end
33
+
34
+ new(
35
+ raw,
36
+ strict: strict,
37
+ year: year, month: month, day: day, hour: hour, minute: minute
38
+ )
39
+ end
40
+
41
+ attr_reader :strict
42
+ attr_reader :year
43
+ attr_reader :month
44
+ attr_reader :day
45
+ attr_reader :hour
46
+ attr_reader :minute
47
+
48
+ def initialize(raw, strict:, year:, month:, day:, hour:, minute:)
49
+ @raw = raw
50
+ @strict = strict
51
+ @year = year
52
+ @month = month
53
+ @day = day
54
+ @hour = hour
55
+ @minute = minute
56
+ end
57
+
58
+ def value
59
+ ::Time.gm(year, month, day, hour, minute)
60
+ end
61
+ end
52
62
  end
53
63
  end
@@ -1,26 +1,37 @@
1
- class Metar::Data::VariableWind < Metar::Data::Base
2
- def self.parse(raw)
3
- return nil if raw.nil?
1
+ # frozen_string_literal: true
4
2
 
5
- m = raw.match(/^(\d+)V(\d+)$/)
6
- return nil if m.nil?
3
+ module Metar
4
+ module Data
5
+ class VariableWind < Metar::Data::Base
6
+ def self.parse(raw)
7
+ return nil if raw.nil?
7
8
 
8
- return new(
9
- raw,
10
- direction1: Metar::Data::Direction.new(m[1]),
11
- direction2: Metar::Data::Direction.new(m[2])
12
- )
13
- end
9
+ m = raw.match(/^(\d+)V(\d+)$/)
10
+ return nil if m.nil?
14
11
 
15
- attr_reader :direction1
16
- attr_reader :direction2
12
+ new(
13
+ raw,
14
+ direction1: Metar::Data::Direction.new(m[1]),
15
+ direction2: Metar::Data::Direction.new(m[2])
16
+ )
17
+ end
17
18
 
18
- def initialize(raw, direction1:, direction2:)
19
- @raw = raw
20
- @direction1, @direction2 = direction1, direction2
21
- end
19
+ attr_reader :direction1
20
+ attr_reader :direction2
21
+
22
+ def initialize(raw, direction1:, direction2:)
23
+ @raw = raw
24
+ @direction1 = direction1
25
+ @direction2 = direction2
26
+ end
22
27
 
23
- def to_s
24
- "#{direction1.to_s(units: :compass)} - #{direction2.to_s(units: :compass)}"
28
+ def to_s
29
+ format(
30
+ "%<direction1>s - %<direction2>s",
31
+ direction1: direction1.to_s(units: :compass),
32
+ direction2: direction2.to_s(units: :compass)
33
+ )
34
+ end
35
+ end
25
36
  end
26
37
  end
@@ -1,31 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "i18n"
2
4
  require "m9t"
3
5
 
4
- class Metar::Data::VerticalVisibility < Metar::Data::Base
5
- def self.parse(raw)
6
- if !raw
7
- return nil
8
- end
9
- m1 = raw.match(/^VV(\d{3})$/)
10
- if m1
11
- return new(raw, distance: Metar::Data::Distance.new(m1[1].to_f * 30.48))
12
- end
6
+ module Metar
7
+ module Data
8
+ class VerticalVisibility < Metar::Data::Base
9
+ def self.parse(raw)
10
+ return nil if !raw
13
11
 
14
- if raw == '///'
15
- return new(raw, distance: Metar::Data::Distance.new)
16
- end
12
+ m1 = raw.match(/^VV(\d{3})$/)
13
+ if m1
14
+ return new(
15
+ raw,
16
+ distance: Metar::Data::Distance.new(m1[1].to_f * 30.48)
17
+ )
18
+ end
17
19
 
18
- nil
19
- end
20
+ return new(raw, distance: Metar::Data::Distance.new) if raw == '///'
20
21
 
21
- attr_reader :distance
22
+ nil
23
+ end
22
24
 
23
- def initialize(raw, distance:)
24
- @raw = raw
25
- @distance = distance
26
- end
25
+ attr_reader :distance
27
26
 
28
- def value
29
- distance.value
27
+ def initialize(raw, distance:)
28
+ @raw = raw
29
+ @distance = distance
30
+ end
31
+
32
+ def value
33
+ distance.value
34
+ end
35
+ end
30
36
  end
31
37
  end
@@ -1,94 +1,106 @@
1
- class Metar::Data::Visibility < Metar::Data::Base
2
- def self.parse(raw)
3
- if !raw
4
- return nil
5
- end
1
+ # frozen_string_literal: true
6
2
 
7
- if raw == '9999'
8
- return new(raw, distance: Metar::Data::Distance.new(10000), comparator: :more_than)
9
- end
3
+ module Metar
4
+ module Data
5
+ class Visibility < Metar::Data::Base
6
+ def self.parse(raw)
7
+ return nil if !raw
10
8
 
11
- m1 = raw.match(/(\d{4})NDV/) # WMO
12
- if m1
13
- return new(raw, distance: Metar::Data::Distance.new(m1[1].to_f)) # Assuming meters
14
- end
9
+ if raw == '9999'
10
+ return new(
11
+ raw,
12
+ distance: Metar::Data::Distance.new(10_000),
13
+ comparator: :more_than
14
+ )
15
+ end
15
16
 
16
- m2 = raw.match(/^((1|2)\s|)([1357])\/([248]|16)SM$/) # US
17
- if m2
18
- miles = m2[1].to_f + m2[3].to_f / m2[4].to_f
19
- distance = Metar::Data::Distance.miles(miles)
20
- distance.serialization_units = :miles
21
- return new(raw, distance: distance)
22
- end
17
+ m1 = raw.match(/(\d{4})NDV/) # WMO
18
+ if m1
19
+ return new(
20
+ raw, distance: Metar::Data::Distance.new(m1[1].to_f)
21
+ ) # Assuming meters
22
+ end
23
23
 
24
- m3 = raw.match(/^(\d+)SM$/) # US
25
- if m3
26
- distance = Metar::Data::Distance.miles(m3[1].to_f)
27
- distance.serialization_units = :miles
28
- return new(raw, distance: distance)
29
- end
24
+ m2 = raw.match(%r{^((1|2)\s|)([1357])/([248]|16)SM$}) # US
25
+ if m2
26
+ numerator = m2[3].to_f
27
+ denominator = m2[4].to_f
28
+ miles = m2[1].to_f + numerator / denominator
29
+ distance = Metar::Data::Distance.miles(miles)
30
+ distance.serialization_units = :miles
31
+ return new(raw, distance: distance)
32
+ end
30
33
 
31
- if raw == 'M1/4SM' # US
32
- distance = Metar::Data::Distance.miles(0.25)
33
- distance.serialization_units = :miles
34
- return new(raw, distance: distance, comparator: :less_than)
35
- end
34
+ m3 = raw.match(/^(\d+)SM$/) # US
35
+ if m3
36
+ distance = Metar::Data::Distance.miles(m3[1].to_f)
37
+ distance.serialization_units = :miles
38
+ return new(raw, distance: distance)
39
+ end
36
40
 
37
- m4 = raw.match(/^(\d+)KM$/)
38
- if m4
39
- return new(raw, distance: Metar::Data::Distance.kilometers(m4[1]))
40
- end
41
+ if raw == 'M1/4SM' # US
42
+ distance = Metar::Data::Distance.miles(0.25)
43
+ distance.serialization_units = :miles
44
+ return new(raw, distance: distance, comparator: :less_than)
45
+ end
41
46
 
42
- m5 = raw.match(/^(\d+)$/) # We assume meters
43
- if m5
44
- return new(raw, distance: Metar::Data::Distance.new(m5[1]))
45
- end
47
+ m4 = raw.match(/^(\d+)KM$/)
48
+ return new(raw, distance: Metar::Data::Distance.kilometers(m4[1])) if m4
46
49
 
47
- m6 = raw.match(/^(\d+)(N|NE|E|SE|S|SW|W|NW)$/)
48
- if m6
49
- return new(
50
- raw,
51
- distance: Metar::Data::Distance.meters(m6[1]),
52
- direction: M9t::Direction.compass(m6[2])
53
- )
54
- end
50
+ m5 = raw.match(/^(\d+)$/) # We assume meters
51
+ return new(raw, distance: Metar::Data::Distance.new(m5[1])) if m5
55
52
 
56
- nil
57
- end
53
+ m6 = raw.match(/^(\d+)(N|NE|E|SE|S|SW|W|NW)$/)
54
+ if m6
55
+ return new(
56
+ raw,
57
+ distance: Metar::Data::Distance.meters(m6[1]),
58
+ direction: M9t::Direction.compass(m6[2])
59
+ )
60
+ end
58
61
 
59
- attr_reader :distance, :direction, :comparator
62
+ nil
63
+ end
60
64
 
61
- def initialize(raw, distance:, direction: nil, comparator: nil)
62
- @raw = raw
63
- @distance, @direction, @comparator = distance, direction, comparator
64
- end
65
+ attr_reader :distance, :direction, :comparator
66
+
67
+ def initialize(raw, distance:, direction: nil, comparator: nil)
68
+ @raw = raw
69
+ @distance = distance
70
+ @direction = direction
71
+ @comparator = comparator
72
+ end
73
+
74
+ def to_s(options = {})
75
+ distance_options = {
76
+ abbreviated: true,
77
+ precision: 0,
78
+ units: :kilometers
79
+ }.merge(options)
80
+
81
+ direction_options = {units: :compass}
65
82
 
66
- def to_s(options = {})
67
- distance_options = {
68
- abbreviated: true,
69
- precision: 0,
70
- units: :kilometers,
71
- }.merge(options)
72
- direction_options = {units: :compass}
73
- case
74
- when (@direction.nil? and @comparator.nil?)
75
- @distance.to_s(distance_options)
76
- when @comparator.nil?
77
- [
78
- @distance.to_s(distance_options),
79
- @direction.to_s(direction_options),
80
- ].join(' ')
81
- when @direction.nil?
82
- [
83
- I18n.t('comparison.' + @comparator.to_s),
84
- @distance.to_s(distance_options),
85
- ].join(' ')
86
- else
87
- [
88
- I18n.t('comparison.' + @comparator.to_s),
89
- @distance.to_s(distance_options),
90
- @direction.to_s(direction_options),
91
- ].join(' ')
83
+ case
84
+ when @direction.nil? && @comparator.nil?
85
+ @distance.to_s(distance_options)
86
+ when @comparator.nil?
87
+ [
88
+ @distance.to_s(distance_options),
89
+ @direction.to_s(direction_options)
90
+ ].join(' ')
91
+ when @direction.nil?
92
+ [
93
+ I18n.t('comparison.' + @comparator.to_s),
94
+ @distance.to_s(distance_options)
95
+ ].join(' ')
96
+ else
97
+ [
98
+ I18n.t('comparison.' + @comparator.to_s),
99
+ @distance.to_s(distance_options),
100
+ @direction.to_s(direction_options)
101
+ ].join(' ')
102
+ end
103
+ end
92
104
  end
93
105
  end
94
106
  end
@@ -1,8 +1,19 @@
1
- class Metar::Data::VisibilityRemark < Metar::Data::Visibility
2
- def self.parse(raw)
3
- metres, direction = raw.scan(/^(\d{4})([NESW]?)$/)[0]
4
- distance = Metar::Data::Distance.new(metres)
1
+ # frozen_string_literal: true
5
2
 
6
- new(raw, distance: distance, direction: direction, comparator: :more_than)
3
+ module Metar
4
+ module Data
5
+ class VisibilityRemark < Metar::Data::Visibility
6
+ def self.parse(raw)
7
+ metres, direction = raw.scan(/^(\d{4})([NESW]?)$/)[0]
8
+ distance = Metar::Data::Distance.new(metres)
9
+
10
+ new(
11
+ raw,
12
+ distance: distance,
13
+ direction: direction,
14
+ comparator: :more_than
15
+ )
16
+ end
17
+ end
7
18
  end
8
19
  end