meteofrance-api 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/.gitignore +56 -0
- data/.travis.yml +7 -0
- data/CHANGELOG.md +0 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +28 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/meteofrance_api/client.rb +275 -0
- data/lib/meteofrance_api/constants.rb +86 -0
- data/lib/meteofrance_api/helpers.rb +119 -0
- data/lib/meteofrance_api/models/place.rb +38 -0
- data/lib/meteofrance_api/models/rain/forecast.rb +11 -0
- data/lib/meteofrance_api/models/rain.rb +44 -0
- data/lib/meteofrance_api/models/warning/current.rb +44 -0
- data/lib/meteofrance_api/models/warning/full.rb +67 -0
- data/lib/meteofrance_api/models/warning/phenomenon.rb +9 -0
- data/lib/meteofrance_api/models/warning.rb +5 -0
- data/lib/meteofrance_api/models/weather/daily_forecast.rb +35 -0
- data/lib/meteofrance_api/models/weather/forecast.rb +63 -0
- data/lib/meteofrance_api/models/weather/hazard_forecast.rb +17 -0
- data/lib/meteofrance_api/models/weather.rb +89 -0
- data/lib/meteofrance_api/models.rb +4 -0
- data/lib/meteofrance_api/version.rb +3 -0
- data/lib/meteofrance_api.rb +10 -0
- data/meteofrance-api.gemspec +44 -0
- metadata +131 -0
@@ -0,0 +1,119 @@
|
|
1
|
+
require "meteofrance_api/constants"
|
2
|
+
|
3
|
+
module MeteofranceApi::Helpers
|
4
|
+
# Convert the color code in readable text.
|
5
|
+
#
|
6
|
+
# Args:
|
7
|
+
# color_code: Color status in int. Value expected between 1 and 4.
|
8
|
+
# lang(Optional): If language is equal :fr (default value) results will
|
9
|
+
# be in French. All other value will give results in English.
|
10
|
+
|
11
|
+
#Returns:
|
12
|
+
#Color status in text. French or English according to the lang parameter.
|
13
|
+
def color_code_to_str(
|
14
|
+
code,
|
15
|
+
lang = :fr
|
16
|
+
)
|
17
|
+
colors = MeteofranceApi::ALERT_COLORS[lang] || MeteofranceApi::ALERT_COLORS[:en]
|
18
|
+
|
19
|
+
colors[code]
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
# Convert the phenomenom code in readable text (Hepler).
|
24
|
+
#
|
25
|
+
# Args:
|
26
|
+
# code: ID of the phenomenom in int. Value expected between 1 and 9.
|
27
|
+
# lang: Optional; If language is equal :fr (default value) results will
|
28
|
+
# be in French. All other value will give results in English.
|
29
|
+
#
|
30
|
+
# Returns:
|
31
|
+
# Phenomenom in text. French or English according to the lang parameter.
|
32
|
+
def alert_code_to_str(
|
33
|
+
code,
|
34
|
+
lang = :fr
|
35
|
+
)
|
36
|
+
alert_types = MeteofranceApi::ALERT_TYPES[lang] || MeteofranceApi::ALERT_TYPES[:en]
|
37
|
+
|
38
|
+
alert_types[code]
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# Identify when a second bulletin is availabe for coastal risks (Helper).
|
43
|
+
#
|
44
|
+
# Args:
|
45
|
+
# department_number: Department number on 2 characters
|
46
|
+
#
|
47
|
+
# Returns:
|
48
|
+
# True if the department have an additional coastal bulletin. False otherwise.
|
49
|
+
#
|
50
|
+
def is_coastal_department?(department_number)
|
51
|
+
MeteofranceApi::COASTAL_DEPARTMENTS.include?(department_number)
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Identify if there is a weather alert bulletin for this department (Helper).
|
56
|
+
#
|
57
|
+
# Weather alert buletins are available only for metropolitan France and Andorre.
|
58
|
+
#
|
59
|
+
# Args:
|
60
|
+
# department_number: Department number on 2 characters.
|
61
|
+
#
|
62
|
+
# Returns:
|
63
|
+
# True if a department is metropolitan France or Andorre.
|
64
|
+
#
|
65
|
+
def is_department?(department_number)
|
66
|
+
MeteofranceApi::VALID_DEPARTMENTS.include?(department_number)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Compute distance in meters between to GPS coordinates using Harvesine formula.
|
70
|
+
#
|
71
|
+
# source: https://janakiev.com/blog/gps-points-distance-python/
|
72
|
+
#
|
73
|
+
# Args:
|
74
|
+
# coord1: Tuple with latitude and longitude in degrees for first point
|
75
|
+
# coord2: Tuple with latitude and longitude in degrees for second point
|
76
|
+
#
|
77
|
+
# Returns:
|
78
|
+
# Distance in meters between the two points
|
79
|
+
def haversine(coord1, coord2)
|
80
|
+
to_radians = ->(v) { v * (Math::PI / 180) }
|
81
|
+
radius = 6372800 # Earth radius in meters
|
82
|
+
|
83
|
+
lat1, lon1 = coord1
|
84
|
+
lat2, lon2 = coord2
|
85
|
+
|
86
|
+
phi1, phi2 = to_radians.call(lat1), to_radians.call(lat2)
|
87
|
+
dphi = to_radians.call(lat2 - lat1)
|
88
|
+
dlambda = to_radians.call(lon2 - lon1)
|
89
|
+
|
90
|
+
a = (
|
91
|
+
Math.sin(dphi / 2) ** 2
|
92
|
+
+ Math.cos(phi1) * Math.cos(phi2) * Math.sin(dlambda / 2) ** 2
|
93
|
+
)
|
94
|
+
|
95
|
+
return 2 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a))
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Order list of places according to the distance to a reference coordinates.
|
100
|
+
#
|
101
|
+
# Note: this helper is compensating the bad results of the API. Results in the API
|
102
|
+
# are generally sorted, but lot of cases identified where the order is inconsistent
|
103
|
+
# (example: Montréal)
|
104
|
+
#
|
105
|
+
# Args:
|
106
|
+
# list_places: List of Place instances to be ordered
|
107
|
+
# gps_coord: Tuple with latitude and longitude in degrees for the reference point
|
108
|
+
#
|
109
|
+
# Returns:
|
110
|
+
# List of Place instances ordered by distance to the reference point (nearest
|
111
|
+
# first)
|
112
|
+
#
|
113
|
+
def sort_places_versus_distance_from_coordinates(
|
114
|
+
places,
|
115
|
+
gps_coord
|
116
|
+
)
|
117
|
+
places.sort_by {|place| haversine(place.latitude.to_i, place.longitude.to_i, gps_coord)}
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class MeteofranceApi::Place
|
2
|
+
|
3
|
+
# INSEE ID of the place.
|
4
|
+
attr_reader :insee
|
5
|
+
# name of the place.
|
6
|
+
attr_reader :name
|
7
|
+
# latitude of the place.
|
8
|
+
attr_reader :latitude
|
9
|
+
# longitude of the place.
|
10
|
+
attr_reader :longitude
|
11
|
+
# country code of the place.
|
12
|
+
attr_reader :country
|
13
|
+
# Province/Region of the place.
|
14
|
+
attr_reader :province
|
15
|
+
# Province/Region's numerical code of the place.
|
16
|
+
attr_reader :province_code
|
17
|
+
# postal code of the place.
|
18
|
+
attr_reader :postal_code
|
19
|
+
|
20
|
+
def initialize(data)
|
21
|
+
@insee = data["insee"]
|
22
|
+
@name = data["name"]
|
23
|
+
@latitude = data["lat"]
|
24
|
+
@longitude = data["lon"]
|
25
|
+
@country = data["country"]
|
26
|
+
@province = data["admin"]
|
27
|
+
@province_code = data["admin2"]
|
28
|
+
@postal_code = data["postCode"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
if country == "FR"
|
33
|
+
"#{name} - #{admin} (#{admin2}) - #{country}"
|
34
|
+
else
|
35
|
+
"#{name} - #{admin} - #{country}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class MeteofranceApi::Rain::Forecast
|
2
|
+
attr_reader :time
|
3
|
+
attr_reader :intensity
|
4
|
+
attr_reader :description
|
5
|
+
|
6
|
+
def intensity(data)
|
7
|
+
@time = Time.parse(data["time"])
|
8
|
+
@intensity = data["rain_intensity"]
|
9
|
+
@description = data["rain_intensity_description"]
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
class MeteofranceApi::Rain
|
2
|
+
# update time of the rain forecast.
|
3
|
+
attr_reader :updated_on
|
4
|
+
# position information of the rain forecast ([latitude, longitude]).
|
5
|
+
attr_reader :place_name
|
6
|
+
attr_reader :position
|
7
|
+
attr_reader :altitude
|
8
|
+
attr_reader :french_department
|
9
|
+
attr_reader :timezone
|
10
|
+
# the rain forecast.
|
11
|
+
attr_reader :forecasts
|
12
|
+
# quality of the rain forecast.
|
13
|
+
attr_reader :confidence
|
14
|
+
|
15
|
+
def initialize(data)
|
16
|
+
@updated_on = Time.at(data["updated_time"]).utc
|
17
|
+
@position = data["geometry"]["coordinates"]
|
18
|
+
@place_name = data["properties"]["name"]
|
19
|
+
@altitude = data["properties"]["altitude"]
|
20
|
+
@french_department = data["properties"]["french_department"]
|
21
|
+
@timezone = data["properties"]["timezone"]
|
22
|
+
@forecasts = data["properties"]["forecast"].map {|d| MeteofranceApi::Rain::Forecast.new(d)}
|
23
|
+
@confidence = data["properties"]["confidence"]
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_locale_timezone(time)
|
27
|
+
time.in_time_zone(position["timezone"])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Estimate the date of the next rain.
|
31
|
+
#
|
32
|
+
# Returns:
|
33
|
+
# A datetime instance representing the date estimation of the next rain within
|
34
|
+
# the next hour.
|
35
|
+
# If no rain is expected in the following hour nil is returned.
|
36
|
+
def next_rain_date(use_position_timezone)
|
37
|
+
# search first cadran with rain
|
38
|
+
next_rain = forecasts.find {|f| f.intensity > 1}
|
39
|
+
|
40
|
+
next_rain&.time
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
require "meteofrance_api/models/rain/forecast"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Class to access the results of a `warning/currentPhenomenons` REST API request.
|
2
|
+
#
|
3
|
+
# For coastal department two bulletins are avalaible corresponding to two different
|
4
|
+
# domains.
|
5
|
+
class MeteofranceApi::Warning::Current
|
6
|
+
# update time of the phenomenoms.
|
7
|
+
attr_reader :update_time
|
8
|
+
# end of validty time of the phenomenoms.
|
9
|
+
attr_reader :end_validity_time
|
10
|
+
# domain ID of the phenomenons. Value is 'France' or a department number
|
11
|
+
attr_reader :domain_id
|
12
|
+
# List of Phenomenon
|
13
|
+
attr_reader :phenomenons
|
14
|
+
|
15
|
+
def initialize(data)
|
16
|
+
@update_time = Time.at(data["update_time"])
|
17
|
+
@end_validity_time = Time.at(data["end_validity_time"])
|
18
|
+
@domain_id = data["domain_id"]
|
19
|
+
@phenomenons = (data["phenomenons_max_colors"] || []).map {|i| Warning::Phenomenon.new(i)}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Merge the classical phenomenoms bulleting with the coastal one.
|
23
|
+
|
24
|
+
# Extend the phenomenomes_max_colors property with the content of the coastal
|
25
|
+
# weather alert bulletin.
|
26
|
+
|
27
|
+
# Args:
|
28
|
+
# coastal_phenomenoms: WarningCurrentPhenomenons instance corresponding to the
|
29
|
+
# coastal weather alert bulletin.
|
30
|
+
#
|
31
|
+
def merge_with_coastal_phenomenons!(coastal_phenomenons)
|
32
|
+
# TODO: Add consitency check
|
33
|
+
@phenomenons += coastal_phenomenoms.phenomenons
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get the maximum level of alert of a given domain (class helper).
|
37
|
+
# Returns:
|
38
|
+
# An integer corresponding to the status code representing the maximum alert.
|
39
|
+
def get_domain_max_color
|
40
|
+
phenomenons.map {|item| item.max_color_id}.max
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
require "meteofrance_api/models/warning/phenomenon"
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# This class allows to access the results of a `warning/full` API command.
|
2
|
+
#
|
3
|
+
# For a given domain we can access the maximum alert, a timelaps of the alert
|
4
|
+
# evolution for the next 24 hours, and a list of alerts.
|
5
|
+
#
|
6
|
+
# For coastal department two bulletins are avalaible corresponding to two different
|
7
|
+
# domains.
|
8
|
+
class MeteofranceApi::Warning::Full
|
9
|
+
# update time of the full bulletin.
|
10
|
+
attr_reader :update_time
|
11
|
+
# end of validty time of the full bulletin.
|
12
|
+
attr_reader :end_validity_time
|
13
|
+
# domain ID of the the full bulletin. Value is 'France' or a department number.
|
14
|
+
attr_reader :domain_id
|
15
|
+
# color max of the domain.
|
16
|
+
attr_reader :color_max
|
17
|
+
# timelaps of each phenomenom for the domain. A list of Hash corresponding to the schedule of each phenomenons in the next 24 hours
|
18
|
+
attr_reader :timelaps
|
19
|
+
# phenomenom list of the domain.
|
20
|
+
attr_reader :phenomenons
|
21
|
+
|
22
|
+
attr_reader :advices
|
23
|
+
attr_reader :consequences
|
24
|
+
attr_reader :max_count_items
|
25
|
+
attr_reader :comments
|
26
|
+
attr_reader :text
|
27
|
+
attr_reader :text_avalanche
|
28
|
+
|
29
|
+
def initialize(data)
|
30
|
+
@update_time = Time.at(data["update_time"])
|
31
|
+
@end_validity_time = Time.at(data["end_validity_time"])
|
32
|
+
@domain_id = data["domain_id"]
|
33
|
+
@color_max = data["color_max"]
|
34
|
+
@timelaps = data["timelaps"]
|
35
|
+
@phenomenons = (data["phenomenons_items"] || []).map {|i| MeteofranceApi::Warning::Phenomenon.new(i)}
|
36
|
+
|
37
|
+
@advices = data["advices"]
|
38
|
+
@consequences = data["consequences"]
|
39
|
+
@max_count_items = data["max_count_items"]
|
40
|
+
@comments = data["comments"]
|
41
|
+
@text = data["text"]
|
42
|
+
@text["text_avalanche"]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Merge the classical phenomenon bulletin with the coastal one.
|
46
|
+
|
47
|
+
# Extend the color_max, timelaps and phenomenons properties with the content
|
48
|
+
# of the coastal weather alert bulletin.
|
49
|
+
|
50
|
+
# Args:
|
51
|
+
# coastal_phenomenoms: Full instance corresponding to the coastal weather
|
52
|
+
# alert bulletin.
|
53
|
+
def merge_with_coastal_phenomenons!(coastal_phenomenons)
|
54
|
+
# TODO: Add consitency check
|
55
|
+
# TODO: Check if other data need to be merged
|
56
|
+
|
57
|
+
@color_max = [color_max, coastal_phenomenons.color_max].max
|
58
|
+
|
59
|
+
# Merge timelaps
|
60
|
+
@timelaps += coastal_phenomenons.timelaps
|
61
|
+
|
62
|
+
# Merge phenomenons
|
63
|
+
@phenomenons += coastal_phenomenons.phenomenons
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
require "meteofrance_api/models/warning/phenomenon"
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class MeteofranceApi::Weather::DailyForecast
|
2
|
+
attr_reader :time
|
3
|
+
|
4
|
+
attr_reader :T_max
|
5
|
+
attr_reader :T_min
|
6
|
+
attr_reader :T_sea
|
7
|
+
|
8
|
+
attr_reader :relative_humidity_max
|
9
|
+
attr_reader :relative_humidity_min
|
10
|
+
|
11
|
+
attr_reader :daily_weather_description
|
12
|
+
attr_reader :daily_weather_icon
|
13
|
+
|
14
|
+
attr_reader :sunrise_time
|
15
|
+
attr_reader :sunset_time
|
16
|
+
|
17
|
+
attr_reader :total_precipitation_24h
|
18
|
+
|
19
|
+
attr_reader :uv_index
|
20
|
+
|
21
|
+
def initialize(data)
|
22
|
+
@time = Time.parse(data["time"])
|
23
|
+
@temperature_max = data["T_max"]
|
24
|
+
@temperature_min = data["T_min"]
|
25
|
+
@temperature_sea = data["T_sea"]
|
26
|
+
@relative_humidity_max = data["relative_humidity_max"]
|
27
|
+
@relative_humidity_min = data["relative_humidity_min"]
|
28
|
+
@weather_description = data["daily_weather_description"]
|
29
|
+
@weather_icon = data["daily_weather_icon"]
|
30
|
+
@sunrise_time = Time.parse(data["sunrise_time"])
|
31
|
+
@sunset_time = Time.parse(data["sunset_time"])
|
32
|
+
@total_precipitation_24h = data["total_precipitation_24h"]
|
33
|
+
@uv_index = data["uv_index"]
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class MeteofranceApi::Weather::Forecast
|
2
|
+
attr_reader :time
|
3
|
+
|
4
|
+
attr_reader :temperature
|
5
|
+
attr_reader :temperature_windchill
|
6
|
+
attr_reader :relative_humidity
|
7
|
+
|
8
|
+
attr_reader :total_cloud_cover
|
9
|
+
|
10
|
+
attr_reader :weather_description
|
11
|
+
attr_reader :weather_icon
|
12
|
+
|
13
|
+
attr_reader :wind_direction
|
14
|
+
attr_reader :wind_icon
|
15
|
+
attr_reader :wind_speed
|
16
|
+
attr_reader :wind_speed_gust
|
17
|
+
|
18
|
+
attr_reader :rain_1h
|
19
|
+
attr_reader :rain_3h
|
20
|
+
attr_reader :rain_6h
|
21
|
+
attr_reader :rain_12h
|
22
|
+
attr_reader :rain_24h
|
23
|
+
|
24
|
+
attr_reader :rain_snow_limit
|
25
|
+
|
26
|
+
attr_reader :snow_1h
|
27
|
+
attr_reader :snow_3h
|
28
|
+
attr_reader :snow_6h
|
29
|
+
attr_reader :snow_12h
|
30
|
+
attr_reader :snow_24h
|
31
|
+
|
32
|
+
attr_reader :P_sea
|
33
|
+
|
34
|
+
attr_reader :iso0
|
35
|
+
|
36
|
+
|
37
|
+
def initialize(data)
|
38
|
+
@time = Time.parse(data["time"])
|
39
|
+
@temperature = data["T"]
|
40
|
+
@temperature_windchill = data["T_windchill"]
|
41
|
+
@relative_humidity = data["relative_humidity"]
|
42
|
+
@P_sea = data["P_sea"]
|
43
|
+
@wind_speed = data["wind_speed"]
|
44
|
+
@wind_speed_gust = data["wind_speed_gust"]
|
45
|
+
@wind_direction = data["wind_direction"]
|
46
|
+
@wind_icon = data["wind_icon"]
|
47
|
+
@rain_1h = data["rain_1h"]
|
48
|
+
@rain_3h = data["rain_3h"]
|
49
|
+
@rain_6h = data["rain_6h"]
|
50
|
+
@rain_12h = data["rain_12h"]
|
51
|
+
@rain_24h = data["rain_24h"]
|
52
|
+
@snow_1h = data["snow_1h"]
|
53
|
+
@snow_3h = data["snow_3h"]
|
54
|
+
@snow_6h = data["snow_6h"]
|
55
|
+
@snow_12h = data["snow_12h"]
|
56
|
+
@snow_24h = data["snow_24h"]
|
57
|
+
@iso0 = data["iso0"]
|
58
|
+
@rain_snow_limit = data["rain_snow_limit"]
|
59
|
+
@total_cloud_cover = data["total_cloud_cover"]
|
60
|
+
@weather_icon = data["weather_icon"]
|
61
|
+
@weather_description = data["weather_description"]
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class MeteofranceApi::Weather::HazardForecast
|
2
|
+
attr_reader :rain_hazard_3h
|
3
|
+
attr_reader :rain_hazard_6h
|
4
|
+
attr_reader :snow_hazard_3h
|
5
|
+
attr_reader :snow_hazard_6h
|
6
|
+
attr_reader :freezing_hazard
|
7
|
+
attr_reader :storm_hazard
|
8
|
+
|
9
|
+
def initialize(data)
|
10
|
+
@rain_hazard_3h = data["rain_hazard_3h"]
|
11
|
+
@rain_hazard_6h = data["rain_hazard_6h"]
|
12
|
+
@snow_hazard_3h = data["snow_hazard_3h"]
|
13
|
+
@snow_hazard_6h = data["snow_hazard_6h"]
|
14
|
+
@freezing_hazard = data["freezing_hazard"]
|
15
|
+
@storm_hazard = data["storm_hazard"]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
class MeteofranceApi::Weather
|
2
|
+
# update Time of the forecast.
|
3
|
+
attr_reader :updated_at
|
4
|
+
|
5
|
+
# position information of the forecast. An Array [longitude, latitude].
|
6
|
+
attr_reader :position
|
7
|
+
# Altitude of the position (in meter)
|
8
|
+
attr_reader :altitude
|
9
|
+
# Name of the position
|
10
|
+
attr_reader :name
|
11
|
+
# Country of the position
|
12
|
+
attr_reader :country
|
13
|
+
attr_reader :timezone
|
14
|
+
attr_reader :insee
|
15
|
+
attr_reader :french_department_code
|
16
|
+
|
17
|
+
attr_reader :bulletin_cote
|
18
|
+
|
19
|
+
# daily forecast for the following days.
|
20
|
+
# A list of Hash to describe the daily forecast for the next 15 days.
|
21
|
+
attr_reader :daily_forecasts
|
22
|
+
# hourly forecast.
|
23
|
+
# A list of Hash to describe the hourly forecast for the next day
|
24
|
+
attr_reader :forecasts
|
25
|
+
# wheather event forecast.
|
26
|
+
# A list of object to describe the event probability forecast (rain, snow, freezing) for next 10 days.
|
27
|
+
attr_reader :hazard_forecasts
|
28
|
+
|
29
|
+
|
30
|
+
def initialize(data)
|
31
|
+
@updated_on = Time.at(data["updated_time"])
|
32
|
+
|
33
|
+
@position = data["geometry"]["coordinates"]
|
34
|
+
@altitude = data["properties"]["altitude"]
|
35
|
+
@name = data["properties"]["name"]
|
36
|
+
@country = data["properties"]["country"]
|
37
|
+
@timezone = data["properties"]["timezone"]
|
38
|
+
@insee = data["properties"]["insee"]&.to_i
|
39
|
+
@french_department_code = data["properties"]["french_department_code"]&.to_i
|
40
|
+
|
41
|
+
@bulletin_cote = data["properties"]["bulletin_cote"]
|
42
|
+
|
43
|
+
@daily_forecasts = data.dig("daily_forecast", []).map {|d| MeteofranceApi::Weather::DailyForecast.new(d)}
|
44
|
+
@forecasts = data.dig("forecast", []).map {|d| MeteofranceApi::Weather::Forecast.new(d)}
|
45
|
+
@hazard_forecasts = data.dig("probability_forecast", []).map {|d| MeteofranceApi::Weather::HazardForecast.new(d)}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the forecast for today. A Hash corresponding to the daily forecast for the current day
|
49
|
+
def today_forecast
|
50
|
+
self.daily_forecasts.first
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return the nearest hourly forecast. A Hash corresponding to the nearest hourly forecast.
|
54
|
+
def nearest_forecast
|
55
|
+
# get timestamp for current time
|
56
|
+
now_timestamp = Time.now
|
57
|
+
|
58
|
+
# sort list of forecast by distance between current timestamp and
|
59
|
+
# forecast timestamp
|
60
|
+
sorted_forecasts = self.forecasts.sort_by {|f| (f.time - now_timestamp).abs }
|
61
|
+
|
62
|
+
sorted_forecasts.first
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return the forecast of the current hour. A Hash corresponding to the hourly forecast for the current hour
|
66
|
+
def current_forecast
|
67
|
+
# Get the timestamp for the current hour.
|
68
|
+
current_hour = Time.now.utc.to_a
|
69
|
+
current_hour[0] = 0
|
70
|
+
current_hour[1] = 0
|
71
|
+
current_hour_timestamp = Time.utc.new(*current_hour).to_i
|
72
|
+
|
73
|
+
# create a Hash using timestamp as keys
|
74
|
+
forecasts_by_datetime = self.forecasts.map {|f| [f.time.to_i, f]}.to_h
|
75
|
+
|
76
|
+
# Return the forecast corresponding to the timestamp of the current hour if
|
77
|
+
# exists.
|
78
|
+
forecasts_by_datetime[current_hour_timestamp]
|
79
|
+
end
|
80
|
+
|
81
|
+
# Convert time in the forecast location timezone
|
82
|
+
def to_locale_time(time)
|
83
|
+
time.in_time_zone(timezone)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
require "meteofrance_api/models/weather/forecast"
|
88
|
+
require "meteofrance_api/models/weather/daily_forecast"
|
89
|
+
require "meteofrance_api/models/weather/hazard_forecast"
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module MeteofranceApi
|
2
|
+
class Error < StandardError; end
|
3
|
+
# Your code goes here...
|
4
|
+
end
|
5
|
+
|
6
|
+
require "meteofrance_api/client"
|
7
|
+
require "meteofrance_api/constants"
|
8
|
+
require "meteofrance_api/helpers"
|
9
|
+
require "meteofrance_api/models"
|
10
|
+
require "meteofrance_api/version"
|
@@ -0,0 +1,44 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "meteofrance_api/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "meteofrance-api"
|
8
|
+
spec.version = MeteofranceApi::VERSION
|
9
|
+
spec.authors = ["Thomas Kuntz"]
|
10
|
+
spec.email = ["thomaskuntz67@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Wrapper around meteofrance.com (french weather website) API.}
|
13
|
+
spec.description = %q{Wrapper around meteofrance.com non-public API. Based on Python lib https://github.com/hacf-fr/meteofrance-api}
|
14
|
+
spec.homepage = "https://github.com/Haerezis/meteofrance-api"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
#spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
+
|
22
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
23
|
+
spec.metadata["source_code_uri"] = "https://github.com/Haerezis/meteofrance-api"
|
24
|
+
spec.metadata["changelog_uri"] = "https://github.com/Haerezis/meteofrance-api/blob/main/CHANGELOG.md"
|
25
|
+
else
|
26
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
27
|
+
"public gem pushes."
|
28
|
+
end
|
29
|
+
|
30
|
+
# Specify which files should be added to the gem when it is released.
|
31
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
32
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
33
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
34
|
+
end
|
35
|
+
spec.bindir = "exe"
|
36
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
37
|
+
spec.require_paths = ["lib"]
|
38
|
+
|
39
|
+
spec.add_dependency "faraday", "~> 2.3.0"
|
40
|
+
|
41
|
+
spec.add_development_dependency "bundler", "~> 1.17"
|
42
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
43
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
44
|
+
end
|