metar-parser 1.4.2 → 1.6.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 +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