metar-parser 0.9.11 → 0.9.12

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.md CHANGED
@@ -32,14 +32,27 @@ puts station.report.to_s
32
32
  Countries
33
33
  ---------
34
34
 
35
+ List countries:
36
+
35
37
  ```ruby
36
- # List countries:
37
38
  puts Metar::Station.countries
39
+ ```
40
+
41
+ Find a country's weather stations:
38
42
 
39
- # Find a country's weather stations:
43
+ ```ruby
40
44
  spanish = Metar::Station.find_all_by_country( 'Spain' )
41
45
  ```
42
46
 
47
+ Get The Data
48
+ ------------
49
+ ```ruby
50
+ station = Metar::Station.find_by_cccc( 'KPDX' )
51
+ parser = station.parser
52
+ puts parser.temperature.value
53
+ ```
54
+
55
+
43
56
  The METAR Data Format
44
57
  =====================
45
58
 
@@ -72,10 +85,3 @@ There are two gems which read the National Oceanic and Atmospheric Association's
72
85
  Interactive map:
73
86
  * http://www.spatiality.at/metarr/frontend/
74
87
 
75
- Testing
76
- =======
77
-
78
- The tests use a local copy of the weather stations list: data/nsd_cccc.txt
79
-
80
- If missing, the file gets downloaded before running tests.
81
-
data/Rakefile CHANGED
@@ -1,8 +1,9 @@
1
- require 'rubygems'
2
- require 'rake/gempackagetask'
3
- require 'rake/rdoctask'
4
- require 'rake/testtask'
1
+ require 'rubygems' if RUBY_VERSION < '1.9'
2
+ require 'rubygems/package_task'
3
+ require 'rdoc/task'
5
4
  require 'rake/clean'
5
+ require 'rcov/rcovtask' if RUBY_VERSION < '1.9'
6
+ require 'rspec/core/rake_task'
6
7
 
7
8
  $:.unshift(File.dirname(__FILE__) + '/lib')
8
9
  require 'metar'
@@ -10,19 +11,26 @@ require 'metar'
10
11
  RDOC_OPTS = ['--quiet', '--title', 'METAR Weather Report Parser', '--main', 'README.rdoc', '--inline-source']
11
12
  RDOC_PATH = 'doc/rdoc'
12
13
 
13
- task :default => :test
14
+ task :default => :spec
14
15
 
15
- Rake::TestTask.new do |t|
16
- t.libs << 'test'
17
- t.test_files = FileList['test/**/*.rb']
18
- t.verbose = true
16
+ RSpec::Core::RakeTask.new do | t |
17
+ t.pattern = 'spec/**/*_spec.rb'
19
18
  end
20
19
 
21
- Rake::RDocTask.new do |rdoc|
22
- rdoc.rdoc_dir = RDOC_PATH
23
- rdoc.options += RDOC_OPTS
24
- rdoc.main = 'README.rdoc'
25
- rdoc.rdoc_files.add ['README.rdoc', 'COPYING', 'lib/**/*.rb']
20
+ if RUBY_VERSION < '1.9'
21
+ Rake::RDocTask.new do |rdoc|
22
+ rdoc.rdoc_dir = RDOC_PATH
23
+ rdoc.options += RDOC_OPTS
24
+ rdoc.main = 'README.rdoc'
25
+ rdoc.rdoc_files.add ['README.rdoc', 'COPYING', 'lib/**/*.rb']
26
+ end
27
+
28
+ RSpec::Core::RakeTask.new( 'spec:rcov' ) do |t|
29
+ t.pattern = 'spec/**/*_spec.rb'
30
+ t.rcov = true
31
+ t.rcov_opts = [ '--exclude', 'spec/,/gems/' ]
32
+ end
33
+
26
34
  end
27
35
 
28
36
  desc "Build the gem"
@@ -34,3 +42,4 @@ desc "Publish a new version of the gem"
34
42
  task :release => :build do
