forecaster 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f918096d5bc3c84b8b1be9ded8a0e9597a3bf700
4
- data.tar.gz: 73a46ed9e59a3c3f05cc45c5a654d8548055fa58
3
+ metadata.gz: 95c34dc2a848deaba80e411a01e246237ed1f10a
4
+ data.tar.gz: 5d7c4597a21d4c9b703495c654406cd7e8ff12f6
5
5
  SHA512:
6
- metadata.gz: dac29049eb42eb4cee58446c28a15d090c9abdfe76426db8c732e499b2cd1dcf8f9405d7a0bfa3479496525291de9e417c54a3cbdc687b87b1d9d73aa5fbe05a
7
- data.tar.gz: 7406bb7e8d9de0afa5ff34854d826d71a34e5412a14871f82c0766d3f9793e0f212238473c32176518f8c45ed6402081efe43d7b3beee83f13dbd2a4f5221e71
6
+ metadata.gz: 04633f93c3228a300c0f88246cacb3a7a239b5e3d03da468531c89e41673d573675561a65a81b2f3307e2b01cb0d188f7fd7f2c18a91480fcc292891d18c57d1
7
+ data.tar.gz: ced4bf158fc21603cc21854f41dc4894df3e2b207d12760eecdd4b5f7d235ae6865e75c7e54e4d7d863127031c631ee63a58a16c73b9da01a8b9bf340b5c0872
data/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## 1.0.0 (2018-03-31)
4
+
5
+ - Colorize and refactor output
6
+ - Update dependencies
7
+ - Replace `Forecast#fetch_index` by `Forecast#fetch_ranges`
8
+ - Fix bug when time is not in UTC
9
+ - Drop support of ruby version < 2.1
10
+ - Increase code coverage
11
+
12
+ ## 0.1.0 (2016-06-11)
13
+
14
+ - Add forecast executable
15
+ - Cache geolocation requests
16
+ - Replace curl wrapper with excon gem
17
+ - Fix cache path
18
+
19
+ ## 0.0.2 (2016-06-09)
20
+
21
+ - Raise error when field could not be read in file
22
+
23
+ ## 0.0.1 (2015-05-04)
24
+
25
+ - Publish initial version
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-2018 Vincent Ollivier
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ Forecaster
2
+ ==========
3
+
4
+ [![Gem](https://img.shields.io/gem/v/forecaster.svg)](https://rubygems.org/gems/forecaster)
5
+ [![Build Status](https://api.travis-ci.org/vinc/forecaster.svg?branch=master)](http://travis-ci.org/vinc/forecaster)
6
+ [![Code Climate](https://codeclimate.com/github/vinc/forecaster.svg)](https://codeclimate.com/github/vinc/forecaster)
7
+ [![Code Coverage](https://codecov.io/gh/vinc/forecaster/branch/master/graph/badge.svg)](https://codecov.io/gh/vinc/forecaster)
8
+ [![Gemnasium](https://img.shields.io/gemnasium/vinc/forecaster.svg)](https://gemnasium.com/github.com/vinc/forecaster)
9
+
10
+ Ruby wrapper around `wgrib2` to read data directly from the Global Forecast
11
+ System (GFS).
12
+
13
+ [![asciicast](https://asciinema.org/a/146117.png)](https://asciinema.org/a/146117)
14
+
15
+
16
+ Installation
17
+ ------------
18
+
19
+ $ gem install forecaster
20
+
21
+ Alternatively you can build the gem from its repository:
22
+
23
+ $ git clone git://github.com/vinc/forecaster.git
24
+ $ cd forecaster
25
+ $ gem build forecaster.gemspec
26
+ $ gem install forecaster-0.1.1.gem
27
+
28
+ In both cases you need to make sure that you have `wgrib2` present in your
29
+ system.
30
+
31
+ To install the `wgrib2` from source:
32
+
33
+ $ wget http://www.ftp.cpc.ncep.noaa.gov/wd51we/wgrib2/wgrib2.tgz
34
+ $ tar -xzvf wgrib2.tgz
35
+ $ cd grib2
36
+ $ export CC=gcc
37
+ $ export FC=gfortran
38
+ $ make
39
+ $ sudo cp wgrib2/wgrib2 /usr/local/bin/
40
+
41
+ Usage
42
+ -----
43
+
44
+ ```ruby
45
+ require "forecaster"
46
+ ```
47
+
48
+ To configure the gem:
49
+
50
+ ```ruby
51
+ Forecaster.configure do |config|
52
+ config.wgrib2_path = "/usr/local/bin/wgrib2"
53
+ config.cache_dir = "/tmp/forecaster"
54
+ config.records = {
55
+ :temperature => ":TMP:2 m above ground:",
56
+ :humidity => ":RH:2 m above ground:",
57
+ :pressure => ":PRES:surface:"
58
+ }
59
+ end
60
+ ```
61
+
62
+ Forecaster saves large files containing the data of GFS runs from the NOAA
63
+ servers in the cache directory, but only the parts of the files containing
64
+ the records defined in the configuration will be downloaded.
65
+
66
+ You can find the list of available records [online][1] or by reading any
67
+ `.idx` files distributed along with the GFS files.
68
+
69
+ A record is identified by a variable and a layer separated by colon
70
+ characters. In the case of the temperature for example, those attributes
71
+ are `TMP` and `2 m above ground`. See the [documentation of wgrib2][2] for
72
+ more information.
73
+
74
+ To fetch a forecast:
75
+
76
+ ```ruby
77
+ t = Time.now.utc # All the dates should be expressed in UTC
78
+ y = t.year # year of GFS run
79
+ m = t.month # month of GFS run
80
+ d = t.day # day of GFS run
81
+ c = 0 # hour of GFS run (must be a multiple of 6)
82
+ h = 12 # hour of forecast (must be a multiple of 3)
83
+ forecast = Forecaster.fetch(y, m, d, c, h) # Forecaster::Forecast
84
+ ```
85
+
86
+ To read the [record][1] of a forecast:
87
+
88
+ ```ruby
89
+ res = forecast.read(:temperature, longitude: 48.1147, latitude: -1.6794) # String in Kelvin
90
+ val = res.to_f - 273.15 # Float in degree Celsius
91
+ ```
92
+
93
+ [1]: http://www.nco.ncep.noaa.gov/pmb/products/gfs/gfs_upgrade/gfs.t06z.pgrb2.0p25.f006.shtml
94
+ [2]: http://www.cpc.ncep.noaa.gov/products/wesley/wgrib2/
95
+
96
+
97
+ Command line
98
+ ------------
99
+
100
+ Forecaster has a command line tool that try to be smart:
101
+
102
+ $ forecast for tomorrow afternoon in auckland
103
+ GFS Weather Forecast
104
+
105
+ Date: 2016-05-13
106
+ Time: 12:00:00
107
+ Zone: +1200
108
+ Latitude: -36.8 °
109
+ Longitude: 174.8 °
110
+
111
+ Pressure: 1013.8 hPa
112
+ Temperature: 21.7 °C
113
+ Wind Direction: 163.5 °
114
+ Wind Speed: 8.0 m/s
115
+ Precipitation: 0.0 mm
116
+ Humidity: 65.1 %
117
+ Cloud Cover: 0.0 %
118
+
119
+ But you can use it in a more verbose way:
120
+
121
+ $ TZ=America/Los_Angeles forecast --time "2016-05-12 09:00:00" \
122
+ --latitude "37.7749295" \
123
+ --longitude "-122.4194155" \
124
+ --debug
125
+ Requested time: 2016-05-12 09:00:00 -0700
126
+ GFS Run time: 2016-05-11 23:00:00 -0700
127
+ Forecast time: 2016-05-12 08:00:00 -0700
128
+
129
+ Downloading: 'http://www.ftp.ncep.noaa.gov/data/nccf/com/gfs/prod/gfs.20160
130
+ 51200/gfs.t00z.pgrb2.0p25.f015'
131
+ Reading index file...
132
+ Length: 4992281 (4.76M)
133
+
134
+ 100% [===========================================>] 696 KB/s Time: 00:00:07
135
+
136
+ GFS Weather Forecast
137
+
138
+ Date: 2016-05-12
139
+ Time: 08:00:00
140
+ Zone: -0700
141
+ Latitude: 37.8 °
142
+ Longitude: -122.4 °
143
+
144
+ Pressure: 1013.5 hPa
145
+ Temperature: 13.4 °C
146
+ Wind Direction: 167.3 °
147
+ Wind Speed: 1.0 m/s
148
+ Precipitation: 0.0 mm
149
+ Humidity: 89.7 %
150
+ Cloud Cover: 0.0 %
151
+
152
+ To use automatically the timezone of a location you will need to create
153
+ a free [GeoNames account][3] and export your username in an environment
154
+ variable:
155
+
156
+ export GEONAMES_USERNAME=<username>
157
+
158
+ And while you're doing that, you can also export your favorite location
159
+ to avoid typing it every time:
160
+
161
+ export FORECAST_LATITUDE=<latitude>
162
+ export FORECAST_LONGITUDE=<longitude>
163
+
164
+ [3]: http://www.geonames.org/login
165
+
166
+
167
+ License
168
+ -------
169
+
170
+ Copyright (c) 2015-2018 Vincent Ollivier. Released under MIT.
@@ -4,6 +4,7 @@ require "chronic"
4
4
  require "timezone"
5
5
  require "geocoder"
6
6
  require "ruby-progressbar"
7
+ require "rainbow"
7
8
 
8
9
  require "forecaster"
9
10
 
@@ -13,8 +14,6 @@ module Forecaster
13
14
  class CLI
14
15
  include Singleton # TODO: Find how best to organize CLI class
15
16
 
16
- FORECAST_FORMAT = " %-15s % 7.1f %s".freeze
17
-
18
17
  def self.start(args, env)
19
18
  instance.start(args, env)
20
19
  end
@@ -31,7 +30,7 @@ module Forecaster
31
30
  cache_file = File.join(Forecaster.configuration.cache_dir, "forecast.yml")
32
31
  @store = YAML::Store.new(cache_file)
33
32
 
34
- puts "GFS Weather Forecast"
33
+ puts Rainbow("GFS Weather Forecast").bright
35
34
  puts
36
35
 
37
36
  lat, lon = get_location(opts, env)
@@ -95,10 +94,10 @@ module Forecaster
95
94
 
96
95
  # Get location
97
96
  def get_location(opts, env)
98
- if opts[:location]
97
+ if (opts[:location] || "").length > 0
99
98
  @store.transaction do
100
99
  if opts[:debug]
101
- puts format("%-15s '%s'", "Geolocalizing:", opts[:location])
100
+ putf_debug("Geolocalizing", opts[:location], "'%s'")
102
101
  end
103
102
 
104
103
  key = "geocoder:#{opts[:location]}"
@@ -106,9 +105,9 @@ module Forecaster
106
105
 
107
106
  if opts[:debug]
108
107
  if lat && lon
109
- puts format("%-15s %s, %s", "Found:", lat, lon)
108
+ putf_debug("Location", lat, "%05.2f, %05.2f", optional: lon)
110
109
  else
111
- puts "Not found"
110
+ puts Rainbow("Location not found").red
112
111
  end
113
112
  puts
114
113
  end
@@ -155,25 +154,26 @@ module Forecaster
155
154
  forecast = Forecast.at(time)
156
155
 
157
156
  if opts[:debug]
158
- puts format("%-15s %s", "Requested time:", time.localtime)
159
- puts format("%-15s %s", "GFS run time:", forecast.run_time.localtime)
160
- puts format("%-15s %s", "Forecast time:", forecast.time.localtime)
157
+ putf_debug("Requested time", time.localtime, "%s")
158
+ putf_debug("GFS run time", forecast.run_time.localtime, "%s")
159
+ putf_debug("Forecast time", forecast.time.localtime, "%s")
161
160
  puts
162
161
  end
163
162
 
164
163
  unless forecast.fetched?
165
164
  if opts[:debug]
166
- puts "Downloading: '#{forecast.url}'"
165
+ putf_debug("Downloading", forecast.url, "'%s'")
167
166
 
168
- puts "Reading index file..."
169
- ranges = forecast.fetch_index
167
+ putf_debug("Reading index file", "", "")
168
+ records = Forecaster.configuration.records.values
169
+ ranges = forecast.fetch_ranges
170
+ ranges = records.map { |k| ranges[k] } # Filter ranges
170
171
 
171
- filesize = ranges.reduce(0) do |acc, range|
172
- first, last = range.split("-").map(&:to_i)
173
- acc + last - first
172
+ filesize = ranges.reduce(0) do |acc, (first, last)|
173
+ acc + last - first # FIXME: `last == nil` on last range of index file
174
174
  end
175
- filesize_in_megabytes = (filesize.to_f / (1 << 20)).round(2)
176
- puts "Length: #{filesize} (#{filesize_in_megabytes}M)"
175
+ n = (filesize.to_f / (1 << 20)).round(2)
176
+ putf_debug("Length", filesize, "%d (%.2fM)", optional: n)
177
177
  puts
178
178
 
179
179
  progressbar = ProgressBar.create(
@@ -200,38 +200,62 @@ module Forecaster
200
200
 
201
201
  # Print forecast
202
202
  def print_forecast(forecast, lat, lon)
203
- forecast_time = forecast.time.localtime
203
+ putf("Date", forecast.time.localtime.strftime("%Y-%m-%d"), "%s")
204
+ putf("Time", forecast.time.localtime.strftime("%T"), "%s")
205
+ putf("Zone", forecast.time.localtime.strftime("%z"), "%s")
204
206
 
205
- puts format(" %-11s % 11s", "Date:", forecast_time.strftime("%Y-%m-%d"))
206
- puts format(" %-11s % 11s", "Time:", forecast_time.strftime("%T"))
207
- puts format(" %-11s % 11s", "Zone:", forecast_time.strftime("%z"))
208
- puts format(FORECAST_FORMAT, "Latitude:", lat, "°")
209
- puts format(FORECAST_FORMAT, "Longitude:", lon, "°")
207
+ # Coordinates rounded to the precision of the GFS model
208
+ putf("Latitude", (lat / 0.25).round / 4.0, "%05.2f °")
209
+ putf("Longitude", (lon / 0.25).round / 4.0, "%05.2f °")
210
210
  puts
211
211
 
212
- pres = forecast.read(:pres, :latitude => lat, :longitude => lon).to_f
213
212
  tmp = forecast.read(:tmp, :latitude => lat, :longitude => lon).to_f
214
213
  ugrd = forecast.read(:ugrd, :latitude => lat, :longitude => lon).to_f
215
214
  vgrd = forecast.read(:vgrd, :latitude => lat, :longitude => lon).to_f
216
215
  prate = forecast.read(:prate, :latitude => lat, :longitude => lon).to_f
217
216
  rh = forecast.read(:rh, :latitude => lat, :longitude => lon).to_f
218
217
  tcdc = forecast.read(:tcdc, :latitude => lat, :longitude => lon).to_f
218
+ pres = forecast.read(:pres, :latitude => lat, :longitude => lon).to_f
219
219
 
220
- pressure = pres / 100.0
221
220
  temperature = tmp - 273.15
222
- wind_speed = Math.sqrt(ugrd**2 + vgrd**2)
223
221
  wind_direction = (270 - Math.atan2(ugrd, vgrd) * 180 / Math::PI) % 360
222
+ wind_speed = Math.sqrt(ugrd**2 + vgrd**2)
224
223
  precipitation = prate * 3600
225
224
  humidity = rh
226
225
  cloud_cover = tcdc
226
+ pressure = pres / 100.0
227
+
228
+ wdir = compass_rose(wind_direction)
227
229
 
228
- puts format(FORECAST_FORMAT, "Pressure:", pressure, "hPa")
229
- puts format(FORECAST_FORMAT, "Temperature:", temperature, "°C")
230
- puts format(FORECAST_FORMAT, "Wind Speed:", wind_speed, "m/s")
231
- puts format(FORECAST_FORMAT, "Wind Direction:", wind_direction, "°")
232
- puts format(FORECAST_FORMAT, "Precipitation:", precipitation, "mm")
233
- puts format(FORECAST_FORMAT, "Humidity:", humidity, "%")
234
- puts format(FORECAST_FORMAT, "Cloud Cover:", cloud_cover, "%")
230
+ putf("Temperature", temperature, "%.0f °C")
231
+ putf("Wind", wind_speed, "%.1f m/s (%s)", optional: wdir)
232
+ putf("Precipitation", precipitation, "%.1f mm")
233
+ putf("Humidity", humidity, "%.0f %")
234
+ putf("Cloud Cover", cloud_cover, "%.0f %")
235
+ putf("Pressure", pressure, "%.0f hPa")
236
+ end
237
+
238
+ def putf(name, value, fmt, optional: "", color: :cyan)
239
+ left_column = Rainbow(format(" %-20s", name)).color(color)
240
+ right_column = Rainbow(format(fmt, value, optional))
241
+ puts "#{left_column} #{right_column}"
242
+ end
243
+
244
+ def putf_debug(name, value, fmt, optional: "")
245
+ putf(name, value, fmt, optional: optional, color: :yellow)
246
+ end
247
+
248
+ def compass_rose(degree)
249
+ case degree
250
+ when 0...45 then "N"
251
+ when 45...90 then "NE"
252
+ when 90...135 then "E"
253
+ when 135...180 then "SE"
254
+ when 180...225 then "S"
255
+ when 225...270 then "SW"
256
+ when 270...315 then "W"
257
+ else "NW"
258
+ end
235
259
  end
236
260
 
237
261
  def geolocalize(location)
@@ -12,7 +12,7 @@ module Forecaster
12
12
  # takes approximately 3 to 5 hours before a run is available online, so
13
13
  # to be on the safe side we return the previous one.
14
14
  now = Time.now.utc
15
- run = Time.new(now.year, now.month, now.day, (now.hour / 6) * 6)
15
+ run = Time.utc(now.year, now.month, now.day, (now.hour / 6) * 6)
16
16
 
17
17
  run - 6 * 3600
18
18
  end
@@ -20,8 +20,8 @@ module Forecaster
20
20
  def self.at(time)
21
21
  # There is a forecast every 3 hours after a run for 384 hours.
22
22
  t = time.utc
23
- fct = Time.new(t.year, t.month, t.day, (t.hour / 3) * 3)
24
- run = Time.new(t.year, t.month, t.day, (t.hour / 6) * 6)
23
+ fct = Time.utc(t.year, t.month, t.day, (t.hour / 3) * 3)
24
+ run = Time.utc(t.year, t.month, t.day, (t.hour / 6) * 6)
25
25
  run -= 6 * 3600 if run == fct
26
26
 
27
27
  last_run = Forecast.last_run_at
@@ -74,34 +74,48 @@ module Forecaster
74
74
  # the configuration will be downloaded.
75
75
  def fetch
76
76
  return if fetched?
77
- ranges = fetch_index
78
- fetch_grib2(ranges)
77
+
78
+ ranges = fetch_ranges
79
+
80
+ # Select which byte ranges to download
81
+ records = Forecaster.configuration.records.values
82
+ filtered_ranges = records.map { |k| ranges[k] }
83
+
84
+ fetch_grib2(filtered_ranges)
79
85
  end
80
86
 
81
- def fetch_index
87
+ # Fetch the index file of a GRIB2 file containing the data of a forecast.
88
+ #
89
+ # Returns a hashmap of every records in the file with their byte ranges.
90
+ #
91
+ # With this method we can avoid downloading unnecessary parts of the GRIB2
92
+ # file by matching the records defined in the configuration. It can also
93
+ # be used to set up the later.
94
+ def fetch_ranges
82
95
  begin
83
96
  res = Excon.get("#{url}.idx")
84
97
  rescue Excon::Errors::Error
85
98
  raise "Download of '#{url}.idx' failed"
86
99
  end
87
-
88
- lines = res.body.lines
89
- lines.each_index.reduce([]) do |r, i|
90
- records = Forecaster.configuration.records
91
- if records.values.any? { |record| lines[i].include?(record) }
92
- first = lines[i].split(":")[1].to_i
93
- last = ""
94
-
95
- j = i
96
- while (j += 1) < lines.count
97
- last = lines[j].split(":")[1].to_i - 1
98
- break if last != first - 1
99
- end
100
-
101
- r << "#{first}-#{last}" # Range header syntax
102
- else
103
- r
104
- end
100
+ lines = res.body.lines.map { |line| line.split(":") }
101
+ lines.each_index.each_with_object({}) do |i, ranges|
102
+ # A typical line (before the split on `:`) looks like this:
103
+ # `12:4593854:d=2016051118:TMP:2 mb:9 hour fcst:`
104
+ line = lines[i]
105
+ next_line = lines[i + 1] # NOTE: Will be `nil` on the last line
106
+
107
+ # The fourth and fifth fields constitue the key to identify the records
108
+ # defined in `Forecaster::Configuration`.
109
+ key = ":#{line[3]}:#{line[4]}:"
110
+
111
+ # The second field is the first byte of the record in the GRIB2 file.
112
+ ranges[key] = [line[1].to_i]
113
+
114
+ # To get the last byte we need to read the next line.
115
+ # If we are on the last line we won't be able to get the last byte,
116
+ # but we don't need it according to the section 14.35.1 Byte Ranges
117
+ # of RFC 2616.
118
+ ranges[key] << next_line[1].to_i - 1 if next_line
105
119
  end
106
120
  end
107
121
 
@@ -114,7 +128,9 @@ module Forecaster
114
128
  progress_block.call(total - remaining, total) if progress_block
115
129
  end
116
130
 
117
- headers = { "Range" => "bytes=#{ranges.join(',')}" }
131
+ byte_ranges = ranges.map { |r| r.join("-") }.join(",")
132
+ headers = { "Range" => "bytes=#{byte_ranges}" }
133
+
118
134
  begin
119
135
  Excon.get(url, :headers => headers, :response_block => streamer)
120
136
  rescue Excon::Errors::Error => e
@@ -1,4 +1,4 @@
1
- # Fetch and read data from the Global Forecast System.
1
+ # Read data from the Global Forecast System
2
2
  module Forecaster
3
- VERSION = "0.1.0".freeze
3
+ VERSION = "1.0.0".freeze
4
4
  end
metadata CHANGED
@@ -1,15 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forecaster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vincent Ollivier
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-11 00:00:00.000000000 Z
11
+ date: 2018-03-31 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: chronic
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.10'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.10.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.10'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.10.0
13
33
  - !ruby/object:Gem::Dependency
14
34
  name: excon
15
35
  requirement: !ruby/object:Gem::Requirement
@@ -30,6 +50,86 @@ dependencies:
30
50
  - - ">="
31
51
  - !ruby/object:Gem::Version
32
52
  version: 0.49.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: geocoder
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.3'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.3.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.3'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.3.0
73
+ - !ruby/object:Gem::Dependency
74
+ name: rainbow
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '3.0'
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.0.0
83
+ type: :runtime
84
+ prerelease: false
85
+ version_requirements: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.0'
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 3.0.0
93
+ - !ruby/object:Gem::Dependency
94
+ name: ruby-progressbar
95
+ requirement: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - "~>"
98
+ - !ruby/object:Gem::Version
99
+ version: '1.8'
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: 1.8.0
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.8'
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 1.8.0
113
+ - !ruby/object:Gem::Dependency
114
+ name: timezone
115
+ requirement: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - "~>"
118
+ - !ruby/object:Gem::Version
119
+ version: '1.2'
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: 1.2.0
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '1.2'
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: 1.2.0
33
133
  - !ruby/object:Gem::Dependency
34
134
  name: trollop
35
135
  requirement: !ruby/object:Gem::Requirement
@@ -51,92 +151,96 @@ dependencies:
51
151
  - !ruby/object:Gem::Version
52
152
  version: 2.1.0
53
153
  - !ruby/object:Gem::Dependency
54
- name: chronic
154
+ name: codecov
55
155
  requirement: !ruby/object:Gem::Requirement
56
156
  requirements:
57
157
  - - "~>"
58
158
  - !ruby/object:Gem::Version
59
- version: '0.10'
159
+ version: '0.1'
60
160
  - - ">="
61
161
  - !ruby/object:Gem::Version
62
- version: 0.10.0
63
- type: :runtime
162
+ version: 0.1.10
163
+ type: :development
64
164
  prerelease: false
65
165
  version_requirements: !ruby/object:Gem::Requirement
66
166
  requirements:
67
167
  - - "~>"
68
168
  - !ruby/object:Gem::Version
69
- version: '0.10'
169
+ version: '0.1'
70
170
  - - ">="
71
171
  - !ruby/object:Gem::Version
72
- version: 0.10.0
172
+ version: 0.1.10
73
173
  - !ruby/object:Gem::Dependency
74
- name: timezone
174
+ name: rspec
75
175
  requirement: !ruby/object:Gem::Requirement
76
176
  requirements:
77
177
  - - "~>"
78
178
  - !ruby/object:Gem::Version
79
- version: '0.99'
179
+ version: '3.7'
80
180
  - - ">="
81
181
  - !ruby/object:Gem::Version
82
- version: 0.99.0
83
- type: :runtime
182
+ version: 3.7.0
183
+ type: :development
84
184
  prerelease: false
85
185
  version_requirements: !ruby/object:Gem::Requirement
86
186
  requirements:
87
187
  - - "~>"
88
188
  - !ruby/object:Gem::Version
89
- version: '0.99'
189
+ version: '3.7'
90
190
  - - ">="
91
191
  - !ruby/object:Gem::Version
92
- version: 0.99.0
192
+ version: 3.7.0
93
193
  - !ruby/object:Gem::Dependency
94
- name: geocoder
194
+ name: simplecov
95
195
  requirement: !ruby/object:Gem::Requirement
96
196
  requirements:
97
197
  - - "~>"
98
198
  - !ruby/object:Gem::Version
99
- version: '1.3'
199
+ version: '0.16'
100
200
  - - ">="
101
201
  - !ruby/object:Gem::Version
102
- version: 1.3.0
103
- type: :runtime
202
+ version: 0.16.1
203
+ type: :development
104
204
  prerelease: false
105
205
  version_requirements: !ruby/object:Gem::Requirement
106
206
  requirements:
107
207
  - - "~>"
108
208
  - !ruby/object:Gem::Version
109
- version: '1.3'
209
+ version: '0.16'
110
210
  - - ">="
111
211
  - !ruby/object:Gem::Version
112
- version: 1.3.0
212
+ version: 0.16.1
113
213
  - !ruby/object:Gem::Dependency
114
- name: ruby-progressbar
214
+ name: timecop
115
215
  requirement: !ruby/object:Gem::Requirement
116
216
  requirements:
117
217
  - - "~>"
118
218
  - !ruby/object:Gem::Version
119
- version: '1.8'
219
+ version: '0.9'
120
220
  - - ">="
121
221
  - !ruby/object:Gem::Version
122
- version: 1.8.0
123
- type: :runtime
222
+ version: 0.9.0
223
+ type: :development
124
224
  prerelease: false
125
225
  version_requirements: !ruby/object:Gem::Requirement
126
226
  requirements:
127
227
  - - "~>"
128
228
  - !ruby/object:Gem::Version
129
- version: '1.8'
229
+ version: '0.9'
130
230
  - - ">="
131
231
  - !ruby/object:Gem::Version
132
- version: 1.8.0
133
- description: Wrapper around curl and wgrib2 to fetch and read GFS data
232
+ version: 0.9.0
233
+ description: Wrapper around wgrib2 to read data directly from the Global Forecast
234
+ System
134
235
  email: v@vinc.cc
135
236
  executables:
136
237
  - forecast
137
238
  extensions: []
138
239
  extra_rdoc_files: []
139
240
  files:
241
+ - CHANGELOG.md
242
+ - LICENSE
243
+ - README.md
140
244
  - bin/forecast
141
245
  - lib/forecaster.rb
142
246
  - lib/forecaster/cli.rb
@@ -163,8 +267,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
267
  version: '0'
164
268
  requirements: []
165
269
  rubyforge_project:
166
- rubygems_version: 2.4.5.1
270
+ rubygems_version: 2.6.11
167
271
  signing_key:
168
272
  specification_version: 4
169
- summary: Wrapper around curl and wgrib2 to fetch and read GFS data
273
+ summary: Wrapper around wgrib2 to read data from the GFS
170
274
  test_files: []