metar-parser 1.5.0 → 1.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -48
- 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 -14
- 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 +105 -93
- data/lib/metar/data.rb +25 -23
- data/lib/metar/i18n.rb +5 -2
- data/lib/metar/parser.rb +46 -21
- data/lib/metar/raw.rb +32 -44
- data/lib/metar/report.rb +31 -20
- data/lib/metar/station.rb +29 -20
- data/lib/metar/version.rb +3 -1
- 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 +192 -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 +92 -52
- metadata +53 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7f1558724e63f52aee14e0f1775c7f4dc90eb187c9d3060426d7ab14de16fe4
|
4
|
+
data.tar.gz: 189365393cc681c9398dc78fe9d941fb56166276dea4645f1f56a93fef19394f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 376de6502034e38fa3581b4c0b7c6cc0df72d4067ae20337c12f3f5f294ca926587628a0a1769848a682545ad04a59b2547fd2697db5aafe73d67e4497c9adb5
|
7
|
+
data.tar.gz: 55bc69ff0cf47c5b7c6a207fe6d56327bc3e0c4c9b9f6b262807c32e13ff554ee611a21158d62acb92426a0223533590a2a221c7dcfab2cced49e8748eb4cdff
|
data/README.md
CHANGED
@@ -1,17 +1,18 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
[![Build Status](https://github.com/joeyates/metar-parser/actions/workflows/main.yml/badge.svg)][CI Status]
|
2
|
+
|
3
|
+
# metar-parser
|
3
4
|
|
4
5
|
*Get latest weather reports from weather stations worldwide*
|
5
6
|
|
6
7
|
* [Source Code]
|
7
8
|
* [API documentation]
|
8
9
|
* [Rubygem]
|
9
|
-
* [
|
10
|
+
* [CI Status]
|
10
11
|
|
11
12
|
[Source Code]: https://github.com/joeyates/metar-parser "Source code at GitHub"
|
12
13
|
[API documentation]: http://rubydoc.info/gems/metar-parser/frames "RDoc API Documentation at Rubydoc.info"
|
13
14
|
[Rubygem]: http://rubygems.org/gems/metar-parser "Ruby gem at rubygems.org"
|
14
|
-
[
|
15
|
+
[CI Status]: https://github.com/joeyates/metar-parser/actions/workflows/main.yml
|
15
16
|
|
16
17
|
The information comes from the National Oceanic and Atmospheric Association's raw data source.
|
17
18
|
|
@@ -115,53 +116,10 @@ If you only want to accept standards-compliant METAR strings, do this:
|
|
115
116
|
Metar::Parse.compliance = :strict
|
116
117
|
```
|
117
118
|
|
118
|
-
Changelog
|
119
|
-
=========
|
120
|
-
|
121
|
-
1.0.0
|
122
|
-
-----
|
123
|
-
This version introduces a major change to the Metar::Raw class.
|
124
|
-
|
125
|
-
Previously, this class downloaded METAR data from the NOAA FTP site.
|
126
|
-
The old functionality has been moved to Metar::Raw::Noaa. The new class,
|
127
|
-
Metar::Raw::Data accepts a METAR string as a parameter - allowing the user to
|
128
|
-
parse METAR strings without necessarily contacting the NOAA.
|
129
|
-
|
130
119
|
Contributors
|
131
120
|
============
|
132
121
|
|
133
122
|
* [Joe Yates](https://github.com/joeyates)
|
134
123
|
* [Derek Johnson](https://github.com/EpicDraws)
|
135
124
|
* [Florian Egermann and Mathias Wollin](https://github.com/math)
|
136
|
-
|
137
|
-
Alternative Software
|
138
|
-
====================
|
139
|
-
|
140
|
-
Ruby
|
141
|
-
----
|
142
|
-
|
143
|
-
Other Ruby libraries offering METAR parsing:
|
144
|
-
|
145
|
-
* [ruby-metar]
|
146
|
-
* [ruby-wx]
|
147
|
-
|
148
|
-
There are many reports (WMO) that these libraries do not parse.
|
149
|
-
|
150
|
-
[ruby-metar]: http://github.com/brandonh/ruby-metar
|
151
|
-
[ruby-wx]: http://hans.fugal.net/src/ruby-wx/doc/
|
152
|
-
|
153
|
-
There are two gems which read the National Oceanic and Atmospheric Association's XML weather data feeds:
|
154
|
-
|
155
|
-
* [noaa]
|
156
|
-
* [noaa-weather]
|
157
|
-
|
158
|
-
[noaa]: http://github.com/outoftime/noaa
|
159
|
-
[noaa-weather]: http://rubygems.org/gems/noaa-weather "Ruby interface to NOAA SOAP interface"
|
160
|
-
|
161
|
-
Other
|
162
|
-
-----
|
163
|
-
|
164
|
-
* [Interactive map]
|
165
|
-
|
166
|
-
[Interactive map]: http://www.spatiality.at/metarr/frontend/
|
167
|
-
|
125
|
+
* [Douglas Robertson](https://github.com/douglasr)
|
data/Rakefile
CHANGED
data/lib/metar/data/base.rb
CHANGED
@@ -1,15 +1,21 @@
|
|
1
|
-
|
2
|
-
def self.parse(raw)
|
3
|
-
new(raw)
|
4
|
-
end
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
3
|
+
module Metar
|
4
|
+
module Data
|
5
|
+
class Base
|
6
|
+
def self.parse(raw)
|
7
|
+
new(raw)
|
8
|
+
end
|
7
9
|
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
attr_reader :raw
|
11
|
+
|
12
|
+
def initialize(raw)
|
13
|
+
@raw = raw
|
14
|
+
end
|
11
15
|
|
12
|
-
|
13
|
-
|
16
|
+
def value
|
17
|
+
raw
|
18
|
+
end
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
@@ -1,15 +1,21 @@
|
|
1
|
-
|
2
|
-
def self.parse(raw)
|
3
|
-
feet = raw[/^(\d+)(FT)/, 1]
|
4
|
-
height = Metar::Data::Distance.feet(feet)
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
|
3
|
+
module Metar
|
4
|
+
module Data
|
5
|
+
class DensityAltitude < Metar::Data::Base
|
6
|
+
def self.parse(raw)
|
7
|
+
feet = raw[/^(\d+)(FT)/, 1]
|
8
|
+
height = Metar::Data::Distance.feet(feet)
|
9
|
+
|
10
|
+
new(raw, height: height)
|
11
|
+
end
|
8
12
|
|
9
|
-
|
13
|
+
attr_accessor :height
|
10
14
|
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
def initialize(raw, height:)
|
16
|
+
@raw = raw
|
17
|
+
@height = height
|
18
|
+
end
|
19
|
+
end
|
14
20
|
end
|
15
21
|
end
|
data/lib/metar/data/direction.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "i18n"
|
2
4
|
require "m9t"
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
module Metar
|
7
|
+
module Data
|
8
|
+
class Direction < M9t::Direction
|
9
|
+
def initialize(direction)
|
10
|
+
direction = M9t::Direction.normalize(direction.to_f)
|
11
|
+
super(direction)
|
12
|
+
end
|
13
|
+
end
|
8
14
|
end
|
9
15
|
end
|
data/lib/metar/data/distance.rb
CHANGED
@@ -1,27 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "i18n"
|
2
4
|
require "m9t"
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
+
module Metar
|
7
|
+
module Data
|
8
|
+
class Distance < M9t::Distance
|
9
|
+
attr_accessor :serialization_units
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
# nil is taken to mean 'data unavailable'
|
12
|
+
def initialize(meters = nil)
|
13
|
+
@serialization_units = :meters
|
14
|
+
if meters
|
15
|
+
super
|
16
|
+
else
|
17
|
+
@value = nil
|
18
|
+
end
|
19
|
+
end
|
16
20
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
# Handles nil case differently to M9t::Distance
|
22
|
+
def to_s(options = {})
|
23
|
+
options = {
|
24
|
+
units: serialization_units,
|
25
|
+
precision: 0,
|
26
|
+
abbreviated: true
|
27
|
+
}.merge(options)
|
28
|
+
return I18n.t("metar.distance.unknown") if @value.nil?
|
29
|
+
|
30
|
+
super(options)
|
31
|
+
end
|
32
|
+
end
|
26
33
|
end
|
27
34
|
end
|
data/lib/metar/data/lightning.rb
CHANGED
@@ -1,74 +1,83 @@
|
|
1
|
-
|
2
|
-
TYPE = {'' => :default}
|
3
|
-
|
4
|
-
def self.parse_chunks(chunks)
|
5
|
-
raw = chunks.shift
|
6
|
-
m = raw.match(/^LTG(|CG|IC|CC|CA)$/)
|
7
|
-
raise 'first chunk is not lightning' if m.nil?
|
8
|
-
type = TYPE[m[1]]
|
9
|
-
|
10
|
-
frequency = nil
|
11
|
-
distance = nil
|
12
|
-
directions = []
|
13
|
-
|
14
|
-
if chunks[0] == 'DSNT'
|
15
|
-
distance = Metar::Data::Distance.miles(10) # Should be >10SM, not 10SM
|
16
|
-
raw += " " + chunks.shift
|
17
|
-
end
|
1
|
+
# frozen_string_literal: true
|
18
2
|
|
19
|
-
|
20
|
-
|
3
|
+
module Metar
|
4
|
+
module Data
|
5
|
+
class Lightning < Metar::Data::Base
|
6
|
+
TYPE = {'' => :default}.freeze
|
21
7
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
next
|
27
|
-
end
|
8
|
+
def self.parse_chunks(chunks)
|
9
|
+
raw = chunks.shift
|
10
|
+
m = raw.match(/^LTG(|CG|IC|CC|CA)$/)
|
11
|
+
raise 'first chunk is not lightning' if m.nil?
|
28
12
|
|
29
|
-
|
30
|
-
directions += ['N', 'E', 'S', 'W']
|
31
|
-
raw += " " + chunks.shift
|
32
|
-
next
|
33
|
-
end
|
13
|
+
type = TYPE[m[1]]
|
34
14
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
15
|
+
frequency = nil
|
16
|
+
distance = nil
|
17
|
+
directions = []
|
18
|
+
|
19
|
+
if chunks[0] == 'DSNT'
|
20
|
+
distance = Metar::Data::Distance.miles(10) # Should be >10SM, not 10SM
|
39
21
|
raw += " " + chunks.shift
|
40
|
-
else
|
41
|
-
break
|
42
22
|
end
|
43
|
-
next
|
44
|
-
end
|
45
23
|
|
46
|
-
|
47
|
-
|
48
|
-
next
|
49
|
-
end
|
24
|
+
loop do
|
25
|
+
break if chunks[0].nil?
|
50
26
|
|
51
|
-
|
52
|
-
|
27
|
+
if compass?(chunks[0])
|
28
|
+
direction = chunks.shift
|
29
|
+
raw += " " + direction
|
30
|
+
directions << direction
|
31
|
+
next
|
32
|
+
end
|
53
33
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
34
|
+
if chunks[0] == 'ALQDS'
|
35
|
+
directions += %w(N E S W)
|
36
|
+
raw += " " + chunks.shift
|
37
|
+
next
|
38
|
+
end
|
60
39
|
|
61
|
-
|
62
|
-
|
63
|
-
|
40
|
+
m = chunks[0].match(/^([NESW]{1,2})-([NESW]{1,2})$/)
|
41
|
+
if m
|
42
|
+
break if !compass?(m[1])
|
43
|
+
break if !compass?(m[2])
|
44
|
+
|
45
|
+
directions += [m[1], m[2]]
|
46
|
+
raw += " " + chunks.shift
|
47
|
+
next
|
48
|
+
end
|
64
49
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
50
|
+
if chunks[0] == 'AND'
|
51
|
+
raw += " " + chunks.shift
|
52
|
+
next
|
53
|
+
end
|
69
54
|
|
70
|
-
|
71
|
-
|
72
|
-
|
55
|
+
break
|
56
|
+
end
|
57
|
+
|
58
|
+
new(
|
59
|
+
raw,
|
60
|
+
frequency: frequency, type: type,
|
61
|
+
distance: distance, directions: directions
|
62
|
+
)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.compass?(direction)
|
66
|
+
direction =~ /^([NESW]|NE|SE|SW|NW)$/
|
67
|
+
end
|
68
|
+
|
69
|
+
attr_accessor :frequency
|
70
|
+
attr_accessor :type
|
71
|
+
attr_accessor :distance
|
72
|
+
attr_accessor :directions
|
73
|
+
|
74
|
+
def initialize(raw, frequency:, type:, distance:, directions:)
|
75
|
+
@raw = raw
|
76
|
+
@frequency = frequency
|
77
|
+
@type = type
|
78
|
+
@distance = distance
|
79
|
+
@directions = directions
|
80
|
+
end
|
81
|
+
end
|
73
82
|
end
|
74
83
|
end
|
data/lib/metar/data/observer.rb
CHANGED
@@ -1,24 +1,30 @@
|
|
1
|
-
|
2
|
-
def self.parse(raw)
|
3
|
-
case
|
4
|
-
when raw == 'AUTO' # WMO 15.4
|
5
|
-
new(raw, value: :auto)
|
6
|
-
when raw == 'COR' # WMO specified code word for correction
|
7
|
-
new(raw, value: :corrected)
|
8
|
-
when raw =~ /CC[A-Z]/ # Canadian correction
|
9
|
-
# Canada uses CCA for first correction, CCB for second, etc...
|
10
|
-
new(raw, value: :corrected)
|
11
|
-
when raw == 'RTD' # Delayed observation, no comments on observer
|
12
|
-
new(raw, value: :rtd)
|
13
|
-
else
|
14
|
-
new(nil, value: :real)
|
15
|
-
end
|
16
|
-
end
|
1
|
+
# frozen_string_literal: true
|
17
2
|
|
18
|
-
|
3
|
+
module Metar
|
4
|
+
module Data
|
5
|
+
class Observer < Metar::Data::Base
|
6
|
+
def self.parse(raw)
|
7
|
+
case
|
8
|
+
when raw == 'AUTO' # WMO 15.4
|
9
|
+
new(raw, value: :auto)
|
10
|
+
when raw == 'COR' # WMO specified code word for correction
|
11
|
+
new(raw, value: :corrected)
|
12
|
+
when raw =~ /CC[A-Z]/ # Canadian correction
|
13
|
+
# Canada uses CCA for first correction, CCB for second, etc...
|
14
|
+
new(raw, value: :corrected)
|
15
|
+
when raw == 'RTD' # Delayed observation, no comments on observer
|
16
|
+
new(raw, value: :rtd)
|
17
|
+
else
|
18
|
+
new(nil, value: :real)
|
19
|
+
end
|
20
|
+
end
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
attr_reader :value
|
23
|
+
|
24
|
+
def initialize(raw, value:)
|
25
|
+
@raw = raw
|
26
|
+
@value = value
|
27
|
+
end
|
28
|
+
end
|
23
29
|
end
|
24
30
|
end
|
data/lib/metar/data/pressure.rb
CHANGED
@@ -1,30 +1,36 @@
|
|
1
|
-
|
2
|
-
def self.parse(raw)
|
3
|
-
return nil if raw.nil?
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
module Metar
|
4
|
+
module Data
|
5
|
+
class Pressure < Metar::Data::Base
|
6
|
+
def self.parse(raw)
|
7
|
+
return nil if raw.nil?
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
m1 = raw.match(/^Q(\d{4})$/)
|
10
|
+
if m1
|
11
|
+
pressure = M9t::Pressure.hectopascals(m1[1].to_f)
|
12
|
+
return new(raw, pressure: pressure)
|
13
|
+
end
|
16
14
|
|
17
|
-
|
18
|
-
|
15
|
+
m2 = raw.match(/^A(\d{4})$/)
|
16
|
+
if m2
|
17
|
+
pressure = M9t::Pressure.inches_of_mercury(m2[1].to_f / 100.0)
|
18
|
+
return new(raw, pressure: pressure)
|
19
|
+
end
|
19
20
|
|
20
|
-
|
21
|
+
nil
|
22
|
+
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
attr_reader :pressure
|
25
|
+
|
26
|
+
def initialize(raw, pressure:)
|
27
|
+
@raw = raw
|
28
|
+
@pressure = pressure
|
29
|
+
end
|
26
30
|
|
27
|
-
|
28
|
-
|
31
|
+
def value
|
32
|
+
pressure.value
|
33
|
+
end
|
34
|
+
end
|
29
35
|
end
|
30
36
|
end
|