darksky_weather-api 0.1.2 → 0.2.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
  SHA256:
3
- metadata.gz: 064e45392826a9604aa9b78bf7997b4037a26451bc6c1b0c48703e94f23baf8e
4
- data.tar.gz: c2f9800eb63a696bba4111f74eaa8202995a7c6714845cc7bf5530a300ec97c9
3
+ metadata.gz: dc0d18c5a51723006895fd65b0888146e29a2125d4f9b372a768a5af3a28e6f5
4
+ data.tar.gz: d2e87e10bfb6be37ef0619fe3ac8db5ebbb4adbf9d6bc424d52cc9b500dd1013
5
5
  SHA512:
6
- metadata.gz: 73b8bb85e37f444420d3a90dd0252894700e4195f2e4c88f1a90bea4edcc8c7688e4fcc0d4d59e43d87c8c7dd4c299ce48f04994d6e11b5c0ce5d3ec93193cc2
7
- data.tar.gz: c470a0f750c40f7069f2a3cef9f4f6e00cbb89610c692afc3289cba11b2087d366ea799e990210807c0c5f20f86387d10b74726102421848480bd4b16fb34f89
6
+ metadata.gz: 18831efffd3edff23f90912344dd882c1aa4505525e797665431694532d655c589168eea6a16ecd529237175567d76b672ba92492c0e903046beb73c61a5ddec
7
+ data.tar.gz: e2ed98299950570703bd68432d15e065e49e98da6cbe4ac60b21ab1dd5618c8d1f45425cac8b99e45bda4b9984f3acaaaa3f99fdbcc9d3e28a7ae1bc3844805a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- darksky_weather-api (0.1.2)
4
+ darksky_weather-api (0.2.0)
5
5
  activesupport
6
6
  httparty
7
7
 
@@ -19,12 +19,12 @@ GEM
19
19
  httparty (0.17.1)
20
20
  mime-types (~> 3.0)
21
21
  multi_xml (>= 0.5.2)
22
- i18n (1.6.0)
22
+ i18n (1.7.0)
23
23
  concurrent-ruby (~> 1.0)
24
24
  mime-types (3.3)
25
25
  mime-types-data (~> 3.2015)
26
26
  mime-types-data (3.2019.0904)
27
- minitest (5.11.3)
27
+ minitest (5.12.2)
28
28
  multi_xml (0.6.0)
29
29
  rake (10.5.0)
30
30
  rspec (3.8.0)