35
43
  `gem push metar-parser-#{Metar::VERSION::STRING}.gem`
36
44
  end
45
+
data/bin/download_raw.rb CHANGED
@@ -16,8 +16,6 @@ Metar::Station.load_local
16
16
 
17
17
  stations = {}
18
18
 
19
- Metar::Raw.cache_connection
20
-
21
19
  Metar::Station.all.each do |station|
22
20
 
23
21
  next if station.cccc[0, 1] < initial
data/lib/metar/data.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # encoding: utf-8
2
- require 'rubygems' if RUBY_VERSION < '1.9'
3
2
  require 'i18n'
4
3
  require 'm9t'
5
4
 
@@ -7,15 +6,13 @@ module Metar
7
6
  locales_path = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'locales'))
8
7
  I18n.load_path += Dir.glob("#{ locales_path }/*.yml")
9
8
 
10
- # Subclasses M9t::Distance
11
- # Uses kilometers as desired default output unit
12
9
  class Distance < M9t::Distance
13
10
 
14
11
  attr_accessor :units
15
12
 
16
13
  # nil is taken to mean 'data unavailable'
17
14
  def initialize( meters = nil )
18
- @units = units || :meters
15
+ @units = :meters
19
16
  if meters
20
17
  super
21
18
  else
@@ -25,8 +22,8 @@ module Metar
25
22
 
26
23
  # Handles nil case differently to M9t::Distance
27
24
  def to_s( options = {} )
28
- options = { :units => @units,
29
- :precision => 0,
25
+ options = { :units => @units,
26
+ :precision => 0,
30
27
  :abbreviated => true }.merge( options )
31
28
  return I18n.t('metar.distance.unknown') if @value.nil?
32
29
  super( options )
@@ -38,6 +35,7 @@ module Metar
38
35
  class Speed < M9t::Speed
39
36
 
40
37
  METAR_UNITS = {
38
+ '' => :kilometers_per_hour,
41
39
  'KMH' => :kilometers_per_hour,
42
40
  'MPS' => :meters_per_second,
43
41
  'KT' => :knots,
@@ -45,11 +43,9 @@ module Metar
45
43
 
46
44
  def Speed.parse(s)
47
45
  case
48
- when s =~ /^(\d+)(KT|MPS|KMH)$/
46
+ when s =~ /^(\d+)(|KT|MPS|KMH)$/
49
47
  # Call the appropriate factory method for the supplied units
50
48
  send( METAR_UNITS[$2], $1.to_i )
51
- when s =~ /^(\d+)$/
52
- kilometers_per_hour( $1.to_i )
53
49
  else
54
50
  nil
55
51
  end
@@ -99,33 +95,46 @@ module Metar
99
95
  def Wind.parse(s)
100
96
  case
101
97
  when s =~ /^(\d{3})(\d{2}(|MPS|KMH|KT))$/
98
+ return nil if $1.to_i > 359
102
99
  new( M9t::Direction.new( $1 ),
103
- Speed.parse( $2 ),
104
- :direction_units => :compass )
100
+ Speed.parse( $2 ) )
105
101
  when s =~ /^(\d{3})(\d{2})G(\d{2,3}(|MPS|KMH|KT))$/
102
+ return nil if $1.to_i > 359
106
103
  new( M9t::Direction.new( $1 ),
107
- Speed.parse( $2 ),
108
- :direction_units => :compass )
104
+ Speed.parse( $2 + $4 ),
105
+ Speed.parse( $3 ) )
109
106
  when s =~ /^VRB(\d{2}(|MPS|KMH|KT))$/
110
- new(:variable_direction, Speed.parse($1))
107
+ new( :variable_direction,
108
+ Speed.parse($1))
111
109
  when s =~ /^\/{3}(\d{2}(|MPS|KMH|KT))$/
