#encoding: utf-8 require_relative "epicenter_code" require_relative "area_code" # 緊急地震速報パーサ # Author:: mmasaki # # 高度利用者向け緊急地震速報コード電文フォーマットを扱う為のライブラリです。 # http://eew.mizar.jp/excodeformat を元に作成しました。 # # str = <" end def ==(other) fastcast == other.fastcast end def <=>(other) __id__ <=> other.__id__ end # 電文種別コード def type case @fastcast[0, 2] when "35" "最大予測震度のみの高度利用者向け緊急地震速報" when "36" "マグニチュード、最大予測震度及び主要動到達予測時刻の高度利用者向け緊急地震速報(B-Δ法、テリトリ法)" when "37" "マグニチュード、最大予測震度及び主要動到達予測時刻の高度利用者向け緊急地震速報(グリッドサーチ法、EPOS自動処理手法)" when "39" "キャンセル報" else raise Error, "電文の形式が不正です(電文種別コード)" end end # 発信官署 def from case @fastcast[3, 2] when "01" "札幌" when "02" "仙台" when "03" "東京" when "04" "大阪" when "05" "福岡" when "06" "沖縄" else raise Error, "電文の形式が不正です(発信官署)" end end # 訓練等の識別符 def drill_type case @fastcast[6, 2] when "00" "通常" when "01" "訓練" when "10" "取り消し" when "11" "訓練取り消し" when "20" "参考情報またはテキスト" when "30" "コード部のみの配信試験" else raise Error, "電文の形式が不正です(識別符)" end end # 訓練かどうか def drill? case @fastcast[6, 2] when "00", "10", "20" return false when "01", "11", "30" return true else raise Error, "電文の形式が不正です(識別符)" end end # 電文の発表時刻のTimeオブジェクトを返します。 def report_time Time.local("20" + @fastcast[9, 2], @fastcast[11, 2], @fastcast[13, 2], @fastcast[15, 2], @fastcast[17, 2], @fastcast[19, 2]) end # 電文がこの電文を含め何通あるか(Integer) def number_of_telegram number_of_telegram = @fastcast[23] return Integer(number_of_telegram, 10) rescue ArgumentError raise Error, "電文の形式が不正です" end # コードが続くかどうか def continue? case @fastcast[24] when "1" true when "0" false else raise Error, "電文の形式が不正です" end end # 地震発生時刻もしくは地震検知時刻のTimeオブジェクトを返します。 def earthquake_time Time.local("20" + @fastcast[26, 2], @fastcast[28, 2], @fastcast[30, 2], @fastcast[32, 2], @fastcast[34, 2], @fastcast[36, 2]) end # 地震識別番号(String) def id id = @fastcast[41, 14] Integer(id, 10) # verify return id rescue ArgumentError raise Error, "電文の形式が不正です(地震識別番号: #{id})" end def __id__ id + number.to_s end # 発表状況(訂正等)の指示 def status case @fastcast[59] when "0" "通常発表" when "6" "情報内容の訂正" when "7" "キャンセルを誤って発表した場合の訂正" when "8" "訂正事項を盛り込んだ最終の高度利用者向け緊急地震速報" when "9" "最終の高度利用者向け緊急地震速報" when "/" "未設定" else raise Error, "電文の形式が不正です" end end # 第1報であればtrueを、そうでなければfalseを返します。 def first? return true if self.number == 1 return false end # 最終報であればtrueを、そうでなければfalseを返します。 def final? case @fastcast[59] when "9" true when "0", "6", "7", "8", "/" false else raise Error, "電文の形式が不正です" end end # 発表する高度利用者向け緊急地震速報の番号(地震単位での通番)(Integer) def number number = @fastcast[60, 2] return Integer(number, 10) rescue ArgumentError raise Error, "電文の形式が不正です(高度利用者向け緊急地震速報の番号)" end alias :revision :number # 震央の名称 def epicenter code = @fastcast[86, 3] code = Integer(code, 10) EpicenterCode.fetch(code) rescue KeyError raise Error, "電文の形式が不正です(震央地名コード: #{code})" rescue ArgumentError raise Error, "電文の形式が不正です(震央の名称)" end # 震央の位置 def position position = @fastcast[90, 10] if position == "//// /////" "不明又は未設定" else raise Error, "電文の形式が不正です(震央の位置)" unless position.match(/N\d{3} E\d{4}/) position.insert(3, ".").insert(10, ".") end end # 震源の深さ(単位 km) def depth depth = @fastcast[101, 3] if depth == "///" return "不明又は未設定" else return Integer(depth, 10) end rescue ArgumentError raise Error, "電文の形式が不正です(震源の深さ)" end # マグニチュード # マグニチュードが不明又は未設定である場合は"不明又は未設定"を返します。 # そうでなければ、マグニチュードをFloatで返します。 def magnitude magnitude = @fastcast[105, 2] if magnitude == "//" return "不明又は未設定" else return Float(magnitude[0] + "." + magnitude[1]) end rescue ArgumentError raise Error, "電文の形式が不正です(マグニチュード)" end # 電文フォーマットの震度を文字列に変換 def to_seismic_intensity(str) case str when "//" "不明又は未設定" when "01" "1" when "02" "2" when "03" "3" when "04" "4" when "5-" "5弱" when "5+" "5強" when "6-" "6弱" when "6+" "6強" when "07" "7" else raise Error, "電文の形式が不正です(震度)" end end # 最大予測震度 def seismic_intensity to_seismic_intensity(@fastcast[108, 2]) end # 震央の確からしさ def probability_of_position case @fastcast[113] when "1" "P波/S波レベル越え、またはテリトリー法(1点)[気象庁データ]" when "2" "テリトリー法(2点)[気象庁データ]" when "3" "グリッドサーチ法(3点/4点)[気象庁データ]" when "4" "グリッドサーチ法(5点)[気象庁データ]" when "5" "防災科研システム(4点以下、または精度情報なし)[防災科学技術研究所データ]" when "6" "防災科研システム(5点以上)[防災科学技術研究所データ]" when "7" "EPOS(海域[観測網外])[気象庁データ]" when "8" "EPOS(内陸[観測網内])[気象庁データ]" when "9" "予備" when "/" "不明又は未設定" else raise Error, "電文の形式が不正です(震央の確からしさ)" end end # 震源の深さの確からしさ def probability_of_depth case @fastcast[114] when "1" "P波/S波レベル越え、またはテリトリー法(1点)[気象庁データ]" when "2" "テリトリー法(2点)[気象庁データ]" when "3" "グリッドサーチ法(3点/4点)[気象庁データ]" when "4" "グリッドサーチ法(5点)[気象庁データ]" when "5" "防災科研システム(4点以下、または精度情報なし)[防災科学技術研究所データ]" when "6" "防災科研システム(5点以上)[防災科学技術研究所データ]" when "7" "EPOS(海域[観測網外])[気象庁データ]" when "8" "EPOS(内陸[観測網内])[気象庁データ]" when "9" "予備" when "/", "0" "不明又は未設定" else raise Error, "電文の形式が不正です(震源の深さの確からしさ)" end end # マグニチュードの確からしさ def probability_of_magnitude case @fastcast[115] when "1" "未定義" when "2" "防災科研システム[防災科学技術研究所データ]" when "3" "全点P相(最大5点)[気象庁データ]" when "4" "P相/全相混在[気象庁データ]" when "5" "全点全相(最大5点)[気象庁データ]" when "6" "EPOS[気象庁データ]" when "7" "未定義" when "8" "P波/S波レベル越え[気象庁データ]" when "9" "予備" when "/", "0" "不明又は未設定" else raise Error, "電文の形式が不正です(マグニチュードの確からしさ)" end end # 震央の確からしさ(※気象庁の部内システムでの利用) def probability_of_position_jma case @fastcast[116] when "1" "P波/S波レベル越え又はテリトリー法(1点)[気象庁データ]" when "2" "テリトリー法(2点)[気象庁データ]" when "3" "グリッドサーチ法(3点/4点)[気象庁データ]" when "4" "グリッドサーチ法(5点)[気象庁データ]" when "/" "不明又は未設定" when "5".."9", "0" "未定義" else raise Error, "電文の形式が不正です(震央の確からしさ[気象庁の部内システムでの利用])" end end # 震源の深さの確からしさ(※気象庁の部内システムでの利用) def probability_of_depth_jma case @fastcast[117] when "1" "P波/S波レベル越え又はテリトリー法(1点)[気象庁データ]" when "2" "テリトリー法(2点)[気象庁データ]" when "3" "グリッドサーチ法(3点/4点)[気象庁データ]" when "4" "グリッドサーチ法(5点)[気象庁データ]" when "/" "不明又は未設定" when "5".."9", "0" "未定義" else raise Error, "電文の形式が不正です(震源の深さの確からしさ[気象庁の部内システムでの利用])" end end # 震央位置の海陸判定 def land_or_sea case @fastcast[121] when "0" "陸域" when "1" "海域" when "/" "不明又は未設定" when "2".."9" "未定義" else raise Error, "電文の形式が不正です(震央位置の海陸判定)" end end # 警報を含む内容であればtrue、そうでなければfalse def warning? case @fastcast[122] when "0", "/", "2".."9" false when "1" true else raise Error, "電文の形式が不正です(警報の判別)" end end # 最大予測震度の変化 def change case @fastcast[129] when "0" "ほとんど変化無し" when "1" "最大予測震度が1.0以上大きくなった" when "2" "最大予測震度が1.0以上小さくなった" when "3".."9" "未定義" when "/" "不明又は未設定" else raise Error, "電文の形式が不正です(最大予測震度の変化)" end end # 最大予測震度に変化があったかどうか def changed? case @fastcast[129] when "0", "3".."9", "/" return false when "1", "2" return true end end # 最大予測震度の変化の理由 def reason_of_change case @fastcast[130] when "0" "変化無し" when "1" "主としてMが変化したため(1.0以上)" when "2" "主として震源位置が変化したため(10.0km以上)" when "3" "M及び震源位置が変化したため" when "4" "震源の深さが変化したため" when "/" "不明又は未設定" when "5".."9" "未定義" else raise Error, "電文の形式が不正です(最大予測震度の変化の理由)" end end # EBIを含むかどうか def has_ebi? if @fastcast[135, 3] == "EBI" return true else return false end end # 地域毎の警報の判別、最大予測震度及び主要動到達予測時刻 # EBIがあればHashを格納したArrayを、なければ空のArrayを返します。Hashに格納されるkeyとvalueはそれぞれ次のようになっています。 # :area_name 地域名称 # :intensity 最大予測震度 # :arrival_time 予想到達時刻のTimeオブジェクト。既に到達している場合はnil # :warning 警報を含んでいればtrue、含んでいなければfalse、電文にこの項目が設定されていなければnil # :arrival 既に到達していればtrue、そうでなければfalse、電文にこの項目が設定されていなければnil def ebi data = [] return data unless @fastcast[135, 3] == "EBI" i = 139 while i + 20 < @fastcast.bytesize local = {} local[:area_code] = @fastcast[i, 3].to_i local[:area_name] = AreaCode[local[:area_code]] # 地域名称 raise Error, "電文の形式が不正でです(地域名称[EBI])" unless local[:area_name] if @fastcast[i+7, 2] == "//" local[:intensity] = "#{to_seismic_intensity(@fastcast[i+5, 2])}以上" # 最大予測震度 elsif @fastcast[i+5, 2] == @fastcast[i+7, 2] local[:intensity] = "#{to_seismic_intensity(@fastcast[i+5, 2])}" else local[:intensity] = "#{to_seismic_intensity(@fastcast[i+7, 2])}から#{to_seismic_intensity(@fastcast[i+5, 2])}" end if @fastcast[i+10, 6] == "//////" local[:arrival_time] = nil # 予想到達時刻 else local[:arrival_time] = Time.local("20" + @fastcast[26, 2], @fastcast[28, 2], @fastcast[30, 2], @fastcast[i+10, 2], @fastcast[i+12, 2], @fastcast[i+14, 2]) end case @fastcast[i+17] when "0" local[:warning] = false # 警報を含むかどうか when "1" local[:warning] = true when "/", "2".."9" local[:warning] = nil else raise Error, "電文の形式が不正でです(警報の判別[EBI])" end case @fastcast[i+18] when "0" local[:arrival] = false # 既に到達しているかどうか when "1" local[:arrival] = true when "/", "2".."9" local[:arrival] = nil else raise Error, "電文の形式が不正でです(主要動の到達予測状況[EBI])" end data << local i += 20 end data end end end EEWParser = EEW::Parser if __FILE__ == $PROGRAM_NAME # テスト str = <