見出し画像

GAS で地震情報を取得・送信する

 久々に大きな地震がありました。東日本大震災からもう 10 年、いろいろあったなあと静かに振り返り、亡くなった人に想いをはせたいというのは、人間のわがまま何でしょうけれど、ね。

 現在私は、管理系の部署にいます。震災時にみんなが無事であるか・無事でないなら助けられることがあるか・それをどう実行すればいいかなどを企画提案実行するお仕事をする部署です。

 恥ずかしながら弊社ではその体制がまだできておらず、緊急連絡網はおろか、災害時に誰にどのような方法で報告をするのかが確立していません。結果、情報が一つ所に集まらず、なんとも言えない気持ちで月曜日を迎えたのでした。結果的には誰も怪我していないし、何人かの家の壁や床が大変なことになったくらいだったのですけれど。

 ああよかったよかった…で済ませるわけには行きません。バックオフィサーとして、システム屋としての矜持ってもんがあります。なので、雑でも良いから、なんとかしましょう(こういうのを hack っていうんですよ)。

1.目的

 今回の究極の目的は、安否を確認できることです。じゃあイイカンジの安否確認システムを打ち込めばエエやろ…となるのですが、そんなすぐすぐ予算が下りるなら苦労はしません。

 じゃあどうするか。目標をちょっと下げて、連絡がつかない or 怪我等をした人がいることを、管理部門はもちろん、全員がわかるようにできることとしましょうか。

2.具体的には

 震災直後に会社への連絡なんてのはしなくて良いんです。後回しで良いのです。まずは目の前の割れた皿や家具の片付け、家族や大事の人の無事を確認するというのが優先されます。その後、あるいは、その最中に「あ、会社にも連絡しなきゃね」と思ってもらえることを第一、第二には「あれ、誰と誰に連絡しなきゃいけないんだっけ…?」と悩ませないこと。この2つを充足させるためには「会社から社員に通知を出し、それに反応してくれ」とお願いすることでしょう。じゃあ、それを実現しましょう。

3.実現方法

 大きな地震が起こったとき自動で発火して「返信してね!」ということが社員へ伝わること。

 そ返信があれば、まずは「無事な人リスト」ができます。在職者からその人達を外せば「連絡がつかない or 通信ができない状況にいる人リスト」ができあがりますよね。その人達にアクションを起こせれば、最終的に安否確認できるでしょうし、何かを提供・支援することができます。会社としてもハッピーです。

 「大きな地震が起こったとき自動で発火」はどうやって感知しましょうか。

 気象庁が用意してくれているこちらを使いましょう。ありがたいことに、天気情報はじめとして、災害情報を配信してくれています。これを定期的に取得・チェックして、大地震のときだけ通知を出すというスクリプトを作りましょう。Google App Script (GAS)で。

4.ソースコード

