トップ «前の日記(2008-04-15) 最新 次の日記(2008-05-01)» 編集

tito Memo

2003|01|02|03|04|05|06|07|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|
2010|02|04|05|06|08|
2011|01|02|03|08|10|12|
2012|01|
2013|03|04|

個人的なメモを記していくためのページです。


2008-04-27

* アメダスのデータを半リアルタイムで自動的にグラフ化

http://gqg.jp/files/amedas.html

rrdtoolを使うと簡単だった。

参考ページ
定期的に実行されるスクリプト

自宅の玄箱で次のようなスクリプトを定期的に実行するようにします。

     1	#!/bin/sh
     2	
     3	
     4	LOC=44132
     5	
     6	cd $HOME/public_html/amedas/ || exit
     7	
     8	HOUR=`date +%H`
     9	
    10	case $HOUR in
    11	00)
    12	wget -q -N http://www.jma.go.jp/jp/amedas_h/yesterday-${LOC}.html
    13	perl update-amedas.pl yesterday-${LOC}.html | sh
    14	;;
    15	*)
    16	wget -q -N http://www.jma.go.jp/jp/amedas_h/today-${LOC}.html
    17	perl update-amedas.pl today-${LOC}.html | sh
    18	;;
    19	esac
    20	
    21	sh ./make-graph-2.sh ${LOC}
    22	scp foo.png ${HOST}:${WWW_PATH}/qmd/files/amedas-${LOC}.png
このスクリプトは以下のことをします。
  1. 気象庁のページからアメダスの該当ページを取得し
  2. そのページから最新のデータを抽出してrrdtoolに渡し
  3. 更にrrdtoolのグラフを作成
  4. できたグラフを公開サーバーへ転送