112
- new(:unknown_direction, Speed.parse($1))
113
- when s =~ /^\/{3}(\/{2}(|MPS|KMH|KT))$/
114
- new(:unknown_direction, :unknown)
110
+ new( :unknown_direction,
111
+ Speed.parse($1))
112
+ when s =~ %r(^/////(|MPS|KMH|KT)$)
113
+ new( :unknown_direction,
114
+ :unknown_speed)
115
115
  else
116
116
  nil
117
117
  end
118
118
  end
119
119
 
120
- attr_reader :direction, :speed, :options
120
+ attr_reader :direction, :speed, :gusts
121
121
 
122
- def initialize( direction, speed, options = {} )
123
- @options = { :direction_units => :compass,
124
- :speed_units => :kilometers_per_hour }.merge( options )
125
- @direction, @speed = direction, speed
122
+ def initialize( direction, speed, gusts = nil )
123
+ @direction, @speed, @gusts = direction, speed, gusts
126
124
  end
127
125
 
128
- def to_s
126
+ def to_s( options = {} )
127
+ options = { :direction_units => :compass,
128
+ :speed_units => :kilometers_per_hour }.merge( options )
129
+ speed =
130
+ case @speed
131
+ when :unknown_speed
132
+ I18n.t('metar.wind.unknown_speed')
133
+ else
134
+ @speed.to_s( :abbreviated => true,
135
+ :precision => 0,
136
+ :units => options[ :speed_units ] )
137
+ end
129
138
  direction =
130
139
  case @direction
131
140
  when :variable_direction
@@ -133,18 +142,16 @@ module Metar
133
142
  when :unknown_direction
134
143
  I18n.t('metar.wind.unknown_direction')
135
144
  else
136
- @direction.to_s( :units => @options[ :direction_units ] )
145
+ @direction.to_s( :units => options[ :direction_units ] )
137
146
  end
138
- speed =
139
- case @speed
140
- when :unknown
141
- I18n.t('metar.wind.unknown_speed')
142
- else
143
- @speed.to_s( :abbreviated => true,
144
- :precision => 0,
145
- :units => @options[ :speed_units ] )
146
- end
147
- "#{ speed } #{ direction }"
147
+ s = "#{ speed } #{ direction }"
148
+ if ! @gusts.nil?
149
+ g = @gusts.to_s( :abbreviated => true,
150
+ :precision => 0,
151
+ :units => options[ :speed_units ] )
152
+ s += " #{ I18n.t('metar.wind.gusts') } #{ g }"
153
+ end
154
+ s
148
155
  end
149
156
 
150
157
  end
@@ -166,7 +173,7 @@ module Metar
166
173
  end
167
174
 
168
175
  def to_s
169
- "#{ @direction1 } - #{ @direction2 }"
176
+ "#{ @direction1.to_s( :units => :compass ) } - #{ @direction2.to_s( :units => :compass ) }"
170
177
  end
171
178
 
172
179
  end
@@ -276,18 +283,25 @@ module Metar
276
283
  distance_options = { :abbreviated => true,
277
284
  :precision => 0,
278
285
  :units => @units }
279
- if @visibility2.nil?
280
- I18n.t('metar.runway_visible_range.runway') +
281
- ' ' + @designator +
282
- ': ' + @visibility1.to_s( distance_options )
283
- else
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 )
286
+ s =
287
+ if @visibility2.nil?
288
+ I18n.t( 'metar.runway_visible_range.runway') +
289
+ ' ' + @designator +
290
+ ': ' + @visibility1.to_s( distance_options )
291
+ else
292
+ I18n.t( 'metar.runway_visible_range.runway') +
293
+ ' ' + @designator +
294
+ ': ' + I18n.t('metar.runway_visible_range.from') +
295
+ ' ' + @visibility1.to_s( distance_options ) +
296
+ ' ' + I18n.t('metar.runway_visible_range.to') +
297
+ ' ' + @visibility2.to_s( distance_options )
298
+ end
299
+
300
+ if ! tendency.nil?
301
+ s += ' ' + I18n.t( "tendency.#{ tendency }" )
290
302
  end
