自分用のプログラムをいろいろ書いてるのなかで、気がついたことを記しています。
イベントがないと表示されないのは、ロード後のでは遅いということのようなので、修正。 photoのelementに対して、onloadのイベントハンドラを追加すればよさそうなのだが、うまくいかず。 また、AJAX化の前に、PHP化。と言っても単にCGI的にGETコマンドラインで引数をとるようにしただけ。
var photoInfo = Object(); var viewInfo = Object(); + photo.src = +"?" + new Date().getTime(); // AJAX化したときは、EXIF情報で埋め込み + photoInfo.width = ; // ditto. + photoInfo.height = // ditto.
追加は上のとおり、viewInfoの下に3行追加。削除は下のとおりに3行削除。
- photo.src = "/image.jpg?" + new Date().getTime(); // AJAX化したときは、動的に与える予定。Dateはキャッシュさせないため。 - photoInfo.width = 3872; // AJAX化の暁には、EXIF情報で埋め込む予定。 - photoInfo.height = 2592 // ditto.
このファイルに対して、?imgName=<イメージファイル名>&imgWidth=<イメージの幅>&imgHeight=<イメージの高さ>&とURL入力すればOK。
Erlang B式を計算させるためのexcelマクロです。標準マクロとして登録することでワークシート関数として使えます。 マクロの作成は、
ツール -> マクロ -> Visual Basic Editor 挿入 -> 標準モジュール ファイル -> 終了してMicrosoft Excelに戻る
そこで、下記の関数を作成。
Function erlang_b(a, s) Dim b As Double b = 0 For i = 0 To s b = i / a * b + 1 Next i erlang_b = 1 / b End Function
aは呼量[Erl]。Sは出線数で、呼損率を求めます。
HTML5のCANVUS機能を使ってbauncerを作ってみた。とりあえずGoogle Chromeで動作を確認。FireFoxでも見れるはず。 IEについては、explorercanvasで互換表示しています。
動作サンプルはこちら。マウスクリックで動作します。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta http-equiv="Content-Language" content="ja" /> <meta http-equiv="Content-Style-Type" content="text/css" /> <meta http-equiv="Content-Script-Type" content="text/javascript" /> <title>Canvas</title> <!--[if IE]><script type="text/javascript" src="./excanvas.js"></script><![endif]--> <script type="text/javascript"> var canvas; // キャンバスオブジェクト var cntxt; // コンテキスト var xSizeMax = 600; // キャンバスのサイズ(x) var ySizeMax = 480; // キャンバスのサイズ(y) var timer; // インターバルタイマのID var vh; // 水平方向の速度。ボタンを押している長さに反比例。 var vv; // 垂直方向の加速度 var speed; // 垂直方向の速度 var xPos=0; // 水平方向のボールの場所 var yPos=200; // 垂直方向のボールの場所 var color; // 指定色 function init() { /* キャンバスの初期化 */ canvas = document.getElementById('bouncecanvas'); cntxt = canvas.getContext('2d'); if ( ! canvas || ! canvas.getContext) { return false; } } // init() function flick(mode) { if (mode==0) { // ボタンが押されたとき vh = (new Date()).getTime(); } else { // ボタンを離したとき /* 離したタイミングでx方向の速度が変わる */ vh = (new Date()).getTime() - vh; vh = xSizeMax/vh; /* いろいろ初期化 */ vv = -1; speed=0; yPos=ySizeMax; xPos=0; color=0xFF0000; /* 描画エリアをクリアする */ cntxt.beginPath(); cntxt.clearRect(0,0,xSizeMax,ySizeMax); // 描画エリアをクリア timer = setInterval("draw()",10); // 10ms毎に描画関数を呼び出す。 } } // flick() function draw() { /* X方向への移動 */ xPos += vh; if ( xPos > xSizeMax ) { // 右端まで表示したら終了(タイマをクリア) clearInterval(timer); } /* y方向への移動 */ speed += vv; // 加速度分スピードアップ yPos += speed; // スピード分 垂直位置を変更 /* バウンド処理 */ if ( yPos < 0 ) { yPos=0; speed *= -0.8; } /* 色のバレルシフト */ if ( 1 & color) { // 最下位ビットが1ならば color >>= 1; color += 0x800000; // 最上位に1ビット追加 } else { color >>= 1; } /* 描画処理 */ cntxt.beginPath(); cntxt.fillStyle = '#'+color.toString(16); // #rrggbb形式で色を設定 cntxt.arc(Math.floor(xPos),ySizeMax-Math.floor(yPos),2,0,Math.PI*2, false); // 座標系を変換して表示 cntxt.fill(); // ボールを表示 } </script> </head> <body onload="init()"> <h1>bouncer#1</h1> <div style="text-align:center;"> <canvas style="border:1px solid gray;" id="bouncecanvas" width="600" height="480"></canvas> </div> <div style="align:center"> <input type="button" value="click me" onMouseDown="flick(0)" onMouseUp="flick(1)" / > <div style="align:center"> </body> </html>
とりあえず、GoogleEarth pluginで軌跡を表示できるようにはなった。
次は、とってきた写真をGoogleEarth上マッピングしたい。KASHMIR 3Dなどを使えばできそうであるが、 それだけを使うのは「鶏を割くに焉んぞ牛刀を用いん」という感じ。なので、perlでKMLを履かせるスクリプトを書いてみた。
以下がそのスクリプトだが、perlのXML系のライブラリはいま一つ使い勝手が悪くて、Geo::GPXなどは使うことをあきらめた。
このスクリプトは、引数として、gpxファイルとexifデータをもった写真のファイルを要求し、標準出力にkmlを吐くというもの。 GPXの時間データ 写真ファイルはglobとして展開するので、ワイルドカードも使えます(unix系では要クオート)。
perl kml-map-photo.pl --gpx="./0730.gpx" --pfile="./images/*.JPG" --correction=-150 > photo-0730.kml
correctinオプションは、カメラの時計とGPSの時間がずれていた場合に補正するためのもの。 非常に遅いが、一応マッピングできた。
#/usr/bin/perl use strict; use XML::Simple; use Getopt::Long; use Time::Local; use IO::File; use Image::ExifTool qw(:Public); use Geo::GoogleEarth::Document; #******************************************************************** #******************************************************************** sub gpx2tm($$) { # use Time::Local; # @$tm : GPXファイルからTrackPointの時系列データを格納するための配列 # $tm->[i]->( # 'time' => TrackPointのgmt 1900/1/1 0:0:0からの経過秒 # 'lat' => TrackPointの緯度 # 'lon' => TrackPointの経度 # 'ele' => TrackPointの高度 # ) # @$in : GPXファイルをXL::Simple->XMLin()で展開したハッシュツリー my ($tm,$in) = @_; my $REF_CHECK = ref($in); # 引数として引き渡されたリファレンスの種別を判定 if ($REF_CHECK eq 'HASH') { # ハッシュの場合 if (defined($in->{'lat'})) { # 緯度が記録されていたら、そのリファレンスはtrkptと判断 # 時刻形式は、ISO 8601形式 & UTCと決め打ち $in->{'time'} =~ /^(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)Z$/; # 特に問題ないので、UTC = GT my $gt = timegm($6,$5,$4,$3,$2-1,$1); push(@$tm,{( # 無名リファレンスの形式で配列に追加 'gmt' => $gt, 'lat' => $in->{'lat'}, 'lon' => $in->{'lon'}, 'ele' => $in->{'ele'}, )} ); return $in; # GPXデータポイントを追加した場合は、処理したハッシュを返す。 } else { # それ以外は、さらに展開 foreach my $out (keys(%$in)) { &gpx2tm($tm,$in->{$out}) if ref($in->{$out}) ne 'SCALAR'; } } } elsif ($REF_CHECK eq 'ARRAY') { # 配列の場合は、その回数分展開し、再帰的に呼び出す my $i_max = @$in; for ( my $i = $[ ; $i < $i_max ; $i++) { &gpx2tm($tm,$in->[$i]) if ref($in->[$i]) ne 'SCALAR'; } } return 0; # 配列への登録がなかった場合は、0を返す。 } # gpx2tm #******************************************************************** #******************************************************************** sub exif_times($$$$) { #******************************************************************** # Exifデータから、タイムスタンプを作成する # 引数1 タイムゾーン # 引数2 時刻補正(GPXデータとEXIFデータの時計ずれ) # 引数3 暦時間をキーとしたハッシュ(戻り値) # %ptm key --> "exifデータのタイムスタンプ(GMT)" # %ptm value --> "ファイル名" # 連写するとバッティングするが、普通は連写データを全部は公開しないので大丈夫。 # 引数4 画像ファイル配列 #******************************************************************** my ($tz,$crct,$ptm,$in) = @_; my($exifTool) = new Image::ExifTool; foreach my $f ( @$in ) { my($exifInfo) = $exifTool->ImageInfo($f); # exif データからタイムスタンプを取得 $exifInfo->{'DateTimeOriginal'} =~ /(\d+)[:\/](\d+)[:\/](\d+) (\d+):(\d+):(\d+)/; # 暦時間に変換 my $lt = timegm($6,$5,$4,$3,$2-1,$1); $f =~ /([^\/\\]*$)/; $ptm->{$lt-3600*$tz+$crct} = $1; } } #eixf_times #******************************************************************** #******************************************************************** sub time_format($$) { #******************************************************************** # タイムスタンプから日時文字列を生成する。 # 引数1 タイムシリアル # 引数2 タイムゾーン #******************************************************************** my ($tserial,$tz) = @_; my @tm = gmtime($tserial+$tz*3600); return sprintf("%d/%d %02d:%02d:%02d",$tm[4]+1,$tm[3],$tm[2],$tm[1],$tm[0]); } #time_format ##################################################################### # # メインルーチン # ##################################################################### #******************************************************************** # 定数 #******************************************************************** my $base = 'url of basement'; my $image_dir = 'directory of images'; my $thumbnail_dir = 'directory of thumbnails'; #******************************************************************** my ($gpx_file); # 読み込むGPXファイル my ($kml_file); # KMLファイルの場所指定(未使用) my ($photo_glob); # 写真保管ディレクトリ my ($xml); # GPXファイルの読み込み用 my (@Placemark); # KML出力用 my (%exif_tm); # EXIFデータ時間 my ($tz); # タイムゾーン my ($crct); # 時刻修正(GPSとカメラの差分) my $kml = Geo::GoogleEarth::Document->new(); GetOptions('gpx=s' => \$gpx_file, 'kml=s' => \$kml_file, 'pfile=s' => \$photo_glob, 'TZ=i' => \$tz, 'correction=i' => \$crct); if ( $gpx_file ) { # GPXファイルが指定されていた場合はXMLデータとして変数にすべて読み込む。 open FH, $gpx_file; local $/; $xml = <FH>; close FH; } my $parser = XML::Simple->new; my $parsed_xml = $parser->XMLin($xml); #******************************************************************** # デフォルト値 #******************************************************************** $tz = +9 if !defined($tz); # EXIFデータのタイムゾーン。指定されなかった場合はJST(+9)。 $crct = 0 if !defined($crct); # EXIFデータとカメラの時間差補正。 #print "TimeZone : $tz \t Time Correction : $crct\n"; my @timestump; &gpx2tm(\@timestump,$parsed_xml); @timestump = sort { $a->{'gmt'} <=> $b->{'gmt'} } @timestump; # 時刻順にソート my @photo_files = glob $photo_glob; # pfileで指定された文字列をglobとして展開。 &exif_times($tz,$crct,\%exif_tm,\@photo_files); my $min_array = $[; # タイムスタンプ配列の最小値 my $max_array = $#timestump; # タイムスタンプ配列の最大値 foreach my $tm (sort keys(%exif_tm)) { my $startp = $min_array; my $endp = $max_array; if ($timestump[$startp]->{'gmt'} > $tm) { # GPXデータの最小値より小さい場合は次のデータを処理 next; } elsif ($timestump[$endp]->{'gmt'} < $tm) { # GPXデータの最大値より大きくなったら終了 last; } for ( my $p = int(($startp+$endp)/2); ; ) { my $tmp; if ($timestump[$p]->{'gmt'} == $tm) { $kml->Placemark( name => &time_format( $tm, $tz ), lat => $timestump[$p]->{'lat'}, lon => $timestump[$p]->{'lon'}, alt => $timestump[$p]->{'ele'}, description => '<a class="gge_photo" href="'.$base.$image_dir.$exif_tm{$tm}.'" target="_photo">'. '<img src="'.$base.$thumbnail_dir.$exif_tm{$tm}.'" /></a>', ); last; } elsif($timestump[$p]->{'gmt'} > $tm) { if ($endp>$p) { $endp=$p; $p = int(($startp+$endp)/2); } else { $kml->Placemark( name => &time_format( $tm, $tz ), lat => ($timestump[$p]->{'lat'}+$timestump[$startp]->{'lat'})/2, lon => ($timestump[$p]->{'lon'}+$timestump[$startp]->{'lon'})/2, alt => ($timestump[$p]->{'ele'}+$timestump[$startp]->{'ele'})/2, description => '<a class="gge_photo" href="'.$base.$image_dir.$exif_tm{$tm}.'" target="_photo">'. '<img src="'.$base.$thumbnail_dir.$exif_tm{$tm}.'" /></a>', ); last; } } else { if ($startp<$p){ $startp=$p; $p = int(($startp+$endp)/2); } else { $kml->Placemark( name => &time_format( $tm, $tz ), lat => ($timestump[$p]->{'lat'}+$timestump[$endp]->{'lat'})/2, lon => ($timestump[$p]->{'lon'}+$timestump[$endp]->{'lon'})/2, alt => ($timestump[$p]->{'ele'}+$timestump[$endp]->{'ele'})/2, description => '<a class="gge_photo" href="'.$base.$image_dir.$exif_tm{$tm}.'" target="_photo">'. '<img src="'.$base.$thumbnail_dir.$exif_tm{$tm}.'" /></a>', ); last; } } } } print $kml->render;