metar-parser 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/metar/data.rb +15 -8
- data/lib/metar/parser.rb +11 -43
- data/lib/metar/report.rb +77 -32
- data/lib/metar/station.rb +5 -5
- data/lib/metar.rb +2 -2
- data/locales/en.yml +32 -1
- data/locales/it.yml +32 -1
- data/test/metar_test_helper.rb +19 -0
- data/test/unit/data_test.rb +31 -16
- data/test/unit/parser_test.rb +46 -0
- data/test/unit/raw_test.rb +10 -1
- data/test/unit/report_test.rb +18 -4
- data/test/unit/station_test.rb +33 -13
- metadata +2 -2
data/lib/metar/data.rb
CHANGED
@@ -41,9 +41,9 @@ module Metar
|
|
41
41
|
case
|
42
42
|
when s =~ /^(\d+)(KT|MPS|KMH)$/
|
43
43
|
# Call the appropriate factory method for the supplied units
|
44
|
-
send(METAR_UNITS[$2], $1.to_i, { :units =>
|
44
|
+
send(METAR_UNITS[$2], $1.to_i, { :units => :kilometers_per_hour, :precision => 0, :abbreviated => true })
|
45
45
|
when s =~ /^(\d+)$/
|
46
|
-
kilometers_per_hour($1.to_i, { :units => :kilometers_per_hour, :precision => 0 })
|
46
|
+
kilometers_per_hour($1.to_i, { :units => :kilometers_per_hour, :precision => 0, :abbreviated => true })
|
47
47
|
else
|
48
48
|
nil
|
49
49
|
end
|
@@ -88,9 +88,9 @@ module Metar
|
|
88
88
|
def Wind.parse(s)
|
89
89
|
case
|
90
90
|
when s =~ /^(\d{3})(\d{2}(|MPS|KMH|KT))$/
|
91
|
-
new(M9t::Direction.new($1, { :abbreviated => true }), Speed.parse($2))
|
91
|
+
new(M9t::Direction.new($1, { :units => :compass, :abbreviated => true }), Speed.parse($2))
|
92
92
|
when s =~ /^(\d{3})(\d{2})G(\d{2,3}(|MPS|KMH|KT))$/
|
93
|
-
new(M9t::Direction.new($1, { :abbreviated => true }), Speed.parse($2))
|
93
|
+
new(M9t::Direction.new($1, { :units => :compass, :abbreviated => true }), Speed.parse($2))
|
94
94
|
when s =~ /^VRB(\d{2}(|MPS|KMH|KT))$/
|
95
95
|
new(:variable_direction, Speed.parse($1))
|
96
96
|
when s =~ /^\/{3}(\d{2}(|MPS|KMH|KT))$/
|
@@ -125,7 +125,7 @@ module Metar
|
|
125
125
|
else
|
126
126
|
@speed.to_s
|
127
127
|
end
|
128
|
-
"#{
|
128
|
+
"#{ speed } #{ direction }"
|
129
129
|
end
|
130
130
|
|
131
131
|
end
|
@@ -325,7 +325,7 @@ module Metar
|
|
325
325
|
def to_s
|
326
326
|
modifier = @modifier ? @modifier + ' ' : ''
|
327
327
|
descriptor = @descriptor ? @descriptor + ' ' : ''
|
328
|
-
I18n.t("metar.
|
328
|
+
I18n.t("metar.present_weather.%s%s%s" % [modifier, descriptor, @phenomenon])
|
329
329
|
end
|
330
330
|
|
331
331
|
end
|
@@ -333,6 +333,7 @@ module Metar
|
|
333
333
|
class SkyCondition
|
334
334
|
|
335
335
|
QUANTITY = {'BKN' => 'broken', 'FEW' => 'few', 'OVC' => 'overcast', 'SCT' => 'scattered'}
|
336
|
+
|
336
337
|
def SkyCondition.parse(sky_condition)
|
337
338
|
case
|
338
339
|
when (sky_condition == 'NSC' or sky_condition == 'NCD') # WMO
|
@@ -355,7 +356,7 @@ module Metar
|
|
355
356
|
when '///'
|
356
357
|
nil
|
357
358
|
else
|
358
|
-
raise
|
359
|
+
raise ParseError.new("Unexpected sky condition type: #$3")
|
359
360
|
end
|
360
361
|
new(quantity, height, type)
|
361
362
|
else
|
@@ -369,11 +370,17 @@ module Metar
|
|
369
370
|
end
|
370
371
|
|
371
372
|
def to_s
|
373
|
+
conditions = to_summary
|
374
|
+
conditions += ' ' + I18n.t('metar.altitude.at') + ' ' + height.to_s if not @height.nil?
|
375
|
+
conditions
|
376
|
+
end
|
377
|
+
|
378
|
+
def to_summary
|
372
379
|
if @quantity == nil and @height == nil and @type == nil
|
373
380
|
I18n.t('metar.sky_conditions.clear skies')
|
374
381
|
else
|
375
382
|
type = @type ? ' ' + @type : ''
|
376
|
-
I18n.t("metar.sky_conditions.#{ @quantity }#{ type }")
|
383
|
+
I18n.t("metar.sky_conditions.#{ @quantity }#{ type }")
|
377
384
|
end
|
378
385
|
end
|
379
386
|
|
data/lib/metar/parser.rb
CHANGED
@@ -4,9 +4,6 @@ require File.join(File.dirname(__FILE__), 'data')
|
|
4
4
|
|
5
5
|
module Metar
|
6
6
|
|
7
|
-
class ParseError < StandardError
|
8
|
-
end
|
9
|
-
|
10
7
|
class Parser
|
11
8
|
include AASM
|
12
9
|
|
@@ -95,7 +92,7 @@ module Metar
|
|
95
92
|
parser
|
96
93
|
end
|
97
94
|
|
98
|
-
attr_reader :station_code, :
|
95
|
+
attr_reader :station_code, :observer, :time, :wind, :variable_wind, :visibility, :runway_visible_range,
|
99
96
|
:present_weather, :sky_conditions, :vertical_visibility, :temperature, :dew_point, :sea_level_pressure, :remarks
|
100
97
|
|
101
98
|
def initialize(raw)
|
@@ -104,26 +101,6 @@ module Metar
|
|
104
101
|
analyze
|
105
102
|
end
|
106
103
|
|
107
|
-
def attributes
|
108
|
-
h = {
|
109
|
-
:station_code => @location.clone,
|
110
|
-
:time => @time.to_s,
|
111
|
-
:observer => Report.symbol_to_s(@observer)
|
112
|
-
}
|
113
|
-
h[:wind] = @wind if @wind
|
114
|
-
h[:variable_wind] = @variable_wind.clone if @variable_wind
|
115
|
-
h[:visibility] = @visibility if @visibility
|
116
|
-
h[:runway_visible_range] = @runway_visible_range if @runway_visible_range
|
117
|
-
h[:present_weather] = @present_weather if @present_weather
|
118
|
-
h[:sky_conditions] = @sky_conditions if @sky_conditions
|
119
|
-
h[:vertical_visibility] = @vertical_visibility if @vertical_visibility
|
120
|
-
h[:temperature] = @temperature
|
121
|
-
h[:dew_point] = @dew_point
|
122
|
-
h[:sea_level_pressure] = @sea_level_pressure
|
123
|
-
h[:remarks] = @remarks.clone if @remarks
|
124
|
-
h
|
125
|
-
end
|
126
|
-
|
127
104
|
def date
|
128
105
|
Date.new(@time.year, @time.month, @time.day)
|
129
106
|
end
|
@@ -133,26 +110,26 @@ module Metar
|
|
133
110
|
def analyze
|
134
111
|
@chunks = @metar.split(' ')
|
135
112
|
|
136
|
-
@
|
113
|
+
@station_code = nil
|
137
114
|
@observer = :real
|
138
115
|
@wind = nil
|
139
116
|
@variable_wind = nil
|
140
117
|
@visibility = nil
|
141
|
-
@runway_visible_range =
|
142
|
-
@present_weather =
|
143
|
-
@sky_conditions =
|
118
|
+
@runway_visible_range = []
|
119
|
+
@present_weather = []
|
120
|
+
@sky_conditions = []
|
144
121
|
@vertical_visibility = nil
|
145
122
|
@temperature = nil
|
146
123
|
@dew_point = nil
|
147
124
|
@sea_level_pressure = nil
|
148
|
-
@remarks =
|
125
|
+
@remarks = []
|
149
126
|
|
150
127
|
aasm_enter_initial_state
|
151
128
|
end
|
152
129
|
|
153
130
|
def seek_location
|
154
131
|
if @chunks[0] =~ /^[A-Z][A-Z0-9]{3}$/
|
155
|
-
@
|
132
|
+
@station_code = @chunks.shift
|
156
133
|
else
|
157
134
|
raise ParseError.new("Expecting location, found '#{ @chunks[0] }'")
|
158
135
|
end
|
@@ -204,18 +181,15 @@ module Metar
|
|
204
181
|
if @chunks[0] == 'CAVOK'
|
205
182
|
@chunks.shift
|
206
183
|
@visibility = Visibility.new(M9t::Distance.kilometers(10), nil, :more_than)
|
207
|
-
@present_weather ||= []
|
208
184
|
@present_weather << Metar::WeatherPhenomenon.new('No significant weather')
|
209
|
-
@sky_conditions
|
210
|
-
@sky_conditions << 'No significant cloud' # TODO: What does NSC stand for?
|
185
|
+
@sky_conditions << SkyCondition.new # = 'clear skies'
|
211
186
|
cavok!
|
212
187
|
return
|
213
188
|
end
|
214
189
|
|
215
190
|
if @observer == :auto # WMO 15.4
|
216
191
|
if @chunks[0] == '////'
|
217
|
-
@chunks.shift
|
218
|
-
@visibility = Visibility.new('Not observed')
|
192
|
+
@chunks.shift # Simply dispose of it
|
219
193
|
visibility!
|
220
194
|
return
|
221
195
|
end
|
@@ -242,7 +216,6 @@ module Metar
|
|
242
216
|
runway_visible_range = RunwayVisibleRange.parse(@chunks[0])
|
243
217
|
if runway_visible_range
|
244
218
|
@chunks.shift
|
245
|
-
@runway_visible_range ||= []
|
246
219
|
@runway_visible_range << runway_visible_range
|
247
220
|
collect_runway_visible_range
|
248
221
|
end
|
@@ -257,7 +230,6 @@ module Metar
|
|
257
230
|
wtp = WeatherPhenomenon.parse(@chunks[0])
|
258
231
|
if wtp
|
259
232
|
@chunks.shift
|
260
|
-
@present_weather ||= []
|
261
233
|
@present_weather << wtp
|
262
234
|
collect_present_weather
|
263
235
|
end
|
@@ -266,7 +238,7 @@ module Metar
|
|
266
238
|
def seek_present_weather
|
267
239
|
if @observer == :auto
|
268
240
|
if @chunks[0] == '//' # WMO 15.4
|
269
|
-
@
|
241
|
+
@chunks.shift # Simply dispose of it
|
270
242
|
@present_weather << Metar::WeatherPhenomenon.new('not observed')
|
271
243
|
present_weather!
|
272
244
|
return
|
@@ -281,7 +253,6 @@ module Metar
|
|
281
253
|
sky_condition = SkyCondition.parse(@chunks[0])
|
282
254
|
if sky_condition
|
283
255
|
@chunks.shift
|
284
|
-
@sky_conditions ||= []
|
285
256
|
@sky_conditions << sky_condition
|
286
257
|
collect_sky_conditions
|
287
258
|
end
|
@@ -290,9 +261,7 @@ module Metar
|
|
290
261
|
def seek_sky_conditions
|
291
262
|
if @observer == :auto # WMO 15.4
|
292
263
|
if @chunks[0] == '///' or @chunks[0] == '//////'
|
293
|
-
@chunks.shift
|
294
|
-
@sky_conditions ||= []
|
295
|
-
@sky_conditions << 'Not observed'
|
264
|
+
@chunks.shift # Simply dispose of it
|
296
265
|
sky_conditions!
|
297
266
|
return
|
298
267
|
end
|
@@ -336,7 +305,6 @@ module Metar
|
|
336
305
|
if @chunks[0] == 'RMK'
|
337
306
|
@chunks.shift
|
338
307
|
end
|
339
|
-
@remarks ||= []
|
340
308
|
@remarks += @chunks.clone
|
341
309
|
@chunks = []
|
342
310
|
remarks!
|
data/lib/metar/report.rb
CHANGED
@@ -1,10 +1,63 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Metar
|
4
|
+
|
4
5
|
class Report
|
5
6
|
|
7
|
+
KNOWN_ATTRIBUTES =
|
8
|
+
[
|
9
|
+
:station_code, :station_name, :station_country,
|
10
|
+
:date, :time, :observer,
|
11
|
+
:wind, :variable_wind,
|
12
|
+
:visibility, :runway_visible_range,
|
13
|
+
:present_weather,
|
14
|
+
:sky_summary, :sky_conditions,
|
15
|
+
:temperature, :dew_point, :remarks
|
16
|
+
]
|
17
|
+
|
18
|
+
DEFAULT_ATTRIBUTES =
|
19
|
+
[
|
20
|
+
:station_name, :station_country,
|
21
|
+
:time,
|
22
|
+
:wind,
|
23
|
+
:visibility,
|
24
|
+
:present_weather,
|
25
|
+
:sky_summary,
|
26
|
+
:temperature
|
27
|
+
]
|
28
|
+
|
29
|
+
instance_eval do
|
30
|
+
|
31
|
+
def reset_options!
|
32
|
+
@attributes = DEFAULT_ATTRIBUTES.clone
|
33
|
+
end
|
34
|
+
|
35
|
+
def attributes
|
36
|
+
@attributes
|
37
|
+
end
|
38
|
+
|
39
|
+
def attributes=(attributes)
|
40
|
+
@attributes = attributes.clone
|
41
|
+
end
|
42
|
+
|
43
|
+
reset_options!
|
44
|
+
end
|
45
|
+
|
6
46
|
def initialize(parser)
|
7
47
|
@parser = parser
|
48
|
+
@station = Station.find_by_cccc(@parser.station_code)
|
49
|
+
end
|
50
|
+
|
51
|
+
def station_name
|
52
|
+
@station.name
|
53
|
+
end
|
54
|
+
|
55
|
+
def station_country
|
56
|
+
@station.country
|
57
|
+
end
|
58
|
+
|
59
|
+
def station_code
|
60
|
+
@parser.station_code
|
8
61
|
end
|
9
62
|
|
10
63
|
def date
|
@@ -15,6 +68,10 @@ module Metar
|
|
15
68
|
"%u:%u" % [@parser.time.hour, @parser.time.min]
|
16
69
|
end
|
17
70
|
|
71
|
+
def observer
|
72
|
+
I18n.t('metar.observer.' + @parser.observer.to_s)
|
73
|
+
end
|
74
|
+
|
18
75
|
def wind
|
19
76
|
@parser.wind.to_s
|
20
77
|
end
|
@@ -35,6 +92,11 @@ module Metar
|
|
35
92
|
@parser.present_weather.to_s
|
36
93
|
end
|
37
94
|
|
95
|
+
def sky_summary
|
96
|
+
return I18n.t('metar.sky_conditions.clear skies') if @parser.sky_conditions.length == 0
|
97
|
+
@parser.sky_conditions[-1].to_summary
|
98
|
+
end
|
99
|
+
|
38
100
|
def sky_conditions
|
39
101
|
@parser.sky_conditions.collect { |sky| sky.to_s }.join(', ')
|
40
102
|
end
|
@@ -55,41 +117,24 @@ module Metar
|
|
55
117
|
@parser.sea_level_pressure.to_s
|
56
118
|
end
|
57
119
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
texts[:variable_wind] = attrib[:variable_wind] if attrib[:variable_wind]
|
69
|
-
texts[:visibility] = "%u meters" % attrib[:visibility].value if attrib[:visibility]
|
70
|
-
texts[:runway_visible_range] = attrib[:runway_visible_range].join(', ') if attrib[:runway_visible_range]
|
71
|
-
texts[:present_weather] = attrib[:present_weather].join(', ') if attrib[:present_weather]
|
72
|
-
texts[:sky_conditions] = attrib[:sky_conditions].join(', ') if attrib[:sky_conditions]
|
73
|
-
texts[:temperature] = "%u celcius" % attrib[:temperature] if attrib[:temperature]
|
74
|
-
texts[:dew_point] = "%u celcius" % attrib[:dew_point] if attrib[:dew_point]
|
75
|
-
texts[:remarks] = attrib[:remarks].join(', ') if attrib[:remarks]
|
76
|
-
|
77
|
-
texts
|
120
|
+
def remarks
|
121
|
+
@parser.remarks.join(', ')
|
122
|
+
end
|
123
|
+
|
124
|
+
def attributes
|
125
|
+
Metar::Report.attributes.reduce([]) do |memo, key|
|
126
|
+
value = self.send(key).to_s
|
127
|
+
memo << {:attribute => key, :value => value} if not value.empty?
|
128
|
+
memo
|
129
|
+
end
|
78
130
|
end
|
79
131
|
|
80
132
|
def to_s
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
[:station_code, :time, :observer, :wind, :variable_wind, :visibility, :runway_visible_range,
|
85
|
-
:present_weather, :sky_conditions, :temperature, :dew_point, :remarks].collect do |key|
|
86
|
-
attr[key] ? self.symbol_to_s(key) + ": " + attr[key] : nil
|
87
|
-
end.compact.join("\n")
|
133
|
+
attributes.collect do |attribute|
|
134
|
+
I18n.t('metar.' + attribute[:attribute].to_s + '.title') + ': ' + attribute[:value]
|
135
|
+
end.join("\n")
|
88
136
|
end
|
89
137
|
|
90
|
-
|
138
|
+
end
|
91
139
|
|
92
|
-
|
93
|
-
def self.symbol_to_s(sym)
|
94
|
-
sym.to_s.gsub(/^([a-z])/) {$1.upcase}.gsub(/_([a-z])/) {" #$1"}
|
95
|
-
end
|
140
|
+
end
|
data/lib/metar/station.rb
CHANGED
@@ -23,7 +23,7 @@ module Metar
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# Load local copy of the station list
|
26
|
-
# and download it first if
|
26
|
+
# and download it first if missing
|
27
27
|
def load_local
|
28
28
|
download_local if not File.exist?(Metar::Station.local_nsd_path)
|
29
29
|
@nsd_cccc = File.open(Metar::Station.local_nsd_path) do |fil|
|
@@ -39,15 +39,15 @@ module Metar
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def find_by_cccc(cccc)
|
43
|
-
all.find { |station| station.cccc == cccc }
|
44
|
-
end
|
45
|
-
|
46
42
|
# Does the given CCCC code exist?
|
47
43
|
def exist?(cccc)
|
48
44
|
not find_data_by_cccc(cccc).nil?
|
49
45
|
end
|
50
46
|
|
47
|
+
def find_by_cccc(cccc)
|
48
|
+
all.find { |station| station.cccc == cccc }
|
49
|
+
end
|
50
|
+
|
51
51
|
def to_longitude(s)
|
52
52
|
s =~ /^(\d+)-(\d+)([EW])/ or return nil
|
53
53
|
($3 == 'E' ? 1.0 : -1.0) * ($1.to_f + $2.to_f / 60.0)
|
data/lib/metar.rb
CHANGED
@@ -8,7 +8,7 @@ module Metar
|
|
8
8
|
module VERSION #:nodoc:
|
9
9
|
MAJOR = 0
|
10
10
|
MINOR = 1
|
11
|
-
TINY =
|
11
|
+
TINY = 4
|
12
12
|
|
13
13
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
14
14
|
end
|
@@ -18,7 +18,7 @@ module Metar
|
|
18
18
|
end
|
19
19
|
|
20
20
|
# Raised when an unrecognized value is found
|
21
|
-
class
|
21
|
+
class ParseError < MetarError
|
22
22
|
end
|
23
23
|
|
24
24
|
end
|
data/locales/en.yml
CHANGED
@@ -11,15 +11,37 @@ en:
|
|
11
11
|
bar:
|
12
12
|
full: bar
|
13
13
|
metar:
|
14
|
+
station_code:
|
15
|
+
title: station code
|
16
|
+
station_name:
|
17
|
+
title: name
|
18
|
+
station_country:
|
19
|
+
title: country
|
20
|
+
time:
|
21
|
+
title: time
|
22
|
+
observer:
|
23
|
+
title: observer
|
24
|
+
real: real
|
25
|
+
auto: automatic
|
26
|
+
corrected: corrected
|
14
27
|
altitude:
|
15
28
|
at: at
|
16
29
|
distance:
|
30
|
+
title: distance
|
17
31
|
unknown: unknown
|
18
32
|
wind:
|
33
|
+
title: wind
|
19
34
|
variable_direction: variable direction
|
20
35
|
unknown_direction: unknown direction
|
21
36
|
unknown_speed: unknown speed
|
37
|
+
variable_wind:
|
38
|
+
title: wind variation
|
39
|
+
visibility:
|
40
|
+
title: visibility
|
41
|
+
sky_summary:
|
42
|
+
title: sky
|
22
43
|
sky_conditions:
|
44
|
+
title: sky conditions
|
23
45
|
clear skies: clear skies
|
24
46
|
broken: broken cloud
|
25
47
|
broken cumulonimbus: broken cumulonimbus
|
@@ -32,10 +54,13 @@ en:
|
|
32
54
|
scattered towering cumulus: scattered towering cumulus clouds
|
33
55
|
overcast: overcast
|
34
56
|
runway_visible_range:
|
57
|
+
title: runway visible range
|
35
58
|
runway: runway
|
36
59
|
from: from
|
37
60
|
to: to
|
38
|
-
|
61
|
+
present_weather:
|
62
|
+
title: weather
|
63
|
+
not observed: not observed
|
39
64
|
mist: mist
|
40
65
|
dust: dust
|
41
66
|
blowing dust: blowing dust
|
@@ -119,3 +144,9 @@ en:
|
|
119
144
|
light thunderstorm and unknown phenomenon: light thunderstorm and unknown phenomenon
|
120
145
|
heavy unknown phenomenon: heavy unknown phenomenon
|
121
146
|
light unknown phenomenon: light unknown phenomenon
|
147
|
+
temperature:
|
148
|
+
title: temperature
|
149
|
+
dew_point:
|
150
|
+
title: punto di rugiada
|
151
|
+
remarks:
|
152
|
+
title: annotazioni
|
data/locales/it.yml
CHANGED
@@ -11,15 +11,37 @@ it:
|
|
11
11
|
bar:
|
12
12
|
full: bar
|
13
13
|
metar:
|
14
|
+
station_code:
|
15
|
+
title: codice stazione
|
16
|
+
station_name:
|
17
|
+
title: nome
|
18
|
+
station_country:
|
19
|
+
title: nazione
|
20
|
+
time:
|
21
|
+
title: ora
|
22
|
+
observer:
|
23
|
+
title: osservatore
|
24
|
+
real: reale
|
25
|
+
auto: automazzato
|
26
|
+
corrected: corretto
|
14
27
|
altitude:
|
15
28
|
at: a
|
16
29
|
distance:
|
30
|
+
title: distanza
|
17
31
|
unknown: sconosciuto
|
18
32
|
wind:
|
33
|
+
title: vento
|
19
34
|
variable_direction: direzione variabile
|
20
35
|
unknown_direction: direzione sconosciuta
|
21
36
|
unknown_speed: velocità sconosciuta
|
37
|
+
variable_wind:
|
38
|
+
title: variazione del vento
|
39
|
+
visibility:
|
40
|
+
title: visibilità
|
41
|
+
sky_summary:
|
42
|
+
title: cielo
|
22
43
|
sky_conditions:
|
44
|
+
title: condizioni del cielo
|
23
45
|
clear skies: cielo sereno
|
24
46
|
broken: nuvolosità parziale
|
25
47
|
broken cumulonimbus: copertura parziale di cumulonembi
|
@@ -32,10 +54,13 @@ it:
|
|
32
54
|
scattered towering cumulus: cumulonembi sparsi
|
33
55
|
overcast: chiuso
|
34
56
|
runway_visible_range:
|
57
|
+
title: visibilità sulle piste
|
35
58
|
runway: pista
|
36
59
|
from: da
|
37
60
|
to: a
|
38
|
-
|
61
|
+
present_weather:
|
62
|
+
title: tempo
|
63
|
+
not observed: non osservato
|
39
64
|
mist: foschia
|
40
65
|
dust: polvere
|
41
66
|
blowing dust: polviscolo
|
@@ -119,3 +144,9 @@ it:
|
|
119
144
|
light thunderstorm and unknown phenomenon: temporale con fenomeno sconosciuto leggero
|
120
145
|
heavy unknown phenomenon: fenomeno sconosciuto intenso
|
121
146
|
light unknown phenomenon: fenomeno sconosciuto leggero
|
147
|
+
temperature:
|
148
|
+
title: temperatura
|
149
|
+
dew_point:
|
150
|
+
title: punto di rugiada
|
151
|
+
remarks:
|
152
|
+
title: annotazioni
|
data/test/metar_test_helper.rb
CHANGED
@@ -11,4 +11,23 @@ Metar::Raw.instance_eval do
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
# Don't load station data from files
|
15
|
+
module Metar
|
16
|
+
class Station
|
17
|
+
|
18
|
+
class << self
|
19
|
+
|
20
|
+
def all_structures
|
21
|
+
[
|
22
|
+
{ :cccc => 'LIRQ', :country => 'Italy', :latitude => '43-48N', :longitude => '011-12E', :name => 'Firenze / Peretola', :state => '' },
|
23
|
+
{ :cccc => 'DAAS', :country => 'Algeria', :latitude => '36-11N', :longitude => '005-25E', :name => 'Setif', :state => '' },
|
24
|
+
{ :cccc => 'ESSB', :country => 'Sweden', :latitude => '59-21N', :longitude => '017-57E',:name => 'Stockholm / Bromma', :state => '' },
|
25
|
+
{ :cccc => 'KPDX', :country => 'United States', :latitude => '45-35N', :longitude => '122-36W', :name => 'PORTLAND INTERNATIONAL AIRPORT', :state => '' },
|
26
|
+
{ :cccc => 'CYXS', :country => 'Canada', :latitude => '53-53N', :longitude => '122-41W', :name => 'Prince George, B. C.', :state => '' },
|
27
|
+
]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
14
33
|
require 'test/unit'
|
data/test/unit/data_test.rb
CHANGED
@@ -43,26 +43,31 @@ class TestMetarData < Test::Unit::TestCase
|
|
43
43
|
assert_not_nil(Metar::Speed.options)
|
44
44
|
end
|
45
45
|
|
46
|
-
def
|
46
|
+
def test_speed_parse_without_units
|
47
47
|
speed = Metar::Speed.parse('12')
|
48
48
|
assert_equal(12, speed.to_kilometers_per_hour)
|
49
|
-
assert_equal(:kilometers_per_hour, speed.options[:units])
|
50
49
|
end
|
51
50
|
|
52
51
|
def test_speed_parse_kilometers_per_hour
|
53
52
|
speed = Metar::Speed.parse('12KMH')
|
54
53
|
assert_equal(12, speed.to_kilometers_per_hour)
|
55
|
-
assert_equal(:kilometers_per_hour, speed.options[:units])
|
56
54
|
end
|
57
55
|
|
58
56
|
def test_speed_parse_knots
|
59
57
|
speed = Metar::Speed.parse('12KT')
|
60
|
-
assert_equal(
|
58
|
+
assert_equal(12.0, speed.to_knots)
|
59
|
+
assert_equal(:kilometers_per_hour, speed.options[:units])
|
61
60
|
end
|
62
61
|
|
63
|
-
def
|
62
|
+
def test_speed_parse_kilometers_per_hour_is_default
|
63
|
+
speed = Metar::Speed.parse('12')
|
64
|
+
assert_equal(:kilometers_per_hour, speed.options[:units])
|
64
65
|
speed = Metar::Speed.parse('12MPS')
|
65
|
-
assert_equal(:
|
66
|
+
assert_equal(:kilometers_per_hour, speed.options[:units])
|
67
|
+
speed = Metar::Speed.parse('12KMH')
|
68
|
+
assert_equal(:kilometers_per_hour, speed.options[:units])
|
69
|
+
speed = Metar::Speed.parse('12KT')
|
70
|
+
assert_equal(:kilometers_per_hour, speed.options[:units])
|
66
71
|
end
|
67
72
|
|
68
73
|
# Temperature
|
@@ -104,55 +109,65 @@ class TestMetarData < Test::Unit::TestCase
|
|
104
109
|
end
|
105
110
|
|
106
111
|
# Wind
|
107
|
-
def
|
112
|
+
def test_wind_parse_without_units
|
108
113
|
wind = Metar::Wind.parse('18012')
|
109
114
|
assert_equal(180, wind.direction.value)
|
110
115
|
assert_equal(12.0, wind.speed.to_kilometers_per_hour)
|
111
|
-
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
112
116
|
end
|
113
117
|
|
114
118
|
def test_wind_parse_mps
|
115
119
|
wind = Metar::Wind.parse('18012MPS')
|
116
120
|
assert_equal(180, wind.direction.value)
|
117
121
|
assert_equal(12.0, wind.speed.value)
|
118
|
-
assert_equal(:meters_per_second, wind.speed.options[:units])
|
119
122
|
end
|
120
123
|
|
121
124
|
def test_wind_parse_kmh
|
122
125
|
wind = Metar::Wind.parse('27012KMH')
|
123
126
|
assert_equal(270, wind.direction.value)
|
124
127
|
assert_equal(12.0, wind.speed.to_kilometers_per_hour)
|
125
|
-
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
126
128
|
end
|
127
129
|
|
128
130
|
def test_wind_parse_knots
|
129
131
|
wind = Metar::Wind.parse('24006KT')
|
130
132
|
assert_equal(240, wind.direction.value)
|
131
133
|
assert_equal(6, wind.speed.to_knots)
|
132
|
-
assert_equal(:
|
134
|
+
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
133
135
|
end
|
134
136
|
|
135
137
|
def test_wind_parse_variable_direction
|
136
138
|
wind = Metar::Wind.parse('VRB20KT')
|
137
139
|
assert_equal(:variable_direction, wind.direction)
|
138
140
|
assert_equal(20, wind.speed.to_knots)
|
139
|
-
assert_equal(
|
140
|
-
assert_equal('variable direction, 20 knots', wind.to_s)
|
141
|
+
assert_equal('37km/h variable direction', wind.to_s)
|
141
142
|
end
|
142
143
|
|
143
144
|
def test_wind_parse_unknown_direction
|
144
145
|
wind = Metar::Wind.parse('///20KT')
|
145
146
|
assert_equal(:unknown_direction, wind.direction)
|
146
147
|
assert_equal(20, wind.speed.to_knots)
|
147
|
-
assert_equal(
|
148
|
-
assert_equal('unknown direction, 20 knots', wind.to_s)
|
148
|
+
assert_equal('37km/h unknown direction', wind.to_s)
|
149
149
|
end
|
150
150
|
|
151
151
|
def test_wind_parse_unknown_direction_and_speed
|
152
152
|
wind = Metar::Wind.parse('/////')
|
153
153
|
assert_equal(:unknown_direction, wind.direction)
|
154
154
|
assert_equal(:unknown, wind.speed)
|
155
|
-
assert_equal('unknown
|
155
|
+
assert_equal('unknown speed unknown direction', wind.to_s)
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_wind_parse_default_output_units_kilometers_per_hour
|
159
|
+
wind = Metar::Wind.parse('18012')
|
160
|
+
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
161
|
+
wind = Metar::Wind.parse('18012MPS')
|
162
|
+
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
163
|
+
wind = Metar::Wind.parse('27012KMH')
|
164
|
+
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
165
|
+
wind = Metar::Wind.parse('24006KT')
|
166
|
+
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
167
|
+
wind = Metar::Wind.parse('VRB20KT')
|
168
|
+
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
169
|
+
wind = Metar::Wind.parse('///20KT')
|
170
|
+
assert_equal(:kilometers_per_hour, wind.speed.options[:units])
|
156
171
|
end
|
157
172
|
|
158
173
|
# VariableWind
|
data/test/unit/parser_test.rb
CHANGED
@@ -15,11 +15,22 @@ class TestMetarParser < Test::Unit::TestCase
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
def test_time_obligatory
|
19
|
+
assert_raise(Metar::ParseError) {
|
20
|
+
setup_parser('PAIL', "2010/02/06 16:10\nPAIL 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
18
24
|
def test_date
|
19
25
|
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
20
26
|
assert_equal(Date.new(2010, 2, 6), parser.date)
|
21
27
|
end
|
22
28
|
|
29
|
+
def test_observer_real
|
30
|
+
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
31
|
+
assert_equal(:real, parser.observer)
|
32
|
+
end
|
33
|
+
|
23
34
|
def test_wind
|
24
35
|
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
25
36
|
assert_in_delta(240, parser.wind.direction.value, 0.0001)
|
@@ -46,6 +57,11 @@ class TestMetarParser < Test::Unit::TestCase
|
|
46
57
|
assert_equal(:no_change, parser.runway_visible_range[0].tendency)
|
47
58
|
end
|
48
59
|
|
60
|
+
def test_runway_visible_range_defaults_to_empty_array
|
61
|
+
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
62
|
+
assert_equal(0, parser.runway_visible_range.length)
|
63
|
+
end
|
64
|
+
|
49
65
|
def test_runway_visible_range_variable
|
50
66
|
parser = setup_parser('KPDX', "2010/02/15 11:08\nKPDX 151108Z 11006KT 1/4SM R10R/1600VP6000FT FG OVC002 05/05 A3022 RMK AO2")
|
51
67
|
assert_equal(1600.0, parser.runway_visible_range[0].visibility1.distance.to_feet)
|
@@ -61,6 +77,11 @@ class TestMetarParser < Test::Unit::TestCase
|
|
61
77
|
assert_equal('snow', parser.present_weather[0].phenomenon)
|
62
78
|
end
|
63
79
|
|
80
|
+
def test_present_weather_defaults_to_empty_array
|
81
|
+
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
82
|
+
assert_equal(0, parser.present_weather.length)
|
83
|
+
end
|
84
|
+
|
64
85
|
def test_sky_conditions
|
65
86
|
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
66
87
|
assert_equal(2, parser.sky_conditions.length)
|
@@ -70,11 +91,22 @@ class TestMetarParser < Test::Unit::TestCase
|
|
70
91
|
assert_equal(900, parser.sky_conditions[1].height.value)
|
71
92
|
end
|
72
93
|
|
94
|
+
def test_sky_conditions_defaults_to_empty_array
|
95
|
+
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN M17/M20 A2910 RMK AO2 P0000")
|
96
|
+
assert_equal(0, parser.sky_conditions.length)
|
97
|
+
end
|
98
|
+
|
73
99
|
def test_vertical_visibility
|
74
100
|
parser = setup_parser('CYXS', "2010/02/15 10:34\nCYXS 151034Z AUTO 09003KT 1/8SM FZFG VV001 M03/M03 A3019 RMK SLP263 ICG")
|
75
101
|
assert_equal(30, parser.vertical_visibility.value)
|
76
102
|
end
|
77
103
|
|
104
|
+
def test_temperature_obligatory
|
105
|
+
assert_raise(Metar::ParseError) {
|
106
|
+
setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 A2910 RMK AO2 P0000")
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
78
110
|
def test_temperature
|
79
111
|
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
80
112
|
assert_equal(-17, parser.temperature.value)
|
@@ -91,6 +123,20 @@ class TestMetarParser < Test::Unit::TestCase
|
|
91
123
|
assert_equal(:bar, parser.sea_level_pressure.options[:units])
|
92
124
|
end
|
93
125
|
|
126
|
+
def test_remarks
|
127
|
+
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
128
|
+
assert_instance_of(Array, parser.remarks)
|
129
|
+
assert_equal(2, parser.remarks.length)
|
130
|
+
assert_equal('AO2', parser.remarks[0])
|
131
|
+
assert_equal('P0000', parser.remarks[1])
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_remarks_defaults_to_empty_array
|
135
|
+
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910")
|
136
|
+
assert_instance_of(Array, parser.remarks)
|
137
|
+
assert_equal(0, parser.remarks.length)
|
138
|
+
end
|
139
|
+
|
94
140
|
private
|
95
141
|
|
96
142
|
def setup_parser(cccc, metar)
|
data/test/unit/raw_test.rb
CHANGED
@@ -18,10 +18,19 @@ class TestMetarRaw < Test::Unit::TestCase
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
21
|
+
def test_cccc
|
22
22
|
raw = Metar::Raw.new('LIRQ')
|
23
23
|
assert_equal('LIRQ', raw.cccc)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_time
|
27
|
+
raw = Metar::Raw.new('LIRQ')
|
24
28
|
assert_instance_of(Time, raw.time)
|
29
|
+
assert_equal(2010, raw.time.year)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_metar
|
33
|
+
raw = Metar::Raw.new('LIRQ')
|
25
34
|
assert_instance_of(String, raw.metar)
|
26
35
|
end
|
27
36
|
|
data/test/unit/report_test.rb
CHANGED
@@ -5,7 +5,11 @@ require File.dirname(__FILE__) + '/../metar_test_helper'
|
|
5
5
|
|
6
6
|
class TestMetarReport < Test::Unit::TestCase
|
7
7
|
def setup
|
8
|
-
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_name
|
11
|
+
report = setup_report('LIRQ', "2010/02/06 15:20\nLIRQ 061520Z 01007KT 350V050 9999 SCT035 BKN080 08/02 Q1005")
|
12
|
+
assert_equal('Firenze / Peretola', report.station_name)
|
9
13
|
end
|
10
14
|
|
11
15
|
def test_date
|
@@ -20,13 +24,12 @@ class TestMetarReport < Test::Unit::TestCase
|
|
20
24
|
|
21
25
|
def test_wind_knots
|
22
26
|
report = setup_report('LIRQ', "2010/02/06 15:20\nLIRQ 061520Z 01007KT 350V050 9999 SCT035 BKN080 08/02 Q1005")
|
23
|
-
assert_equal('
|
24
|
-
I18n.locale = :it
|
25
|
-
assert_equal('10°, 7 nodi', report.wind)
|
27
|
+
assert_equal('13km/h N', report.wind)
|
26
28
|
end
|
27
29
|
|
28
30
|
def test_variable_wind
|
29
31
|
report = setup_report('LIRQ', "2010/02/06 15:20\nLIRQ 061520Z 01007KT 350V050 9999 SCT035 BKN080 08/02 Q1005")
|
32
|
+
I18n.locale = :en
|
30
33
|
assert_equal('350 degrees - 50 degrees', report.variable_wind)
|
31
34
|
end
|
32
35
|
|
@@ -37,11 +40,15 @@ class TestMetarReport < Test::Unit::TestCase
|
|
37
40
|
|
38
41
|
def test_runway_visible_range
|
39
42
|
report = setup_report('ESSB', "2010/02/15 10:20\nESSB 151020Z 26003KT 2000 R12/1000N R30/1500N VV002 M07/M07 Q1013 1271//55")
|
43
|
+
I18n.locale = :en
|
40
44
|
assert_equal('runway 12: 1000m, runway 30: 1500m', report.runway_visible_range)
|
45
|
+
I18n.locale = :it
|
46
|
+
assert_equal('pista 12: 1000m, pista 30: 1500m', report.runway_visible_range)
|
41
47
|
end
|
42
48
|
|
43
49
|
def test_runway_visible_range_variable
|
44
50
|
report = setup_report('KPDX', "2010/02/15 11:08\nKPDX 151108Z 11006KT 1/4SM R10R/1600VP6000FT FG OVC002 05/05 A3022 RMK AO2")
|
51
|
+
I18n.locale = :en
|
45
52
|
assert_equal('runway 10R: from 1600ft to more than 6000ft', report.runway_visible_range)
|
46
53
|
end
|
47
54
|
|
@@ -80,6 +87,13 @@ class TestMetarReport < Test::Unit::TestCase
|
|
80
87
|
assert_equal('1.00500 bar', report.sea_level_pressure)
|
81
88
|
end
|
82
89
|
|
90
|
+
def test_to_s
|
91
|
+
report = setup_report('LIRQ', "2010/02/06 15:20\nLIRQ 061520Z 01007KT 350V050 9999 SCT035 BKN080 08/02 Q1005")
|
92
|
+
Metar::Report.attributes -= [:station_code, :variable_wind, :observer, :remarks]
|
93
|
+
I18n.locale = :en
|
94
|
+
assert_equal("name: Firenze / Peretola\ncountry: Italy\ntime: 15:20\nwind: 13km/h N\nvisibility: more than 10km\nsky: broken cloud\ntemperature: 8°C", report.to_s)
|
95
|
+
end
|
96
|
+
|
83
97
|
private
|
84
98
|
|
85
99
|
def setup_report(cccc, metar)
|
data/test/unit/station_test.rb
CHANGED
@@ -24,6 +24,26 @@ class TestStation < Test::Unit::TestCase
|
|
24
24
|
assert_not_nil Metar::Station.find_by_cccc('LIRQ')
|
25
25
|
end
|
26
26
|
|
27
|
+
def test_to_latitude_incorrect_gives_nil
|
28
|
+
assert_nil Metar::Station.to_latitude('23-15')
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_to_latitude_n
|
32
|
+
assert_equal 43.8, Metar::Station.to_latitude('43-48N')
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_to_latitude_s
|
36
|
+
assert_equal -23.25, Metar::Station.to_latitude('23-15S')
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_to_longitude_e
|
40
|
+
assert_equal 11.2, Metar::Station.to_longitude('11-12E')
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_to_longitude_w
|
44
|
+
assert_equal -11.2, Metar::Station.to_longitude('11-12W')
|
45
|
+
end
|
46
|
+
|
27
47
|
def test_instantiation_sets_cccc
|
28
48
|
station = Metar::Station.new('LIRQ')
|
29
49
|
assert_equal 'LIRQ', station.cccc
|
@@ -40,29 +60,29 @@ class TestStation < Test::Unit::TestCase
|
|
40
60
|
assert station.loaded?
|
41
61
|
end
|
42
62
|
|
43
|
-
def
|
63
|
+
def test_name
|
44
64
|
station = Metar::Station.new('LIRQ')
|
45
|
-
|
65
|
+
assert_equal 'Firenze / Peretola', station.name
|
46
66
|
end
|
47
67
|
|
48
|
-
def
|
68
|
+
def test_state
|
49
69
|
station = Metar::Station.new('LIRQ')
|
50
|
-
|
70
|
+
assert_equal '', station.state
|
51
71
|
end
|
52
72
|
|
53
|
-
def
|
54
|
-
|
73
|
+
def test_country
|
74
|
+
station = Metar::Station.new('LIRQ')
|
75
|
+
assert_equal 'Italy', station.country
|
55
76
|
end
|
56
77
|
|
57
|
-
def
|
58
|
-
|
78
|
+
def test_latitude_is_a_decimal_number
|
79
|
+
station = Metar::Station.new('LIRQ')
|
80
|
+
assert_equal station.latitude.to_s, station.latitude.to_f.to_s
|
59
81
|
end
|
60
82
|
|
61
|
-
def
|
62
|
-
|
83
|
+
def test_longitude_is_a_decimal_number
|
84
|
+
station = Metar::Station.new('LIRQ')
|
85
|
+
assert_equal station.longitude.to_s, station.longitude.to_f.to_s
|
63
86
|
end
|
64
87
|
|
65
|
-
def test_to_longitude_west
|
66
|
-
assert(Metar::Station.to_longitude('011-12W') == -11.2)
|
67
|
-
end
|
68
88
|
end
|