Flashで雨雲レーダーをアニメーション表示する #2

ナウキャスト画像

ナウキャスト画像

Flashで雨雲レーダーをアニメーション表示する #1の続きです。

Flashアニメーションを生成するアプレットはFlanisを利用することとしました。これはこれで、それなりに準備が必要だったのですが、まずは雨雲レーダーが画像の元となるものを取得しなければなりません。
もちろん雨雲レーダーなどを観測する設備は持っていませんので、気象庁のナウキャストからレーダー画像を取得してみます。あくまで、技術的な実験です。

気象庁ホームページの「レーダー・ナウキャスト(降水・雷・竜巻):全国」のページを表示すると、雨雲レーダー画像が表示されます。
レーダー画像の上で、右クリックをして「画像のURL」を取得すると、「http://www.jma.go.jp/jp/radnowc/imgs/radar/000/201311051915-00.png」などとなります。末尾の「201311051915-00.png」が画像のファイル名となっており、ファイル名の一部に画像が表現している日時が埋め込まれています。この例ならば、2013年11月05日19時15分の雨雲レーダー画像という訳です。「-00」の部分は、現在の現況図であることを示しています。気象庁のナウキャストでは現在時から1時間後までの雨雲の予想図も表示ができるのですが、予想図のファイル名は末尾が「-01、-02・・・」などと未来になるほど大きくなっています。10分単位での予測のようですから、最も未来を表しているファイル名の末尾は「-06」となります。
また、/radar/000/の部分は、/radar/が降水強度分布予測画像であることを示しています。つまり雨雲レーダー画像というより、雨の強度を示した画像といった方が正確ですね。雷の発生地域を示している画像なら、この部分が/thunder/、竜巻のナウキャストなら/tornado/となるようです。その他の部分の構成は、降水強度も雷も竜巻のナウキャストもすべて同じです。

/radar/の後の/000/は全国図であることを示しています。たとえば中部地方なら、この部分が/210/となっています。したがって、この部分の数値を調査すれば任意の地方の画像を取得することができます。
さらに、これらの画像は5分おきに更新されているようです。

以上のことから、この降水強度の分布図を取得するためのスクリプトを作成するために分かったことを整理すると次のようになります。

  • ベースとなるURL「http://www.jma.go.jp/jp/radnowc/imgs/radar/000/」は不変である。
  • 現在時から、気象庁サイトにアップロードされているであろうナウキャスト画像のファイル名を、規則に基づいて生成する必要がある。
  • CRONで5分単位でスクリプトを実行し、連続的にナウキャスト画像を取得する必要がある。

さらにスクリプトの仕様として、次のようなことを定めておきます。

  • 直近2時間分の画像をまとめてアニメーション化したいので、24枚分の画像を常に処理の対象とする。
  • 2時間より前の画像は、アーカイブしておく。(使い道は考えていないけど・・・)

後はスクリプトを作りながら、必要な機能は追加していくことにします。
今回は、サーバサイドで運用することを考えていますので、Linuxで定番のPerlで記述することとします。
実際に作成したスクリプトは次のようなものです。時間短縮のために、CPANのモジュールをふんだんに使っちゃってます。

#!/usr/bin/perl
use strict;
use warnings;
use LWP;
use LWP::UserAgent;
use DateTime;
use DateTime::TimeZone;
use DateTime::Format::Strptime;
use LWP::Simple;
use File::Copy;

# 取得先(気象庁のNowcastから)
my $url_base = "http://www.jma.go.jp/jp/radnowc/imgs/radar/000/";
# &CreateFileNameで現在時から遡って24枚分のレーダー画像のファイル名を得る
my @TargetFiles = &CreateFileName;

my $i=0;
my $data;
# 画像保存先のディレクトリ名
my $newFileDir="/hoge/hogehoge/ani/images/";
# 古い画像の保存先ディレクトリ名
my $archivesDir="/hoge/hogehoge/ani/images/archives/";
# 画像保存先のディレクトリが存在しなければ作成する
&MakeDir($newFileDir);
&MakeDir($archivesDir);
# レーダー画像のダウンロード(全国版)
for($i=0;$i<=23;$i++){
	# ローカルにレーダー画像が存在しなければダウンロード処理へ
	if(&LocalFileExit($newFileDir . $TargetFiles[$i])==1){	
		# 気象庁サーバーに画像が存在すれば(コード200でチェック)ダウンロードを実行	
		if (&FileExit($url_base,$TargetFiles[$i])==0){
			$data=getstore($url_base.$TargetFiles[$i],$newFileDir . $TargetFiles[$i]);
		}
	}
}