303
+
304
+ s
291
305
  end
292
306
 
293
307
  end
@@ -367,9 +381,7 @@ module Metar
367
381
  end
368
382
 
369
383
  def to_s
370
- modifier = @modifier ? @modifier + ' ' : ''
371
- descriptor = @descriptor ? @descriptor + ' ' : ''
372
- I18n.t("metar.present_weather.%s%s%s" % [modifier, descriptor, @phenomenon])
384
+ I18n.t("metar.present_weather.%s" % [@modifier, @descriptor, @phenomenon].compact.join(' '))
373
385
  end
374
386
 
375
387
  end
@@ -377,31 +389,27 @@ module Metar
377
389
  class SkyCondition
378
390
 
379
391
  QUANTITY = {'BKN' => 'broken', 'FEW' => 'few', 'OVC' => 'overcast', 'SCT' => 'scattered'}
392
+ CONDITION = {
393
+ 'CB' => 'cumulonimbus',
394
+ 'TCU' => 'towering cumulus',
395
+ '///' => nil,
396
+ '' => nil
397
+ }
398
+ CLEAR_SKIES = [
399
+ 'NSC', # WMO
400
+ 'NCD', # WMO
401
+ 'CLR',
402
+ 'SKC',
403
+ ]
380
404
 
381
405
  def SkyCondition.parse(sky_condition)
382
406
  case
383
- when (sky_condition == 'NSC' or sky_condition == 'NCD') # WMO
384
- new
385
- when sky_condition == 'CLR'
386
- new
387
- when sky_condition == 'SKC'
407
+ when CLEAR_SKIES.include?( sky_condition )
388
408
  new
389
- when sky_condition =~ /^(BKN|FEW|OVC|SCT)(\d+)(CB|TCU|\/{3})?$/
390
- quantity = QUANTITY[$1]
391
- height = Distance.new( $2.to_i * 30.0 )
392
- type =
393
- case $3
394
- when 'CB'
395
- 'cumulonimbus'
396
- when 'TCU'
397
- 'towering cumulus'
398
- when nil
399
- nil
400
- when '///'
401
- nil
402
- else
403
- raise ParseError.new("Unexpected sky condition type: #$3")
404
- end
409
+ when sky_condition =~ /^(BKN|FEW|OVC|SCT)(\d+)(CB|TCU|\/{3}|)?$/
410
+ quantity = QUANTITY[ $1 ]
411
+ height = Distance.new( $2.to_i * 30.0 )
412
+ type = CONDITION[ $3 ]
405
413
  new(quantity, height, type)
406
414
  else
407
415
  nil
@@ -414,9 +422,11 @@ module Metar
414
422
  end
415
423
 
416
424
  def to_s
417
- conditions = to_summary
418
- conditions += ' ' + I18n.t('metar.altitude.at') + ' ' + height.to_s if not @height.nil?
419
- conditions
425
+ if @height.nil?
426
+ to_summary
427
+ else
428
+ to_summary + ' ' + I18n.t('metar.altitude.at') + ' ' + height.to_s
429
+ end
420
430
  end
421
431
 
422
432
  def to_summary
@@ -446,3 +456,4 @@ module Metar
446
456
  end
447
457
 
448
458
  end
459
+
data/lib/metar/parser.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'rubygems' if RUBY_VERSION < '1.9'
2
1
  require 'aasm'
3
2
  require File.join(File.dirname(__FILE__), 'data')
4
3
 
@@ -84,12 +83,10 @@ module Metar
84
83
  transitions :from => [:remarks], :to => :end
85
84
  end
86
85
 
87
- def Parser.for_cccc(cccc)
86
+ def self.for_cccc(cccc)
88
87
  station = Metar::Station.new(cccc)
89
88
  raw = Metar::Raw.new(station)
