weather_gov 0.1.0

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.
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