今回は、気象庁ホームページの内部apiを利用したアメダス(気温や雨量の観測データ)の表示ページを作成してみます
apiのURL
特定時刻の全アメダスの観測値のデータは https://www.jma.go.jp/bosai/amedas/data/map/{ymdhns}.json に格納されています。
{ymdhns}の部分は観測時刻(JST、10分単位。20260319161000 など)で、最新の観測時刻は https://www.jma.go.jp/bosai/amedas/data/latest_time.txt に格納されています。
現在時刻に近いデータを取得する際には、必ずあらかじめ最新の観測時刻を取得してから、それ以前の時刻のデータを取得するようにしてください
最新時刻を決め打ちで取得すると、まだデータができていなかった際に404エラーが返り、その応答が中継サーバーのキャッシュに記録されることで最新のデータができた後もしばらくデータを表示できなくなるおそれがあります。気象庁ホームページの通常利用者にも迷惑のかかるおそれがありますので、決め打ちでの取得は控えてください
特定のアメダスの過去数時間の観測値のデータは、 https://www.jma.go.jp/bosai/amedas/data/point/{amedasCode}/{ymd_h}.json に格納されています。{amedasCode}の部分はアメダス番号、{ymd_h}の部分は観測時刻(JST、3時間単位。20260319_15 など)です。20260319_15 には15時から17時50分の観測データが入ります。すなわち、最新時刻を3時間単位に切り捨てた時刻のデータを取得すればいいことになります
アメダス番号と観測点名などの対応は https://www.jma.go.jp/bosai/amedas/const/amedastable.json に格納されています
apiの構造
観測点情報(amedastable.json)
| 要素名 | 説明 |
|---|---|
| {amedasCode}.type | 観測点種別 |
| {amedasCode}.elem | 観測要素 |
| {amedasCode}.lat | 緯度[度,分] |
| {amedasCode}.lon | 経度[度,分] |
| {amedasCode}.alt | 標高 |
| {amedasCode}.kjName | 漢字名 |
| {amedasCode}.knName | カタカナ名 |
| {amedasCode}.enName | 英語名 |
観測点種別は現状 A(官署)、B(特別地域気象観測所)、C(アメダス)、D(父島)、E(南鳥島)、F(富士山)、G(その他) の7種類です
観測要素は "11112010" 等8桁の文字列で、1桁目から順に気温、降水量、風向、風速、日照時間、積雪深、湿度、気圧(・視程・天気)の観測の有無を示します。1が観測を行っていること、0が観測を行っていないこと、2が観測を行っていないが推計値が作成されていることを示します
カタカナ名には "ムサシ:大分空港" のように空港名が含まれている地点があります
特定時刻の観測データ(map/{ymdhns}.json)
| 要素名 | 説明 | |
|---|---|---|
| {amedasCode}.precipitation10m | 前10分間降水量[mm] | 0.5mm単位 |
| {amedasCode}.precipitation1h | 前1時間降水量[mm] | 0.5mm単位 |
| {amedasCode}.precipitation3h | 前3時間降水量[mm] | 0.5mm単位 |
| {amedasCode}.precipitation24h | 前24時間降水量[mm] | 0.5mm単位 |
| {amedasCode}.snow | 積雪深[cm] | 1cm単位 |
| {amedasCode}.snow1h | 前1時間降雪量[cm] | 1cm単位 |
| {amedasCode}.snow6h | 前6時間降雪量[cm] | 1cm単位 |
| {amedasCode}.snow12h | 前12時間降雪量[cm] | 1cm単位 |
| {amedasCode}.snow24h | 前24時間降雪量[cm] | 1cm単位 |
| {amedasCode}.sun10m | 前10分間日照時間[分] | 1分単位 |
| {amedasCode}.sun1h | 前1時間日照時間[時間] | 0.1時間単位 |
| {amedasCode}.temp | 気温[℃] | 0.1℃単位 |
| {amedasCode}.windDirection | 風向 | |
| {amedasCode}.wind | 風速[m/s] | 0.1m/s単位 |
| {amedasCode}.humidity | 湿度[%] | 1%単位 |
| {amedasCode}.pressure | 現地気圧[hPa] | 0.1hPa単位 |
| {amedasCode}.normalPressure | 海面更正気圧 | 0.1hPa単位 |
| {amedasCode}.visibility | 視程[m] | (おそらく)10m単位 |
| {amedasCode}.weather | 自動観測による天気 |
各要素は
[ 12.3, 0]
のように配列となっており、1つ目の値が観測値を、2つ目の値が品質管理用のフラグを指します
品質管理用のフラグについては、配信資料に関する仕様 No.13301中の「観測値のAQC識別符」と同じものを用いているようです
| フラグ | 説明 |
|---|---|
| 0 | 正常 |
| 1 | 準正常(やや疑わしい) |
| 2 | 非常に疑わしい |
| 3 | 利用に適さない |
| 4 | 観測値は期間内で資料数が不足している |
| 5 | 点検又は計画休止のため欠測 |
| 6 | 障害のため欠測 |
| 7 | この要素の観測はしていない |
積雪深・降雪量・自動観測による天気は毎正時のみの配信です
風向は16方位で 1 北北東、2 北東、3 東北東、……(以下時計回り)……、16 北 の順になっています。風速が弱く風向が特定できない場合には 0 となります
海面更正気圧は、標高が高くて更正の精度を担保できない地点(軽井沢など)では配信されません
視程は10m単位で20kmが上限の地点が多いようですが、一部の観測所(空港)では40kmや50kmが上限となっているようです
自動観測による天気は前述の配信資料内の「自動観測による天気」と同じものを用いているようです。(5、6、8、11~15は、運用上は使用されていないようです)
| 値 | 説明 |
|---|---|
| 0 | 晴 |
| 1 | 曇 |
| 2 | 煙霧 |
| 3 | 霧 |
| 4 | 降水またはしゅう雨性の降水 |
| 5 | 霧雨 |
| 6 | 着氷性の霧雨 |
| 7 | 雨 |
| 8 | 着氷性の雨 |
| 9 | みぞれ |
| 10 | 雪 |
| 11 | 凍雨 |
| 12 | 霧雪 |
| 13 | しゅう雨または止み間のある雨 |
| 14 | しゅう雪または止み間のある雪 |
| 15 | ひょう |
| 16 | 雷 |
特定地点の観測データ(point/{amedasCode}/{ymd_h}.json)
| 要素名 | 説明 |
|---|---|
| {ymdhns}.prefNumber | 府県番号(アメダス番号の上2桁) |
| {ymdhns}.observationNumber | 地点番号(アメダス番号の下3桁) |
| {ymdhns}.pressure | 現地気圧[hPa] |
| {ymdhns}.normalPressure | 海面更正気圧[hPa] |
| {ymdhns}.temp | 気温[℃] |
| {ymdhns}.humidity | 湿度[%] |
| {ymdhns}.visibility | 視程[m] |
| {ymdhns}.snow | 積雪深[cm] |
| {ymdhns}.weather | 自動観測による天気 |
| {ymdhns}.snow1h | 前1時間降雪量[cm] |
| {ymdhns}.snow6h | 前6時間降雪量[cm] |
| {ymdhns}.snow12h | 前12時間降雪量[cm] |
| {ymdhns}.snow24h | 前24時間降雪量[cm] |
| {ymdhns}.sun10m | 前10分間日照時間[分] |
| {ymdhns}.sun1h | 前1時間日照時間[時間] |
| {ymdhns}.precipitation10m | 前10分間降水量[mm] |
| {ymdhns}.precipitation1h | 前1時間降水量[mm] |
| {ymdhns}.precipitation3h | 前3時間降水量[mm] |
| {ymdhns}.precipitation24h | 前24時間降水量[mm] |
| {ymdhns}.windDirection | 風向 |
| {ymdhns}.wind | 風速[m/s] |
特定時刻のデータではアメダス番号{amedasCode}だった部分が観測時刻{ymdhns}となっていますが、おおむね同じ構造です
| 要素名 | 説明 |
|---|---|
| {ymdhns}.maxTempTime | 最高気温観測時刻 |
| {ymdhns}.maxTemp | 最高気温[℃] |
| {ymdhns}.minTempTime | 最低気温観測時刻 |
| {ymdhns}.minTemp | 最低気温[℃] |
| {ymdhns}.gustTime | 最大瞬間風速観測時刻 |
| {ymdhns}.gustDirection | 最大瞬間風速の風向[m/s] |
| {ymdhns}.gust | 最大瞬間風速[m/s] |
特定地点のデータでは、最高気温、最低気温、最大瞬間風速が格納されています。観測時刻は
{"hour":21,"minute":15}
のような形式でUTCで格納されています(日本時間に変換するには9時間足す必要があります)
積雪を観測していない地点でも降雪量が格納されていたり、風や気温を観測していない地点でも最高気温の観測時刻等が格納されている場合があります。その場合は値や品質管理フラグが null となっています
表示用のコード
ここまでの内容を表に起こしていきます。
今回は、特定時刻全国のデータから指定した都道府県のデータを表形式で表示する形で作成してみます。
色々とこだわりようはあると思いますので、あくまで参考としてお考えください
都道府県とアメダス地点の対応付けのために対応表を用意しておきます
const amedasAreas = {
"11":"宗谷","12":"上川","13":"留萌","14":"石狩","15":"空知","16":"後志","17":"オホーツク","18":"根室","19":"釧路","20":"十勝","21":"胆振","22":"日高","23":"渡島","24":"檜山",
"31":"青森","32":"秋田","33":"岩手","34":"宮城","35":"山形","36":"福島",
"40":"茨城","41":"栃木","42":"群馬","43":"埼玉","44":"東京","45":"千葉","46":"神奈川","48":"長野","49":"山梨",
"50":"静岡","51":"愛知","52":"岐阜","53":"三重","54":"新潟","55":"富山","56":"石川","57":"福井",
"60":"滋賀","61":"京都","62":"大阪","63":"兵庫","64":"奈良","65":"和歌山",
"66":"岡山","67":"広島","68":"島根","69":"鳥取","71":"徳島","72":"香川","73":"愛媛","74":"高知",
"81":"山口","82":"福岡","83":"大分","84":"長崎","85":"佐賀","86":"熊本","87":"宮崎","88":"鹿児島",
"91":"沖縄本島","92":"大東島","93":"宮古島","94":"八重山"
};
都道府県選択後、最新の観測時刻を取得して、その後に観測データを取得します
function getLatestTime(){
let instant = Temporal.Now.instant(); // URLパラメーターに時刻を付加して最新の時刻が取得できるようにする
fetch("https://www.jma.go.jp/bosai/amedas/data/latest_time.txt?__time__=" + instant.epochSeconds)
.then((response) => response.text()) // テキスト形式
.then((latestTimeText) => {
let latestTime = Temporal.ZonedDateTime.from( latestTimeText+"[Asia/Tokyo]");
document.getElementById("targetTime").value = dateFormat(latestTime,'y-m-dTh:n');
get(); // 観測データの取得
});
}
表示部では、地点データの観測要素と、それに対応する要素名の対応表を用意しておきます
const amedasElemsAry = [["temp"],["precipitation1h"],["windDirection"],["wind"],["sun1h"],["snow","snow1h"],["humidity"],["normalPressure","visibility","weather"]];
また、要素名と日本語表記の対応表を用意しておきます。観測データの数値形式をそのまま表示すると、「0ミリ」「1.5ミリ」「5ミリ」のように不統一になってしまうため、特定の桁数に丸めるよう桁数も定義しておきます
const amedasElems = {
"temp":{"ja":"気温[℃]","digit":1},
"precipitation1h":{"ja":"1時間降水量[mm]","digit":1},
"windDirection":{"ja":"風向"}, "wind":{"ja":"風速[m/s]","digit":1},
"sun1h":{"ja":"1時間日照時間[h]","digit":1},
"snow":{"ja":"積雪[cm]","digit":0}, "snow1h":{"ja":"1時間降雪量[cm]","digit":0},
"humidity":{"ja":"湿度[%]","digit":0},
"normalPressure":{"ja":"気圧[hPa]","digit":1}, "visibility":{"ja":"視程[m]","digit":0}, "weather":{"ja":"天気"}
};
品質管理値とそれに負荷する記号は、気象観測統計指針(気象庁)の「観測値、統計値の分類」に記載のものを参考にしました
const qcFlags = {
"0":{"status":"正常","mark":""},
"1":{"status":"準正常","mark":")"},
"2":{"status":"疑問値","mark":"#"},
"3":{"status":"利用不適","mark":"×"},
"4":{"status":"資料不足","mark":"]"},
"5":{"status":"計画欠測","overwrite":"-"},
"6":{"status":"未受信/障害欠測","overwrite":"×"},
"7":{"status":"未実施","overwrite":""}
};
風向・天気の数字と日本語表記の対応表も用意しておきます
const windDirections = ["-","北北東","北東","東北東","東","東南東","南東","南南東","南","南南西","南西","西南西","西","西北西","北西","北北西","北"];
const weathers = ["晴れ","くもり","煙霧","霧","降水","霧雨","着氷性の霧雨","雨","着氷性の雨","みぞれ","雪","凍雨","霧雪","しゅう雨","しゅう雪","ひょう","雷"];
amedastable.json に記載のアメダス番号ごとに、指定した都道府県に属するか判定を行い、属している場合には表示していきます。その際、アメダス番号の上2桁と所在府県が一致しない地点があるため、一部読み替えを行います
for( let amedasCode in amedasInfos){
let prefCode = [amedasCode.substring(0,2)];
// 13011(幌延)は番号13(留萌)だが所在地は11(宗谷)、15041(朱鞠内)・15076(幌加内)は番号15(空知)だが所在地は12(上川)
// 24141(熊石)は番号24(檜山)だが所在地は23(渡島)だが気象庁の細分では24(檜山)。利用用途に応じて"24141":["23"]の読み替えを削除されたい
// 50066(富士山)は番号50(静岡)だが所在地は49(山梨)・50(静岡)、72176(竜王山)は番号72(香川)だが所在地は71(徳島)
const prefCodeOverWrites = {"13011":["11"],"15041":["12"],"15076":["12"],"24141":["23"],"50066":["49","50"],"72176":["71"]};
if( prefCodeOverWrites[amedasCode]!=undefined){
prefCode = prefCodeOverWrites[amedasCode];
}
if( !prefCode.includes(targetPrefCode)){
continue;
}
}
観測要素ごとに値・品質管理フラグを読み込み、読み替えと桁数の統一、品質管理フラグによる記号の付加を行います
let val = amedas[amedasCode][elemName][0], qcFlag = amedas[amedasCode][elemName][1];
if( elemName == "windDirection"){
val = windDirections[val];
}else if( elemName == "weather"){
val = weathers[val];
}else if( val!=null && amedasElems[elemName]['digit']!=undefined){
val = val.toFixed( amedasElems[elemName]['digit']); // 要素ごとに有効桁数に四捨五入
}
if( qcFlags[qcFlag]['overwrite']!=undefined){ // 障害欠測など重大な異常値は記号で上書き
val = qcFlags[qcFlag]['overwrite'];
}else if( qcFlags[qcFlag]['mark']!=undefined){ // 準正常値など軽微な異常値は記号を追加
val += qcFlags[qcFlag]['mark'];
}
out += "<td>" + val + "</td>";
表示ページ全体のソースコード
2026/03/31 Safariでも動くよう、Temporal APIに替えてDate APIを使用した版を追加しました(サンプルページのみ)
サンプルページ
サンプルページ(Date APIを使用した版)
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>アメダス</title>
<style>
*{ font-family:sans-serif;}
table, tr, td, th{ border-collapse:collapse; text-align:center; white-space:nowrap;}
th,td{ padding:2px 8px; border-style:solid; border-width:1px 0; border-color:#d8d8db;}
th{ background-color:#f1f1f4;}
</style>
</head>
<body>
<h1>アメダス</h1>
<div id="menu">
<select id="prefSelect"></select>
<input type="datetime-local" id="targetTime">
<button id="display">表示</button>
<button id="getLatestTime">最新</button>
</div>
<div id="out"></div>
<script>
"use strict";
let amedasInfos = {};
// アメダス番号の上2桁と、対応する地域名
const amedasAreas = {
"11":"宗谷","12":"上川","13":"留萌","14":"石狩","15":"空知","16":"後志","17":"オホーツク","18":"根室","19":"釧路","20":"十勝","21":"胆振","22":"日高","23":"渡島","24":"檜山",
"31":"青森","32":"秋田","33":"岩手","34":"宮城","35":"山形","36":"福島",
"40":"茨城","41":"栃木","42":"群馬","43":"埼玉","44":"東京","45":"千葉","46":"神奈川","48":"長野","49":"山梨",
"50":"静岡","51":"愛知","52":"岐阜","53":"三重","54":"新潟","55":"富山","56":"石川","57":"福井",
"60":"滋賀","61":"京都","62":"大阪","63":"兵庫","64":"奈良","65":"和歌山",
"66":"岡山","67":"広島","68":"島根","69":"鳥取","71":"徳島","72":"香川","73":"愛媛","74":"高知",
"81":"山口","82":"福岡","83":"大分","84":"長崎","85":"佐賀","86":"熊本","87":"宮崎","88":"鹿児島",
"91":"沖縄本島","92":"大東島","93":"宮古島","94":"八重山"
};
initialize();
// 都道府県選択欄の作成、アメダス定数ファイルの取得
function initialize(){
let prefSelect = "";
for( let prefCode in amedasAreas){
let prefName = amedasAreas[prefCode];
prefSelect += "<option value='" + prefCode + "'>" + prefName + "</option>";
}
document.getElementById("prefSelect").innerHTML = prefSelect;
fetch("https://www.jma.go.jp/bosai/amedas/const/amedastable.json")
.then((response) => response.json())
.then((response) => {
amedasInfos = response;
getLatestTime();
});
}
document.getElementById("getLatestTime").addEventListener("click",function(e){
getLatestTime();
});
// 入電している最新のアメダス時刻の取得
function getLatestTime(){
let instant = Temporal.Now.instant(); // URLパラメーターに時刻を付加して最新の時刻が取得できるようにする
fetch("https://www.jma.go.jp/bosai/amedas/data/latest_time.txt?__time__=" + instant.epochSeconds)
.then((response) => response.text())
.then((latestTimeText) => {
let latestTime = Temporal.ZonedDateTime.from( latestTimeText+"[Asia/Tokyo]");
document.getElementById("targetTime").value = dateFormat(latestTime,'y-m-dTh:n');
get();
});
}
document.getElementById("display").addEventListener("click",function(e){
get();
});
// アメダスデータの取得
function get(){
let targetTime = Temporal.ZonedDateTime.from( document.getElementById("targetTime").value + "+09:00[Asia/Tokyo]");
fetch("https://www.jma.go.jp/bosai/amedas/data/map/" + dateFormat(targetTime,'ymdhns') + ".json")
.then((response) => response.json())
.then((amedas) => {
display(amedas);
});
}
// アメダスデータの表示
function display(amedas){
let targetPrefCode = document.getElementById("prefSelect").value;
const amedasElemsAry = [["temp"],["precipitation1h"],["windDirection"],["wind"],["sun1h"],["snow","snow1h"],["humidity"],["normalPressure","visibility","weather"]];
// 各要素の日本語名と小数点以下の有効桁数
const amedasElems = {
"temp":{"ja":"気温[℃]","digit":1},
"precipitation1h":{"ja":"1時間降水量[mm]","digit":1},
"windDirection":{"ja":"風向"}, "wind":{"ja":"風速[m/s]","digit":1},
"sun1h":{"ja":"1時間日照時間[h]","digit":1},
"snow":{"ja":"積雪[cm]","digit":0}, "snow1h":{"ja":"1時間降雪量[cm]","digit":0},
"humidity":{"ja":"湿度[%]","digit":0},
"normalPressure":{"ja":"気圧[hPa]","digit":1}, "visibility":{"ja":"視程[m]","digit":0}, "weather":{"ja":"天気"}
};
// 品質管理フラグの一覧と付加するマーク、上書きする文字
const qcFlags = { "0":{"status":"正常","mark":""}, "1":{"status":"準正常","mark":")"}, "2":{"status":"疑問値","mark":"#"}, "3":{"status":"利用不適","mark":"×"}, "4":{"status":"資料不足","mark":"]"}, "5":{"status":"計画欠測","overwrite":"-"}, "6":{"status":"未受信/障害欠測","overwrite":"×"}, "7":{"status":"未実施","overwrite":""}};
// 風向・天気は数字で示されるので対応表を定義
const windDirections = ["-","北北東","北東","東北東","東","東南東","南東","南南東","南","南南西","南西","西南西","西","西北西","北西","北北西","北"];
const weathers = ["晴れ","くもり","煙霧","霧","降水","霧雨","着氷性の霧雨","雨","着氷性の雨","みぞれ","雪","凍雨","霧雪","しゅう雨","しゅう雪","ひょう","雷"];
let out = "";
out += "<table>";
out += "<tr><th></th>";
for( let i=0; i<amedasElemsAry.length; i++){
for( let elemName of amedasElemsAry[i]){
out += "<th>" + amedasElems[elemName]['ja'] + "</th>";
}
}
out += "</tr>";
for( let amedasCode in amedasInfos){
let amedasName = amedasInfos[amedasCode]['kjName'], elems = amedasInfos[amedasCode]['elems'], prefCode = [amedasCode.substring(0,2)];
// 13011(幌延)は番号13(留萌)だが所在地は11(宗谷)、15041(朱鞠内)・15076(幌加内)は番号15(空知)だが所在地は12(上川)
// 24141(熊石)は番号24(檜山)だが所在地は23(渡島)だが気象庁の細分では24(檜山)。利用用途に応じて"24141":["23"]の読み替えを削除されたい
// 50066(富士山)は番号50(静岡)だが所在地は49(山梨)・50(静岡)、72176(竜王山)は番号72(香川)だが所在地は71(徳島)
const prefCodeOverWrites = {"13011":["11"],"15041":["12"],"15076":["12"],"24141":["23"],"50066":["49","50"],"72176":["71"]};
if( prefCodeOverWrites[amedasCode]!=undefined){
prefCode = prefCodeOverWrites[amedasCode];
}
if( !prefCode.includes(targetPrefCode)){
continue;
}
out += "<tr><th>" + amedasName + "</th>";
for( let i=0; i<elems.length; i++){
for( let elemName of amedasElemsAry[i]){
if( elems[i]==0 || amedas[amedasCode]==undefined || amedas[amedasCode][elemName]==undefined){
out += "<td></td>";
}else{
let val = amedas[amedasCode][elemName][0], qcFlag = amedas[amedasCode][elemName][1];
if( elemName == "windDirection"){
val = windDirections[val];
}else if( elemName == "weather"){
val = weathers[val];
}else if( val!=null && amedasElems[elemName]['digit']!=undefined){
val = val.toFixed( amedasElems[elemName]['digit']); // 要素ごとに有効桁数に四捨五入
}
if( qcFlags[qcFlag]['overwrite']!=undefined){ // 障害欠測など重大な異常値は記号で上書き
val = qcFlags[qcFlag]['overwrite'];
}else if( qcFlags[qcFlag]['mark']!=undefined){ // 準正常値など軽微な異常値は記号を追加
val += qcFlags[qcFlag]['mark'];
}
out += "<td>" + val + "</td>";
}
}
}
out += "</tr>";
}
out += "</table>";
document.getElementById("out").innerHTML = out;
}
// Temporal.ZonedDateTime を文字列形式に変換
function dateFormat( t, f='y-m-dTh:n:s', is24=false){
const j=["","月","火","水","木","金","土","日"];const e=["","Mon","Tue","Wed","Thu","Fri","Sat","Sun"];
if(is24&&t.hour==0){t.substract({days:1});f=f.replace(/h/g,"24").replace(/H/g,24);}
const y=t.year,m=t.month,d=t.day,h=t.hour,n=t.minute,s=t.second,w=t.dayOfWeek;
f=f.replace(/y/g,("000"+y).slice(-4)).replace(/Y/g,y).replace(/m/g,("0"+m).slice(-2)).replace(/M/g,m).replace(/d/g,("0"+d).slice(-2)).replace(/D/g,d);
f=f.replace(/w/g,j[w]).replace(/W/g,e[w]);
f=f.replace(/h/g,("0"+h).slice(-2)).replace(/H/g,h).replace(/n/g,("0"+n).slice(-2)).replace(/N/g,n).replace(/s/g,("0"+s).slice(-2)).replace(/S/g,s);
return f;
}
</script>
</body>
</html>