自分用のプログラムをいろいろ書いてるのなかで、気がついたことを記しています。
イベントがないと表示されないのは、ロード後のでは遅いということのようなので、修正。 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;