cronには以下のような行を設定しました。
23 * * * * /home/tito/public_html/amedas/fetch-amedas.sh >/dev/null 2>&1
update-amedas.plは以下のperlスクリプトです。表中の最近のデータを取得しrrdtoolのコマンドラインを生成します。生成されたコマンドはfetch-amedas.sh でパイプラインでつながったシェルに渡され実行されています。しかしこれはセキュリティ的に問題が発生しやすいのでperlで適当なモジュールを使ってrrdtoolを起動するべきでしょう。一応以下のスクリプト中でもデータのサニタイズ等に気は使ったつもりですが。
     1	#!/usr/local/bin/perl
     2	
     3	use utf8;
     4	
     5	use strict;
     6	use Data::Dumper;
     7	use HTML::TreeBuilder;
     8	
     9	use Time::Local;
    10	
    11	binmode STDOUT, ":encoding(euc-jp)";
    12	binmode STDERR, ":encoding(euc-jp)";
    13	
    14	our %wd;
    15	our $location_code;
    16	our( $year,$month,$day, $location, $location_yomi);
    17	our $hour;
    18	our $last_hour;
    19	our $time_0 ;
    20	our @dp_hour;
    21	
    22	
    23	my $i = 0;
    24	foreach ("北", "北北東", "北東", "東北東",
    25	     "東", "東南東", "南東", "南南東",
    26	     "南", "南南西", "南西", "西南西",
    27	     "西", "西北西", "北西", "北北西"){
    28	        $wd{$_}=$i++;
    29	utf8::encode( $wd{$_});
    30	}
    31	
    32	
    33	foreach my $file (grep( -f $_, @ARGV)) {
    34	  ($location_code) = $file=~ /-([0-9]+).html/;
    35	    
    36	    
    37	  open FH, "<:encoding(Shift_JIS)", $file || die;
    38	  my @c = <FH>;
    39	  my $contents = join('', @c);
    40	  close(FH);
    41	  my $h = HTML::TreeBuilder->new;
    42	utf8::encode( $contents );
    43	  $h->parse($contents);
    44	
    45	  my @title = $h->look_down( "_tag", "table",
    46	                             "id", "tbl_title" );
    47	  my @tr = $title[0]->look_down( "_tag", "tr" );
    48	  
    49	  my $title=$tr[1]->as_text();
    50	utf8::decode($title);
    51	  ( $year,$month,$day, $location, $location_yomi) =
    52	      $title =~ /(.*)年(.*)月(.*)日 (.*)\((.*)\)/;
    53	  $hour=0;
    54	  $time_0 = timelocal( 0,0,0, $day, $month-1, $year );
    55	
    56	  
    57	  my @table = $h->look_down( "_tag", "table",
    58	                             "id", "tbl_list");
    59	
    60	  @tr = $table[0]->look_down( "_tag", "tr" );
    61	
    62	  my @dp= $tr[0]->look_down( "_tag", "td" );
    63	  my @dp_name = ();
    64	  foreach (@dp){
    65	 	my $cc = $_->as_text();
    66		utf8::decode( $cc );
    67	      push( @dp_name, $cc );
    68	  }
    69	  
    70	  $hour =0;
    71	  @dp_hour=();
    72	
    73	  foreach my $j( @tr[2..$#tr] ){
    74	      my @td = $j->look_down( "_tag", "td" );
    75	
    76	      my $i = 0;
    77	      $hour = $td[0]->as_text();
    78	       $hour =~ y/^0-9//;
    79	
    80	      $dp_hour[ $hour ]={};
    81	      my $dp_count=0;
    82	      foreach (@td){
    83	          my $c = $_->as_text();
    84	          $c =~ s/\x{00a0}//;
    85	          if( $c ne ""){
    86	              $dp_hour[$hour]->{ $dp_name[ $i ] } = $c;
    87	              $dp_count++;
    88	          }
    89	          $i++;
    90	#          print " $c";
    91	      }
    92	      if( $dp_count > 1){
    93	          $last_hour = $hour;
    94	#  rrd_update( $last_hour );
    95	
    96	      }
    97	#      print "\n";
    98	
    99	  }
   100	  rrd_update( $last_hour );
   101	  
   102	  
   103	  $h = $h->delete(); # nuke it!
   104	}
   105	exit;
   106	
   107	sub sanitize
   108	{
   109	    my($v) = @_;
   110	    $v =~ y/0-9.//cd;
   111	    return $v;
   112	}
   113	
   114	sub rrd_update
   115	{
   116	
   117	    my($last_hour) = @_;
   118	  print STDERR "$year-$month-${day}T${last_hour}:00:00 $location($location_yomi) $location_code\n";
   119	
   120	  my $d = $dp_hour[ $last_hour ];
   121	  my $cmd;
   122	  $cmd  = "rrdtool update $location_code.rrd ";
   123	  $cmd .= join( ':',
   124	      sprintf("%d",$time_0+$last_hour*3600),
   125	      &sanitize($d->{気温}),
   126	      &sanitize($d->{降水量}),
   127	      $wd{$d->{風向}},
   128	      &sanitize($d->{風速}),
   129	      &sanitize($d->{日照時間}),
   130	      &sanitize($d->{積雪深}),
   131	      &sanitize($d->{湿度}),
   132	      &sanitize($d->{気圧}));
   133	
   134	  print "$cmd\n";
   135	
   136	  $cmd  = "rrdtool update $location_code-y.rrd ";
   137	  $cmd .= join( ':',
   138	      sprintf("%d",$time_0+$last_hour*3600+24*3600 ),
   139	      &sanitize($d->{気温}),
   140	      &sanitize($d->{降水量}),
   141	      $wd{$d->{風向}},
   142	      &sanitize($d->{風速}),
   143	      &sanitize($d->{日照時間}),
   144	      &sanitize($d->{積雪深}),
   145	      &sanitize($d->{湿度}),
   146	      &sanitize($d->{気圧}));
   147	
   148	  print "$cmd\n";
   149	
   150	
   151	}
   152	
   153	
   154	__END__

ところどころ utf8::decode、utf8::encodeが入っているのが魔法的ですが私もよく判っていません。別の環境ではこれが無くても動作しました。(入れると動かない)

amedasによって得られるデータの種類は地点によって違います。気象台がある地点以外では種類が少なく降水量しかなかったりします。一応他の地点でも使えるようにデータの種類も表中から得るようにしています。

rrdupdateサブルーチンではrrdtool update行を2行出力していますがこれは一つのグラフに今日のデータと一日前のデータと両方を出すためのトリックです。(RRDtoolの使い方のp.52) 例えば 44132.rrd と 44132-y.rrd というデータベースを用意して 44132-y.rrdには時間をずらしてデータを入れます。

グラフを生成するスクリプトです。rrdtool graphを呼び出しているだけです。

     1	#!/bin/sh
     2	
     3	LOC=$1
     4	
     5	cat <<EOF | rrdtool -;
     6	graph foo.png \
     7	 -s `date +%s -d '1 day ago'`  \
     8	 -e `date +%s`  \
     9	 -a PNG \
    10	 DEF:temp=${LOC}.rrd:temp:AVERAGE \
    11	 GPRINT:temp:LAST:"temp %3.1lf\r" \
    12	 DEF:rh=${LOC}.rrd:rh:AVERAGE \
    13	 CDEF:rh1=rh,100,/,10,* \
    14	 GPRINT:rh:LAST:"rh %3.1lf\r" \
    15	 DEF:tempy=${LOC}-y.rrd:temp:AVERAGE LINE1:tempy#ff8888:"temp-y" \
    16	 LINE1:temp#ff0000:temp \
    17	 DEF:rhy=${LOC}-y.rrd:rh:AVERAGE \
    18	 CDEF:rh2=rhy,100,/,10,* LINE1:rh2#88ccff:"rh-y" \
    19	 LINE1:rh1#0033cc:rh \
    20	 --width=400 --height=120 \
    21	 --title "Tokyo Temp/RH (derive from AMEDAS)"
    22	EOF

rrdtoolのデータベースを作るスクリプト。 以上のスクリプトのためには1地点について二つのrrdファイルが必要です。

$ ./createrrd 44132
$ ./createrrd 44132-y
     1	rrdtool create $1.rrd --step 3600 \
     2	--start 1195189900 \
     3	DS:temp:GAUGE:7000:-50:50 \
     4	DS:prec:GAUGE:7000:0:100 \
     5	DS:wd:ABSOLUTE:7000:U:U \
     6	DS:ws:GAUGE:7000:0:30 \
     7	DS:dl:GAUGE:7000:0:1 \
     8	DS:sg:GAUGE:7000:0:U \
     9	DS:rh:GAUGE:7000:0:100 \
    10	DS:ap:GAUGE:7000:900:1100 \
    11	RRA:AVERAGE:0.5:1:48 \
    12	RRA:AVERAGE:0.5:24:30 \
    13	RRA:MIN:0.5:12:63 \
    14	RRA:MAX:0.5:12:63 \
    15	RRA:LAST:0.5:1:48
必要なツール/モジュール等のインストール

debianならば rrdtool と libhtml-tree-perl をaptやaptitude でインストールします。手動でインストールする場合は perl のモジュールは HTML::TreeBuilder です。 rrdtoolも最新版だと グラフィックスがcairoベースできれいになってたりするので そちらを入れてもよいでしょう。debian sargeのパッケージでは1.0.49ですが別マシンではrrdtool-1.2.99908020600を入れて使っています。その場合のグラフはこんな感じです。

rrdtool graph
[]

このページ内のリンクがクリックされたことを知るのにjavascriptを使用しています。javascriptを無効にするとあなたがどのリンクをクリックしたか通知されなくなります。javascriptを無効にしてもこのページの閲覧に問題はありません。javascriptを無効にするページもご覧ください。
クラシック・ドーム クラシック・ドームソフト・ドーム ソフト・ドームソフト・リム ソフト・リム
キャップ10個入り。3種類の詰め合わせ(3種x2=6個入り)の、トラックポイント・キャップ・コレクションはIBMダイレクトで945円(税込み)