jma_feed 0.0.4 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jma_feed/atom/eqvol.rb +7 -0
  3. data/lib/jma_feed/{api → atom}/extra.rb +1 -1
  4. data/lib/jma_feed/atom/other.rb +7 -0
  5. data/lib/jma_feed/{api → atom}/regular.rb +1 -1
  6. data/lib/jma_feed/{api.rb → atom.rb} +5 -1
  7. data/lib/jma_feed/entity/weather_alert/metrics_item.rb +14 -0
  8. data/lib/jma_feed/entity/weather_alert.rb +177 -0
  9. data/lib/jma_feed/node/base_node.rb +4 -0
  10. data/lib/jma_feed/report.rb +7 -0
  11. data/lib/jma_feed/report_entry/jmx/discharge.rb +4 -0
  12. data/lib/jma_feed/report_entry/jmx/earthquake.rb +22 -0
  13. data/lib/jma_feed/report_entry/jmx/earthquake_intensity.rb +14 -0
  14. data/lib/jma_feed/report_entry/jmx/humidity.rb +20 -0
  15. data/lib/jma_feed/report_entry/jmx/precipitation.rb +13 -0
  16. data/lib/jma_feed/report_entry/jmx/significancy.rb +14 -0
  17. data/lib/jma_feed/report_entry/jmx/snow_fall_depth.rb +20 -0
  18. data/lib/jma_feed/report_entry/jmx/tidal_level.rb +20 -0
  19. data/lib/jma_feed/report_entry/jmx/time_define.rb +18 -0
  20. data/lib/jma_feed/report_entry/jmx/visibility.rb +20 -0
  21. data/lib/jma_feed/report_entry/jmx/water_level.rb +4 -0
  22. data/lib/jma_feed/report_entry/jmx/wave_height.rb +20 -0
  23. data/lib/jma_feed/report_entry/jmx/wind_direction.rb +20 -0
  24. data/lib/jma_feed/report_entry/jmx/wind_speed.rb +20 -0
  25. data/lib/jma_feed/report_entry/vphw50.rb +4 -4
  26. data/lib/jma_feed/report_entry/vprn50.rb +5 -4
  27. data/lib/jma_feed/report_entry/vpww54.rb +220 -31
  28. data/lib/jma_feed/report_entry/vxse53.rb +135 -0
  29. data/lib/jma_feed/report_entry.rb +19 -2
  30. data/lib/jma_feed/result.rb +4 -0
  31. data/lib/jma_feed/result_doc.rb +6 -2
  32. data/lib/jma_feed/result_entry.rb +24 -1
  33. data/lib/jma_feed/version.rb +1 -1
  34. data/lib/jma_feed.rb +7 -3
  35. metadata +24 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5c76dfa5b24cec264aa047de3a21871a1cce3fcf7bee5908a88ecd0029d8fda1
4
- data.tar.gz: 10abdfb01733d13e2d733a6f2499e6a2a614f3fb181797852602db2f5b45441a
3
+ metadata.gz: 80ad84e584d816cd507897d8d62cf1f03ec347f5a1170e32d00cd7996bab57a3
4
+ data.tar.gz: 2a81329ec41932a8c6ab872fa3fd221551999536a86bbc209dd5f90f879b3a70
5
5
  SHA512:
6
- metadata.gz: 31aa0529c9797ef626849ecf3392dbea29a449917751f142c765dc0818b4f73e35642d4e481d9f05fd92acf8ec23ef2d51a333f8c6d9cdc7af2a1b92ce2affc7
7
- data.tar.gz: ca936a1a99345e281a278c8aa1d234397ca90c600c2a4e3efc500d2bcdb463276ee28faac9bd45ba5d5bbd0dc4fc67773c0148f620635023517b82784bcce3cf
6
+ metadata.gz: 40b32b0fb88b0bee5c3c3a0cb569b2f2b6197979520526522d103e5bb113f17e020407bac8c0b7493793556927d6ff14a21549b9d4edaeb8bfe738fa9baa7427
7
+ data.tar.gz: 8a98a853ab3dc1ea3ef244bea641a84180e91c29825d23ff001fefc746b62733ce8ae4bc5c8db7289981551a36215db09c1764edd141abb5e8152146130619e5
@@ -0,0 +1,7 @@
1
+ class JMAFeed::Eqvol
2
+ include JMAFeed::Atom
3
+
4
+ def feed_type
5
+ :eqvol
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  class JMAFeed::Extra
2
- include JMAFeed::Api
2
+ include JMAFeed::Atom
3
3
 
4
4
  def feed_type
5
5
  :extra
@@ -0,0 +1,7 @@
1
+ class JMAFeed::Other
2
+ include JMAFeed::Atom
3
+
4
+ def feed_type
5
+ :other
6
+ end
7
+ end
@@ -1,5 +1,5 @@
1
1
  class JMAFeed::Regular
2
- include JMAFeed::Api
2
+ include JMAFeed::Atom
3
3
 
4
4
  def feed_type
5
5
  :regular
@@ -1,6 +1,6 @@
1
1
  require "net/http"
2
2
 
3
- module JMAFeed::Api
3
+ module JMAFeed::Atom
4
4
 
5
5
  def self.type(feed_type, **args)
6
6
  feed_type = feed_type.to_sym
@@ -8,6 +8,10 @@ module JMAFeed::Api
8
8
  JMAFeed::Regular.new(**args)
9
9
  elsif feed_type == :extra
10
10
  JMAFeed::Extra.new(**args)
11
+ elsif feed_type == :eqvol
12
+ JMAFeed::Eqvol.new(**args)
13
+ elsif feed_type == :other
14
+ JMAFeed::Other.new(**args)
11
15
  else
