weather_gov 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/dependabot.yml +8 -0
- data/.github/workflows/rspec.yml +23 -0
- data/.github/workflows/rubocop.yml +23 -0
- data/.gitignore +13 -0
- data/.rspec +3 -0
- data/.rubocop.yml +29 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +40 -0
- data/Rakefile +8 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/weather_gov.rb +13 -0
- data/lib/weather_gov/address.rb +27 -0
- data/lib/weather_gov/alert.rb +45 -0
- data/lib/weather_gov/alert_collection.rb +28 -0
- data/lib/weather_gov/api.rb +93 -0
- data/lib/weather_gov/client.rb +115 -0
- data/lib/weather_gov/feature.rb +40 -0
- data/lib/weather_gov/feature_collection.rb +20 -0
- data/lib/weather_gov/forecast.rb +27 -0
- data/lib/weather_gov/forecast_period.rb +63 -0
- data/lib/weather_gov/gridpoint.rb +75 -0
- data/lib/weather_gov/identifier.rb +47 -0
- data/lib/weather_gov/identifier/alert.rb +13 -0
- data/lib/weather_gov/identifier/county_zone.rb +13 -0
- data/lib/weather_gov/identifier/fire_zone.rb +13 -0
- data/lib/weather_gov/identifier/forecast_zone.rb +13 -0
- data/lib/weather_gov/identifier/gridpoint.rb +29 -0
- data/lib/weather_gov/identifier/office.rb +13 -0
- data/lib/weather_gov/identifier/point.rb +21 -0
- data/lib/weather_gov/identifier/problem.rb +13 -0
- data/lib/weather_gov/identifier/station.rb +13 -0
- data/lib/weather_gov/observation_station.rb +27 -0
- data/lib/weather_gov/observation_station_collection.rb +20 -0
- data/lib/weather_gov/office.rb +68 -0
- data/lib/weather_gov/point.rb +81 -0
- data/lib/weather_gov/product.rb +29 -0
- data/lib/weather_gov/product_list.rb +17 -0
- data/lib/weather_gov/relative_location.rb +23 -0
- data/lib/weather_gov/valid_duration_value.rb +26 -0
- data/lib/weather_gov/valid_time.rb +48 -0
- data/lib/weather_gov/version.rb +5 -0
- data/lib/weather_gov/zone.rb +33 -0
- data/weather_gov.gemspec +39 -0
- 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,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,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
|