/**
* 気象庁防災情報XMLのうち、「地震火山」情報を取得し、
* マグニチュードが 5.5 以上(中地震程度)の場合のみ
* 通知を送信する
* 
* @author hrfr84
* @version 1.0
*/
function sendEQAlert() {
 
 // constants
 // ネームスペース
 const eqvolxm_uri = 'http://www.data.jma.go.jp/developer/xml/feed/eqvol.xml';
 const w3org05_uri = 'http://www.w3.org/2005/Atom';
 const seismol_uri = 'http://xml.kishou.go.jp/jmaxml1/body/seismology1/';
 const element_uri = 'http://xml.kishou.go.jp/jmaxml1/elementBasis1/';
 const informa_uri = 'http://xml.kishou.go.jp/jmaxml1/informationBasis1/';
 // 
 const earthqu_str = '震源・震度に関する情報';
 const diffmin_int = 660000;   // 11分をミリ秒で
 const magnitu_num = 5.5;      // 通知下限マグニチュード
 // variables
 // 地震火山 XML 関連
 let eqvolxm_conText, eqvolxm_document, eqvolxm_element;
 // 詳細情報 XML 関連
 let vxse53_conText, vxse53_document, vxse53_element;
 // ネームスペース
 let w3org05_ns, seismol_ns, element_ns, informa_ns;
 // 配列・配列要素
 let entryfd_array = [], entrynd_array = [], entrynd_elem;
 // 文字・数字
 let trigger_date, title_str, urlid_str, magnit_str;
 let eqinfo_str, hypoce_str, tunami_str;
 // XML 取得
 eqvolxm_conText = UrlFetchApp.fetch(eqvolxm_uri).getContentText();
 eqvolxm_document = XmlService.parse(eqvolxm_conText);
 eqvolxm_element = eqvolxm_document.getRootElement();
 // entry ノードをすべて entrydf_array 配列に push する
 entrynd_array = eqvolxm_element.getDescendants();
 for(var i in entrynd_array) {
   entrynd_elem = entrynd_array[i].asElement();
   if ( entrynd_elem != null && entrynd_elem.getName() == 'entry') {
       entryfd_array.push(entrynd_elem);
   }
 }
 // xmlns 定義
 w3org05_ns = XmlService.getNamespace(w3org05_uri);
 seismol_ns = XmlService.getNamespace(seismol_uri);
 element_ns = XmlService.getNamespace(element_uri);
 informa_ns = XmlService.getNamespace(informa_uri);
 
 // トリガー発動時刻取得(ミリ秒)
 trigger_date = new Date().getTime();
 // entry の一つ一つの要素を true になるまで見る
 entryfd_array.some(function(fdElement, j) {
   
   // title と updated を取得
   title_str = fdElement.getChild('title', w3org05_ns).getText();
   updat_date = (new Date(fdElement.getChild('updated',w3org05_ns).getText())).getTime();
    // タイトルが「震源・震度に関する情報」 かつ 11分前から現在に更新されたものを取得する
   if(earthqu_str == title_str){
    if(trigger_date - diffmin_int <= updat_date){
 
       // 詳細情報を取得
       urlid_str = fdElement.getChild('id', w3org05_ns).getText(); 
       vxse53_conText = UrlFetchApp.fetch(urlid_str).getContentText();
       vxse53_document = XmlService.parse(vxse53_conText);
       vxse53_element = vxse53_document.getRootElement();
       // マグニチュード取得
       magnit_str = vxse53_element.getChild('Body',seismol_ns).getChild('Earthquake',seismol_ns)
                   .getChild('Magnitude',element_ns).getText();
       // マグニチュードが規定値以上なら情報取得
       if(magnit_str >= magnitu_num){
         eqinfo_str = vxse53_element.getChild('Head',informa_ns).getChild('Headline',informa_ns)
                     .getChild('Text',informa_ns).getText(); 
         hypoce_str = vxse53_element.getChild('Body',seismol_ns).getChild('Earthquake',seismol_ns)
                     .getChild('Hypocenter',seismol_ns).getChild('Area',seismol_ns)
                     .getChild('Name',seismol_ns).getText(); 
         tunami_str = vxse53_element.getChild('Body',seismol_ns).getChild('Comments',seismol_ns)
                     .getChild('ForecastComment',seismol_ns).getChild('Text',seismol_ns)
                     .getText(); 
         
         // メッセージ作成
         sendmsg_str = "【" + eqinfo_str + "】\r\n" + 
                     "マグニチュード: " + magnit_str + "\r\n" + 
                     "震源地: " + hypoce_str + "\r\n" +
                     tunami_str + "\r\n\r\n" +
                     "マグニチュード " + magnitu_num + " 以上の強い地震が発生しました\r\n" +
                     "無事な方は無事であることを、そうでない方は状況を、このスレッドに返信してください\r\n\r\n";
         
         // メッセージ送信
         sendSlack(sendmsg_str);
         sendLine(sendmsg_str);
       }else{
         Logger.log("マグニチュードが " + magnit_str + " のため処理せず");
       }
     }else{
       // true を返すことで some から抜ける
       return true;
     }
   }
 });
}

※ コードの解説は今の所するつもりはありませんが、質問等あればコメントください。汚いのはごめんなさいごめんなさい。

 これを GAS の環境にコピペして実行すると、とりあえず動きます。( sendSlack() / sendLine() は適当に作成してください)10分おきに動作するようにトリガーを設定しておけば、有事の際に動くはずです。

 動作確認は、2021年2月13日の詳細 XML とダミーの XML を使用して検証はしてみました。災害時に想定した XML が流れてくるかがわからないので、きちんと動くかはわかりません。あくまで気休めです。

5.実際の通知

 Slack に通知するとこんな感じになります。

画像1

 LINE WORKS に通知するとこんな感じ。

画像2

※ 今回はテスト用にマグニチュード 3.5 以上での通知です

6.目指したこと

 この仕組はつまり、「大地震後に会社へ一報入れること・どうやって一報を入れるかのリマインダ」です。地震が起こったときに、大抵の人はスマホを見ると思います。そのときに「あ、会社からなんか来てる」と気づいてもらえて、アクションを起こしてもらえればまずは良いんです。(震災直後って、結構頭悪くなってる事が多いので、ガイドがあることが大事です)

 誰かが無事であることに安心できたり、無事じゃ無いことに対してアクションを起こせる、そのトリガーになれば良いかな、とおもいます。

 ソースは汚いですが、とりあえず汎用ですぐ使えるはずです。よければ使ってやってください。

​※ 2021/3/3
 北海道沖地震で M5.5 をマークしました。実データでもきちんと稼働できることを確認しましたので、使えないということはなさそうです。


この記事が気に入ったらサポートをしてみませんか?