12
16
  raise "feed_type=#{feed_type} not supported"
13
17
  end
@@ -0,0 +1,14 @@
1
+ class JMAFeed::WeatherAlert::MetricsItem < Struct.new(
2
+ :name, :value, :description, :time_define,
3
+ keyword_init: true
4
+ )
5
+
6
+ def to_h
7
+ {
8
+ name: name,
9
+ value: value,
10
+ description: description,
11
+ time_define: time_define.to_h,
12
+ }
13
+ end
14
+ end
@@ -161,6 +161,171 @@ class JMAFeed::WeatherAlert < Struct.new(:code, :name, :risk_level, :cluster, ke
161
161
  },
162
162
  }
163
163
 
164
+ # 気象警報・注意(H27)_解説資料.pdf > 別表4
165
+ METRICS = {
166
+ storm_metrics: {
167
+ "風危険度" => {
168
+ name: :significancy,
169
+ property_type: "危険度",
170
+ },
171
+ "風向" => {
172
+ name: :wind_direction,
173
+ property_type: "風",
174
+ },
175
+ "最大風速" => {
176
+ name: :wind_speed,
177
+ property_type: "風",
178
+ },
179
+ },
180
+ rain_metrics: {
181
+ "土砂災害危険度" => {
182
+ name: :significancy,
183
+ property_type: "危険度",
184
+ },
185
+ "浸水害危険度" => {
186
+ name: :significancy,
187
+ property_type: "危険度",
188
+ },
189
+ "1時間最大雨量" => {
190
+ name: :precipitation,
191
+ property_type: "雨",
192
+ },
193
+ "3時間最大雨量" => {
194
+ name: :precipitation,
195
+ property_type: "雨",
196
+ },
197
+ },
198
+ flood_metrics: {
199
+ "洪水害危険度" => {
200
+ name: :significancy,
201
+ property_type: "危険度",
202
+ },
203
+ },
204
+ snow_metrics: {
205
+ "雪危険度" => {
206
+ name: :significancy,
207
+ property_type: "危険度",
208
+ },
209
+ "6時間最大降雪量" => {
210
+ name: :snow_fall_depth,
211
+ property_type: "雪",
212
+ },
213
+ "12時間最大降雪量" => {
214
+ name: :snow_fall_depth,
215
+ property_type: "雪",
216
+ },
217
+ "24時間最大降雪量" => {
218
+ name: :snow_fall_depth,
219
+ property_type: "雪",
220
+ },
221
+ },
222
+ wave_metrics: {
223
+ "波危険度" => {
224
+ name: :significancy,
225
+ property_type: "危険度",
226
+ },
227
+ "波高" => {
228
+ name: :wave_height,
229
+ property_type: "波",
230
+ },
231
+ },
232
+ tide_metrics: {
233
+ "高潮危険度" => {
234
+ name: :significancy,
235
+ property_type: "危険度",
236
+ },
237
+ "最高潮位" => {
238
+ name: :tidal_level,
239
+ property_type: "高",
240
+ },
241
+ },
242
+ thunder_metrics: {
243
+ "雷危険度" => {
244
+ name: :significancy,
245
+ property_type: "危険度",
246
+ },
247
+ },
248
+ snow_melting_metrics: {
249
+ "融雪危険度" => {
250
+ name: :significancy,
251
+ property_type: "危険度",
252
+ },
253
+ },
254
+ fog_metrics: {
255
+ "濃霧危険度" => {
256
+ name: :significancy,
257
+ property_type: "危険度",
258
+ },
259
+ "視程" => {
260
+ name: :visibility,
261
+ property_type: "濃霧",
262
+ },
263
+ },
264
+ dry_air_metrics: {
265
+ "乾燥危険度" => {
266
+ name: :significancy,
267
+ property_type: "危険度",
268
+ },
269
+ "実効湿度" => {
270
+ name: :humidity,
271
+ property_type: "乾燥",
272
+ },
273
+ "最小湿度" => {
274
+ name: :humidity,
275
+ property_type: "乾燥",
276
+ },
277
+ },
278
+ avalanche_metrics: {
279
+ "なだれ危険度" => {
280
+ name: :significancy,
281
+ property_type: "危険度",
282
+ },
283
+ },
284
+ low_temperature_metrics: {
285
+ "低温危険度" => {
286
+ name: :significancy,
287
+ property_type: "危険度",
288
+ },
289
+ },
290
+ frost_metrics: {
291
+ "霜危険度" => {
292
+ name: :significancy,
293
+ property_type: "危険度",
294
+ },
295
+ },
296
+ ice_accretion_metrics: {
297
+ "着氷危険度" => {
298
+ name: :significancy,
299
+ property_type: "危険度",
300
+ },
301
+ },
302
+ snow_accretion_metrics: {
303
+ "着雪危険度" => {
304
+ name: :significancy,
305
+ property_type: "危険度",
306
+ },
307
+ },
308
+ }
309
+
310
+ CLUSTER_METRICS = {
311
+ storm: METRICS[:storm_metrics],
312
+ snowstorm: METRICS[:storm_metrics],
313
+ heavy_rain: METRICS[:rain_metrics],
314
+ flood: METRICS[:flood_metrics],
315
+ heavy_snow: METRICS[:snow_metrics],
316
+ high_wave: METRICS[:wave_metrics],
317
+ storm_surge: METRICS[:tide_metrics],
318
+ thunderstorm: METRICS[:thunder_metrics],
319
+ snow_melting: METRICS[:snow_melting_metrics],
320
+ dense_fog: METRICS[:fog_metrics],
321
+ dry_air: METRICS[:dry_air_metrics],
322
+ avalanche: METRICS[:avalanche_metrics],
323
+ low_temperature: METRICS[:low_temperature_metrics],
324
+ frost: METRICS[:frost_metrics],
325
+ ice_accretion: METRICS[:ice_accretion_metrics],
326
+ snow_accretion: METRICS[:snow_accretion_metrics],
327
+ }
328
+
164
329
  def self.clusters
