metar-parser 0.1.8 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -11
- data/Rakefile +9 -25
- data/bin/download_raw.rb +2 -0
- data/lib/metar/data.rb +85 -42
- data/lib/metar/parser.rb +3 -1
- data/lib/metar/raw.rb +10 -34
- data/lib/metar/report.rb +0 -5
- data/lib/metar/station.rb +4 -7
- data/lib/metar/version.rb +11 -0
- data/lib/metar.rb +1 -8
- data/test/unit/data_test.rb +39 -39
- data/test/unit/parser_test.rb +3 -6
- data/test/unit/raw_test.rb +1 -1
- data/test/unit/report_test.rb +4 -4
- data/test/unit/station_test.rb +1 -1
- metadata +25 -66
data/README.rdoc
CHANGED
@@ -6,13 +6,12 @@ The information comes from the National Oceanic and Atmospheric Association's ra
|
|
6
6
|
|
7
7
|
* Parses METAR strings using a state machine.
|
8
8
|
|
9
|
-
= Data format
|
9
|
+
= Data format descrition
|
10
10
|
|
11
11
|
* WMO
|
12
12
|
* http://www.wmo.int/pages/prog/www/WMOCodes/Manual/Volume-I-selection/Sel2.pdf (pages 27-38)
|
13
13
|
* http://dcaa.slv.dk:8000/icaodocs/Annex%203%20-%20Meteorological%20Service%20for%20International%20Air%20Navigation/Cover%20sheet%20to%20AMDT%2074.pdf (Table A3-2. Template for METAR and SPECI)
|
14
14
|
* http://booty.org.uk/booty.weather/metinfo/codes/METAR_decode.htm
|
15
|
-
* http://www.flyingineurope.be/metar_taf_decode.htm
|
16
15
|
|
17
16
|
* United states:
|
18
17
|
* http://www.nws.noaa.gov/oso/oso1/oso12/fmh1/fmh1ch12.htm
|
@@ -20,15 +19,6 @@ The information comes from the National Oceanic and Atmospheric Association's ra
|
|
20
19
|
* http://weather.cod.edu/notes/metar.html
|
21
20
|
* http://www.met.tamu.edu/class/METAR/metar-pg3.html - incomplete
|
22
21
|
|
23
|
-
= Sources
|
24
|
-
|
25
|
-
* Single Files
|
26
|
-
** ftp://tgftp.nws.noaa.gov/data/observations/metar/stations/[CCCC].TXT - plain text, time 3.1s
|
27
|
-
** http://weather.noaa.gov/cgi-bin/mgetmetar.pl?cccc=LIRQ - embedded in HTML, time 0.3s
|
28
|
-
* Cycle Files
|
29
|
-
** http://weather.noaa.gov/weather/metar.shtml
|
30
|
-
** ftp://tgftp.nws.noaa.gov/data/observations/metar/cycles/[NN]Z.TXT
|
31
|
-
|
32
22
|
= Other software
|
33
23
|
|
34
24
|
Other Ruby libraries offering METAR parsing:
|
data/Rakefile
CHANGED
@@ -12,32 +12,9 @@ RDOC_PATH = 'doc/rdoc'
|
|
12
12
|
|
13
13
|
task :default => :test
|
14
14
|
|
15
|
-
spec = Gem::Specification.new do |s|
|
16
|
-
s.name = 'metar-parser'
|
17
|
-
s.summary = 'A Ruby library for METAR weather reports'
|
18
|
-
s.description = 'A Ruby library which handle METAR weather reports. Provides weather station listings and info. Downloads and parses reports. Presents localized full text reports'
|
19
|
-
s.version = Metar::VERSION::STRING
|
20
|
-
|
21
|
-
s.homepage = 'http://github.com/joeyates/metar-parser'
|
22
|
-
s.author = 'Joe Yates'
|
23
|
-
s.email = 'joe.g.yates@gmail.com'
|
24
|
-
|
25
|
-
s.files = ['README.rdoc', 'COPYING', 'Rakefile'] + FileList['{bin,lib,test}/**/*.rb'] + FileList['locales/**/*.{rb,yml}']
|
26
|
-
s.require_paths = ['lib']
|
27
|
-
s.add_dependency('aasm', '>= 2.1.5')
|
28
|
-
s.add_dependency('i18n', '>= 0.3.5')
|
29
|
-
s.add_dependency('m9t', '>= 0.1.12')
|
30
|
-
|
31
|
-
s.has_rdoc = true
|
32
|
-
s.rdoc_options += RDOC_OPTS
|
33
|
-
s.extra_rdoc_files = ['README.rdoc', 'COPYING']
|
34
|
-
|
35
|
-
s.test_file = 'test/all_tests.rb'
|
36
|
-
end
|
37
|
-
|
38
15
|
Rake::TestTask.new do |t|
|
39
16
|
t.libs << 'test'
|
40
|
-
t.test_files = FileList['test
|
17
|
+
t.test_files = FileList['test/**/*.rb']
|
41
18
|
t.verbose = true
|
42
19
|
end
|
43
20
|
|
@@ -48,5 +25,12 @@ Rake::RDocTask.new do |rdoc|
|
|
48
25
|
rdoc.rdoc_files.add ['README.rdoc', 'COPYING', 'lib/**/*.rb']
|
49
26
|
end
|
50
27
|
|
51
|
-
|
28
|
+
desc "Build the gem"
|
29
|
+
task :build do
|
30
|
+
system "gem build metar-parser.gemspec"
|
31
|
+
end
|
32
|
+
|
33
|
+
desc "Publish a new version of the gem"
|
34
|
+
task :release => :build do
|
35
|
+
system "gem push metar-parser-#{Metar::VERSION}"
|
52
36
|
end
|
data/bin/download_raw.rb
CHANGED
data/lib/metar/data.rb
CHANGED
@@ -6,25 +6,30 @@ require 'm9t'
|
|
6
6
|
module Metar
|
7
7
|
locales_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'locales'))
|
8
8
|
I18n.load_path += Dir.glob("#{ locales_path }/*.yml")
|
9
|
-
I18n.reload!
|
10
9
|
|
11
10
|
# Subclasses M9t::Distance
|
12
11
|
# Uses kilometers as desired default output unit
|
13
12
|
class Distance < M9t::Distance
|
14
13
|
|
14
|
+
attr_accessor :units
|
15
|
+
|
15
16
|
# nil is taken to mean 'data unavailable'
|
16
|
-
def initialize(meters = nil
|
17
|
+
def initialize( meters = nil )
|
18
|
+
@units = units || :meters
|
17
19
|
if meters
|
18
|
-
super
|
20
|
+
super
|
19
21
|
else
|
20
22
|
@value = nil
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
24
26
|
# Handles nil case differently to M9t::Distance
|
25
|
-
def to_s
|
27
|
+
def to_s( options = {} )
|
28
|
+
options = { :units => @units,
|
29
|
+
:precision => 0,
|
30
|
+
:abbreviated => true }.merge( options )
|
26
31
|
return I18n.t('metar.distance.unknown') if @value.nil?
|
27
|
-
super
|
32
|
+
super( options )
|
28
33
|
end
|
29
34
|
|
30
35
|
end
|
@@ -42,9 +47,9 @@ module Metar
|
|
42
47
|
case
|
43
48
|
when s =~ /^(\d+)(KT|MPS|KMH)$/
|
44
49
|
# Call the appropriate factory method for the supplied units
|
45
|
-
send(METAR_UNITS[$2], $1.to_i
|
50
|
+
send( METAR_UNITS[$2], $1.to_i )
|
46
51
|
when s =~ /^(\d+)$/
|
47
|
-
kilometers_per_hour($1.to_i
|
52
|
+
kilometers_per_hour( $1.to_i )
|
48
53
|
else
|
49
54
|
nil
|
50
55
|
end
|
@@ -60,12 +65,17 @@ module Metar
|
|
60
65
|
sign = $1
|
61
66
|
value = $2.to_i
|
62
67
|
value *= -1 if sign == 'M'
|
63
|
-
new(value
|
68
|
+
new( value )
|
64
69
|
else
|
65
70
|
nil
|
66
71
|
end
|
67
72
|
end
|
68
73
|
|
74
|
+
def to_s( options = {} )
|
75
|
+
options = { :abbreviated => true, :precision => 0 }.merge( options )
|
76
|
+
super( options )
|
77
|
+
end
|
78
|
+
|
69
79
|
end
|
70
80
|
|
71
81
|
# Adds a parse method to the M9t base class
|
@@ -89,9 +99,13 @@ module Metar
|
|
89
99
|
def Wind.parse(s)
|
90
100
|
case
|
91
101
|
when s =~ /^(\d{3})(\d{2}(|MPS|KMH|KT))$/
|
92
|
-
new(M9t::Direction.new($1
|
102
|
+
new( M9t::Direction.new( $1 ),
|
103
|
+
Speed.parse( $2 ),
|
104
|
+
:direction_units => :compass )
|
93
105
|
when s =~ /^(\d{3})(\d{2})G(\d{2,3}(|MPS|KMH|KT))$/
|
94
|
-
new(M9t::Direction.new($1
|
106
|
+
new( M9t::Direction.new( $1 ),
|
107
|
+
Speed.parse( $2 ),
|
108
|
+
:direction_units => :compass )
|
95
109
|
when s =~ /^VRB(\d{2}(|MPS|KMH|KT))$/
|
96
110
|
new(:variable_direction, Speed.parse($1))
|
97
111
|
when s =~ /^\/{3}(\d{2}(|MPS|KMH|KT))$/
|
@@ -103,9 +117,11 @@ module Metar
|
|
103
117
|
end
|
104
118
|
end
|
105
119
|
|
106
|
-
attr_reader :direction, :speed, :
|
120
|
+
attr_reader :direction, :speed, :options
|
107
121
|
|
108
|
-
def initialize(direction, speed,
|
122
|
+
def initialize( direction, speed, options = {} )
|
123
|
+
@options = { :direction_units => :compass,
|
124
|
+
:speed_units => :kilometers_per_hour }.merge( options )
|
109
125
|
@direction, @speed = direction, speed
|
110
126
|
end
|
111
127
|
|
@@ -117,14 +133,16 @@ module Metar
|
|
117
133
|
when :unknown_direction
|
118
134
|
I18n.t('metar.wind.unknown_direction')
|
119
135
|
else
|
120
|
-
@direction.to_s
|
136
|
+
@direction.to_s( :units => @options[ :direction_units ] )
|
121
137
|
end
|
122
138
|
speed =
|
123
139
|
case @speed
|
124
140
|
when :unknown
|
125
141
|
I18n.t('metar.wind.unknown_speed')
|
126
142
|
else
|
127
|
-
@speed.to_s
|
143
|
+
@speed.to_s( :abbreviated => true,
|
144
|
+
:precision => 0,
|
145
|
+
:units => @options[ :speed_units ] )
|
128
146
|
end
|
129
147
|
"#{ speed } #{ direction }"
|
130
148
|
end
|
@@ -158,22 +176,28 @@ module Metar
|
|
158
176
|
def Visibility.parse(s)
|
159
177
|
case
|
160
178
|
when s == '9999'
|
161
|
-
new(Distance.new(10000), nil, :more_than)
|
179
|
+
new( Distance.new( 10000 ), nil, :more_than )
|
162
180
|
when s =~ /(\d{4})NDV/ # WMO
|
163
|
-
new(Distance.new($1.to_f)) # Assuming meters
|
181
|
+
new( Distance.new( $1.to_f ) ) # Assuming meters
|
164
182
|
when (s =~ /^((1|2)\s|)([13])\/([248])SM$/) # US
|
165
|
-
miles
|
166
|
-
|
183
|
+
miles = $1.to_f + $3.to_f / $4.to_f
|
184
|
+
distance = Distance.miles( miles )
|
185
|
+
distance.units = :miles
|
186
|
+
new( distance )
|
167
187
|
when s =~ /^(\d+)SM$/ # US
|
168
|
-
|
188
|
+
distance = Distance.miles( $1.to_f )
|
189
|
+
distance.units = :miles
|
190
|
+
new( distance )
|
169
191
|
when s == 'M1/4SM' # US
|
170
|
-
|
192
|
+
distance = Distance.miles( 0.25 )
|
193
|
+
distance.units = :miles
|
194
|
+
new( distance, nil, :less_than )
|
171
195
|
when s =~ /^(\d+)KM$/
|
172
|
-
new(Distance.kilometers($1))
|
196
|
+
new( Distance.kilometers( $1 ) )
|
173
197
|
when s =~ /^(\d+)$/ # Units?
|
174
|
-
new(Distance.kilometers($1))
|
198
|
+
new( Distance.kilometers( $1 ) )
|
175
199
|
when s =~ /^(\d+)(N|NE|E|SE|S|SW|W|NW)$/
|
176
|
-
new(Distance.kilometers($1), M9t::Direction.compass($2))
|
200
|
+
new( Distance.kilometers( $1 ), M9t::Direction.compass( $2 ) )
|
177
201
|
else
|
178
202
|
nil
|
179
203
|
end
|
@@ -185,18 +209,27 @@ module Metar
|
|
185
209
|
@distance, @direction, @comparator = distance, direction, comparator
|
186
210
|
end
|
187
211
|
|
188
|
-
def to_s
|
212
|
+
def to_s( options = {} )
|
213
|
+
distance_options = { :abbreviated => true,
|
214
|
+
:precision => 0,
|
215
|
+
:units => :kilometers }.merge( options )
|
216
|
+
direction_options = { :units => :compass }
|
189
217
|
case
|
190
|
-
when (@direction.nil? and @comparator.nil?)
|
191
|
-
@distance.to_s
|
218
|
+
when ( @direction.nil? and @comparator.nil? )
|
219
|
+
@distance.to_s( distance_options )
|
192
220
|
when @comparator.nil?
|
193
|
-
"%s %s" % [@distance.to_s,
|
221
|
+
"%s %s" % [ @distance.to_s( distance_options ),
|
222
|
+
@direction.to_s( direction_options ) ]
|
194
223
|
when @direction.nil?
|
195
|
-
"%s %s" % [I18n.t('comparison.' + @comparator.to_s),
|
224
|
+
"%s %s" % [ I18n.t( 'comparison.' + @comparator.to_s ),
|
225
|
+
@distance.to_s( distance_options ) ]
|
196
226
|
else
|
197
|
-
"%s %s %s" % [I18n.t('comparison.' + @comparator.to_s),
|
227
|
+
"%s %s %s" % [ I18n.t( 'comparison.' + @comparator.to_s ),
|
228
|
+
@distance.to_s( distance_options ),
|
229
|
+
@direction.to_s( direction_options ) ]
|
198
230
|
end
|
199
231
|
end
|
232
|
+
|
200
233
|
end
|
201
234
|
|
202
235
|
class RunwayVisibleRange
|
@@ -213,7 +246,7 @@ module Metar
|
|
213
246
|
count = $3.to_f
|
214
247
|
tendency = TENDENCY[$4]
|
215
248
|
units = UNITS[$5]
|
216
|
-
distance = Distance.send(units, count
|
249
|
+
distance = Distance.send( units, count )
|
217
250
|
visibility = Visibility.new(distance, nil, comparator)
|
218
251
|
new(designator, visibility, nil, tendency)
|
219
252
|
when runway_visible_range =~ /^R(\d+[RLC]?)\/(P|M|)(\d{4})V(P|M|)(\d{4})(N|U|D)?(FT)?$/
|
@@ -224,26 +257,36 @@ module Metar
|
|
224
257
|
count2 = $5.to_f
|
225
258
|
tendency = TENDENCY[$6]
|
226
259
|
units = UNITS[$7]
|
227
|
-
distance1 = Distance.send(units, count1
|
228
|
-
distance2 = Distance.send(units, count2
|
229
|
-
visibility1 = Visibility.new(distance1, nil, comparator1)
|
230
|
-
visibility2 = Visibility.new(distance2, nil, comparator2)
|
231
|
-
new(designator, visibility1, visibility2, tendency)
|
260
|
+
distance1 = Distance.send( units, count1 )
|
261
|
+
distance2 = Distance.send( units, count2 )
|
262
|
+
visibility1 = Visibility.new( distance1, nil, comparator1 )
|
263
|
+
visibility2 = Visibility.new( distance2, nil, comparator2 )
|
264
|
+
new( designator, visibility1, visibility2, tendency, units )
|
232
265
|
else
|
233
266
|
nil
|
234
267
|
end
|
235
268
|
end
|
236
269
|
|
237
270
|
attr_reader :designator, :visibility1, :visibility2, :tendency
|
238
|
-
def initialize(designator, visibility1, visibility2 = nil, tendency = nil)
|
239
|
-
@designator, @visibility1, @visibility2, @tendency = designator, visibility1, visibility2, tendency
|
271
|
+
def initialize( designator, visibility1, visibility2 = nil, tendency = nil, units = :meters )
|
272
|
+
@designator, @visibility1, @visibility2, @tendency, @units = designator, visibility1, visibility2, tendency, units
|
240
273
|
end
|
241
274
|
|
242
275
|
def to_s
|
276
|
+
distance_options = { :abbreviated => true,
|
277
|
+
:precision => 0,
|
278
|
+
:units => @units }
|
243
279
|
if @visibility2.nil?
|
244
|
-
I18n.t('metar.runway_visible_range.runway') +
|
280
|
+
I18n.t('metar.runway_visible_range.runway') +
|
281
|
+
' ' + @designator +
|
282
|
+
': ' + @visibility1.to_s( distance_options )
|
245
283
|
else
|
246
|
-
I18n.t('metar.runway_visible_range.runway') +
|
284
|
+
I18n.t('metar.runway_visible_range.runway') +
|
285
|
+
' ' + @designator +
|
286
|
+
': ' + I18n.t('metar.runway_visible_range.from') +
|
287
|
+
' ' + @visibility1.to_s( distance_options ) +
|
288
|
+
' ' + I18n.t('metar.runway_visible_range.to') +
|
289
|
+
' ' + @visibility2.to_s( distance_options )
|
247
290
|
end
|
248
291
|
end
|
249
292
|
|
@@ -345,7 +388,7 @@ module Metar
|
|
345
388
|
new
|
346
389
|
when sky_condition =~ /^(BKN|FEW|OVC|SCT)(\d+)(CB|TCU|\/{3})?$/
|
347
390
|
quantity = QUANTITY[$1]
|
348
|
-
height = Distance.new($2.to_i * 30.0
|
391
|
+
height = Distance.new( $2.to_i * 30.0 )
|
349
392
|
type =
|
350
393
|
case $3
|
351
394
|
when 'CB'
|
@@ -389,10 +432,10 @@ module Metar
|
|
389
432
|
|
390
433
|
class VerticalVisibility
|
391
434
|
|
392
|
-
def VerticalVisibility.parse(vertical_visibility)
|
435
|
+
def VerticalVisibility.parse( vertical_visibility )
|
393
436
|
case
|
394
437
|
when vertical_visibility =~ /^VV(\d{3})$/
|
395
|
-
Distance.new($1.to_f * 30.0
|
438
|
+
Distance.new( $1.to_f * 30.0 )
|
396
439
|
when vertical_visibility == '///'
|
397
440
|
Distance.new
|
398
441
|
else
|
data/lib/metar/parser.rb
CHANGED
@@ -88,6 +88,7 @@ module Metar
|
|
88
88
|
station = Metar::Station.new(cccc)
|
89
89
|
raw = Metar::Raw.new(station)
|
90
90
|
parser = new(raw)
|
91
|
+
parser.analyze
|
91
92
|
parser
|
92
93
|
end
|
93
94
|
|
@@ -180,7 +181,8 @@ module Metar
|
|
180
181
|
if @chunks[0] == 'CAVOK'
|
181
182
|
@chunks.shift
|
182
183
|
@visibility = Visibility.new(M9t::Distance.kilometers(10), nil, :more_than)
|
183
|
-
@
|
184
|
+
@present_weather << Metar::WeatherPhenomenon.new('No significant weather')
|
185
|
+
@sky_conditions << SkyCondition.new # = 'clear skies'
|
184
186
|
cavok!
|
185
187
|
return
|
186
188
|
end
|
data/lib/metar/raw.rb
CHANGED
@@ -9,46 +9,22 @@ module Metar
|
|
9
9
|
|
10
10
|
@connection = nil
|
11
11
|
|
12
|
+
def cache_connection
|
13
|
+
@connection = connection
|
14
|
+
end
|
15
|
+
|
12
16
|
def connection
|
13
17
|
return @connection if @connection
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
connection = Net::FTP.new('tgftp.nws.noaa.gov')
|
19
|
+
connection.login
|
20
|
+
connection.chdir('data/observations/metar/stations')
|
21
|
+
connection.passive = true
|
22
|
+
connection
|
18
23
|
end
|
19
24
|
|
20
25
|
def fetch(cccc)
|
21
|
-
fetch_file("#{ cccc }.TXT", '/data/observations/metar/stations')
|
22
|
-
end
|
23
|
-
|
24
|
-
# Downloads the latest cycle file (http://weather.noaa.gov/weather/metar.shtml#files)
|
25
|
-
# The files contain a lot (circa 80%) of duplicates
|
26
|
-
# Returns a hash { '[CCCC code]' => 2 line METAR report }
|
27
|
-
# This function takes 10s of seconds to download
|
28
|
-
# N.B.: There is a 10m period each day when each file is not present (the 10m before data is collected)
|
29
|
-
def hourly(hour = current_cycle_hour)
|
30
|
-
cycle_file_name = "%02uZ.TXT" % hour
|
31
|
-
cycle_file = fetch_file(cycle_file_name, '/data/observations/metar/cycles')
|
32
|
-
cycle_file.each_line("\n\n").reduce({}) do |memo, metar|
|
33
|
-
metar =~ /\n([A-Z]...)\s/
|
34
|
-
cccc = $1
|
35
|
-
memo[cccc] = metar
|
36
|
-
memo
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# For logic, see http://weather.noaa.gov/weather/metar.shtml - A Discussion of METAR Cycles
|
41
|
-
def current_cycle_hour
|
42
|
-
time = Time.now.gmtime
|
43
|
-
time.min > 44 ? time.hour : time.hour - 1
|
44
|
-
end
|
45
|
-
|
46
|
-
private
|
47
|
-
|
48
|
-
def fetch_file(file, directory = nil)
|
49
|
-
connection.chdir(directory) if directory
|
50
26
|
s = ''
|
51
|
-
connection.retrbinary("RETR #{
|
27
|
+
connection.retrbinary("RETR #{ cccc }.TXT", 1024) do |chunk|
|
52
28
|
s << chunk
|
53
29
|
end
|
54
30
|
s
|
data/lib/metar/report.rb
CHANGED
data/lib/metar/station.rb
CHANGED
@@ -49,15 +49,13 @@ module Metar
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def to_longitude(s)
|
52
|
-
s =~ /^(\d+)-(\d+)(
|
53
|
-
|
54
|
-
($5 == 'E' ? 1.0 : -1.0) * ($1.to_f + $2.to_f / 60.0 + seconds)
|
52
|
+
s =~ /^(\d+)-(\d+)([EW])/ or return nil
|
53
|
+
($3 == 'E' ? 1.0 : -1.0) * ($1.to_f + $2.to_f / 60.0)
|
55
54
|
end
|
56
55
|
|
57
56
|
def to_latitude(s)
|
58
|
-
s =~ /^(\d+)-(\d+)(
|
59
|
-
|
60
|
-
($5 == 'N' ? 1.0 : -1.0) * ($1.to_f + $2.to_f / 60.0 + seconds)
|
57
|
+
s =~ /^(\d+)-(\d+)([SN])/ or return nil
|
58
|
+
($3 == 'N' ? 1.0 : -1.0) * ($1.to_f + $2.to_f / 60.0)
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
@@ -161,7 +159,6 @@ module Metar
|
|
161
159
|
|
162
160
|
# Get data from the NOAA data file (very slow on first call!)
|
163
161
|
def load!
|
164
|
-
return if @loaded
|
165
162
|
noaa_data = Station.find_data_by_cccc(@cccc)
|
166
163
|
raise "Station identifier '#{ @cccc }' not found" if noaa_data.nil?
|
167
164
|
@name = noaa_data[:name]
|
data/lib/metar.rb
CHANGED
@@ -2,17 +2,10 @@ require File.join(File.dirname(__FILE__), 'metar', 'raw')
|
|
2
2
|
require File.join(File.dirname(__FILE__), 'metar', 'station')
|
3
3
|
require File.join(File.dirname(__FILE__), 'metar', 'parser')
|
4
4
|
require File.join(File.dirname(__FILE__), 'metar', 'report')
|
5
|
+
require File.join(File.dirname(__FILE__), 'metar', 'version')
|
5
6
|
|
6
7
|
module Metar
|
7
8
|
|
8
|
-
module VERSION #:nodoc:
|
9
|
-
MAJOR = 0
|
10
|
-
MINOR = 1
|
11
|
-
TINY = 8
|
12
|
-
|
13
|
-
STRING = [MAJOR, MINOR, TINY].join('.')
|
14
|
-
end
|
15
|
-
|
16
9
|
# Base class for all Metar exceptions
|
17
10
|
class MetarError < StandardError
|
18
11
|
end
|
data/test/unit/data_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
|
-
require
|
4
|
+
require 'metar_test_helper'
|
5
5
|
|
6
6
|
class TestMetarData < Test::Unit::TestCase
|
7
7
|
|
@@ -10,7 +10,10 @@ class TestMetarData < Test::Unit::TestCase
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_m9t_translations_available
|
13
|
-
|
13
|
+
distance = M9t::Distance.new( 10000 )
|
14
|
+
|
15
|
+
assert_equal( '10 kilometers', distance.to_s( :units => :kilometers,
|
16
|
+
:precision => 0 ) )
|
14
17
|
end
|
15
18
|
|
16
19
|
# Distance
|
@@ -25,12 +28,12 @@ class TestMetarData < Test::Unit::TestCase
|
|
25
28
|
def test_distance_with_default_options
|
26
29
|
distance = Metar::Distance.new(123)
|
27
30
|
assert_equal(123, distance.value)
|
28
|
-
assert_equal(:kilometers, distance.options[:units])
|
29
31
|
end
|
30
32
|
|
31
33
|
def test_distance_setting_options
|
32
|
-
distance = Metar::Distance.new(123
|
33
|
-
|
34
|
+
distance = Metar::Distance.new( 123 )
|
35
|
+
|
36
|
+
assert_equal('404ft', distance.to_s( :units => :feet ) )
|
34
37
|
end
|
35
38
|
|
36
39
|
# Speed
|
@@ -55,19 +58,22 @@ class TestMetarData < Test::Unit::TestCase
|
|
55
58
|
|
56
59
|
def test_speed_parse_knots
|
57
60
|
speed = Metar::Speed.parse('12KT')
|
61
|
+
|
58
62
|
assert_equal(12.0, speed.to_knots)
|
59
|
-
assert_equal(:kilometers_per_hour, speed.options[:units])
|
60
63
|
end
|
61
64
|
|
62
65
|
def test_speed_parse_kilometers_per_hour_is_default
|
63
|
-
speed = Metar::Speed.parse('12')
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
speed
|
68
|
-
|
69
|
-
speed = Metar::Speed.parse('
|
70
|
-
|
66
|
+
speed = Metar::Speed.parse( '12' )
|
67
|
+
assert_in_delta( M9t::Speed.kilometers_per_hour( 12 ), speed.value, 0.000001 )
|
68
|
+
|
69
|
+
speed = Metar::Speed.parse( '12MPS' )
|
70
|
+
assert_in_delta( 12, speed.value, 0.000001 )
|
71
|
+
|
72
|
+
speed = Metar::Speed.parse( '12KMH' )
|
73
|
+
assert_in_delta( M9t::Speed.kilometers_per_hour( 12 ), speed.value, 0.000001 )
|
74
|
+
|
75
|
+
speed = Metar::Speed.parse( '12KT' )
|
76
|
+
assert_in_delta( M9t::Speed.knots( 12 ), speed.value, 0.000001 )
|
71
77
|
end
|
72
78
|
|
73
79
|
# Temperature
|
@@ -84,7 +90,6 @@ class TestMetarData < Test::Unit::TestCase
|
|
84
90
|
def test_temperature_parse_positive
|
85
91
|
temperature = Metar::Temperature.parse('12')
|
86
92
|
assert_equal(12, temperature.value)
|
87
|
-
assert_equal(:degrees, temperature.options[:units])
|
88
93
|
end
|
89
94
|
|
90
95
|
def test_temperature_parse_negative
|
@@ -110,7 +115,8 @@ class TestMetarData < Test::Unit::TestCase
|
|
110
115
|
|
111
116
|
# Wind
|
112
117
|
def test_wind_parse_without_units
|
113
|
-
wind = Metar::Wind.parse('18012')
|
118
|
+
wind = Metar::Wind.parse( '18012' )
|
119
|
+
|
114
120
|
assert_equal(180, wind.direction.value)
|
115
121
|
assert_equal(12.0, wind.speed.to_kilometers_per_hour)
|
116
122
|
end
|
@@ -129,16 +135,18 @@ class TestMetarData < Test::Unit::TestCase
|
|
129
135
|
|
130
136
|
def test_wind_parse_knots
|
131
137
|
wind = Metar::Wind.parse('24006KT')
|
132
|
-
|
133
|
-
assert_equal(
|
134
|
-
assert_equal(
|
138
|
+
|
139
|
+
assert_equal( 240, wind.direction.value )
|
140
|
+
assert_equal( 6, wind.speed.to_knots )
|
141
|
+
assert_equal( :kilometers_per_hour, wind.options[ :speed_units ] )
|
135
142
|
end
|
136
143
|
|
137
144
|
def test_wind_parse_variable_direction
|
138
|
-
wind = Metar::Wind.parse('VRB20KT')
|
139
|
-
|
140
|
-
assert_equal(
|
141
|
-
assert_equal(
|
145
|
+
wind = Metar::Wind.parse( 'VRB20KT' )
|
146
|
+
|
147
|
+
assert_equal( :variable_direction, wind.direction )
|
148
|
+
assert_equal( 20, wind.speed.to_knots )
|
149
|
+
assert_equal( '37km/h variable direction', wind.to_s )
|
142
150
|
end
|
143
151
|
|
144
152
|
def test_wind_parse_unknown_direction
|
@@ -157,17 +165,17 @@ class TestMetarData < Test::Unit::TestCase
|
|
157
165
|
|
158
166
|
def test_wind_parse_default_output_units_kilometers_per_hour
|
159
167
|
wind = Metar::Wind.parse('18012')
|
160
|
-
assert_equal(:kilometers_per_hour, wind.
|
168
|
+
assert_equal(:kilometers_per_hour, wind.options[:speed_units])
|
161
169
|
wind = Metar::Wind.parse('18012MPS')
|
162
|
-
assert_equal(:kilometers_per_hour, wind.
|
170
|
+
assert_equal(:kilometers_per_hour, wind.options[:speed_units])
|
163
171
|
wind = Metar::Wind.parse('27012KMH')
|
164
|
-
assert_equal(:kilometers_per_hour, wind.
|
172
|
+
assert_equal(:kilometers_per_hour, wind.options[:speed_units])
|
165
173
|
wind = Metar::Wind.parse('24006KT')
|
166
|
-
assert_equal(:kilometers_per_hour, wind.
|
174
|
+
assert_equal(:kilometers_per_hour, wind.options[:speed_units])
|
167
175
|
wind = Metar::Wind.parse('VRB20KT')
|
168
|
-
assert_equal(:kilometers_per_hour, wind.
|
176
|
+
assert_equal(:kilometers_per_hour, wind.options[:speed_units])
|
169
177
|
wind = Metar::Wind.parse('///20KT')
|
170
|
-
assert_equal(:kilometers_per_hour, wind.
|
178
|
+
assert_equal(:kilometers_per_hour, wind.options[:speed_units])
|
171
179
|
end
|
172
180
|
|
173
181
|
# VariableWind
|
@@ -204,31 +212,23 @@ class TestMetarData < Test::Unit::TestCase
|
|
204
212
|
def test_visibility_parse_us_fractions_1_4
|
205
213
|
visibility = Metar::Visibility.parse('1/4SM')
|
206
214
|
assert_equal(M9t::Distance.miles(0.25).value, visibility.distance.value)
|
207
|
-
assert_equal(:miles, visibility.distance.options[:units])
|
208
215
|
end
|
209
216
|
|
210
217
|
def test_visibility_parse_us_fractions_2_1_2
|
211
218
|
visibility = Metar::Visibility.parse('2 1/2SM')
|
212
219
|
assert_equal(M9t::Distance.miles(2.5).value, visibility.distance.value)
|
213
|
-
assert_equal(:miles, visibility.distance.options[:units])
|
214
220
|
end
|
215
221
|
|
216
222
|
def test_visibility_parse_kilometers
|
217
223
|
visibility = Metar::Visibility.parse('5KM')
|
218
224
|
assert_equal(5000.0, visibility.distance.value)
|
219
|
-
assert_equal(:kilometers, visibility.distance.options[:units])
|
220
225
|
end
|
221
226
|
|
222
227
|
def test_visibility_parse_compass
|
223
228
|
visibility = Metar::Visibility.parse('5NE')
|
224
229
|
assert_equal(5000.0, visibility.distance.value)
|
225
|
-
assert_equal(:kilometers, visibility.distance.options[:units])
|
226
230
|
assert_equal(45.0, visibility.direction.value)
|
227
|
-
visibility.
|
228
|
-
visibility.distance.options[:abbreviated] = true
|
229
|
-
visibility.distance.options[:precision] = 0
|
230
|
-
visibility.direction.options[:units] = :compass
|
231
|
-
assert_equal('5km NE', visibility.to_s)
|
231
|
+
assert_equal( '5km NE', visibility.to_s )
|
232
232
|
end
|
233
233
|
|
234
234
|
# RunwayVisibleRange
|
@@ -328,7 +328,7 @@ class TestMetarData < Test::Unit::TestCase
|
|
328
328
|
end
|
329
329
|
|
330
330
|
def test_sky_condition_sct
|
331
|
-
sky_condition = Metar::SkyCondition.parse('SCT016')
|
331
|
+
sky_condition = Metar::SkyCondition.parse( 'SCT016' )
|
332
332
|
assert_equal('scattered', sky_condition.quantity)
|
333
333
|
assert_equal(480, sky_condition.height.value)
|
334
334
|
assert_equal('scattered cloud at 480m', sky_condition.to_s)
|
data/test/unit/parser_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
|
-
require
|
4
|
+
require 'metar_test_helper'
|
5
5
|
|
6
6
|
class TestMetarParser < Test::Unit::TestCase
|
7
7
|
|
@@ -45,8 +45,7 @@ class TestMetarParser < Test::Unit::TestCase
|
|
45
45
|
|
46
46
|
def test_visibility_miles_and_fractions
|
47
47
|
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
48
|
-
assert_in_delta(1.75, parser.visibility.distance.to_miles, 0.01)
|
49
|
-
assert_equal(:miles, parser.visibility.distance.options[:units])
|
48
|
+
assert_in_delta( 1.75, parser.visibility.distance.to_miles, 0.01 )
|
50
49
|
end
|
51
50
|
|
52
51
|
def test_runway_visible_range
|
@@ -64,10 +63,9 @@ class TestMetarParser < Test::Unit::TestCase
|
|
64
63
|
|
65
64
|
def test_runway_visible_range_variable
|
66
65
|
parser = setup_parser('KPDX', "2010/02/15 11:08\nKPDX 151108Z 11006KT 1/4SM R10R/1600VP6000FT FG OVC002 05/05 A3022 RMK AO2")
|
66
|
+
|
67
67
|
assert_equal(1600.0, parser.runway_visible_range[0].visibility1.distance.to_feet)
|
68
|
-
assert_equal(:feet, parser.runway_visible_range[0].visibility1.distance.options[:units])
|
69
68
|
assert_equal(6000.0, parser.runway_visible_range[0].visibility2.distance.to_feet)
|
70
|
-
assert_equal(:feet, parser.runway_visible_range[0].visibility2.distance.options[:units])
|
71
69
|
end
|
72
70
|
|
73
71
|
def test_present_weather
|
@@ -120,7 +118,6 @@ class TestMetarParser < Test::Unit::TestCase
|
|
120
118
|
def test_sea_level_pressure
|
121
119
|
parser = setup_parser('PAIL', "2010/02/06 16:10\nPAIL 061610Z 24006KT 1 3/4SM -SN BKN016 OVC030 M17/M20 A2910 RMK AO2 P0000")
|
122
120
|
assert_equal(29.10, parser.sea_level_pressure.to_inches_of_mercury)
|
123
|
-
assert_equal(:bar, parser.sea_level_pressure.options[:units])
|
124
121
|
end
|
125
122
|
|
126
123
|
def test_remarks
|
data/test/unit/raw_test.rb
CHANGED
data/test/unit/report_test.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# encoding: utf-8
|
3
3
|
|
4
|
-
require
|
4
|
+
require 'metar_test_helper'
|
5
5
|
|
6
6
|
class TestMetarReport < Test::Unit::TestCase
|
7
7
|
def setup
|
@@ -53,7 +53,7 @@ class TestMetarReport < Test::Unit::TestCase
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def test_present_weather
|
56
|
-
report = setup_report('DAAS', "2010/02/15 10:00\nDAAS 151000Z 16012KT 9999 -RA FEW010 BKN026 06/05 Q1006")
|
56
|
+
report = setup_report('DAAS', "2010/02/15 10:00\nDAAS 151000Z 16012KT 9999 -RA FEW010 BKN026 06/05 Q1006")
|
57
57
|
assert_equal('light rain', report.present_weather)
|
58
58
|
I18n.locale = :it
|
59
59
|
assert_equal('pioggia leggera', report.present_weather)
|
@@ -79,7 +79,7 @@ class TestMetarReport < Test::Unit::TestCase
|
|
79
79
|
|
80
80
|
def test_dew_point
|
81
81
|
report = setup_report('LIRQ', "2010/02/06 15:20\nLIRQ 061520Z 01007KT 350V050 9999 SCT035 BKN080 08/02 Q1005")
|
82
|
-
assert_equal('2°C', report.dew_point)
|
82
|
+
assert_equal( '2°C', report.dew_point )
|
83
83
|
end
|
84
84
|
|
85
85
|
def test_sea_level_pressure
|
@@ -91,7 +91,7 @@ class TestMetarReport < Test::Unit::TestCase
|
|
91
91
|
report = setup_report('LIRQ', "2010/02/06 15:20\nLIRQ 061520Z 01007KT 350V050 9999 SCT035 BKN080 08/02 Q1005")
|
92
92
|
Metar::Report.attributes -= [:station_code, :variable_wind, :observer, :remarks]
|
93
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)
|
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
95
|
end
|
96
96
|
|
97
97
|
private
|
data/test/unit/station_test.rb
CHANGED
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metar-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
8
|
+
- 2
|
7
9
|
- 1
|
8
|
-
|
9
|
-
version: 0.1.8
|
10
|
+
version: 0.2.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Joe Yates
|
@@ -14,113 +15,71 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date:
|
18
|
+
date: 2011-04-04 00:00:00 +01:00
|
18
19
|
default_executable:
|
19
|
-
dependencies:
|
20
|
-
|
21
|
-
name: aasm
|
22
|
-
prerelease: false
|
23
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
segments:
|
28
|
-
- 2
|
29
|
-
- 1
|
30
|
-
- 5
|
31
|
-
version: 2.1.5
|
32
|
-
type: :runtime
|
33
|
-
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: i18n
|
36
|
-
prerelease: false
|
37
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
-
requirements:
|
39
|
-
- - ">="
|
40
|
-
- !ruby/object:Gem::Version
|
41
|
-
segments:
|
42
|
-
- 0
|
43
|
-
- 3
|
44
|
-
- 5
|
45
|
-
version: 0.3.5
|
46
|
-
type: :runtime
|
47
|
-
version_requirements: *id002
|
48
|
-
- !ruby/object:Gem::Dependency
|
49
|
-
name: m9t
|
50
|
-
prerelease: false
|
51
|
-
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
-
requirements:
|
53
|
-
- - ">="
|
54
|
-
- !ruby/object:Gem::Version
|
55
|
-
segments:
|
56
|
-
- 0
|
57
|
-
- 1
|
58
|
-
- 12
|
59
|
-
version: 0.1.12
|
60
|
-
type: :runtime
|
61
|
-
version_requirements: *id003
|
20
|
+
dependencies: []
|
21
|
+
|
62
22
|
description: A Ruby library which handle METAR weather reports. Provides weather station listings and info. Downloads and parses reports. Presents localized full text reports
|
63
23
|
email: joe.g.yates@gmail.com
|
64
24
|
executables: []
|
65
25
|
|
66
26
|
extensions: []
|
67
27
|
|
68
|
-
extra_rdoc_files:
|
69
|
-
|
70
|
-
- COPYING
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
71
30
|
files:
|
72
31
|
- README.rdoc
|
73
32
|
- COPYING
|
74
33
|
- Rakefile
|
75
|
-
- bin/parse_raw.rb
|
76
34
|
- bin/download_raw.rb
|
77
|
-
-
|
78
|
-
- lib/metar/station.rb
|
79
|
-
- lib/metar/raw.rb
|
80
|
-
- lib/metar/parser.rb
|
35
|
+
- bin/parse_raw.rb
|
81
36
|
- lib/metar/data.rb
|
37
|
+
- lib/metar/parser.rb
|
38
|
+
- lib/metar/raw.rb
|
82
39
|
- lib/metar/report.rb
|
40
|
+
- lib/metar/station.rb
|
41
|
+
- lib/metar/version.rb
|
42
|
+
- lib/metar.rb
|
83
43
|
- test/all_tests.rb
|
84
44
|
- test/metar_test_helper.rb
|
85
45
|
- test/unit/data_test.rb
|
46
|
+
- test/unit/parser_test.rb
|
47
|
+
- test/unit/raw_test.rb
|
86
48
|
- test/unit/report_test.rb
|
87
49
|
- test/unit/station_test.rb
|
88
|
-
- test/unit/raw_test.rb
|
89
|
-
- test/unit/parser_test.rb
|
90
|
-
- locales/it.yml
|
91
50
|
- locales/en.yml
|
51
|
+
- locales/it.yml
|
92
52
|
has_rdoc: true
|
93
53
|
homepage: http://github.com/joeyates/metar-parser
|
94
54
|
licenses: []
|
95
55
|
|
96
56
|
post_install_message:
|
97
|
-
rdoc_options:
|
98
|
-
|
99
|
-
- --title
|
100
|
-
- METAR Weather Report Parser
|
101
|
-
- --main
|
102
|
-
- README.rdoc
|
103
|
-
- --inline-source
|
57
|
+
rdoc_options: []
|
58
|
+
|
104
59
|
require_paths:
|
105
60
|
- lib
|
106
61
|
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
107
63
|
requirements:
|
108
64
|
- - ">="
|
109
65
|
- !ruby/object:Gem::Version
|
66
|
+
hash: 3
|
110
67
|
segments:
|
111
68
|
- 0
|
112
69
|
version: "0"
|
113
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
114
72
|
requirements:
|
115
73
|
- - ">="
|
116
74
|
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
117
76
|
segments:
|
118
77
|
- 0
|
119
78
|
version: "0"
|
120
79
|
requirements: []
|
121
80
|
|
122
|
-
rubyforge_project:
|
123
|
-
rubygems_version: 1.3.
|
81
|
+
rubyforge_project: nowarning
|
82
|
+
rubygems_version: 1.3.7
|
124
83
|
signing_key:
|
125
84
|
specification_version: 3
|
126
85
|
summary: A Ruby library for METAR weather reports
|