meteoalarm 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +14 -0
  3. data/Gemfile +16 -0
  4. data/Gemfile.lock +44 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +90 -0
  7. data/Rakefile +14 -0
  8. data/countries/austria.json +116122 -0
  9. data/countries/belgium.json +369 -0
  10. data/countries/bosnia-herzegovina.json +474 -0
  11. data/countries/bulgeria.json +1084 -0
  12. data/countries/croatia.json +129694 -0
  13. data/countries/cyprus.json +67 -0
  14. data/countries/czechia.json +839 -0
  15. data/countries/denmark.json +2359 -0
  16. data/countries/estonia.json +838 -0
  17. data/countries/finland.json +3546 -0
  18. data/countries/france.json +4601 -0
  19. data/countries/germany.json +13918 -0
  20. data/countries/greece.json +1958 -0
  21. data/countries/hungary.json +545 -0
  22. data/countries/iceland.json +653 -0
  23. data/countries/ireland.json +1949 -0
  24. data/countries/israel.json +1733 -0
  25. data/countries/italy.json +3192 -0
  26. data/countries/latvia.json +164392 -0
  27. data/countries/lithuania.json +556 -0
  28. data/countries/luxembourg.json +52 -0
  29. data/countries/malta.json +47 -0
  30. data/countries/moldova.json +1609 -0
  31. data/countries/montenegro.json +193 -0
  32. data/countries/netherlands.json +956 -0
  33. data/countries/norway.json +7295 -0
  34. data/countries/poland.json +16858 -0
  35. data/countries/portugal.json +5923 -0
  36. data/countries/republic-of-north-macedonia.json +908 -0
  37. data/countries/romania.json +2122 -0
  38. data/countries/serbia.json +881 -0
  39. data/countries/slovenia.json +256 -0
  40. data/countries/slovkia.json +2628 -0
  41. data/countries/spain.json +14032 -0
  42. data/countries/sweden.json +3660 -0
  43. data/countries/switzerland.json +30511 -0
  44. data/countries/united-kingdom.json +98131 -0
  45. data/lib/meteoalarm/country_mapping.rb +41 -0
  46. data/lib/meteoalarm/tasks/areas.rake +17 -0
  47. data/lib/meteoalarm/tasks/countries.rake +10 -0
  48. data/lib/meteoalarm/version.rb +5 -0
  49. data/lib/meteoalarm.rb +185 -0
  50. metadata +93 -0
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meteoalarm
4
+ COUNTRY_MAPPING = { 'AT' => 'austria',
5
+ 'BA' => 'bosnia-herzegovina',
6
+ 'BE' => 'belgium',
7
+ 'BG' => 'bulgeria',
8
+ 'CH' => 'switzerland',
9
+ 'CY' => 'cyprus',
10
+ 'CZ' => 'czechia',
11
+ 'DE' => 'germany',
12
+ 'DK' => 'denmark',
13
+ 'EE' => 'estonia',
14
+ 'ES' => 'spain',
15
+ 'FI' => 'finland',
16
+ 'FR' => 'france',
17
+ 'GR' => 'greece',
18
+ 'HR' => 'croatia',
19
+ 'HU' => 'hungary',
20
+ 'IE' => 'ireland',
21
+ 'IL' => 'israel',
22
+ 'IS' => 'iceland',
23
+ 'IT' => 'italy',
24
+ 'LT' => 'lithuania',
25
+ 'LU' => 'luxembourg',
26
+ 'LV' => 'latvia',
27
+ 'MD' => 'moldova',
28
+ 'ME' => 'montenegro',
29
+ 'MK' => 'republic-of-north-macedonia',
30
+ 'MT' => 'malta',
31
+ 'NL' => 'netherlands',
32
+ 'NO' => 'norway',
33
+ 'PL' => 'poland',
34
+ 'PT' => 'portugal',
35
+ 'RO' => 'romania',
36
+ 'RS' => 'serbia',
37
+ 'SE' => 'sweden',
38
+ 'SI' => 'slovenia',
39
+ 'SK' => 'slovkia',
40
+ 'UK' => 'united-kingdom' }.freeze
41
+ end
@@ -0,0 +1,17 @@
1
+ require_relative '../country_mapping'
2
+ require 'json'
3
+
4
+ namespace :meteoalarm do
5
+ desc 'List all areas of given COUNTRY_CODE in Meteoalarm system'
6
+ task :areas do
7
+ country = Meteoalarm::COUNTRY_MAPPING[ENV['COUNTRY_CODE']&.upcase]
8
+ if country
9
+ path = File.expand_path(__dir__)
10
+ data = File.read("#{path}/../../../countries/#{country}.json")
11
+ spec = JSON.parse(data)
12
+ spec.each { |s| p s["area"] }
13
+ else
14
+ p 'Error: Invalid country code'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ require_relative '../country_mapping'
2
+
3
+ namespace :meteoalarm do
4
+ desc 'List all countries in Meteoalarm system'
5
+ task :countries do
6
+ Meteoalarm::COUNTRY_MAPPING.each do |code, country|
7
+ p "#{code}: #{country}"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meteoalarm
4
+ VERSION = "0.1.2"
5
+ end
data/lib/meteoalarm.rb ADDED
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+ require 'net/http'
3
+ require 'uri'
4
+ require 'json'
5
+ require 'time'
6
+ require_relative 'meteoalarm/version'
7
+ require_relative 'meteoalarm/country_mapping'
8
+
9
+ module Meteoalarm
10
+ class Error < StandardError; end
11
+ class ArgumentError < Error; end
12
+ class APIError < Error; end
13
+
14
+ class Client
15
+ BASE_URL = 'https://feeds.meteoalarm.org/api/v1/warnings/'
16
+
17
+ # Use this class to retrieve meteo alarms for a specific country.
18
+ #
19
+ # Example:
20
+ # >> Meteoalarm::Client.alarms('FR')
21
+ #
22
+ # Arguments:
23
+ # country_code: (String) - ISO 3166-1 A-2 code representing the country
24
+ # options: (Hash) - Additional search options
25
+ # latitude:, longitude: (Float) - Coordinates for location-based alarm search
26
+ # area: (String) - Area to filter alarms (ignored if coordinates are provided)
27
+ # active_now: (Boolean) - Search for currently active alarms
28
+ # expired: (Boolean) - List alarms that have expired
29
+ # date: (Date) - Search alarms by a specified future date
30
+ #
31
+ # Returns an array of weather alarms based on the provided criteria.
32
+
33
+ def self.alarms(country_code, options = {})
34
+ country = Meteoalarm::COUNTRY_MAPPING[country_code.upcase]
35
+ raise Meteoalarm::ArgumentError, "The provided country code is not supported. Refer to the rake tasks to view a list of available country codes." unless country
36
+ raise Meteoalarm::ArgumentError, "Both latitude and longitude must be provided." if (options[:latitude] && !options[:longitude]) || (!options[:latitude] && options[:longitude])
37
+ raise Meteoalarm::ArgumentError, "Incorrect date format provided." if options[:date] && !options[:date].is_a?(Date)
38
+ raise Meteoalarm::ArgumentError, "The date must be set in the future." if options[:date] && options[:date] < Date.today
39
+ warn "Provided coordinates will override the specified area." if (options[:latitude] && options[:area])
40
+
41
+ new.send(:alarms, country, options)
42
+ end
43
+
44
+ private
45
+
46
+ def alarms(country, options = {})
47
+ endpoint = "#{ BASE_URL }feeds-#{ country }"
48
+ response = send_http_request(endpoint)
49
+ check_status_code(response.code)
50
+
51
+ warnings = JSON.parse(response.body, symbolize_names: true)[:warnings]
52
+ reject_expired_warnings(warnings) unless options[:expired]
53
+
54
+ if options[:latitude] && options[:longitude]
55
+ warnings = check_warnings_in_coordinates(warnings, country, options[:latitude], options[:longitude])
56
+ elsif options[:area]
57
+ warnings = find_warnings_in_area(warnings, options[:area].downcase)
58
+ check_area(country, options[:area]) if warnings == []
59
+ end
60
+
61
+ warnings = future_alarms(warnings) if options[:future_alarms]
62
+ warnings = currently_active_alarms(warnings) if options[:active_now]
63
+ warnings = alarms_filter_by_date(warnings, options[:date]) if options[:date]
64
+
65
+ warnings
66
+ end
67
+
68
+ def check_area(country, area)
69
+ path = File.expand_path(__dir__)
70
+ data = File.read("#{path}/../countries/#{country}.json")
71
+ spec = JSON.parse(data)
72
+ unless spec.find { |s| s["area"].downcase == area.downcase }
73
+ raise Meteoalarm::ArgumentError, 'The provided area name is not supported. Refer to the rake tasks to view a list of available area names.'
74
+ end
75
+ end
76
+
77
+ def send_http_request(endpoint)
78
+ uri = URI.parse(endpoint)
79
+ http = Net::HTTP.new(uri.host, uri.port)
80
+ http.use_ssl = true
81
+ request = Net::HTTP::Get.new(uri.path)
82
+ http.request(request)
83
+ end
84
+
85
+ def check_status_code(status_code)
86
+ if status_code == '404'
87
+ raise Meteoalarm::APIError, "The requested page could not be found. Please consider upgrading the Meteoalarm gem or opening an issue."
88
+ elsif status_code.to_i >= 500
89
+ raise Meteoalarm::APIError, "Server error - status code: #{status_code}"
90
+ elsif status_code.to_i != 200
91
+ raise Meteoalarm::APIError, "Server returned unexpected status code: #{status_code}"
92
+ end
93
+ end
94
+
95
+ def reject_expired_warnings(warnings)
96
+ warnings.reject! do |warning|
97
+ Time.parse(warning.dig(:alert, :info, 0, :expires)) < Time.now
98
+ end
99
+ end
100
+
101
+ def check_warnings_in_coordinates(warnings, country, latitude, longitude)
102
+ path = File.expand_path(__dir__)
103
+ country_spec = File.read("#{path}/../countries/#{country}.json")
104
+ parsed_data = JSON.parse(country_spec)
105
+
106
+ alerts = []
107
+ parsed_data.each do |area|
108
+ if area['type'] == 'MultiPolygon'
109
+ multipolygon = area['coordinates'].first
110
+ if point_in_multipolygon(longitude, latitude, multipolygon)
111
+ alerts << find_warnings_in_code(warnings, area['code'])
112
+ end
113
+ else
114
+ polygon = area['coordinates'].first
115
+ if point_in_polygon(longitude, latitude, polygon)
116
+ alerts << find_warnings_in_code(warnings, area['code'])
117
+ end
118
+ end
119
+ end
120
+ alerts.flatten
121
+ end
122
+
123
+ def point_in_polygon(long, lat, polygon)
124
+ odd_nodes = false
125
+ j = polygon.length - 1
126
+
127
+ (0...polygon.length).each do |i|
128
+ if (polygon[i][1] < lat && polygon[j][1] >= lat) || (polygon[j][1] < lat && polygon[i][1] >= lat)
129
+ if (polygon[i][0] + (lat - polygon[i][1]) / (polygon[j][1] - polygon[i][1]) * (polygon[j][0] - polygon[i][0]) < long)
130
+ odd_nodes = !odd_nodes
131
+ end
132
+ end
133
+ j = i
134
+ end
135
+
136
+ odd_nodes
137
+ end
138
+
139
+ def point_in_multipolygon(long, lat, multipolygon)
140
+ multipolygon.each do |polygon|
141
+ return true if point_in_polygon(long, lat, polygon)
142
+ end
143
+
144
+ false
145
+ end
146
+
147
+ def find_warnings_in_code(warnings, code)
148
+ warnings.each_with_object([]) do |alert, area_alerts|
149
+ alert.dig(:alert, :info, 0, :area).each do |area|
150
+ area_alerts << alert if area[:geocode].any? { |geocode| geocode[:value] == code }
151
+ end
152
+ end
153
+ end
154
+
155
+ def find_warnings_in_area(warnings, area)
156
+ warnings.select do |alert|
157
+ alert.dig(:alert, :info, 0, :area).any? { |alert_area| alert_area[:areaDesc].downcase == area }
158
+ end
159
+ end
160
+
161
+ def currently_active_alarms(warnings)
162
+ warnings.select do |alert|
163
+ onset_time = Time.parse(alert.dig(:alert, :info, 0, :onset))
164
+ expires_time = Time.parse(alert.dig(:alert, :info, 0, :expires))
165
+
166
+ onset_time <= Time.now && expires_time > Time.now
167
+ end
168
+ end
169
+
170
+ def future_alarms(warnings)
171
+ warnings.select do |alert|
172
+ onset_time = Time.parse(alert.dig(:alert, :info, 0, :onset))
173
+ expires_time = Time.parse(alert.dig(:alert, :info, 0, :expires))
174
+
175
+ onset_time > Time.now
176
+ end
177
+ end
178
+
179
+ def alarms_filter_by_date(warnings, date)
180
+ warnings.select do |alert|
181
+ Time.parse(alert.dig(:alert, :info, 0, :onset)).to_date == date
182
+ end
183
+ end
184
+ end
185
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: meteoalarm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Em Jov
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-01-13 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: API wrapper for meteoalarm.org.
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - CHANGELOG.md
20
+ - Gemfile
21
+ - Gemfile.lock
22
+ - LICENSE.txt
23
+ - README.md
24
+ - Rakefile
25
+ - countries/austria.json
26
+ - countries/belgium.json
27
+ - countries/bosnia-herzegovina.json
28
+ - countries/bulgeria.json
29
+ - countries/croatia.json
30
+ - countries/cyprus.json
31
+ - countries/czechia.json
32
+ - countries/denmark.json
33
+ - countries/estonia.json
34
+ - countries/finland.json
35
+ - countries/france.json
36
+ - countries/germany.json
37
+ - countries/greece.json
38
+ - countries/hungary.json
39
+ - countries/iceland.json
40
+ - countries/ireland.json
41
+ - countries/israel.json
42
+ - countries/italy.json
43
+ - countries/latvia.json
44
+ - countries/lithuania.json
45
+ - countries/luxembourg.json
46
+ - countries/malta.json
47
+ - countries/moldova.json
48
+ - countries/montenegro.json
49
+ - countries/netherlands.json
50
+ - countries/norway.json
51
+ - countries/poland.json
52
+ - countries/portugal.json
53
+ - countries/republic-of-north-macedonia.json
54
+ - countries/romania.json
55
+ - countries/serbia.json
56
+ - countries/slovenia.json
57
+ - countries/slovkia.json
58
+ - countries/spain.json
59
+ - countries/sweden.json
60
+ - countries/switzerland.json
61
+ - countries/united-kingdom.json
62
+ - lib/meteoalarm.rb
63
+ - lib/meteoalarm/country_mapping.rb
64
+ - lib/meteoalarm/tasks/areas.rake
65
+ - lib/meteoalarm/tasks/countries.rake
66
+ - lib/meteoalarm/version.rb
67
+ homepage: https://github.com/em-jov/meteoalarm
68
+ licenses:
69
+ - MIT
70
+ metadata:
71
+ homepage_uri: https://github.com/em-jov/meteoalarm
72
+ source_code_uri: https://github.com/em-jov/meteoalarm
73
+ changelog_uri: https://github.com/em-jov/meteoalarm/blob/main/CHANGELOG.md
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.6.0
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubygems_version: 3.4.10
90
+ signing_key:
91
+ specification_version: 4
92
+ summary: API wrapper for meteoalarm.org.
93
+ test_files: []