15.雨雲レーダーのアニメーションの仕方
今回は雨雲レーダーのデータが、3時間前から1時間後まで存在するので、スクロールバーを用いてアニメーションの仕方をご説明いたします。
では早速、雨雲レーダーのコードを見てみましょう‼️🌧️☔⛈️
PHPでgetしたナウキャストjsonデータを変数$baseTimeに代入しています。
<?php
// ナウキャストjson3時間前までのデータのアドレス
$url = "https://www.jma.go.jp/bosai/jmatile/data/nowc/targetTimes_N1.json";
// ナウキャストjson1時間後までのデータのアドレス
$url2 = "https://www.jma.go.jp/bosai/jmatile/data/nowc/targetTimes_N2.json";
$json1 = file_get_contents($url1);
$json2 = file_get_contents($url2);
$data1 = json_decode($json1,true);
$data2 = json_decode($json2,true);
$baseTimes1 = array_column($data1, 'basetime');
$validTimes1 = array_column($data1, 'validtime');
$baseTimes2 = array_column($data2, 'basetime');
$validTimes2 = array_column($data2, 'validtime');
?>
PHPでgetしたナウキャストjsonデータをarray_column関数を使用して、$data1および$data2からbasetimeとvalidtimeという特定のキーの値をそれぞれ抽出し、$basetime1、$validtime1、$basetime2、$validtime2という配列に格納しています。
このコードの目的は、気象データのbasetime(基準時刻)とvalidtime(有効時刻)を抽出することです。
<script>
var baseTimes1 = <?php echo json_encode($baseTimes1); ?>;
var validTimes1 = <?php echo json_encode($validTimes1); ?>;
var baseTimes2 = <?php echo json_encode($baseTimes2); ?>;
var validTimes2 = <?php echo json_encode($validTimes2); ?>;
var nowCastLayer = [];
for (var i = baseTimes1.length - 1, j = 0; i >= 0; i--, j++) {
nowCastLayer[j] = L.tileLayer(`https://www.jma.go.jp/bosai/jmatile/data/nowc/${baseTimes1[i]}/none/${validTimes1[i]}/surf/hrpns/{z}/{x}/{y}.png`, {zIndex: 20, maxNativeZoom: 10, opacity: 0.6});
}
for (var t = 0; t < baseTimes2.length; t++) {
nowCastLayer[baseTimes1.length + t] = L.tileLayer(`https://www.jma.go.jp/bosai/jmatile/data/nowc/${baseTimes2.slice().reverse()[t]}/none/${validTimes2.slice().reverse()[t]}/surf/hrpns/{z}/{x}/{y}.png`, {zIndex: 20, maxNativeZoom: 10, opacity: 0.6});
}
// 初期表示のレイヤーを地図に追加する(現在)
var initialIndex = baseTimes1.length; // 初期のインデックスを設定 (現在)
map.addLayer(nowCastLayer[initialIndex]);
</script>
ここではPHPから配列($baseTimes1、$validTimes1など)が提供されており、それをJavaScriptの変数baseTimes1、validTimes1、baseTimes2、validTimes2に変換しています。
json_encode関数を用いてPHP配列をJSON形式に変換しているため、データがJavaScript配列として扱えます。
for (var i = baseTimes1.length - 1, j = 0; i >= 0; i--, j++) {
nowCastLayer[j] = L.tileLayer(`https://www.jma.go.jp/bosai/jmatile/data/nowc/${baseTimes1[i]}/none/${validTimes1[i]}/surf/hrpns/{z}/{x}/{y}.png`, {zIndex: 20, maxNativeZoom: 10, opacity: 0.6});
}
ナウキャストjson3時間前までのデータのアドレスが、下記になります。
$url = "https://www.jma.go.jp/bosai/jmatile/data/nowc/targetTimes_N1.json";
このjsonデータは現在をbasetime1[0]として、3時間前のbasetime1[36]まであります。
スライドバーの一番左端をbasetime1[36]とする為、最初のforループはbaseTimes1とvalidTimes1を基に、降水ナウキャストのURLを使用してタイルレイヤーを作成し、逆順に格納しています。
for (var t = 0; t < baseTimes2.length; t++) {
nowCastLayer[baseTimes1.length + t] = L.tileLayer(`https://www.jma.go.jp/bosai/jmatile/data/nowc/${baseTimes2.slice().reverse()[t]}/none/${validTimes2.slice().reverse()[t]}/surf/hrpns/{z}/{x}/{y}.png`, {zIndex: 20, maxNativeZoom: 10, opacity: 0.6});
}
ナウキャストjson1時間後までのデータのアドレスが、下記になります。
$url2 = "https://www.jma.go.jp/bosai/jmatile/data/nowc/targetTimes_N2.json";
このjsonデータは、validTimes2[0]が1時間後の予測データ、validTimes2[11]が5分後の予測データになります。
全部で12個の配列が、validTimes2に格納されています。
${validTimes2.slice().reverse()[t]}は、配列validTimes2の要素を逆順にして、その逆順にした配列のt番目の要素を取得する操作を行っています。
具体的な処理は以下のようになります:
- validTimes2.slice()
- slice()メソッドは、配列のコピーを作成するために使われています。この場合、元の配列validTimes2に変更を加えず、コピーした新しい配列を作成します。
- .reverse()
- コピーされた配列に対してreverse()メソッドが使われ、配列の要素順が逆になります。つまり、validimes2が [a, b, c] なら、validTimes2.slice().reverse()は [c, b, a] となります。
- この操作により、元のvalidTimes2配列には影響を与えずに、逆順にした配列が得られます。
- [t]
- tは配列のインデックスを指定する変数で、逆順にされた配列のt番目の要素が取得されます。
例えば、validTimes2が [1, 2, 3, 4] で、tが 0 の場合、
${validTimes2.slice().reverse()[t]} は 4 となります。
nowCastLayer[baseTimes1.length + t]としているのは、スクロールバーで表示順序をbaseTimes1.length(現在)の次にvalidTimes2[0]を配置している為です。
// 初期表示のレイヤーを地図に追加する(現在)
var initialIndex = baseTimes1.length; // 初期のインデックスを設定 (現在)
map.addLayer(nowCastLayer[initialIndex]);
var currentIndex = initialIndex;
var intervalId = null;
document.getElementById('scrollbar').min = 0;
document.getElementById('scrollbar').max = nowCastLayer.length - 1;
document.getElementById('scrollbar').value = initialIndex;
document.getElementById('scrollbar').oninput = function() {
updateMapLayer(parseInt(this.value));
};
document.getElementById('play-pause').onclick = function() {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
this.textContent = '再生';
} else {
intervalId = setInterval(function() {
if (currentIndex < nowCastLayer.length - 1) {
currentIndex++;
} else {
currentIndex = 0;
}
document.getElementById('scrollbar').value = currentIndex;
updateMapLayer(currentIndex);
}, 500);
this.textContent = '停止';
}
};
function updateMapLayer(index) {
nowCastLayer.forEach(function(layer) {
map.removeLayer(layer);
});
map.addLayer(nowCastLayer[index]).bringToFront();
currentIndex = index;
}
1.初期レイヤーの追加
// 初期表示のレイヤーを地図に追加する(現在)
var initialIndex = baseTimes1.length; // 初期のインデックスを設定 (現在)
map.addLayer(nowCastLayer[initialIndex]);
スクロールバーの初期値をbasetime1[0](現在)とする為に、var initialIndex = baseTimes1.length;で37番目の値を初期値basetime1[0](現在)とし、
addLayer(...)メソッドで、指定したレイヤーを地図上に追加しています。
2.グローバル変数の設定
var currentIndex = initialIndex;
var intervalId = null;
currentIndexは現在表示しているレイヤーのインデックスを保持します。
intervalIdは、再生/停止ボタンのタイマーIDを保持し、再生中か停止中かを判別します。
3.スクロールバー(スライダー)の設定
document.getElementById('scrollbar').min = 0;
document.getElementById('scrollbar').max = nowCastLayer.length - 1;
document.getElementById('scrollbar').value = initialIndex;
スクロールバー(スライダー)の最小値と最大値を設定しています。
スライダーの範囲はnowCastLayerのインデックス範囲に合わせられています。
初期値としてinitialIndexを設定し、初期レイヤーの位置を反映させています。
4.HTMLコードのスクロールバーの設定
下記コードを<body>の下に書いておきます。
<div id="scrollbar-container">
<input type="range" min="0" max="48" step="1" value="0" id="scrollbar">
<div id="scrollbar-labels">
<!-- JavaScriptでラベルを挿入 -->
</div>
<button id="play-pause">再生</button>
</div>
- スライダー(<input type="range" ... id="scrollbar">)
- <input type="range">は、ユーザーがスライド操作で値を変更できるスクロールバーです。
- min="0": スクロールバーの最小値が0です。
- max="48": スクロールバーの最大値が48です。この範囲内でインデックスを指定し、データの表示ができます。
- step="1": 値が1ずつ増減する設定です。
- value="0": 初期値として0を設定しています。
- id="scrollbar": このスクロールバーに対してJavaScriptやCSSからアクセスするためのIDです。コードでイベントリスナーを追加して、ユーザーがスクロールバーを動かすたびに表示が更新されるようにしています。
- ラベル用のコンテナ(
<
div id="scrollbar-labels">
)- id="scrollbar-labels"はラベルを挿入するためのコンテナです。JavaScriptを使って動的にスクロールバーの位置に応じたラベル(例えば時間や日付などの情報)を追加する予定で、ラベルのデータが動的に生成されるようになっています。
- 再生/停止ボタン(<button id="play-pause">再生</button>)
- ボタンタグで、スクロールバーの自動再生や停止を制御します。
- id="play-pause"はJavaScriptからアクセスするためのIDで、再生/停止ボタンの機能が実装され、クリックするたびに再生と停止が切り替わります。
- 初期表示のテキストは「再生」として設定され、ユーザーが一度クリックすると再生が開始され、ボタンの表示が「停止」に変わる仕組みです。
この構成により、スクロールバーと再生ボタンを使って地図の気象データの表示をユーザーが手動または自動でコントロールできるインターフェースが提供されます。
5.スクロールバーの操作イベント
document.getElementById('scrollbar').oninput = function() {
updateMapLayer(parseInt(this.value));
};
①document.getElementById('scrollbar')
- HTML内のidがscrollbarの要素(スクロールバー)を取得します。
②oninputイベント
- oninputイベントは、スクロールバーの値が変更されるたびに発生します。ユーザーがスライダーを動かすたびにこのイベントがトリガーされます。
③無名関数の実行
- oninputイベントに無名関数(function() {...})が設定され、スクロールバーの値が変更されるとこの関数が実行されます。
④
parseInt(this.value)
- this.valueでスクロールバーの現在の値を取得し、parseIntで整数に変換します。スクロールバーの値は、インデックスとして扱われるため、整数にする必要があります。
⑤
updateMapLayer(parseInt(this.value))
- 取得したインデックス(スクロールバーの値)をupdateMapLayer関数に引数として渡します。
- updateMapLayer関数は、指定されたインデックスに基づいて地図のレイヤーを更新する役割を果たします。これにより、ユーザーがスクロールバーを動かすと、その位置に対応する気象データやレイヤーが地図上に表示されます。
6.地図レイヤーを更新する関数
function updateMapLayer(index) {
nowCastLayer.forEach(function(layer) {
map.removeLayer(layer);
});
map.addLayer(nowCastLayer[index]).bringToFront();
currentIndex = index;
}
- updateMapLayerは、地図上のレイヤーを更新する関数です。
- nowCastLayer配列内の全てのレイヤーをmap.removeLayerで削除します。
- 指定されたインデックスindexに対応するレイヤーをmap.addLayerで追加し、bringToFront()メソッドで最前面に表示します。
- currentIndexを更新し、現在表示されているレイヤーのインデックスとして保持します。
このコードにより、地図上に気象情報を時系列でスムーズに表示し、スクロールバーや再生ボタンでユーザーが簡単に操作できるようになっています。
7.再生/停止ボタンの設定
document.getElementById('play-pause').onclick = function() {
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
this.textContent = '再生';
} else {
intervalId = setInterval(function() {
if (currentIndex < nowCastLayer.length - 1) {
currentIndex++;
} else {
currentIndex = 0;
}
document.getElementById('scrollbar').value = currentIndex;
updateMapLayer(currentIndex);
}, 500);
this.textContent = '停止';
}
};
このコードは、再生/停止ボタン(play-pause)のクリックに応じて、自動で地図のレイヤーを切り替えるか、または停止する機能を実装しています。
①document.getElementById('play-pause').onclick
- HTML要素id="play-pause"のボタンに対し、クリックイベントリスナーを設定しています。このボタンがクリックされると、次の処理が行われます。
②if (intervalId)条件
- intervalIdが設定されている(再生中)かどうかを確認します。
- 再生中(intervalIdが存在する): 現在のアニメーションを停止します。
- 停止中(intervalIdがnull): 新たにタイマーをセットしてアニメーションを開始します。
③アニメーション停止処理
clearInterval(intervalId);
intervalId = null;
this.textContent = '再生';
c
learInterval(intervalId)でタイマーを解除してアニメーションを停止します。- intervalIdをnullにリセットし、停止中の状態を示します。
- ボタンの表示テキスト(textContent)を「再生」に変更し、次に再生できる状態であることを示します。
④アニメーション開始処理
intervalId = setInterval(function() {
if (currentIndex < nowCastLayer.length - 1) {
currentIndex++;
} else {
currentIndex = 0;
}
document.getElementById('scrollbar').value = currentIndex;
updateMapLayer(currentIndex);
}, 500);
this.textContent = '停止';
- setIntervalで500ミリ秒(0.5秒)ごとに無名関数が実行され、intervalIdにタイマーIDが保存されます。
- この無名関数では、以下の処理が行われます:
- インデックス更新: currentIndexが配列の最後に達していなければ1つ増やします。最後に達していれば
0
にリセットし、最初から繰り返します。 - スクロールバー位置の更新: スクロールバー(scrollbar)の値をcurrentIndexに更新し、スクロールバー位置と表示レイヤーを同期します。
- レイヤー更新: updateMapLayer(currentIndex)を呼び出し、currentIndexに対応するレイヤーを地図上に表示します。
- ボタンのテキストを「停止」に変更し、再生中であることを示します。
このコードにより、再生ボタンをクリックすることで地図上の気象レイヤーが0.5秒ごとに自動で切り替わり、スライドショーのような視覚的アニメーションが得られます。再度クリックするとアニメーションが停止し、レイヤーが固定されます。
8.スクロールバーのラベルを設定
最後に、スクロールバーのラベル設定を説明します。
このコードは、気象データのタイムラインを表すスクロールバーに、各時刻のラベルを表示するためのHTMLを動的に生成しています。
タイムラインには、各時間に対応するラベルが一定の間隔で配置され、表示されます。
// basetimeに9時間を加える関数
function addNineHours(time) {
return moment(time, 'YYYYMMDDHHmm').add(9, 'hours').format('HH:mm');
}
// スクロールバーのラベルを設定
var labelsHtml = [];
labelsHtml.push(${addNineHours(baseTimes1[36])});
labelsHtml.push(${addNineHours(baseTimes1[30])});
labelsHtml.push(${addNineHours(baseTimes1[24])});
labelsHtml.push(${addNineHours(baseTimes1[18])});
labelsHtml.push(${addNineHours(baseTimes1[12])});
labelsHtml.push(${addNineHours(baseTimes1[6])});
// "現在"のラベルを追加し、時刻も表示(位置を右にずらす)
labelsHtml.push(${addNineHours(baseTimes1[0])}現在);
// baseTimes2のラベルを追加
labelsHtml.push(${addNineHours(validTimes2[6])});
labelsHtml.push(${addNineHours(validTimes2[0])});
document.getElementById('scrollbar-labels').innerHTML = labelsHtml.join("");
①9時間を加える関数 addNineHours
function addNineHours(time) {
return moment(time, 'YYYYMMDDHHmm').add(9, 'hours').format('HH:mm');
}
世界各地の標準時は協定世界時(UTC)を基準として定められており、日本標準時(JST)は、協定世界時より9時間進んでいます(東経135度分の時差)。
このことから、日本標準時にする為に9時間を加えています。
- 引数として受け取ったtimeに9時間を加えて、新しい時刻をHH:mm形式(例: 14:00)で返す関数です。
- momentライブラリを使って、文字列timeをYYYYMMDDHHmm形式で解析し、9時間を追加した結果をフォーマットして返しています。これにより、JST(日本標準時)としてラベルを表示することが可能になります。
②"現在"のラベル追加
labelsHtml.push(`${addNineHours(baseTimes1[0])}現在`);
- baseTimes1[0]は最新の基準時間(現在)を指し、これに9時間を加えて「現在」のラベルを表示します。
- left: 76.5%に配置しているため、スクロールバーの適切な位置にラベルが表示されます。
③ラベルの挿入
document.getElementById('scrollbar-labels').innerHTML = labelsHtml.join("");
- 配列labelsHtmlの要素をすべて文字列として結合し、scrollbar-labelsというIDを持つ要素のinnerHTMLに設定します。
- これにより、HTML要素内に生成したラベルのHTMLが挿入され、スクロールバー上に各時刻を表すラベルが表示されます。
このコードにより、ユーザーがスクロールバー上で時刻を視覚的に確認しやすくなり、地図の気象データがどの時刻に対応するかが直感的に理解できるようになります。
お疲れ様でした‼️
ここまで雨雲レーダーのアニメーション化の仕方について、解説しました。
私も雨雲レーダーのアニメーション化は、大変苦労しました。
それでもChatGPTを駆使して、作成する事が出来ました。
この記事を読んで頂き、皆さまが雨雲レーダーのアニメーション化が実現することを心より願っております。
分からないことがあれば、ChatGPTに聞きまくるのがお勧めです🤖
ご武運をお祈りしております‼️