# 現在時から25枚前以前の画像データを削除(移動)する
&DelOldFiles($newFileDir,@TargetFiles);
# FlAniSの設定ファイルを書き込む
&CreateLoadFile(@TargetFiles);


`convert -geometry 300x260! -delay 50 -loop 0 /hoge/hogehoge/ani/images/*.png /hoge/hogehoge/ani/images/animation_n.gif`;

#print "perl_n";

sub CreateLoadFile{
# FlAniSの設定ファイルを作成する
# GetTime関数用の引数として@parを確保する 内容はレーダー画像のファイル名を順次代入する
	my @par = @_;
	# FlAniSの設定ファイルのフルパス
	my $loadFile = "/hoge/hogehoge/ani/images/loadlist.txt";
	# ex:--->> ./ani/images/201103211840-00.png "2011/03/21 18:40" overlay=./ani/overlay.png
	# レーダー画像の保存先	
	#my $ServerPath = "./ani/images/";
	# オーバーレイ用ファイルの保存先
	my $overlayPath = "./ani/overlay.png";
	# レーダー画像のファイル名を時系列に並べ替え
	@par=reverse @par;
	@par=&GetTime(@par);
	# 配列の要素を改行コードを区切りとしてスカラー変数に一文で格納
	my $scalar=join("\n",@par)."\n";
	# ファイルをオープン	
	open(my $FH,">",$loadFile);
	# 上書きで書き込み	
	print $FH $scalar;
	close $FH;
}

sub GetTime{
# レーダー画像のファイル名から画像の撮影時刻を得る FlAにSの設定ファイル内に記述する
	my @FileName = @_;
	my @myArray = @FileName;
	#                        year      month     day       hour      minutes
@myArray = map {$_ =~ s/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})-00.png/\.\/ani\/images\/$1$2$3$4$5-00\.png "$1\/$2\/$3 $4:$5" overlay=\.\/ani\/overlay\.png/g; $_} @myArray;
	# print "$_=myArray\n" foreach(@myArray);
	return @myArray;
}

sub DelOldFiles{
	# 古いレーダー画像を削除する
	my @deletefile=@_;
	# 指定ディレクトリ内の拡張子pngのファイルの一覧を得る	
	my @file = glob $deletefile[0] . "*.png";
	# 配列の差分を格納する
	my @deff;
	# 配列の要素となっている文字列にディレクトリ部分を付加しフルパス名にする
	@deletefile = map {$_ =~ s/(\d+-00.png)/$deletefile[0]$1/g; $_} @deletefile;
	# 配列の先頭要素を削除する(引数として対象ディレクトリ名が格納されている)	
	shift @deletefile;
	# 配列を比較して、不一致のファイル名を抽出する
	# @fileにあって@deletefileにないものをリストする すべて一致すれば真を返す
	@deff = grep { !{map{$_,1}@deletefile }-> {$_}}@file;
	foreach my $temp (@deff){
		move $temp,$archivesDir;
	}
}

sub MakeDir{
# サーバーに引数で指定されたディレクトリが存在しなければ作成する
	my @dirname = @_;	
	if (!-d $dirname[0]){
		mkdir $dirname[0];
	}
	else{
		# print "Directory already exists!\n";
	}
}

sub LocalFileExit{
# ローカルのファイルの存在チェック ある:0 ない:1
	my @filename = @_;
	my $result;
	if( -f $filename[0] ) {
		$result=0;
	}
	else {
	   	$result=1;
	}
	#print "Result=",$result," filename=",$filename[0],"\n";
	return $result;
}