165
330
  all.group_by(&:cluster)
166
331
  end
@@ -168,4 +333,16 @@ class JMAFeed::WeatherAlert < Struct.new(:code, :name, :risk_level, :cluster, ke
168
333
  def self.all
169
334
  @all ||= LIST.map{|k,v| new(v.merge(code: k.to_s))}
170
335
  end
336
+
337
+ def self.metrics_properties
338
+ METRICS.values.reduce(&:merge)
339
+ end
340
+
341
+ def self.metrics_components
342
+ metrics_properties.values.uniq
343
+ end
344
+
345
+ def metrics
346
+ CLUSTER_METRICS[cluster.to_sym] || {}
347
+ end
171
348
  end
@@ -0,0 +1,4 @@
1
+ class JMAFeed::BaseNode < Giri::BaseNode
2
+ self.with_name_default_for_node = :camelize
3
+ self.with_name_default_for_attribute = :lower_camelcase
4
+ end
@@ -95,4 +95,11 @@ class JMAFeed::Report < Struct.new(
95
95
  def has_kind?(k)
96
96
  Array(kind).include?(k)
97
97
  end
98
+
99
+ def report_entry_class
100
+ return nil unless code_represented
101
+ JMAFeed.const_get(code_represented)
102
+ rescue NameError => e
103
+ JMAFeed::ReportEntry
104
+ end
98
105
  end
@@ -4,4 +4,8 @@ class JMAFeed::JMX::Discharge < Giri::TextNodeBigDecimal
4
4
  xml_attribute :ref_id, with_name: "refID"
5
5
  xml_attribute :condition
6
6
  xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
7
11
  end
@@ -0,0 +1,22 @@
1
+ # jmx_eb
2
+ class JMAFeed::JMX::Earthquake < Giri::BaseNode
3
+ date_time_node :origin_time
4
+ date_time_node :arrival_time
5
+
6
+ xml_node :hypocenter do
7
+ xml_node :area do
8
+ text_node :name
9
+ text_node :code do
10
+ xml_attribute :type
11
+ end
12
+ text_node :coordinate do
13
+ text_node :description
14
+ text_node :datum
15
+ end
16
+ end
17
+ end
18
+ big_decimal_node :magnitude do
19
+ xml_attribute :type
20
+ xml_attribute :description
21
+ end
22
+ end
@@ -0,0 +1,14 @@
1
+ class JMAFeed::JMX::EarthquakeIntensity < Giri::TextNodeString
2
+ # self:
3
+ # 1, 2, 3, 4, 5-, 5+, 6-, 6+, 7, 震度5弱以上未入電
4
+
5
+ def intensity
6
+ value = self.sub(/\-\z/, '').sub(/\+\z/, '.5').to_f
7
+ value > 0 ? value : nil
8
+ end
9
+
10
+ def title
11
+ return self unless intensity
12
+ "震度" + self.sub(/\-\z/, '弱').sub(/\+\z/, '強')
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ class JMAFeed::JMX::Humidity < Giri::TextNodeInteger
2
+ xml_attribute :type
3
+ xml_attribute :unit
4
+ xml_attribute :ref_id, with_name: "refID"
5
+ # xml_attribute :condition
6
+ xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
20
+ end
@@ -4,4 +4,17 @@ class JMAFeed::JMX::Precipitation < Giri::TextNodeInteger
4
4
  xml_attribute :ref_id, with_name: "refID"
5
5
  xml_attribute :condition
6
6
  xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
7
20
  end
@@ -1,7 +1,21 @@
1
1
  # jmx_mete
2
2
  class JMAFeed::JMX::Significancy < Giri::BaseNode
3
3
  xml_attribute :type
4
+ xml_attribute :ref_id, with_name: "refID"
4
5
  text_node :name
5
6
  text_node :code
6
7
  text_node :condition
8
+
9
+ def time_define
10
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
11
+ end
12
+
13
+ def metrics_item
14
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
15
+ name: type,
16
+ value: code,
17
+ description: name,
18
+ time_define: time_define,
19
+ )
20
+ end
7
21
  end
@@ -0,0 +1,20 @@
1
+ class JMAFeed::JMX::SnowFallDepth < Giri::TextNodeInteger
2
+ xml_attribute :type
3
+ xml_attribute :unit
4
+ xml_attribute :ref_id, with_name: "refID"
5
+ # xml_attribute :condition
6
+ xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class JMAFeed::JMX::TidalLevel < Giri::TextNodeBigDecimal
2
+ xml_attribute :type
3
+ xml_attribute :unit
4
+ xml_attribute :ref_id, with_name: "refID"
5
+ # xml_attribute :condition
6
+ xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ class JMAFeed::JMX::TimeDefine < Giri::BaseNode
2
+ xml_attribute :time_id, with_name: :lower_camelcase
3
+ date_time_node :date_time
4
+ duration_node :duration
5
+ text_node :name
6
+
7
+ def start_at
8
+ date_time
9
+ end
10
+
11
+ def end_at
12
+ start_at + duration.duration_time
13
+ end
14
+
15
+ def to_h
16
+ {name: name, start_at: start_at, end_at: end_at, duration: duration}
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ class JMAFeed::JMX::Visibility < Giri::TextNodeInteger
2
+ xml_attribute :type
3
+ xml_attribute :unit
4
+ xml_attribute :ref_id, with_name: "refID"
5
+ xml_attribute :condition
6
+ xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
20
+ end
@@ -4,4 +4,8 @@ class JMAFeed::JMX::WaterLevel < Giri::TextNodeBigDecimal
4
4
  xml_attribute :ref_id, with_name: "refID"
5
5
  xml_attribute :condition
6
6
  xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
7
11
  end
@@ -0,0 +1,20 @@
1
+ class JMAFeed::JMX::WaveHeight < Giri::TextNodeBigDecimal
2
+ xml_attribute :type
3
+ xml_attribute :unit
4
+ xml_attribute :ref_id, with_name: "refID"
5
+ # xml_attribute :condition
6
+ xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class JMAFeed::JMX::WindDirection < Giri::TextNodeString
2
+ xml_attribute :type
3
+ xml_attribute :unit
4
+ xml_attribute :ref_id, with_name: "refID"
5
+ xml_attribute :condition
6
+ xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ class JMAFeed::JMX::WindSpeed < Giri::TextNodeInteger
2
+ xml_attribute :type
3
+ xml_attribute :unit
4
+ xml_attribute :ref_id, with_name: "refID"
5
+ xml_attribute :condition
6
+ xml_attribute :description
7
+
8
+ def time_define
9
+ (context.time_define_list || []).find{ref_id && ref_id == _1.time_id}
10
+ end
11
+
12
+ def metrics_item
13
+ @metrics_item ||= JMAFeed::WeatherAlert::MetricsItem.new(
14
+ name: type,
15
+ value: self,
16
+ description: description,
17
+ time_define: time_define,
18
+ )
19
+ end
20
+ end
@@ -22,19 +22,19 @@ class JMAFeed::VPHW50 < JMAFeed::ReportEntry
22
22
  end
23
23
  end
24
24
 
25
- def warning_district_report
25
+ def warning_area_type1
26
26
  warning.find{|w| w.type == "竜巻注意情報(発表細分)"}
27
27
  end
28
28
 
29
- def warning_district_1st
29
+ def warning_area_type2
30
30
  warning.find{|w| w.type == "竜巻注意情報(一次細分区域等)"}
31
31
  end
32
32
 
33
- def warning_district_aggregated
33
+ def warning_area_type3
34
34
  warning.find{|w| w.type == "竜巻注意情報(市町村等をまとめた地域等)"}
35
35
  end
36
36
 
37
- def warning_district_2nd
37
+ def warning_area_type4
38
38
  warning.find{|w| w.type == "竜巻注意情報(市町村等)"}
39
39
  end
40
40
  end
@@ -40,6 +40,7 @@ class JMAFeed::VPRN50 < JMAFeed::ReportEntry
40
40
 
41
41
  xml_node :body do
42
42
  xml_node_collection :meteorological_infos do
43
+ xml_attribute :type
43
44
  xml_node_collection :meteorological_info do
44
45
  date_time_node :date_time
45
46
  xml_node_collection :item do
@@ -82,19 +83,19 @@ class JMAFeed::VPRN50 < JMAFeed::ReportEntry
82
83
  end
83
84
  end
84
85
 
85
- def info_district_forecast
86
+ def info_area_type1
86
87
  meteorological_infos[0].meteorological_info[0]
87
88
  end
88
89
 
89
- def info_district_1st
90
+ def info_area_type2
90
91
  meteorological_infos[1].meteorological_info[0]
91
92
  end
92
93
 
93
- def info_district_aggregated
94
+ def info_area_type3
94
95
  meteorological_infos[2].meteorological_info[0]
95
96
  end
96
97
 
97
- def info_district_2nd
98
+ def info_area_type4
98
99
  meteorological_infos[3].meteorological_info[0]
99
100
  end
100
101
  end
@@ -11,26 +11,43 @@
11
11
  # </MeteorologicalInfos>
12
12
  #
13
13
  class JMAFeed::VPWW54 < JMAFeed::ReportEntry
14
+ class WarningItemKind < Giri::BaseNode
15
+ text_node :name
16
+ text_node :code
17
+ text_node :status
18
+ text_node :condition
19
+ xml_node :last_kind do
20
+ text_node :name
21
+ text_node :code
22
+ end
23
+ xml_node_collection :next_kinds do
24
+ xml_node :next_kind do
25
+ text_node :name
26
+ text_node :code
27
+ date_time_node :date_time do
28
+ xml_attribute :precision, type: :duration
29
+ end
30
+ text_node :sentence
31
+ end
32
+ end
33
+ xml_node :attention do
34
+ text_node :note, collection: true
35
+ end
36
+ xml_node :addition do
37
+ text_node :note, collection: true
38
+ end
39
+
40
+ def weather_alert
41
+ @weather_alert ||= JMAFeed::WeatherAlert.all.find{|a| a.code == code}
42
+ end
43
+ end
44
+
14
45
  xml_node :body do
46
+ text_node :notice, collection: true
15
47
  xml_node_collection :warning do
16
48
  xml_attribute :type
17
49
  xml_node_collection :item do
18
- xml_node_collection :kind do
19
- text_node :name
20
- text_node :code
21
- text_node :status
22
- text_node :condition
23
- xml_node :attention do
24
- text_node :note, collection: true
25
- end
26
- xml_node :addition do
27
- text_node :note, collection: true
28
- end
29
-
30
- def weather_alert
31
- @weather_alert ||= JMAFeed::WeatherAlert.all.find{|a| a.code == code}
32
- end
33
- end
50
+ xml_node_collection :kind, type: "JMAFeed::VPWW54::WarningItemKind"
34
51
  text_node :change_status
35
52
  xml_node :area, type: "JMAFeed::JMX::Area"
36
53
 
@@ -41,37 +58,209 @@ class JMAFeed::VPWW54 < JMAFeed::ReportEntry
41
58
  end
42
59
  end
43
60
  end
61
+
44
62
  xml_node :meteorological_infos do
45
63
  xml_attribute :type
46
64
  xml_node :time_series_info do
65
+ context :time_define_list do
66
+ time_defines.time_define
67
+ end
68
+
47
69
  xml_node :time_defines do
48
- xml_node_collection :time_define do
49
- xml_attribute :time_id, with_name: :lower_camelcase
50
- date_time_node :date_time
51
- duration_node :duration
52
- text_node :name
53
- end
70
+ xml_node_collection :time_define, type: "JMAFeed::JMX::TimeDefine"
54
71
  end
55
72
  xml_node_collection :item do
56
- # TODO
73
+ xml_node_collection :kind do
74
+ text_node :name
75
+ text_node :code
76
+ text_node :condition
77
+ xml_node_collection :property do
78
+ text_node :type
79
+
80
+ JMAFeed::WeatherAlert.metrics_components.each do |metr_component|
81
+ xml_node_collection "#{metr_component[:name]}_part".to_sym do
82
+ xml_node :base do
83
+ xml_node_collection metr_component[:name], type: "JMAFeed::JMX::#{metr_component[:name].to_s.camelize}"
84
+
85
+ xml_node_collection :local do
86
+ text_node :area_name
87
+ xml_node_collection metr_component[:name], type: "JMAFeed::JMX::#{metr_component[:name].to_s.camelize}"
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+
94
+ def weather_alert
95
+ @weather_alert ||= JMAFeed::WeatherAlert.all.find{|a| a.code == code}
96
+ end
97
+
98
+ # EXAMPLE of data structure
99
+ # {
100
+ # "土砂災害危険度" => {"全体" => [...]},
101
+ # "浸水害危険度" => {"全体" => [...]},
102
+ # "1時間最大雨量" => {"全体" => [...]},
103
+ # "3時間最大雨量" => {"全体" => [...]},
104
+ # }
105
+ # or
106
+ # {
107
+ # "土砂災害危険度" => {"山地" => [...], "平地" => [...]},
108
+ # "浸水害危険度" => {"山地" => [...], "平地" => [...]},
109
+ # "1時間最大雨量" => {"山地" => [...], "平地" => [...]},
110
+ # "3時間最大雨量" => {"山地" => [...], "平地" => [...]},
111
+ # }
112
+ def weather_alert_metrics
113
+ return nil unless weather_alert
114
+ weather_alert.metrics.map do |k,v|
115
+ metrics_part = property.find{_1.type == v[:property_type]}&.public_send("#{v[:name]}_part")
116
+ metrics_bases = metrics_part&.map(&:base)
117
+ metrics_items = if metrics_bases && metrics_bases.length > 0
118
+ primary_base = metrics_bases.first
119
+ metrics_bases_area_items = if primary_base.local && primary_base.local.length > 0
120
+ metrics_bases.map{|b|
121
+ b.local.map{|l|
122
+ [
123
+ l.area_name,
124
+ l.public_send(v[:name]).map(&:metrics_item)
125
+ ]
126
+ }.to_h
127
+ }
128
+ else
129
+ metrics_bases.map{|b|
130
+ [
131
+ [
132
+ "全体",
133
+ b.public_send(v[:name]).map(&:metrics_item)
134
+ ]
135
+ ].to_h
136
+ }
137
+ end
138
+
139
+ metrics_bases_area_items.find{|area_items|
140
+ local_area_name, area_metrics_items = area_items.first
141
+ area_metrics_items.first.name == k
142
+ }
143
+ else
144
+ nil
145
+ end
146
+
147
+ if metrics_items
148
+ [k, metrics_items]
149
+ else
150
+ nil
151
+ end
152
+ end.compact.to_h
153
+ end
154
+ end
155
+
156
+ JMAFeed::WeatherAlert.clusters.each do |cluster,alerts|
157
+ define_method cluster do
158
+ kind.find{|k| alerts.map(&:code).include?(k.code)}
159
+ end
160
+ end
161
+
162
+ xml_node :area, type: "JMAFeed::JMX::Area"
57
163
  end
58
164
  end
59
165
  end
60
166
 
61
- def warning_district_forecast
62
- warning[0]
167
+ def warning_area_type1
168
+ warning.find{|w| w.type == "気象警報・注意報(府県予報区等)"}
169
+ end
170
+
171
+ def warning_area_type2
172
+ warning.find{|w| w.type == "気象警報・注意報(一次細分区域等)"}
173
+ end
174
+
175
+ def warning_area_type3
176
+ warning.find{|w| w.type == "気象警報・注意報(市町村等をまとめた地域等)"}
177
+ end
178
+
179
+ def warning_area_type4
180
+ warning.find{|w| w.type == "気象警報・注意報(市町村等)"}
181
+ end
182
+ end
183
+
184
+ def detect_with_area(area, weather_alert_clusters: nil)
185
+ weather_alert_clusters ||= JMAFeed::WeatherAlert.clusters.keys
186
+ warning_item = detect_warning_item_with_area(area)
187
+ return nil unless warning_item
188
+
189
+ warning_alerts = weather_alert_clusters.map{|cluster|
190
+ warning_item.public_send(cluster)
191
+ }.compact
192
+
193
+ info_item = detect_info_item_with_area(area)
194
+ info_alerts = info_item && weather_alert_clusters.map{|cluster|
195
+ info_item.public_send(cluster)
196
+ }.compact
197
+
198
+ AreaAlert.new(
199
+ entry: self,
200
+ area: area,
201
+ weather_warnings: warning_alerts.map{|w|
202
+ WeatherWarning.new(w, info: info_alerts&.find{|i| i.code == w.code})
203
+ }
204
+ )
205
+ end
206
+
207
+ def detect_warning_item_with_area(area)
208
+ items = if area.is_a?(JMACode::AreaInformationCity)
209
+ body.warning_area_type4.item
210
+ else
211
+ if area.used_by.include?("area_forecast_type3_in_weather_alert")
212
+ body.warning_area_type3.item
213
+ elsif area.used_by.include?("area_forecast_type2_in_weather_alert")
214
+ body.warning_area_type2.item
215
+ elsif area.used_by.include?("area_forecast_type1_in_weather_alert")
216
+ body.warning_area_type1.item
217
+ else
218
+ nil
219
+ end
220
+ end
221
+ items&.find{_1.area.code == area.code}
222
+ end
223
+
224
+ def detect_info_item_with_area(area)
225
+ # NOTE: 当該電文で特別警報・警報・注意報の全てが解除される場合、MeteorologicalInfos 部は省略される。 (気象警報・注意報(H27)_解説資料.pdf > 3-3)
226
+ return nil unless body.meteorological_infos
227
+
228
+ if area.is_a?(JMACode::AreaInformationCity)
229
+ body.meteorological_infos.time_series_info.item.find{_1.area.code == area.code}
230
+ else
231
+ nil
232
+ end
233
+ end
234
+
235
+ class AreaAlert < Struct.new(:entry, :area, :weather_warnings, keyword_init: true)
236
+ end
237
+
238
+ class WeatherWarning < DelegateClass(WarningItemKind)
239
+ attr_reader :info
240
+ def initialize(warning, info:)
241
+ @warning = warning
242
+ @info = info
243
+ super(warning)
244
+ end
245
+
246
+ def cluster
247
+ weather_alert.cluster
248
+ end
249
+
250
+ def risk_level
251
+ weather_alert.risk_level
63
252
  end
64
253
 
65
- def warning_district_1st
66
- warning[1]
254
+ def attentions
255
+ attention&.note
67
256
  end
68
257
 
69
- def warning_district_aggregated
70
- warning[2]
258
+ def additions
259
+ addition&.note
71
260
  end
72
261
 
73
- def warning_district_2nd
74
- warning[3]
262
+ def metrics
263
+ info&.weather_alert_metrics
75
264
  end
76
265
  end
77
266
  end
@@ -0,0 +1,135 @@
1
+ # 地震情報(震源・震度に関する情報)
2
+ # https://xml.kishou.go.jp/tec_material.html
3
+ # 電文毎の解説資料 > 解説資料セットzip > 地震火山関連_解説資料.pdf > (ウ)地震情報(震源・震度に関する情報)
4
+ #
5
+ class JMAFeed::VXSE53 < JMAFeed::ReportEntry
6
+ xml_node :head do
7
+ text_node :event_id, with_name: "EventID"
8
+ end
9
+
10
+ xml_node :body do
11
+ xml_node :earthquake, type: "JMAFeed::JMX::Earthquake"
12
+
13
+ xml_node :intensity do
14
+ xml_node :observation do
15
+ xml_node :code_define do
16
+ text_node :type, collection: true do
17
+ xml_attribute :xpath
18
+ end
19
+ end
20
+
21
+ text_node :max_int
22
+
23
+ xml_node_collection :pref do
24
+ text_node :name
25
+ text_node :code
26
+ xml_node :max_int, type: "JMAFeed::JMX::EarthquakeIntensity"
27
+ text_node :revise
28
+ xml_node_collection :area do
29
+ text_node :name
30
+ text_node :code
31
+ xml_node :max_int, type: "JMAFeed::JMX::EarthquakeIntensity"
32
+ text_node :revise
33
+ xml_node_collection :city do
34
+ text_node :name
35
+ text_node :code
36
+ xml_node :max_int, type: "JMAFeed::JMX::EarthquakeIntensity"
37
+ text_node :revise
38
+ text_node :condition
39
+ xml_node_collection :intensity_station do
40
+ text_node :name
41
+ text_node :code
42
+ xml_node :int, type: "JMAFeed::JMX::EarthquakeIntensity"
43
+ text_node :revise
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ xml_node :comments do
52
+ xml_node :forecast_comment do
53
+ xml_attribute :code_type
54
+ text_node :text
55
+ text_node :code
56
+ end
57
+ xml_node :var_comment do
58
+ xml_attribute :code_type
59
+ text_node :text
60
+ text_node :code
61
+ end
62
+ xml_node :free_form_comment do
63
+ text_node :text
64
+ end
65
+ end
66
+ end
67
+
68
+ def detect_with_area(area)
69
+ pref = detect_pref_with_area(area)
70
+ return nil unless pref
71
+
72
+ earthquake = body.earthquake
73
+ comments = [
74
+ body.comments&.forecast_comment&.text,
75
+ body.comments&.var_comment&.text,
76
+ body.comments&.free_form_comment&.text,
77
+ ].compact
78
+ if area.is_a?(JMACode::Prefecture)
79
+ AreaAlert.new(entry: self, area: area, intensity: pref, earthquake: earthquake, comments: comments)
80
+ else
81
+ local_area = detect_local_area_with_area(area, pref)
82
+ if local_area && area.is_a?(JMACode::AreaForecastLocalE)
83
+ AreaAlert.new(entry: self, area: area, intensity: local_area, earthquake: earthquake, comments: comments)
84
+ else
85
+ city = detect_city_with_area(area, local_area)
86
+ if city && area.is_a?(JMACode::AreaInformationCity)
87
+ AreaAlert.new(entry: self, area: area, intensity: city, earthquake: earthquake, comments: comments)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def detect_pref_with_area(area)
94
+ intensity = body.intensity
95
+ return nil unless intensity
96
+
97
+ prefs = intensity.observation.pref
98
+ pref_code = if area.is_a?(JMACode::Prefecture)
99
+ area.code
100
+ elsif area.is_a?(JMACode::AreaForecastLocalE)
101
+ area.prefecture_code
102
+ elsif area.is_a?(JMACode::AreaInformationCity)
103
+ area.prefecture_code
104
+ else
105
+ nil
106
+ end
107
+
108
+ prefs.find{|pr| pr.code == pref_code}
109
+ end
110
+
111
+ def detect_local_area_with_area(area, pref_node)
112
+ area_code = if area.is_a?(JMACode::AreaForecastLocalE)
113
+ area.code
114
+ elsif area.is_a?(JMACode::AreaInformationCity)
115
+ area.area_forecast_local_e_code
116
+ else
117
+ nil
118
+ end
119
+
120
+ pref_node.area.find{|ar| ar.code == area_code}
121
+ end
122
+
123
+ def detect_city_with_area(area, local_area_node)
124
+ city_code = if area.is_a?(JMACode::AreaInformationCity)
125
+ area.code
126
+ else
127
+ nil
128
+ end
129
+
130
+ local_area_node.city.find{|c| c.code == city_code}
131
+ end
132
+
133
+ class AreaAlert < Struct.new(:entry, :area, :intensity, :earthquake, :comments, keyword_init: true)
134
+ end
135
+ end
@@ -1,10 +1,13 @@
1
1
  require "nokogiri"
2
- require "giri"
3
2
 
4
- class JMAFeed::ReportEntry < Giri::BaseNode
3
+ class JMAFeed::ReportEntry < JMAFeed::BaseNode
5
4
  class << self
6
5
  def get(url)
7
6
  xml = Net::HTTP.get(URI.parse(url))
7
+ build(xml)
8
+ end
9
+
10
+ def build(xml)
8
11
  doc = Nokogiri::XML(xml)
9
12
  new(doc.root)
10
13
  end
@@ -25,16 +28,30 @@ class JMAFeed::ReportEntry < Giri::BaseNode
25
28
  text_node :info_type
26
29
  text_node :info_kind
27
30
  text_node :info_kind_version
31
+ xml_node :headline do
32
+ text_node :text
33
+ end
28
34
  end
29
35
  end
30
36
 
31
37
  require "jma_feed/report_entry/jmx/area"
38
+ require "jma_feed/report_entry/jmx/time_define"
32
39
  require "jma_feed/report_entry/jmx/significancy"
33
40
  require "jma_feed/report_entry/jmx/precipitation"
34
41
  require "jma_feed/report_entry/jmx/water_level"
35
42
  require "jma_feed/report_entry/jmx/discharge"
43
+ require "jma_feed/report_entry/jmx/wind_direction"
44
+ require "jma_feed/report_entry/jmx/wind_speed"
45
+ require "jma_feed/report_entry/jmx/wave_height"
46
+ require "jma_feed/report_entry/jmx/visibility"
47
+ require "jma_feed/report_entry/jmx/snow_fall_depth"
48
+ require "jma_feed/report_entry/jmx/humidity"
49
+ require "jma_feed/report_entry/jmx/tidal_level"
50
+ require "jma_feed/report_entry/jmx/earthquake"
51
+ require "jma_feed/report_entry/jmx/earthquake_intensity"
36
52
  require "jma_feed/report_entry/vprn50"
37
53
  require "jma_feed/report_entry/vpww54"
38
54
  require "jma_feed/report_entry/vxko"
39
55
  require "jma_feed/report_entry/vphw50"
40
56
  require "jma_feed/report_entry/vptaii"
57
+ require "jma_feed/report_entry/vxse53"
@@ -56,4 +56,8 @@ class JMAFeed::Result
56
56
  def entries
57
57
  doc.entries
58
58
  end
59
+
60
+ def all_entries
61
+ doc.all_entries
62
+ end
59
63
  end
@@ -34,8 +34,8 @@ class JMAFeed::ResultDoc
34
34
  text('link[@rel="related"]/@href')
35
35
  end
36
36
 
37
- def entries
38
- @entries ||= doc.xpath('/atom:feed/atom:entry', namespace).map do |entry|
37
+ def all_entries
38
+ @all_entries ||= doc.xpath('/atom:feed/atom:entry', namespace).map do |entry|
39
39
  JMAFeed::ResultEntry.new(
40
40
  title: entry.xpath('atom:title', namespace).text,
41
41
  link: entry.xpath('atom:link/@href', namespace).text,
@@ -46,4 +46,8 @@ class JMAFeed::ResultDoc
46
46
  )
47
47
  end
48
48
  end
49
+
50
+ def entries
51
+ all_entries.uniq(&:identity)
52
+ end
49
53
  end
@@ -9,16 +9,39 @@ class JMAFeed::ResultEntry < Struct.new(
9
9
  time, number, report_code, report_area_code = identifier.split('_')
10
10
  end
11
11
 
12
+ # 気象警報・注意報(H27)_解説資料.pdf
13
+ # ---------------
14
+ # 「前回電文」とは、参照中の電文(当該電文)と、
15
+ # - 情報名称(Control/Title)
16
+ # - 運用種別(Control/Status)
17
+ # - 発信官署(Control/EditorialOffice)
18
+ # が同一である電文の中で、発表時刻(Head/ReportDateTime)が当該電文の直近過去である電文を指す。
19
+ #
20
+ # idに記載されるファイル名アンダースコア区切りの2番目がStatusを意味しているのかどうかは2024Aug時点で不明
21
+ # Status="通常" の電文しか取得できておらず確証はないが、この値をidに含んでいる可能性が高いと見切っての実装
22
+ def identity
23
+ [report_code, report_status, report_area_code].join('_')
24
+ end
25
+
12
26
  def report_code
13
27
  @report_code ||= identifier_components[2]
14
28
  end
15
29
 
16
30
  def report
17
31
  return nil if report_code.nil?
18
- @report ||= JMAFeed::Report.latest.detect{_1.has_code?(report_code)}
32
+ @report ||= JMAFeed::Report.get.detect{_1.has_code?(report_code)}
19
33
  end
20
34
 
21
35
  def report_area_code
22
36
  @report_area_code ||= identifier_components[3]
23
37
  end
38
+
39
+ def report_status
40
+ @report_status ||= identifier_components[1]
41
+ end
42
+
43
+ def get_report_entry!
44
+ return unless report
45
+ report.report_entry_class.get(id)
46
+ end
24
47
  end
@@ -1,3 +1,3 @@
1
1
  module JMAFeed
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.7"
3
3
  end
data/lib/jma_feed.rb CHANGED
@@ -11,10 +11,14 @@ require "jma_feed/report"
11
11
  require "jma_feed/result"
12
12
  require "jma_feed/result_doc"
13
13
  require "jma_feed/result_entry"
14
- require "jma_feed/api"
15
- require "jma_feed/api/extra"
16
- require "jma_feed/api/regular"
14
+ require "jma_feed/atom"
15
+ require "jma_feed/atom/extra"
16
+ require "jma_feed/atom/regular"
17
+ require "jma_feed/atom/eqvol"
18
+ require "jma_feed/atom/other"
19
+ require "jma_feed/node/base_node"
17
20
  require "jma_feed/entity/weather_alert"
21
+ require "jma_feed/entity/weather_alert/metrics_item"
18
22
  require "jma_feed/entity/risk_level"
19
23
 
20
24
  require "jma_feed/report_entry"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jma_feed
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - metheglin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-08-09 00:00:00.000000000 Z
11
+ date: 2024-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -44,28 +44,28 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.0.4
47
+ version: 0.0.6
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.0.4
54
+ version: 0.0.6
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: giri
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.0.3
61
+ version: 0.0.5
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.0.3
68
+ version: 0.0.5
69
69
  description: JMA Feed
70
70
  email: pigmybank@gmail.com
71
71
  executables: []
@@ -75,23 +75,38 @@ files:
75
75
  - README.md
76
76
  - data/jmaxml_20240216_format_v1_3_hyo1.csv
77
77
  - lib/jma_feed.rb
78
- - lib/jma_feed/api.rb
79
- - lib/jma_feed/api/extra.rb
80
- - lib/jma_feed/api/regular.rb
78
+ - lib/jma_feed/atom.rb
79
+ - lib/jma_feed/atom/eqvol.rb
80
+ - lib/jma_feed/atom/extra.rb
81
+ - lib/jma_feed/atom/other.rb
82
+ - lib/jma_feed/atom/regular.rb
81
83
  - lib/jma_feed/entity/risk_level.rb
82
84
  - lib/jma_feed/entity/weather_alert.rb
85
+ - lib/jma_feed/entity/weather_alert/metrics_item.rb
86
+ - lib/jma_feed/node/base_node.rb
83
87
  - lib/jma_feed/report.rb
84
88
  - lib/jma_feed/report_entry.rb
85
89
  - lib/jma_feed/report_entry/jmx/area.rb
86
90
  - lib/jma_feed/report_entry/jmx/discharge.rb
91
+ - lib/jma_feed/report_entry/jmx/earthquake.rb
92
+ - lib/jma_feed/report_entry/jmx/earthquake_intensity.rb
93
+ - lib/jma_feed/report_entry/jmx/humidity.rb
87
94
  - lib/jma_feed/report_entry/jmx/precipitation.rb
88
95
  - lib/jma_feed/report_entry/jmx/significancy.rb
96
+ - lib/jma_feed/report_entry/jmx/snow_fall_depth.rb
97
+ - lib/jma_feed/report_entry/jmx/tidal_level.rb
98
+ - lib/jma_feed/report_entry/jmx/time_define.rb
99
+ - lib/jma_feed/report_entry/jmx/visibility.rb
89
100
  - lib/jma_feed/report_entry/jmx/water_level.rb
101
+ - lib/jma_feed/report_entry/jmx/wave_height.rb
102
+ - lib/jma_feed/report_entry/jmx/wind_direction.rb
103
+ - lib/jma_feed/report_entry/jmx/wind_speed.rb
90
104
  - lib/jma_feed/report_entry/vphw50.rb
91
105
  - lib/jma_feed/report_entry/vprn50.rb
92
106
  - lib/jma_feed/report_entry/vptaii.rb
93
107
  - lib/jma_feed/report_entry/vpww54.rb
94
108
  - lib/jma_feed/report_entry/vxko.rb
109
+ - lib/jma_feed/report_entry/vxse53.rb
95
110
  - lib/jma_feed/result.rb
96
111
  - lib/jma_feed/result_doc.rb
97
112
  - lib/jma_feed/result_entry.rb