metar-parser 0.1.8 → 0.2.1
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.
- 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
|