sub FileExit{
# Web上にあるファイルが存在するかチェックする	
	# 気象庁のサイトのURLおよび取得したいレーダー画像のファイル名を引数として受け取り配列に格納する
	my @array = @_;
	my $url = "http://www.chameleon-weather.net/x.report.html";
	my $ua = LWP::UserAgent->new;
	# ダウンロード用URLを引数から作成
	$url=$array[0] . $array[1];
	# ユーザーエージェントをIE9互換にする
	$ua->agent("Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)");

	# リクエスト
	my $request = new HTTP::Request(HEAD => $url);
	my $header = $ua->request($request);
	my $result;
	# ヘッダ出力のチェック(ファイルが存在すればレスポンスにエラーコード「200」が返る) 
	if ($header->is_success) { 
		$result=0;
	}
	else{
		$result=1;
	}
	return $result;
}

sub CreateFileName{
	# 現在時間から24枚分の画像ファイル名を生成する(過去に向かって5分刻み)
	# レーダー画像の生成される間隔(5分)	
	my $IntervalTime = 5;
	my $i = 0;
	my @ListRadarFiles;
	# DateTimeオブジェクトを生成する
	my $time_zone = DateTime::TimeZone->new(name=>'Asia/Tokyo');
	my $dt=DateTime->now(time_zone => $time_zone);
	# 現在時から分を切り出す	
	my $min=$dt->strftime("%M");
	# 5分の倍数で切りのいい直近の「分」を作成する	
	$min=int($min / $IntervalTime) * $IntervalTime;
	# 現在時に基づいて基準になる時刻を文字列として作る
	my $fname=$dt->strftime("%Y/%m/%d %H");
	# 分と秒を連結
	$fname=$fname . ":" . $min .":00";
	# 現在時に基づいて基準となる時刻のオブジェクトを生成する
	my $basetime = DateTime -> new(year => $dt -> strftime("%Y"),month => $dt->strftime("%m"),day => $dt -> strftime("%d"),hour => $dt -> strftime("%H"),minute => $min,second => 0,time_zone => 'Asia/Tokyo');
	# 生成したDateTimeオブジェクトを一時変数に格納
	my $temp=$basetime;
	my $strp;
	for($i=0;$i<=23;$i++){
		# 5分ずつ減算
		$ListRadarFiles[$i]=$temp -> subtract(minutes => ($IntervalTime * $i)) -> strftime("%Y%m%d%H%M-00.png");
		# 変数$tempに再度、起点となる現在時をセットしなおす(時間計算をすると$tempの起点時間が変わってしまうため)
		$strp=DateTime::Format::Strptime->new(pattern=>'%Y-%m-%dT%H:%M:%S');
		$temp=$strp->parse_datetime($basetime);
	}
	return (@ListRadarFiles);
}

上記のコードは、ナウキャスト画像を取得するだけでなく、2時間以上前の古い画像は削除して、アーカイブ用ディレクトリにアーカイブしておくように記述しています。
また、フラッシュアニメーション作成用のプログラムFlanisのための、各種設定ファイルを自動生成するルーチンも書いています。このあたりのことは、また次回以降に記述していきます。
ナウキャスト画像を取得するための具体的なプログラムの動きはコードを読解すれば分かると思います。適宜コメントも入れておきました。少し未整理で、もっといい記述方法ができそうな部分も多々ありますが、とりあえず初期の目的は達成できています。

Flashで雨雲レーダーをアニメーション表示する #1


Flashで雨雲レーダーをアニメーション表示してみたので、作り方を何度かに分けて記録しておくことにします。

方針として、

  • 気象庁のレーダー画像を取得する。
  • 直近2時間分をコマ送りのフラッシュアニメーションにして、雲が動いているように見せる。
  • 任意のポイントで、アニメーションを止めることができる。
  • 再生スピードを調整できる。
  • 自動的にアニメーションを生成し、常に最新のレーダー画像となるようにする。
  • 任意の画像をオーバーレイできるようにしておく。
  • サーバーサイドで運用する。

というような仕様とする。

これらのことを考え合わせて、諸条件をクリアできて、複雑な設定が必要ないものを探したところ、次のようなものが見つかりました。

Documentation for FlAniS – the Flash AnimationS applet

Exampleのページを見ると、フラッシュアニメーションを様々にコントロールする機能が実装できることがわかります。

ここで学習して一気に作り上げたものが、この記事の最上部で表示しているアニメーションです。時間をおいてページを表示すると、常に直近のレーダー画像に更新されているのが確認できます。
次以降の記事で、構築までの手順を記載していきます。