裕木奈江さんのブログを購読している関係で、その掲載先である「note」という
記事サイトをなんとなく散策していると、
(記事1)
という記事を見つけました。
タイトルは何歩も先を行っていて難しいのですが、
「JSON形式で気象データが(リアルタイムで)ダウンロードできるようになっている」
というのが肝で、その元ネタは、記事内でリンクされる
(記事2)
というブログらしく、昨年春に気象界隈で話題になっていたようですね。
気づきませんでした。
気象庁のオフィシャル観測値データは、今までも、そして今も、気象庁HPの
からダウンロードできます。
地点・日時指定や、統計値の取得もでき、csv形式でDLできるので、
手動操作にはなりますが、統計解析をする場合にはこちらが便利です。
これをJSON形式に変換して提供する民間(個人)無料サービスもあったり。
データを使いやすくして提供する有料サービスはほかに多数あります。
でも、気象庁から直接リアルタイムに無料でデータを、
HPの表示データのスクレイピングではなく、データフォーマットで入手できるのなら、
それに越したことはありません。いろいろ応用の夢も広がります。
スクレイピングも、IEのサポートが今年6月に終了してしまうと、
ExcelからIE経由で気軽に行うこともできなくなりますし。
で、お正月休みの遊びネタとしては手ごろな感じですので、
JavaScriptでの読み出しにチャレンジしてみました。
データは、「JSON」という殺人鬼のような名称の形式のテキストファイルで保存されています。
例えばアメダス観測点のリストを例に出すと、2地点分で、
(データ例1)
{"11001": {"type":"C", "elems":"11112010", "lat":[45,31.2], "lon":[141,56.1], "alt":26, "kjName":"稚内", "knName":"ワッカナイ", "enName":"Wakkanai"} ・・・} |
みたいなやつ。これが改行なく例えば1000地点分以上、デヤーっと拡張子「json」のテキストファイルに格納されています。
プロの方から見ると、扱いやすいとか自由度があるとか、改行を気にしなくてよいとか
いろいろメリットがあると思うのですが、必要に応じてポチポチとプログラミング
するだけの素人としては面食らいました。csvがよかったなぁ。。。
で、いろいろググって、
(解説1)
のコードを参考にネットワーク上のJSONファイルを読み出して、
中身を取り出すことができました。
選んだデータは、まずは、ということでアメダス。
アメダスの情報は、
アメダスの観測点(全国1000か所以上)の位置や名称、観測点番号等の情報を格納した
観測点情報ファイル(上記「データ例1」参照)
https://www.jma.go.jp/bosai/amedas/const/amedastable.json
と、観測点番号ごとに観測値が格納された、10分ごとの観測値情報ファイル
https://www.jma.go.jp/bosai/amedas/data/map/(YYYYMMDDhhmm00).json
(「(YYYYMMDDHH」」の部分には年月日時分が入る)
とがあり、観測値情報ファイルには観測点番号だけで観測点の名称等が記載されていない
ので、両方を取得する必要があります。
なお、観測点情報ファイルの方は、そんなに変更されないので、新しい観測点番号が誕生
しない限り、最初の一回だけ取得して手許データ化すればOKです。
観測値情報ファイルは以下のようなもの。(改行は当方で付与)
(データ例2)
{"11001":{"temp":[-3.3,0],"humidity":[100,0],"snow1h":[0,null],"snow6h":[0,null],"snow12h":[0,null],"snow24h":[0,null],"sun10m":[0,0],"sun1h":[0.0,0],"precipitation10m":[0.0,0],"precipitation1h":[0.5,0],"precipitation3h":[4.0,0],"precipitation24h":[5.5,0],"windDirection":[14,0],"wind":[13.2,0]}, "11016":{"pressure":[999.1,0],"normalPressure":[1000.6,0],"temp":[-3.1,0],"humidity":[82,0],"visibility":[2050.0,0],"snow":[56,0],"weather":[10,0],"snow1h":[0,0],"snow6h":[9,0],"snow12h":[10,0],"snow24h":[12,0],"sun10m":[0,0],"sun1h":[0.0,0],"precipitation10m":[0.0,0],"precipitation1h":[1.0,0],"precipitation3h":[5.5,0],"precipitation24h":[11.5,0],"windDirection":[13,0],"wind":[4.6,0]}, "11046":{"temp":[-4.7,0],"humidity":[71,0],"snow1h":[0,null],"snow6h":[0,null],"snow12h":[0,null],"snow24h":[0,null],"sun10m":[0,0],"sun1h":[0.0,0],"precipitation10m":[0.0,0],"precipitation1h":[0.0,0],"precipitation3h":[0.5,0],"precipitation24h":[3.5,0],"windDirection":[14,0],"wind":[2.4,0]}, ・・・} |
観測点情報(データ例1)、観測値情報(データ例2)ともに、
観測点ごとのデータが個々に、例えば「"site":"11001","data":{"temp":0.3・・・」のように項目名ではなく、
いきなり数字だけの観測点番号そのものを項目名(例えば「"11001":{"temp":・・・」)として羅列されているところが
ググった各種JSON×JavaScript解説コラムの例と異なっているのと、
各データ項目がさらに配列(例えば「"temp":[-3.3,0]」)になっているのが面倒そうです。
で、観測点情報ファイルと、観測値情報ファイルとをイントラネット上にダウンロードして、
上記《解説1》を参考に、読み出しテスト用のHPをイントラネット上で作成しました。
HTMLの構造は次のようなもの。
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script type="text/javascript"> function getJson() { ・・・ } function getJsonAmeD() { ・・・ } ・・・観測点コードと観測点名の対照データ(連想配列)・・・ </script> </head> <body> <input id="Button_Get" type="button" value="アメダス観測点表JSON読み込み" onclick="getJson();" /> <input id="Button_Get" type="button" value="アメダス観測値JSON読み込み" onclick="getJsonAmeD();" /> <hr /> <div id="taitoru"></div> <div> <table border=1 id="kakikomit"> </table> </div> </body> </html> |
ページの2つのボタンをそれぞれクリックすると、
function getJson()またはfunction getJsonAmeD()のいずれかの関数が実行され、
アメダス観測点情報の一覧またはアメダス観測値の一覧が
表形式でページ下部に表示されます。
なお、観測点コードと観測点名の対照データは、観測値情報の表示で
用いるために、上記ページで取得した観測点情報一覧の表をExcelに取り込んで、
配列形式のJavaScriptの式列に変換したもの(下記)を、Scriptの本体部分に貼り付けています。
var AmePo = new Array(); AmePo[11001]="宗谷岬"; AmePo[11016]="稚内"; AmePo[11046]="礼文"; AmePo[11061]="声問"; AmePo[11076]="浜鬼志別"; ・・・ |
観測点情報ファイルからは、次の関数でJSONデータを読み出して、
「地点番号、地点名、緯度(度・分)、経度(度、分)、標高」を
表形式で「id="kakikomit"」の表に書き出させています。
function getJson() { url='http://★★★/json/amedastable.json';//観測点情報のJSONファイルのイントラネット上のパス var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", url); xmlhttp.send(); xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 200) { var kakidasi = JSON.parse(xmlhttp.responseText); var elemt = document.getElementById("taitoru"); elemt.innerHTML = "アメダス観測点表"; var elem = document.getElementById("kakikomit"); elem.innerHTML = ""; elem.innerHTML += " <tr><td>地点コード</td><td>地点名</td><td>緯度(度)</td><td> 緯度(分)</td><td>経度(度)</td><td>経度(分)</td><td>標高(m)</td></tr>"; for (var d in kakidasi) { lat_do= kakidasi[d]["lat"][0]; lat_fun= kakidasi[d]["lat"][1]; lon_do= kakidasi[d]["lon"][0]; lon_fun= kakidasi[d]["lon"][1]; elem.innerHTML += "<tr><TD>" + d + "</td><td>" + kakidasi[d].kjName + "</td><td>" + lat_do + "</td><td>" + lat_fun + "</td><td>" + lon_do + "</td><td>" + lon_fun + "</td><td>" + kakidasi[d].alt + "</td></tr>"; } } else { } } } } |
「var kakidasi = JSON.parse(xmlhttp.responseText);」でファイルから読み出した
文字列をJSONとしてオブジェクト解析し、
「 for (var d in kakidasi) {」でプロパティごとに書き出すと、
地点番号ごとにプロパティを取り出すことができました。
「緯度(度・分)、経度(度、分)」は 2要素の配列になっていますので、
「lat_do= kakidasi[d]["lat"][0];」
などで配列要素番号別に度、分をそれぞれ取り出しています。
実行すると、
のような形で
「 <table border=1 id="kakikomit"></table>」
の個所に観測点情報の表が作成されます。
1200か所あまりで、5~10秒かかりました。
同様に、アメダスの観測値データの解析関数は次のようなもの。
function getJsonAmeD() { url='http://★★★/json/20220102210000.json'; var xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", url); xmlhttp.send(); xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4) { if (xmlhttp.status == 200) { var kakidasi = JSON.parse(xmlhttp.responseText); var elemt = document.getElementById("taitoru"); elemt.innerHTML = "アメダス観測値"; var elem = document.getElementById("kakikomit"); elem.innerHTML = ""; elem.innerHTML += " <tr><td>地点コード</td><td>地点名</td><td>気温(℃)</td><td>降水量(mm)</td><td>風速(m/s)</td><td>風向</td></tr>"; var winDir; winDir = new Array("-","北北東","北東","東北東","東","東南東","南東","南南東","南","南南西","南西","西南西","西","西北西","北西","北北西","北"); for (var d in kakidasi) { var temp = "-"; var precip = "-"; var winS = "-"; var winD = 0; if (kakidasi[d]["temp"] != null) { if(kakidasi[d]["temp"][0]!=null){temp= kakidasi[d]["temp"][0];} } if (kakidasi[d]["precipitation1h"] != null) { if(kakidasi[d]["precipitation1h"][0]!=null){precip = kakidasi[d]["precipitation1h"][0];} } if (kakidasi[d]["wind"] != null) { if(kakidasi[d]["wind"][0] != null){winS = kakidasi[d]["wind"][0];} } if (kakidasi[d]["windDirection"] != null) { if (kakidasi[d]["windDirection"][0] != null) { winD = kakidasi[d]["windDirection"][0]+0; winD = winDir[winD]; } else { winD = winDir[0]; } } else { winD = winDir[winD]; } var AmePoName; AmePoName="?"; if (AmePo[d]!=null){AmePoName=AmePo[d];} elem.innerHTML += "<tr><TD>" + d + "</td><td>" + AmePoName + "</td><td>" + temp + "</td><td>" + precip + "</td><td>" + winS + "</td><td>" + winD + "</td></tr>"; } } else { } } } } |
データ値がすべて配列なので、面倒になっています。
「 if (AmePo[d]!=null){AmePoName=AmePo[d];}」
で、アメダス観測点の番号と名称を関連付けた連想配列を呼び出して、
観測値表に地点名も表示できるようにしています。
また、アメダス観測点ごとに観測要素数が異なりますし、
(雨量だけとか、風向風速気温も測定しているとか)
欠測(「null」値が代入されている)もありますので、
その辺の手当てもしています。
風向は16方位の数値なので、漢字名に変換しています。
実行すると、
のように全地点分の観測値が表示されます。
実行時間は5~9秒ぐらいでした。
ということで、無事、正月休みの自主課題は完了しました。
もっと早く効率的に処理できるよ、とか、間違ってるよ、とかのご助言があれば、
読者の方の参考にもなりますので、ぜひ書き込んでいただければ幸いです。
お天気koalaのマイブーム