data/README.md CHANGED
@@ -47,52 +47,20 @@ The gem makes a [forecast request](https://darksky.net/dev/docs#forecast-request
47
47
 
48
48
  To fetch the current weather for a given location, you'll need to know the latitude and longitude of that location. You can obtain this information via a number of methods in a process called "geocoding". Both Google Maps and Bing Maps provide geocoding API's that will transform an address into a latitude, longitude pair. It's beyond the scope of this documentation to discuss their use in detail. Once you have a latitude, longitude pair to work with, retrieving weather is as simple as instantiating a client and calling a single method:
49
49
 
50
- ```ruby
51
- client = DarkskyWeather::Api::Client.new
52
- lat, lng = [37.8267, -122.4233]
53
- client.get_weather(lat: lat, lng: lng)
54
- ```
55
-
56
- Note the use of kwargs in the method call. Calling the `get_weather` method will return a `WeatherData` object that defines a number of useful methods/attributes:
57
-
58
- Calling the `attributes` method on a `WeatherData` instance will return a Hash of the following information:
59
-
60
- * `current_precipitation`: The amount of liquid rain in inches that has fallen as of the time of the weather report.
61
- * `current_accumulation`: The amount of snow accumulation, in inches, that is present as of the time of the weather report.
62
- * `current_visibility`: The number of miles of visibility at the time of the weather report.
63
- * `current_temperature`: The temperature, in fahrenheit, at the time of the weather report.
64
- * `current_wind_speed`: The sustained wind speed, in miles per hour, at the time of the weather report.
65
- * `current_wind_gust`: The wind gust speed, in miles per hour, at the time of the weather report.
66
- * `max_precipitation`: The largest amount of liquid rain, in inches, expected to fall on the day of the weather report.
67
- * `max_accumulation`: The largest amount of snow accumulation, in inches, expected for the day of the report.
68
- * `worst_visibility`: The worst visbility, (in terms of miles), expected for the day of the report.
69
- * `max_temperature`: The highest temperature, in fahrenheit, expected for the day of the report.
70
- * `min_temperature`: The lowest temperature, in fahrenheit, expected for the day of the report.
71
- * `max_wind_speed`: The highest sustained wind speed, in miles per hour, expected for the day of the report.
72
- * `max_wind_gust`: The highest wind gust speed, in miles per hour, expected for the day of the report.
73
- * `report_date`: The date that the report was requested for.
74
-
75
- Each of the data points in the Hash above _also_ corresponds to a method on the `WeatherData` instance. So, if you just want to know what the `max_precipitation` is, this is perfectly valid:
76
-
77
50
  ```ruby
78
51
  client = DarkskyWeather::Api::Client.new
79
52
  lat, lng = [37.8267, -122.4233]
80
53
  weather_data = client.get_weather(lat: lat, lng: lng)
81
-
82
- puts weather_data.max_precipitation
83
-
84
54
  ```
85
- These attributes and convenience methods do _not_ represent the entirety of the data returned by the API call. Instead, the gem focuses on trying to present the data that's likely to be most useful for most contexts. That said, it is always possible to get back the entire API response as a Hash:
86
55
 
87
- ```ruby
88
- weather_data = client.get_weather(lat: lat, lng: lng)
89
- response = weather_data.raw
90
- ```
56
+ Note the use of kwargs in the method call. Calling the `get_weather` method will return a `WeatherData` object that defines a number of useful methods/attributes. The attributes all correspond to the snake-cased version of the JSON key present in the API response. For instance, `weather_data.currently.apparent_temperature` corresponds with the JSON at `raw_response['currently']['apparentTemperature']`.
91
57
 
92
58
  For more information about all of the data that comes back from the request, [see the documentation](https://darksky.net/dev/docs).
93
59
 
94
60
  In addition, you can retrieve the URL for your particular API request by calling `source_url` on the `WeatherData` instance.
95
61
 
62
+ There are additional methods that are useful for analyzing returned weather data that will be discussed further down.
63
+
96
64
  ### Fetching Historical Weather
97
65
 
98
66
  The gem also supports making [Dark Sky Time Machine Requests](https://darksky.net/dev/docs#time-machine-request). These work identically to a normal forecast request, with the exception that all of the data is related to the specified time in the past. You can make a time machine request by simply passing in a Unix timestamp along with the latitude and longitude in the `get_weather` call:
@@ -105,6 +73,49 @@ weather_data = client.get_weather(lat: lat, lng: lng, timestamp: 30.days.ago.to_
105
73
 
106
74
  You'll get back a `WeatherData` object, just as before, with all of the same methods and attributes to work with.
107
75
 
76
+ ### Aggregating Weather Information
77
+
78
+ The Darksky API is limited in the sense that you may only retrieve one day of historical weather at a time. However, there are many circumstances where you might want to _analyze_ that data as a single unit. For a simple instance, you may want to know what the maximum temperature over a 72 hour period was. To faciliate this sort of multi-day grouping, this gem introduces the concept of a `WeatherCollection`. A `WeatherCollection` is instantiated by passing two or more instances of `WeatherData` to a constructor:
79
+
80
+ ```ruby
81
+ client = DarkskyWeather::Api::Client.new
82
+ lat, lng = [37.8267, -122.4233]
83
+ wd1 = client.get_weather(lat: lat, lng: lng, timestamp: 3.days.ago.to_i)
84
+ wd2 = client.get_weather(lat: lat, lng: lng, timestamp: 2.days.ago.to_i)
85
+ wd3 = client.get_weather(lat: lat, lng: lng, timestamp: 1.days.ago.to_i)
86
+
87
+ collection = DarkskyWeather::Api::WeatherCollection.new(wd1, wd2, wd3)
88
+ ```
89
+
90
+ A `WeatherCollection` is similar to a `WeatherData` object, in that it contains many of the same attributes, (`daily`, `hourly` and `minutely`), and all of the same methods for analyzing hour-by-hour weather information, discussed below. The primary difference lies in the fact that a `WeatherCollection` contains data for the full range of days, collated together.
91
+
92
+ ### Analyzing Weather Information
93
+
94
+ Whether you're dealing with a `WeatherData` object or a `WeatherCollection`, the gem provides a handful of methods helpful for isolating and analyzing particular information about the weather. These methods are:
95
+
96
+ * `max_precipitation`: Returns the largest amount of rainfall per hour within the data/collection.
97
+ * `max_preciptiation_datetime`: Returns the datetime when the largest amount of rainful per hour occurred within the data/collection.
98
+ * `max_accumulation`: Returns the largest amount of snowfall accumulation that occurred within the data/collection.
99
+ * `max_accumulation_datetime`: Returns the datetime when the largest amount of snowfall accumulation was recorded within the data/collection.
100
+ * `worst_visibility`: Returns the worst visibility number within the data/collection.
101
+ * `worst_visibility_datetime`: Returns the datetime when the worst visibility within the data/collection was observed.
102
+ * `max_temperature`: Returns the highest observed temperature within the data/collection
103
+ * `max_temperature_datetime`: Returns the datetime when the highest observed temperature occurred within the data/collection
104
+ * `min_temperature`: Returns the lowest observed temperature within the data/collection
105
+ * `min_temperature_datetime`: Returns the datetime when the lowest observed temperature occurred within the data/collection
106
+ * `max_wind_speed`: Returns the highest observed windspeed within the data/collection.
107
+ * `max_wind_speed_datetime`: Returns the datetime when the highest windspeed within the data/collection was observed.
108
+ * `max_wind_gust`: Returns the highest wind gust speed within the data/collection.
109
+ * `max_wind_gust_datetime`: Returns the datetime when the highest observed wind gust speed occurred within the data/collection.
110
+
111
+ Each of the methods above takes an optional array of "hours" as a parameter. If provided, the method will only considered the passed range of hours when returning a value. If left out, the entire array of "hours" returned by the API response will be considered.
112
+
113
+ In order to get back a narrowed list of hours to work with, you could use standard array slicing methods, but the gem provides two methods that work on either a `WeatherData` or `WeatherCollection` object that will return narrowed sets of hours:
114
+
115
+ The `weather_at` method takes an integer, (Unix) timestamp, and returns the hour of weather that corresponds with that timestamp. The timestamp doesn't have to be exact. If you pass the Unix time for 12:15 PM, the 12 PM hour will be returned.
116
+
117
+ The `weather_between` method takes two integer (Unix) timestamps, and returns the hours of weather that fall between them, inclusive. Again, the timestamp doesn't have to be exact.
118
+
108
119
  ## Contributing
109
120
 
110
121
  Bug reports and pull requests are welcome on GitHub at https://github.com/Lojistic/DarkSky-gem. In particular, exposing more weather data through convenience methods and/or adding methods to analyze the data more effectively would be especially appreciated.
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "bundler/setup"
4
- require "darksky/api"
4
+ require "darksky_weather/api"
5
5
 
6
6
  # You can add fixtures and/or initialization code here to make experimenting
7
7
  # with your gem easier. You can also use a different console, if you like.
@@ -1,8 +1,11 @@
1
- require "darksky_weather/api/version"
2
- require "darksky_weather/api/client"
3
- require "darksky_weather/api/weather_data"
4
1
  require "active_support/time"
5
2
 
3
+ require File.dirname(__FILE__) + "/api/version"
4
+ require File.dirname(__FILE__) + "/api/client"
5
+ require File.dirname(__FILE__) + "/api/weather_analysis"
6
+ require File.dirname(__FILE__) + "/api/weather_data"
7
+ require File.dirname(__FILE__) + "/api/weather_collection"
8
+
6
9
  module DarkskyWeather
7
10
  module Api
8
11
  class Error < StandardError; end
@@ -15,7 +15,7 @@ module DarkskyWeather
15
15
  raw_result = self.class.get(request_path)
16
16
  request_uri = raw_result.request.uri.to_s
17
17
 
18
- return WeatherData.new(request_uri, raw_result.parsed_response)
18
+ return WeatherData.new(timestamp, request_uri, raw_result.parsed_response)
19
19
  end
20
20
  end
21
21
  end
@@ -1,5 +1,5 @@
1
1
  module DarkskyWeather
2
2
  module Api
3
- VERSION = "0.1.2"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -0,0 +1,85 @@
1
+ # A set of methods for analyzing and collating weather data
2
+ module DarkskyWeather
3
+ module Api
4
+ module WeatherAnalysis
5
+
6
+ # Get the range of hourly weather data that occurred inclusively between start_time and end_time
7
+ def weather_between(start_time, end_time)
8
+ normalized_start = Time.strptime(start_time.to_s, '%s').beginning_of_hour.to_i
9
+ normalized_end = Time.strptime(end_time.to_s, '%s').beginning_of_hour.to_i
10
+
11
+ return hourly.select{|h| h.time.to_i >= normalized_start && h.time.to_i <= normalized_end }.sort_by(&:time)
12
+ end
13
+
14
+ # Get the hourly weather data that encompasses the passed time.
15
+ def weather_at(time)
16
+ compare_time = Time.strptime(time.to_s, '%s').beginning_of_hour
17
+ hourly.select{|h| h.time == compare_time.to_i }.first
18
+ end
19
+
20
+ def max_precipitation(hours = hourly)
21
+ hours.map{|h| h.precip_intensity }.max || 0
22
+ end
23
+
24
+ def max_precipitation_datetime(hours = hourly)
25
+ stamp = hours.sort_by{|h| h.precip_intensity.to_f }.last.time
26
+ return DateTime.strptime(stamp.to_s, "%s")
27
+ end
28
+
29
+ def max_accumulation(hours = hourly)
30
+ hours.map{|h| h.precip_accumulation }.max || 0
31
+ end
32
+
33
+ def max_accumulation_datetime(hours = hourly)
34
+ stamp = hours.sort_by{|h| h.precip_accumulation }.last.time
35
+ return DateTime.strptime(stamp.to_s, "%s")
36
+ end
37
+
38
+ def worst_visibility(hours = hourly)
39
+ hours.map{|h| h.visibility }.min || 10
40
+ end
41
+
42
+ def worst_visibility_datetime(hours = hourly)
43
+ stamp = hours.sort_by{|h| h.visibility }.first.time
44
+ return DateTime.strptime(stamp.to_s, "%s")
45
+ end
46
+
47
+ def max_temperature(hours = hourly)
48
+ hours.map{|h| h.temperature }.max
49
+ end
50
+
51
+ def max_temperature_datetime(hours = hourly)
52
+ stamp = hours.sort_by{|h| h.temperature }.last.time
53
+ return DateTime.strptime(stamp.to_s, "%s")
54
+ end
55
+
56
+ def min_temperature(hours = hourly)
57
+ hours.map{|h| h.temperature }.min
58
+ end
59
+
60
+ def min_temperature_datetime(hours = hourly)
61
+ stamp = hours.sort_by{|h| h.temperature }.first.time
62
+ return DateTime.strptime(stamp.to_s, "%s")
63
+ end
64
+
65
+ def max_wind_speed(hours = hourly)
66
+ hours.map{|h| h.wind_speed }.max
67
+ end
68
+
69
+ def max_wind_speed_datetime(hours = hourly)
70
+ stamp = hours.sort_by{|h| h.wind_speed }.last.time
71
+ return DateTime.strptime(stamp.to_s, "%s")
72
+ end
73
+
74
+ def max_wind_gust(hours = hourly)
75
+ hours.map{|h| h.wind_gust }.max
76
+ end
77
+
78
+ def max_wind_gust_datetime(hours = hourly)
79
+ stamp = hours.sort_by{|h| h.wind_gust }.last.time
80
+ return DateTime.strptime(stamp.to_s, "%s")
81
+ end
82
+
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,48 @@
1
+ # Given a set of WeatherData objects, merge, normalize and aggregate data points for analysis purposes
2
+
3
+ module DarkskyWeather
4
+ module Api
5
+ class WeatherCollection
6
+ include WeatherAnalysis
7
+
8
+ attr_reader :start_date, :end_date, :daily, :hourly, :minutely
9
+
10
+ def initialize(*weather_datas)
11
+ @weather_datas = weather_datas
12
+
13
+ normalize_timestamps
14
+ normalize_data
15
+ end
16
+
17
+ private
18
+
19
+ def normalize_data(*args)
20
+ daily = []
21
+ hourly = []
22
+ minutely = []
23
+
24
+ @weather_datas.map do |wd|
25
+ hourly << wd.hourly.map(&:to_h) if wd.hourly
26
+ daily << wd.daily.to_h if wd.daily
27
+ minutely << wd.minutely.map(&:to_h) if wd.minutely
28
+ end
29
+
30
+ ds, hs, ms = [daily.compact, hourly.compact, minutely.compact].map(&:flatten!).map do |arr|
31
+ arr.map{|hsh| OpenStruct.new(hsh) }.sort_by{|s| s.time }.reverse if arr
32
+ end
33
+
34
+ @daily = ds
35
+ @hourly = hs
36
+ @minutely = ms
37
+ end
38
+
39
+ def normalize_timestamps
40
+ start_timestamp = @weather_datas.sort_by{|wd| wd.timestamp }.first.timestamp
41
+ end_timestamp = @weather_datas.sort_by{|wd| wd.timestamp }.last.timestamp
42
+ @start_date = DateTime.strptime(start_timestamp.to_s, "%s").to_date
43
+ @end_date = DateTime.strptime(end_timestamp.to_s, "%s").to_date
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -1,97 +1,46 @@
1
1
  module DarkskyWeather
2
2
  module Api
3
3
  class WeatherData
4
+ include WeatherAnalysis
4
5
 
5
- attr_reader :latitude, :longitude, :timestamp, :currently, :minutely, :hourly, :daily, :raw, :flags, :source_url
6
+ attr_reader :latitude, :longitude, :offset, :timezone, :timestamp, :sources, :raw
6
7
 
7
- def initialize(source_url, json_data)
8
+ def initialize(timestamp, source_url, raw)
9
+ @timestamp = timestamp
8
10
  @source_url = source_url
9
- @raw = json_data
10
- @latitude = json_data['latitude']
11
- @longitude = json_data['longitude']
12
- @timestamp = json_data['currently']['time']
11
+ @raw = raw
13
12
 
14
- json_data.each do |k, v|
15
- instance_variable_set(:"@#{k}", v) if self.respond_to?(k)
16
- end
17
- end
18
-
19
- def attributes
20
- atts = {}
21
- [ :current_precipitation,
22
- :current_accumulation,
23
- :current_visibility,
24
- :current_temperature,
25
- :current_wind_speed,
26
- :current_wind_gust,
27
- :max_precipitation,
28
- :max_accumulation,
29
- :worst_visibility,
30
- :max_temperature,
31
- :min_temperature,
32
- :max_wind_speed,
33
- :max_wind_gust,
34
- :report_date ].each do |att_name|
35
- atts[att_name] = self.send(att_name)
36
- end
37
-
38
- return atts
39
- end
40
-
41
- def report_date
42
- DateTime.strptime(currently['time'].to_s,'%s').to_date
43
- end
44
-
45
- def current_precipitation
46
- currently['precipIntensity'] || 0
47
- end
48
-
49
- def current_accumulation
50
- currently['precipAccumulation'] || 0
51
- end
52
-
53
- def current_visibility
54
- currently['visibility'].to_f
55
- end
56
-
57
- def current_temperature
58
- currently['temperature'].to_f
13
+ @latitude = raw['latitude']
14
+ @longitude = raw['longitude']
15
+ @offset = raw['offset']
16
+ @timezone = raw['timezone']
17
+ @sources = raw['flags']['sources']
59
18
  end
60
19
 
61
- def current_wind_speed
62
- currently['windSpeed'].to_f
20
+ def currently
21
+ return nil unless @raw['currently']
22
+ return OpenStruct.new(@raw['currently'].deep_transform_keys{|k| k.underscore })
63
23
  end
64
24
 
65
- def current_wind_gust
66
- currently['windGust'].to_f
67
- end
68
-
69
- def max_precipitation
70
- daily['data'].map{|d| d['precipIntensityMax'].to_f }.max || 0
71
- end
72
-
73
- def max_accumulation
74
- daily['data'].map{|d| d['precipAccumulation'].to_f }.max || 0
75
- end
76
-
77
- def worst_visibility
78
- hourly['data'].map{|d| d['visibility'].to_f }.min
79
- end
80
-
81
- def max_temperature
82
- daily['data'].map{|d| d['temperatureMax'].to_f }.max
83
- end
84
-
85
- def min_temperature
86
- daily['data'].map{|d| d['temperatureMin'].to_f }.max
25
+ def daily
26
+ return nil unless @raw['daily']
27
+ @raw['daily']['data'].map do |hsh|
28
+ OpenStruct.new(hsh.deep_transform_keys{|k| k.underscore })
29
+ end
87
30
  end
88
31
 
89
- def max_wind_speed
90
- hourly['data'].map{|d| d['windSpeed'].to_f }.max
32
+ def hourly
33
+ return nil unless @raw['hourly']
34
+ @raw['hourly']['data'].map do |hsh|
35
+ OpenStruct.new(hsh.deep_transform_keys{|k| k.underscore })
36
+ end
91
37
  end
92
38
 
93
- def max_wind_gust
94
- hourly['data'].map{|d| d['windGust'].to_f }.max
39
+ def minutely
40
+ return nil unless @raw['minutely']
41
+ @raw['minutely']['data'].map do |hsh|
42
+ OpenStruct.new(hsh.deep_transform_keys{|k| k.underscore })
43
+ end
95
44
  end
96
45
 
97
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: darksky_weather-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lojistic Dev Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-09-20 00:00:00.000000000 Z
11
+ date: 2019-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -102,6 +102,8 @@ files:
102
102
  - lib/darksky_weather/api.rb
103
103
  - lib/darksky_weather/api/client.rb
104
104
  - lib/darksky_weather/api/version.rb
105
+ - lib/darksky_weather/api/weather_analysis.rb
106
+ - lib/darksky_weather/api/weather_collection.rb
105
107
  - lib/darksky_weather/api/weather_data.rb
106
108
  homepage:
107
109
  licenses: