趣味の開発ノート

ITの学習やプログラミング・ノーコードアプリ開発のことなど。

【GAS】取得した気象庁の天気予報JSONから読みやすいテキストを作ろう

GASでLINE Botを制作していきます。題材は「気象庁のデータを取得し、天気予報を通知してくれるLINE Bot」です。

前回は、JSONを取得してデータの中身を見てみるところまでをやりました。今回の記事では、そこから情報を取り出して読みやすいテキストにしていきたいと思います。

▼前回の記事はこちら

labo.agrifeel.net

前回のおさらい

こちらの記事で紹介されているものと同じLINE Botを制作していきます。

www.nakakamado.com

かんたんに要件を書き出すと、以下の通りです。

  • 気象庁の天気予報の情報をテキスト取得する
  • LINE Messaging APIでプッシュメッセージを送る
    • GASのトリガー設定で指定時間に実行

前回は、GASを動かして気象庁の「天気予報」の情報を取得するところまでやりました。僕は十勝在住なので、十勝地域の情報が得られる釧路・根室地域の予報区コード014100を使いました。このコードをぜひ皆さんの地域のコードに変更してみてくださいね。

ここまでのスクリプトはこちらです。

function getForecastData() {

  const url = "https://www.jma.go.jp/bosai/forecast/data/forecast/014100.json"; // 釧路・根室地方
 
  const res = UrlFetchApp.fetch(url);
  const data = JSON.parse(res.getContentText());
 
  console.log(data);
}

作りたいテキストを考える

まずは、気象庁のAPIで得られたデータのうち、どんな情報を用いてどんなテキストを作りたいかを整理しておきましょう。

ここでは「今日・明日・明後日の天気予報」を元に、以下のようなテキストを作ってみることにします。

報告者:釧路地方気象台
報告日時:2024-02-11 11:00:00

■十勝地方の天気予報
今日の天気:晴れ(北西の風)
明日明日の天気:晴れ 時々 くもり(西の風)
明後日:晴れ 時々 くもり(西の風)

■降水確率
02/11 12時 | 0%
02/11 18時 | 10%
02/12 00時 | 10%
02/12 06時 | 10%
02/12 12時 | 10%
02/12 18時 | 0%

■十勝地方の気温
02/11 09時 | 1℃
02/11 00時 | 1℃
02/12 00時 | -12℃
02/12 09時 | 2℃

このテキストは、4つの段落に分かれて情報を記載しています。

  1. 基本情報(気象台名と報告日時)
  2. 今日・明日・明後日の天気予報
  3. 明日までの時間帯ごとの降水確率
  4. 明日までの時間帯ごとの気温

では、この順番に沿って実際にテキストを作っていきましょう。

段落ごとにテキストを作っていく

基本情報

まずは、気象台名と報告日時といった基本情報をテキストにしてみます。

スクリプトを以下のように書き換えてみましょう。APIを使って気象データを取得して、変数dataにオブジェクトとして格納するところまでは、前回と全く同じです。

function getForecastData() {

  // 気象データ取得
  const url = "https://www.jma.go.jp/bosai/forecast/data/forecast/014100.json"; // 釧路・根室地方
  const res = UrlFetchApp.fetch(url);
  const data = JSON.parse(res.getContentText());

  // 基本情報
  const publisher = data[0].publishingOffice;
  const reportDatetimeObj = new Date(data[0].reportDatetime); // 文字列で取得したものを一旦Dateオブジェクト化する
  const reportDatetimeStr = Utilities.formatDate(reportDatetimeObj, 'JST', 'yyyy/MM/dd HH時'); // Dateオブジェクトを文字列としてフォーマットする

  let msg1 = `報告者:${publisher}\n報告日時:${reportDatetimeStr}\n`

  console.log(msg1)

このスクリプトを実行すると、このようにログ出力されます。

要点だけ解説しますね。ここでは、ゲットしたdataオブジェクトから、以下のようにして必要な情報をゲットしています。

  • 気象台名 data[0].publishingOffice
  • 報告日時 data[0].reportDatetime

ただし報告日時については、このままだと表記が決まった文字列型として取得してしまうため、非常に扱いづらいです。なのでまずはDateオブジェクトに変換してから、それをUtilitiesクラスのformatDate()メソッドで読みやすいフォーマットに整える、という一工夫をしています。

そして、テキストとして整えたものを変数msg1に格納しておきます。

▼参考

tetsuooo.net

今日・明日・明後日の天気予報

次に、スクリプトを以下のように書き換えてみましょう。先ほどのスクリプトに「// 今日・明日・明後日の天気」以降を追記します。

function getForecastData() {

  // 気象データ取得
  // (省略)

  // 今日・明日・明後日の天気
  const weatherDataObj = data[0].timeSeries[0];
  const areaName = weatherDataObj.areas[2].area.name; // 十勝地方
  
  const weathersArr = weatherDataObj.areas[2].weathers;
  const windsArr = weatherDataObj.areas[2].winds;
  // const wavesArr = weatherDataObj.areas[2].waves;

  const labelArr = ['今日の天気', '明日明日の天気', '明後日'];

  let msg2 = `■${areaName}の天気予報\n`
  for(const [i, v] of labelArr.entries()) {
    msg2 += `${v}:${weathersArr[i]}(${windsArr[i].split(' ')[0]})\n` // 例:今日の天気:晴れ(北西の風)
  };

  console.log(msg2)
}

このスクリプトを実行すると、このようにログ出力されます。

今日・明日・明後日の天気予報のデータは、data[0].timeSeries[0]の中にまとめて入っています。さらにその地域のエリアごとに分かれた配列として入っているので、以下のようにareas[2]のインデックス番号で自身のエリアを指定して、各情報を取得します。

  • 天気予報 data[0].timeSeries[0].areas[2].weathers
  • 風の予報 data[0].timeSeries[0].areas[2].winds 
  • 波の予報 data[0].timeSeries[0].areas[2].waves

あとは、良い感じにテキストを整えて、変数msg2に格納しておきます。今回はわかりやすいように変数を分けていますが、もちろん先ほどのmsg1の末尾に付け加える形でテキストを作っていっても問題ありません。

明日までの時間帯ごとの降水確率

次に、スクリプトを以下のように書き換えてみましょう。先ほどのスクリプトに「// 降水確率」以降を追記します。

function getForecastData() {

  // 気象データ取得
  // (省略)

  // 降水確率
  const rainDataObj = data[0].timeSeries[1];
  const rainTimeDefinesArr = rainDataObj.timeDefines;
  const rainPopsArr = rainDataObj.areas[2].pops;
  
  let msg3 = '■降水確率\n'
  for(const [i, v] of rainTimeDefinesArr.entries()) {
    const datetimeStr = Utilities.formatDate(new Date(v), 'JST', 'MM/dd HH時')
    msg3 += `${datetimeStr} | ${rainPopsArr[i]}%\n` // 例:02/11 12時 | 0%
  };

  console.log(msg3)
}

このスクリプトを実行すると、このようにログ出力されます。

降水確率に関するデータは、data[0].timeSeries[1]の中に入っています。これも先ほどと同じくエリアごとに配列で分かれているので、同じ要領でエリアのインデックス番号を指定して取得します。

  • 降水確率の予報日時 data[0].timeSeries[1].timeDefines
  • 各日時における降水確率 data[0].timeSeries[1].areas[2].pops

ここでも、日時情報をテキストとして整えるために、Dateオブジェクトに変換してUtilities.formatDate()メソッドを使いました。

明日までの時間帯ごとの気温

さて、あともう一息です。スクリプトを以下のように書き換えてみましょう。先ほどのスクリプトに「// 気温」以降を追記します。

function getForecastData() {

  // 気象データ取得
  // (省略)

  // 気温
  const tempDataObj = data[0].timeSeries[2];
  const cityName = tempDataObj.areas[2].area.name; // 帯広
  const tempTimeDefinesArr = tempDataObj.timeDefines;
  const tempsArr = tempDataObj.areas[2].temps;
  
  let msg4 = `■${cityName}の気温\n`
  for(const [i, v] of tempTimeDefinesArr.entries()) {
    const datetimeStr = Utilities.formatDate(new Date(v), 'JST', 'MM/dd HH時')
    msg4 += `${datetimeStr} | ${tempsArr[i]}\n` // 例:02/11 09時 | 1℃
  };

  console.log(msg4)

}

このスクリプトを実行すると、このようにログ出力されます。

気温に関するデータは、data[0].timeSeries[2]の中に入っています。エリアや日時のフォーマットについても、先ほどと同様です。

  • 気温の予報日時 data[0].timeSeries[2].timeDefines
  • 各日時における予報気温 data[0].timeSeries[1].areas[2].temps

これでようやく、必要なテキストを4つの変数msg1msg4としてゲットすることができました。

すべて繋げたテキストを表示する

では最後に、すべてのテキストを繋げてログ表示させてみましょう。

function getForecastData() {

  // 気象データ取得
  // (省略)

  // 今日・明日・明後日の天気
  // (省略)

  // 降水確率
  // (省略)  
  
  // 気温
  // (省略)

  // テキストを繋げる
  const msg = `${msg1}\n${msg2}\n${msg3}\n${msg4}`;
  console.log(msg)

}

このスクリプトを実行すると、このようにログ出力されます。

ちょっと大変でしたが、なんとか目的のテキストをゲットすることができましたね!

全体のスクリプトについては記事の末尾に記載していますので、必要に応じて参考にしてみてください。

おわりに

ここまで、気象庁から取得したデータから必要な情報を取り出し、LINEに送るためのテキストを作成してきました!

前回記事では「今日・明日・明後日の天気予報」の他に「7日先までの週間天気予報」データも取得できることを確認しましたが、今回はその情報は使っていません。やり方は同じなので、気になる方はぜひ試してみてくださいね。

では次回、LINE Botを作ってそちらにプッシュメッセージを送る実装をしていきたいと思います。

参考

今回作成したgetForeastData()関数の全体のスクリプトです。

function getForecastData() {

  // 気象データ取得
  const url = "https://www.jma.go.jp/bosai/forecast/data/forecast/014100.json"; // 釧路・根室地方
  const res = UrlFetchApp.fetch(url);
  const data = JSON.parse(res.getContentText());

  // 基本情報
  const publisher = data[0].publishingOffice;
  const reportDatetimeObj = new Date(data[0].reportDatetime); // 文字列で取得したものを一旦Dateオブジェクト化する
  const reportDatetimeStr = Utilities.formatDate(reportDatetimeObj, 'JST', 'yyyy/MM/dd HH時'); // Dateオブジェクトを文字列としてフォーマットする

  let msg1 = `報告者:${publisher}\n報告日時:${reportDatetimeStr}\n`


  // 今日・明日・明後日の天気
  const weatherDataObj = data[0].timeSeries[0];
  const areaName = weatherDataObj.areas[2].area.name; // 十勝地方
  
  const weathersArr = weatherDataObj.areas[2].weathers;
  const windsArr = weatherDataObj.areas[2].winds;
  // const wavesArr = weatherDataObj.areas[2].waves;

  const labelArr = ['今日の天気', '明日明日の天気', '明後日'];

  let msg2 = `■${areaName}の天気予報\n`
  for(const [i, v] of labelArr.entries()) {
    msg2 += `${v}:${weathersArr[i]}(${windsArr[i].split(' ')[0]})\n` // 例:今日の天気:晴れ(北西の風)
  };


  // 降水確率
  const rainDataObj = data[0].timeSeries[1];
  const rainTimeDefinesArr = rainDataObj.timeDefines;
  const rainPopsArr = rainDataObj.areas[2].pops;
  
  let msg3 = '■降水確率\n'
  for(const [i, v] of rainTimeDefinesArr.entries()) {
    const datetimeStr = Utilities.formatDate(new Date(v), 'JST', 'MM/dd HH時')
    msg3 += `${datetimeStr} | ${rainPopsArr[i]}%\n` // 例:02/11 12時 | 0%
  };


  // 気温
  const tempDataObj = data[0].timeSeries[2];
  const cityName = tempDataObj.areas[2].area.name; // 帯広
  const tempTimeDefinesArr = tempDataObj.timeDefines;
  const tempsArr = tempDataObj.areas[2].temps;
  
  let msg4 = `■${cityName}の気温\n`
  for(const [i, v] of tempTimeDefinesArr.entries()) {
    const datetimeStr = Utilities.formatDate(new Date(v), 'JST', 'MM/dd HH時')
    msg4 += `${datetimeStr} | ${tempsArr[i]}\n` // 02/11 09時 | 1℃
  };

  // テキストを繋げる
  const msg = `${msg1}\n${msg2}\n${msg3}\n${msg4}`;
  console.log(msg)
}