90
- parser = new(raw)
91
- parser.analyze
92
- parser
89
+ new(raw)
93
90
  end
94
91
 
95
92
  attr_reader :raw, :metar, :time
@@ -313,9 +310,6 @@ module Metar
313
310
  end
314
311
 
315
312
  def seek_end
316
- if @chunks.length > 0
317
- raise ParseError.new("Unexpected tokens found at end of string: found '#{ @chunks.join(' ') }'")
318
- end
319
313
  done!
320
314
  end
321
315
 
data/lib/metar/raw.rb CHANGED
@@ -1,4 +1,3 @@
1
- require 'rubygems' if RUBY_VERSION < '1.9'
2
1
  require 'net/ftp'
3
2
 
4
3
  module Metar
@@ -9,10 +8,6 @@ module Metar
9
8
 
10
9
  class << self
11
10
 
12
- def cache_connection
13
- @@connection = connection
14
- end
15
-
16
11
  def connection
17
12
  return @@connection if @@connection
18
13
  connect
@@ -93,3 +88,4 @@ module Metar
93
88
  end
94
89
 
95
90
  end
91
+
data/lib/metar/report.rb CHANGED
@@ -4,50 +4,22 @@ module Metar
4
4
 
5
5
  class Report
6
6
 
7
- KNOWN_ATTRIBUTES =
8
- [
9
- :station_code, :station_name, :station_country,
10
- :date, :time, :observer,
11
- :wind, :variable_wind,
12
- :visibility, :runway_visible_range,
13
- :present_weather,
14
- :sky_summary, :sky_conditions,
15
- :temperature, :dew_point, :remarks
16
- ]
17
-
18
- DEFAULT_ATTRIBUTES =
19
- [
20
- :station_name, :station_country,
21
- :time,
22
- :wind,
23
- :visibility,
24
- :present_weather,
25
- :sky_summary,
26
- :temperature
27
- ]
28
-
29
- instance_eval do
30
-
31
- def reset_options!
32
- @attributes = DEFAULT_ATTRIBUTES.clone
33
- end
34
-
35
- def attributes
36
- @attributes
37
- end
38
-
39
- def attributes=(attributes)
40
- @attributes = attributes.clone
41
- end
42
-
43
- reset_options!
44
- end
7
+ ATTRIBUTES = [
8
+ :station_name,
9
+ :station_country,
10
+ :time,
11
+ :wind,
12
+ :visibility,
13
+ :present_weather,
14
+ :sky_summary,
15
+ :temperature
16
+ ]
45
17
 
46
18
  attr_reader :parser, :station
47
19
 
48
20
  def initialize(parser)
49
21
  @parser = parser
50
- @station = Station.find_by_cccc(@parser.station_code)
22
+ @station = Station.find_by_cccc(@parser.station_code) # TODO: parser should return the station
51
23
  end
52
24
 
53
25
  def station_name
@@ -122,19 +94,21 @@ module Metar
122
94
  def remarks
123
95
  @parser.remarks.join(', ')
124
96
  end
125
-
126
- def attributes
127
- Metar::Report.attributes.reduce([]) do |memo, key|
128
- value = self.send(key).to_s
129
- memo << {:attribute => key, :value => value} if not value.empty?
130
- memo
131
- end
132
- end
133
97
 
134
98
  def to_s
135
99
  attributes.collect do |attribute|
136
100
  I18n.t('metar.' + attribute[:attribute].to_s + '.title') + ': ' + attribute[:value]
137
- end.join("\n")
101
+ end.join("\n") + "\n"
102
+ end
103
+
104
+ private
105
+
106
+ def attributes
107
+ a = Metar::Report::ATTRIBUTES.map do | key |
108
+ value = self.send(key).to_s
109
+ {:attribute => key, :value => value} if not value.empty?
110
+ end
111
+ a.compact
138
112
  end
139
113
 
140
114
  end