weather_gov 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +8 -0
  3. data/.github/workflows/rspec.yml +23 -0
  4. data/.github/workflows/rubocop.yml +23 -0
  5. data/.gitignore +13 -0
  6. data/.rspec +3 -0
  7. data/.rubocop.yml +29 -0
  8. data/Gemfile +6 -0
  9. data/Gemfile.lock +84 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +40 -0
  12. data/Rakefile +8 -0
  13. data/bin/console +15 -0
  14. data/bin/setup +8 -0
  15. data/lib/weather_gov.rb +13 -0
  16. data/lib/weather_gov/address.rb +27 -0
  17. data/lib/weather_gov/alert.rb +45 -0
  18. data/lib/weather_gov/alert_collection.rb +28 -0
  19. data/lib/weather_gov/api.rb +93 -0
  20. data/lib/weather_gov/client.rb +115 -0
  21. data/lib/weather_gov/feature.rb +40 -0
  22. data/lib/weather_gov/feature_collection.rb +20 -0
  23. data/lib/weather_gov/forecast.rb +27 -0
  24. data/lib/weather_gov/forecast_period.rb +63 -0
  25. data/lib/weather_gov/gridpoint.rb +75 -0
  26. data/lib/weather_gov/identifier.rb +47 -0
  27. data/lib/weather_gov/identifier/alert.rb +13 -0
  28. data/lib/weather_gov/identifier/county_zone.rb +13 -0
  29. data/lib/weather_gov/identifier/fire_zone.rb +13 -0
  30. data/lib/weather_gov/identifier/forecast_zone.rb +13 -0
  31. data/lib/weather_gov/identifier/gridpoint.rb +29 -0
  32. data/lib/weather_gov/identifier/office.rb +13 -0
  33. data/lib/weather_gov/identifier/point.rb +21 -0
  34. data/lib/weather_gov/identifier/problem.rb +13 -0
  35. data/lib/weather_gov/identifier/station.rb +13 -0
  36. data/lib/weather_gov/observation_station.rb +27 -0
  37. data/lib/weather_gov/observation_station_collection.rb +20 -0
  38. data/lib/weather_gov/office.rb +68 -0
  39. data/lib/weather_gov/point.rb +81 -0
  40. data/lib/weather_gov/product.rb +29 -0
  41. data/lib/weather_gov/product_list.rb +17 -0
  42. data/lib/weather_gov/relative_location.rb +23 -0
  43. data/lib/weather_gov/valid_duration_value.rb +26 -0
  44. data/lib/weather_gov/valid_time.rb +48 -0
  45. data/lib/weather_gov/version.rb +5 -0
  46. data/lib/weather_gov/zone.rb +33 -0
  47. data/weather_gov.gemspec +39 -0
  48. metadata +190 -0
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/identifier"
4
+
5
+ module WeatherGov
6
+ class Identifier
7
+ class Gridpoint < Identifier
8
+ def self.base_uri
9
+ URI.join(super, "/gridpoints/").to_s
10
+ end
11
+
12
+ def office_id
13
+ @office_id ||= id.split("/")[0]
14
+ end
15
+
16
+ def grid_id
17
+ @grid_id ||= id.split("/")[1]
18
+ end
19
+
20
+ def grid_x
21
+ @grid_x ||= grid_id.split(",")[0]
22
+ end
23
+
24
+ def grid_y
25
+ @grid_y ||= grid_id.split(",")[1]
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/identifier"
4
+
5
+ module WeatherGov
6
+ class Identifier
7
+ class Office < Identifier
8
+ def self.base_uri
9
+ URI.join(super, "/offices/").to_s
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/identifier"
4
+
5
+ module WeatherGov
6
+ class Identifier
7
+ class Point < Identifier
8
+ def self.base_uri
9
+ URI.join(super, "/points/").to_s
10
+ end
11
+
12
+ def lat
13
+ @lat ||= id.split(",")[0]
14
+ end
15
+
16
+ def lon
17
+ @lon ||= id.split(",")[1]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/identifier"
4
+
5
+ module WeatherGov
6
+ class Identifier
7
+ class Problem < Identifier
8
+ def self.base_uri
9
+ URI.join(super, "/problems/").to_s
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/identifier"
4
+
5
+ module WeatherGov
6
+ class Identifier
7
+ class Station < Identifier
8
+ def self.base_uri
9
+ URI.join(super, "/stations/").to_s
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/feature"
4
+
5
+ module WeatherGov
6
+ class ObservationStation < Feature
7
+ def forecast_zone
8
+ @forecast_zone ||= Identifier::ForecastZone.new(properties.fetch("forecast"))
9
+ end
10
+
11
+ def fire_weather_zone
12
+ @fire_weather_zone ||= Identifier::FireZone.new(properties.fetch("fireWeatherZone"))
13
+ end
14
+
15
+ def station_identifier
16
+ properties.fetch("stationIdentifier")
17
+ end
18
+
19
+ def name
20
+ properties.fetch("name")
21
+ end
22
+
23
+ def elevation
24
+ properties.fetch("elevation")
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/feature_collection"
4
+ require "weather_gov/observation_station"
5
+
6
+ module WeatherGov
7
+ class ObservationStationCollection < FeatureCollection
8
+ def self.feature_class
9
+ ObservationStation
10
+ end
11
+
12
+ def update_time
13
+ @update_time ||= Time.parse(data.fetch("updated"))
14
+ end
15
+
16
+ def observation_stations
17
+ data.fetch("observationStations")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/feature"
4
+ require "weather_gov/address"
5
+
6
+ module WeatherGov
7
+ class Office < Feature
8
+ def name
9
+ data.fetch("name")
10
+ end
11
+
12
+ def address
13
+ @address ||= Address.new(data.fetch("address"))
14
+ end
15
+
16
+ def telephone
17
+ data.fetch("telephone")
18
+ end
19
+
20
+ def fax
21
+ data.fetch("faxNumber")
22
+ end
23
+
24
+ def email
25
+ data.fetch("email")
26
+ end
27
+
28
+ def nws_region
29
+ data.fetch("nwsRegion")
30
+ end
31
+
32
+ def parent_organization
33
+ data.fetch("parentOrganization")
34
+ end
35
+
36
+ def responsible_counties
37
+ data.fetch("responsibleCounties", [])
38
+ end
39
+
40
+ def responsible_county_ids
41
+ data.fetch("responsibleCounties", []).map { |uri| Identifier::CountyZone.new(uri).id }
42
+ end
43
+
44
+ def responsible_forecast_zones
45
+ data.fetch("responsibleForecastZones", [])
46
+ end
47
+
48
+ def responsible_forecast_zone_ids
49
+ data.fetch("responsibleForecastZones", []).map { |uri| Identifier::ForecastZone.new(uri).id }
50
+ end
51
+
52
+ def responsible_fire_zones
53
+ data.fetch("responsibleFireZones", [])
54
+ end
55
+
56
+ def responsible_fire_zone_ids
57
+ data.fetch("responsibleFireZones", []).map { |uri| Identifier::FireZone.new(uri).id }
58
+ end
59
+
60
+ def approved_observation_stations
61
+ data.fetch("approvedObservationStations", []).map { |uri| client.station(uri: uri, value: uri) }
62
+ end
63
+
64
+ def approved_observation_station_ids
65
+ data.fetch("approvedObservationStations", []).map { |uri| Identifier::Station.new(uri).id }
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/feature"
4
+ require "weather_gov/relative_location"
5
+
6
+ module WeatherGov
7
+ class Point < Feature
8
+ def forecast_office_identifier
9
+ @forecast_office_identifier ||= Identifier::Office.new(properties.fetch("forecastOffice"))
10
+ end
11
+
12
+ def forecast_office
13
+ @forecast_office ||= client.office(uri: forecast_office_identifier.uri)
14
+ end
15
+
16
+ def forecast_zone_identifier
17
+ @forecast_zone_identifier ||= Identifier::ForecastZone.new(properties.fetch("forecastZone"))
18
+ end
19
+
20
+ def forecast_zone
21
+ @forecast_zone ||= client.zone(uri: forecast_zone_identifier.uri)
22
+ end
23
+
24
+ def county_zone_identifier
25
+ @county_zone_identifier ||= Identifier::CountyZone.new(properties.fetch("county"))
26
+ end
27
+
28
+ def county_zone
29
+ @county_zone ||= client.zone(uri: county_zone_identifier.uri)
30
+ end
31
+
32
+ def fire_zone_identifier
33
+ @fire_zone_identifier ||= Identifier::FireZone.new(properties.fetch("fireWeatherZone"))
34
+ end
35
+
36
+ def fire_zone
37
+ @fire_zone ||= client.zone(uri: fire_zone_identifier.uri)
38
+ end
39
+
40
+ def forecast_grid_data
41
+ @forecast_grid_data ||= Identifier::Gridpoint.new(properties.fetch("forecastGridData"))
42
+ end
43
+
44
+ def relative_location
45
+ @relative_location ||= RelativeLocation.new(client: client, data: properties.fetch("relativeLocation"))
46
+ end
47
+
48
+ def radar_station
49
+ properties.fetch("radarStation")
50
+ end
51
+
52
+ def observation_stations
53
+ @observation_stations ||= client.stations(uri: properties.fetch("observationStations"))
54
+ end
55
+
56
+ def alerts_active
57
+ @alerts_active ||= client.alerts_active(zone: forecast_zone_identifier.id)
58
+ end
59
+
60
+ def gridpoint
61
+ @gridpoint ||= client.gridpoint(uri: properties.fetch("forecastGridData"))
62
+ end
63
+
64
+ def forecast
65
+ @forecast ||= client.forecast(uri: properties.fetch("forecast"))
66
+ end
67
+
68
+ def forecast_hourly
69
+ @forecast_hourly ||= client.forecast(uri: properties.fetch("forecastHourly"))
70
+ end
71
+
72
+ def products(type)
73
+ @products ||= {}
74
+ @products[type] ||= client.products(type: type, location: forecast_office_identifier.id)
75
+ end
76
+
77
+ def product(type)
78
+ products(type).first.product
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WeatherGov
4
+ class Product < Feature
5
+ def issuing_office
6
+ data.fetch("issuingOffice")
7
+ end
8
+
9
+ def issuance_time
10
+ Time.parse(data.fetch("issuanceTime"))
11
+ end
12
+
13
+ def code
14
+ data.fetch("productCode")
15
+ end
16
+
17
+ def name
18
+ data.fetch("productName")
19
+ end
20
+
21
+ def text
22
+ data.fetch("productText")
23
+ end
24
+
25
+ def product
26
+ client.product(id: id) if id
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/feature"
4
+ require "weather_gov/product"
5
+ require "forwardable"
6
+
7
+ module WeatherGov
8
+ class ProductList < Feature
9
+ extend Forwardable
10
+
11
+ def items
12
+ @items ||= data.fetch("@graph").map { |item| Product.new(client: client, data: item) }
13
+ end
14
+
15
+ def_delegators :items, :[], :size, :each, :first, :last, :map
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/feature"
4
+
5
+ module WeatherGov
6
+ class RelativeLocation < Feature
7
+ def city
8
+ properties.fetch("city")
9
+ end
10
+
11
+ def state
12
+ properties.fetch("state")
13
+ end
14
+
15
+ def distance
16
+ properties.fetch("distance")
17
+ end
18
+
19
+ def bearing
20
+ properties.fetch("bearing")
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "weather_gov/valid_time"
4
+
5
+ module WeatherGov
6
+ class ValidDurationValue
7
+ attr_reader :data, :unit
8
+
9
+ def initialize(data:, unit: nil)
10
+ @data = data
11
+ @unit = unit
12
+ end
13
+
14
+ def valid_time
15
+ @valid_time ||= ValidTime.new(data.fetch("validTime"))
16
+ end
17
+
18
+ def valid?
19
+ valid_time.valid?
20
+ end
21
+
22
+ def value
23
+ @value ||= data.fetch("value")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/time"
4
+ require "active_support/duration"
5
+
6
+ module WeatherGov
7
+ class ValidTime
8
+ def self.parse(valid_time_string)
9
+ ValidTime.new(valid_time_string)
10
+ end
11
+
12
+ def initialize(valid_time_string)
13
+ @valid_time_string = valid_time_string
14
+ end
15
+
16
+ def fields
17
+ @fields ||= @valid_time_string.split("/")
18
+ end
19
+
20
+ def start_time
21
+ @start_time ||= Time.parse(fields.first)
22
+ end
23
+
24
+ def duration
25
+ @duration ||= ActiveSupport::Duration.parse(fields.last)
26
+ end
27
+
28
+ def end_time
29
+ start_time + duration
30
+ end
31
+
32
+ def range
33
+ start_time...end_time
34
+ end
35
+
36
+ def valid?(time = Time.now)
37
+ range.include?(time)
38
+ end
39
+
40
+ def elapsed(time = Time.now)
41
+ ActiveSupport::Duration.build(time - start_time)
42
+ end
43
+
44
+ def remaining(time = Time.now)
45
+ ActiveSupport::Duration.build(end_time - time)
46
+ end
47
+ end
48
+ end