meteoalarm 0.1.2

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 (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: []