metar-parser 1.4.2 → 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 +7 -49
- 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 -13
- 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 +106 -87
- data/lib/metar/data.rb +25 -23
- data/lib/metar/i18n.rb +5 -2
- data/lib/metar/parser.rb +47 -22
- 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 +4 -2
- 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 +224 -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 -43
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
|
|
@@ -191,7 +201,7 @@ module Metar
|
|
191
201
|
end
|
192
202
|
|
193
203
|
def seek_wind
|
194
|
-
@wind = Metar::Data::Wind.parse(@chunks[0])
|
204
|
+
@wind = Metar::Data::Wind.parse(@chunks[0], strict: strict?)
|
195
205
|
@chunks.shift if @wind
|
196
206
|
@wind
|
197
207
|
end
|
@@ -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
|
data/lib/metar/raw.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
require 'net/ftp'
|
3
5
|
require 'time'
|
@@ -7,21 +9,23 @@ module Metar
|
|
7
9
|
class Base
|
8
10
|
attr_reader :metar
|
9
11
|
attr_reader :time
|
10
|
-
alias
|
12
|
+
alias to_s metar
|
11
13
|
end
|
12
14
|
|
13
15
|
##
|
14
16
|
# Use this class when you have a METAR string and the date of reading
|
15
17
|
class Data < Base
|
16
18
|
def initialize(metar, time = nil)
|
17
|
-
if time
|
18
|
-
warn <<-
|
19
|
+
if time.nil?
|
20
|
+
warn <<-WARNING
|
19
21
|
Using Metar::Raw::Data without a time parameter is deprecated.
|
20
22
|
Please supply the reading time as the second parameter.
|
21
|
-
|
23
|
+
WARNING
|
22
24
|
time = Time.now
|
23
25
|
end
|
24
|
-
|
26
|
+
|
27
|
+
@metar = metar
|
28
|
+
@time = time
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
@@ -39,6 +43,7 @@ module Metar
|
|
39
43
|
|
40
44
|
def time
|
41
45
|
return @time if @time
|
46
|
+
|
42
47
|
dom = day_of_month
|
43
48
|
date = Date.today
|
44
49
|
loop do
|
@@ -57,60 +62,42 @@ module Metar
|
|
57
62
|
def datetime
|
58
63
|
datetime = metar[/^\w{4} (\d{6})Z/, 1]
|
59
64
|
raise "The METAR string must have a 6 digit datetime" if datetime.nil?
|
65
|
+
|
60
66
|
datetime
|
61
67
|
end
|
62
68
|
|
63
69
|
def day_of_month
|
64
70
|
dom = datetime[0..1].to_i
|
65
71
|
raise "Day of month must be at most 31" if dom > 31
|
66
|
-
raise "Day of month must be greater than 0" if dom
|
72
|
+
raise "Day of month must be greater than 0" if dom.zero?
|
73
|
+
|
67
74
|
dom
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
71
78
|
# Collects METAR data from the NOAA site via FTP
|
72
79
|
class Noaa < Base
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
@@connection.login
|
86
|
-
@@connection.chdir('data/observations/metar/stations')
|
87
|
-
@@connection.passive = true
|
88
|
-
end
|
89
|
-
|
90
|
-
def disconnect
|
91
|
-
return if @@connection.nil?
|
92
|
-
@@connection.close
|
93
|
-
@@connection = nil
|
94
|
-
end
|
95
|
-
|
96
|
-
def fetch(cccc)
|
97
|
-
attempts = 0
|
98
|
-
while attempts < 2
|
99
|
-
begin
|
100
|
-
s = ''
|
101
|
-
connection.retrbinary("RETR #{ cccc }.TXT", 1024) do |chunk|
|
102
|
-
s << chunk
|
103
|
-
end
|
104
|
-
disconnect
|
105
|
-
return s
|
106
|
-
rescue Net::FTPPermError, Net::FTPTempError, EOFError => e
|
107
|
-
connect
|
108
|
-
attempts += 1
|
80
|
+
def self.fetch(cccc)
|
81
|
+
connection = Net::FTP.new('tgftp.nws.noaa.gov')
|
82
|
+
connection.login
|
83
|
+
connection.chdir('data/observations/metar/stations')
|
84
|
+
connection.passive = true
|
85
|
+
|
86
|
+
attempts = 0
|
87
|
+
while attempts < 2
|
88
|
+
begin
|
89
|
+
s = ''
|
90
|
+
connection.retrbinary("RETR #{cccc}.TXT", 1024) do |chunk|
|
91
|
+
s += chunk
|
109
92
|
end
|
93
|
+
connection.close
|
94
|
+
return s
|
95
|
+
rescue Net::FTPPermError, Net::FTPTempError, EOFError
|
96
|
+
attempts += 1
|
110
97
|
end
|
111
|
-
raise "Net::FTP.retrbinary failed #{attempts} times"
|
112
98
|
end
|
113
99
|
|
100
|
+
raise "Net::FTP.retrbinary failed #{attempts} times"
|
114
101
|
end
|
115
102
|
|
116
103
|
# Station is a string containing the CCCC code, or
|
@@ -124,7 +111,7 @@ module Metar
|
|
124
111
|
@data
|
125
112
|
end
|
126
113
|
# #raw is deprecated, use #data
|
127
|
-
alias
|
114
|
+
alias raw data
|
128
115
|
|
129
116
|
def time
|
130
117
|
fetch
|
@@ -140,6 +127,7 @@ module Metar
|
|
140
127
|
|
141
128
|
def fetch
|
142
129
|
return if @data
|
130
|
+
|
143
131
|
@data = Noaa.fetch(@cccc)
|
144
132
|
parse
|
145
133
|
end
|
data/lib/metar/report.rb
CHANGED
@@ -1,24 +1,27 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "metar/data/remark"
|
2
4
|
|
3
5
|
module Metar
|
4
6
|
class Report
|
5
|
-
ATTRIBUTES =
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
ATTRIBUTES = %i(
|
8
|
+
station_name
|
9
|
+
station_country
|
10
|
+
time
|
11
|
+
wind
|
12
|
+
visibility
|
13
|
+
minimum_visibility
|
14
|
+
present_weather
|
15
|
+
sky_summary
|
16
|
+
temperature
|
17
|
+
).freeze
|
16
18
|
|
17
19
|
attr_reader :parser, :station
|
18
20
|
|
19
21
|
def initialize(parser)
|
20
22
|
@parser = parser
|
21
|
-
|
23
|
+
# TODO: parser should return the station
|
24
|
+
@station = Station.find_by_cccc(@parser.station_code)
|
22
25
|
end
|
23
26
|
|
24
27
|
def station_name
|
@@ -38,7 +41,10 @@ module Metar
|
|
38
41
|
end
|
39
42
|
|
40
43
|
def time
|
41
|
-
|
44
|
+
format(
|
45
|
+
"%<hour>u:%02<min>u",
|
46
|
+
hour: @parser.time.hour, min: @parser.time.min
|
47
|
+
)
|
42
48
|
end
|
43
49
|
|
44
50
|
def observer
|
@@ -62,7 +68,7 @@ module Metar
|
|
62
68
|
end
|
63
69
|
|
64
70
|
def runway_visible_range
|
65
|
-
@parser.runway_visible_range.
|
71
|
+
@parser.runway_visible_range.map(&:to_s).join(', ')
|
66
72
|
end
|
67
73
|
|
68
74
|
def present_weather
|
@@ -70,12 +76,15 @@ module Metar
|
|
70
76
|
end
|
71
77
|
|
72
78
|
def sky_summary
|
73
|
-
|
79
|
+
if @parser.sky_conditions.empty?
|
80
|
+
return I18n.t('metar.sky_conditions.clear skies')
|
81
|
+
end
|
82
|
+
|
74
83
|
@parser.sky_conditions[-1].to_summary
|
75
84
|
end
|
76
85
|
|
77
86
|
def sky_conditions
|
78
|
-
@parser.sky_conditions.
|
87
|
+
@parser.sky_conditions.map(&:to_s).join(', ')
|
79
88
|
end
|
80
89
|
|
81
90
|
def vertical_visibility
|
@@ -100,7 +109,9 @@ module Metar
|
|
100
109
|
|
101
110
|
def to_s
|
102
111
|
attributes.collect do |attribute|
|
103
|
-
I18n.t('metar.' + attribute[:attribute].to_s + '.title') +
|
112
|
+
I18n.t('metar.' + attribute[:attribute].to_s + '.title') +
|
113
|
+
': ' +
|
114
|
+
attribute[:value]
|
104
115
|
end.join("\n") + "\n"
|
105
116
|
end
|
106
117
|
|
@@ -108,8 +119,8 @@ module Metar
|
|
108
119
|
|
109
120
|
def attributes
|
110
121
|
a = Metar::Report::ATTRIBUTES.map do |key|
|
111
|
-
value =
|
112
|
-
{:
|
122
|
+
value = send(key).to_s
|
123
|
+
{attribute: key, value: value} if !value.empty?
|
113
124
|
end
|
114
125
|
a.compact
|
115
126
|
end
|
data/lib/metar/station.rb
CHANGED
@@ -1,19 +1,24 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'uri'
|
2
5
|
require 'set'
|
3
6
|
|
4
7
|
# A Station can be created without downloading data from the Internet.
|
5
|
-
# The class downloads and caches the NOAA station list
|
6
|
-
#
|
8
|
+
# The class downloads and caches the NOAA station list
|
9
|
+
# when it is first requested.
|
10
|
+
# As soon of any of the attributes are read, the data is downloaded
|
11
|
+
# (if necessary), and attributes are set.
|
7
12
|
|
8
13
|
module Metar
|
9
14
|
class Station
|
10
|
-
NOAA_STATION_LIST_URL = '
|
15
|
+
NOAA_STATION_LIST_URL = 'https://tgftp.nws.noaa.gov/data/nsd_cccc.txt'
|
11
16
|
|
12
17
|
class << self
|
13
18
|
@nsd_cccc = nil # Contains the text of the station list
|
14
19
|
|
15
20
|
def countries
|
16
|
-
all_structures.reduce(Set.new) { |a, s| a.add(s[
|
21
|
+
all_structures.reduce(Set.new) { |a, s| a.add(s[:country]) }.to_a.sort
|
17
22
|
end
|
18
23
|
|
19
24
|
def all
|
@@ -30,33 +35,36 @@ module Metar
|
|
30
35
|
|
31
36
|
# Does the given CCCC code exist?
|
32
37
|
def exist?(cccc)
|
33
|
-
|
38
|
+
!find_data_by_cccc(cccc).nil?
|
34
39
|
end
|
35
40
|
|
36
41
|
def find_all_by_country(country)
|
37
42
|
all.select { |s| s.country == country }
|
38
43
|
end
|
39
44
|
|
40
|
-
def to_longitude(
|
41
|
-
m =
|
45
|
+
def to_longitude(longitude)
|
46
|
+
m = longitude.match(/^(\d+)-(\d+)([EW])/)
|
42
47
|
return nil if !m
|
48
|
+
|
43
49
|
(m[3] == 'E' ? 1.0 : -1.0) * (m[1].to_f + m[2].to_f / 60.0)
|
44
50
|
end
|
45
51
|
|
46
|
-
def to_latitude(
|
47
|
-
m =
|
52
|
+
def to_latitude(latitude)
|
53
|
+
m = latitude.match(/^(\d+)-(\d+)([SN])/)
|
48
54
|
return nil if !m
|
55
|
+
|
49
56
|
(m[3] == 'E' ? 1.0 : -1.0) * (m[1].to_f + m[2].to_f / 60.0)
|
50
57
|
end
|
51
58
|
end
|
52
59
|
|
53
60
|
attr_reader :cccc, :name, :state, :country, :longitude, :latitude, :raw
|
54
|
-
alias
|
61
|
+
alias code cccc
|
55
62
|
|
56
63
|
# No check is made on the existence of the station
|
57
64
|
def initialize(cccc, noaa_data)
|
58
65
|
raise "Station identifier must not be nil" if cccc.nil?
|
59
66
|
raise "Station identifier must not be empty" if cccc.to_s == ''
|
67
|
+
|
60
68
|
@cccc = cccc
|
61
69
|
load! noaa_data
|
62
70
|
end
|
@@ -76,7 +84,9 @@ module Metar
|
|
76
84
|
@structures = nil
|
77
85
|
|
78
86
|
def download_stations
|
79
|
-
|
87
|
+
uri = URI.parse(NOAA_STATION_LIST_URL)
|
88
|
+
response = Net::HTTP.get_response(uri)
|
89
|
+
response.body
|
80
90
|
end
|
81
91
|
|
82
92
|
def all_structures
|
@@ -88,13 +98,13 @@ module Metar
|
|
88
98
|
@nsd_cccc.each_line do |station|
|
89
99
|
fields = station.split(';')
|
90
100
|
@structures << {
|
91
|
-
cccc:
|
92
|
-
name:
|
93
|
-
state:
|
94
|
-
country:
|
95
|
-
latitude:
|
101
|
+
cccc: fields[0],
|
102
|
+
name: fields[3],
|
103
|
+
state: fields[4],
|
104
|
+
country: fields[5],
|
105
|
+
latitude: fields[7],
|
96
106
|
longitude: fields[8],
|
97
|
-
raw:
|
107
|
+
raw: station.clone
|
98
108
|
}
|
99
109
|
end
|
100
110
|
|
@@ -104,7 +114,6 @@ module Metar
|
|
104
114
|
def find_data_by_cccc(cccc)
|
105
115
|
all_structures.find { |station| station[:cccc] == cccc }
|
106
116
|
end
|
107
|
-
|
108
117
|
end
|
109
118
|
|
110
119
|
def load!(noaa_data)
|
data/lib/metar/version.rb
CHANGED
data/lib/metar.rb
CHANGED
data/locales/de.yml
CHANGED
data/locales/en.yml
CHANGED
data/locales/it.yml
CHANGED
data/locales/pt-BR.yml
CHANGED
data/spec/data/distance_spec.rb
CHANGED