予告通り、ローカル領域にあるjpegファイルのEXIF情報を表示するWEBサービス、EXIFviewerの機能を追加してみた。今回追加した機能は、jpegファイルのEXIFの中に撮影地情報が格納されていた場合、Google Mapsで表示するというもの。EXIFファイルの中のGPS情報をテーブルに表示するまでは前のヴァージョンでも実現していたため、今回の説明はGoogle Maps APIと連携する際の注意点が中心となる。
連携の前段階、既存プログラムの手直し
前のヴァージョンのEXIFviewerの作成手順では、表の成形部分で特にid指定などもしないセルを吐き出していた。今回、緯度経度情報をGoogle Maps APIに渡すには、JavaScript(jQuery)を使ったクライアントサイドでの値取得が必要となる。そのため、値の入ったセルにidで一意の名前をつけるようにする。また、前回手を付けられなかった値のサニタイジングもついでに行う。
if(!empty($_POST["path"])){
if($exif = exif_read_data($_POST["path"],NULL,true)){
echo "<h2>EXIF情報</h2>";
echo "<table class='exiftable table table-striped'><thead><tr><th>キー名</th><th>値</th></tr></thead><tbody>";
foreach ($exif as $key => $section) {
foreach ($section as $name => $val) {
if(is_array($val)){
foreach ($val as $valname => $vval) {
echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($valname,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name . $valname,ENT_QUOTES,"UTF-8") . '">' . "$vval</td></tr>";
}
} else {
echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name,ENT_QUOTES,"UTF-8") . '">' . htmlentities($val,ENT_QUOTES,"UTF-8") . "</td></tr>";
}
}
}
echo "</tbody></table>";
} else {
echo "画像のEXIFデータが見つかりませんでした。";
}
} else {
echo "画像をセットして下さい。";
} |
if(!empty($_POST["path"])){
if($exif = exif_read_data($_POST["path"],NULL,true)){
echo "<h2>EXIF情報</h2>";
echo "<table class='exiftable table table-striped'><thead><tr><th>キー名</th><th>値</th></tr></thead><tbody>";
foreach ($exif as $key => $section) {
foreach ($section as $name => $val) {
if(is_array($val)){
foreach ($val as $valname => $vval) {
echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($valname,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name . $valname,ENT_QUOTES,"UTF-8") . '">' . "$vval</td></tr>";
}
} else {
echo "<tr><td>" . htmlentities($key,ENT_QUOTES,"UTF-8") . ' ' . htmlentities($name,ENT_QUOTES,"UTF-8") . "</td><td id=" . '"' . htmlentities($name,ENT_QUOTES,"UTF-8") . '">' . htmlentities($val,ENT_QUOTES,"UTF-8") . "</td></tr>";
}
}
}
echo "</tbody></table>";
} else {
echo "画像のEXIFデータが見つかりませんでした。";
}
} else {
echo "画像をセットして下さい。";
}
これがPHP部分。画像のEXIFにGPSの情報が不足なく入っていた場合に初めて処理を行うようにするので、このPHP部分で情報の不足をチェックし、地図表示の実行フラグとして非表示のセルにでも入れれば、JavaScript側の記述がスマートに済む筈。今回それをしなかったのは、不特定多数が利用する可能性があるため、出来るだけサーバ側の負担を軽く、クライアントサイドで処理を担保してもらうように設計したため。
Google Maps APIの使用方法
Google Maps APIを使いたい場合、htmlのヘッダ部で読み込む事になる。
<head>
.
.
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
</head> |
<head>
.
.
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
</head>
こういった具合。クエリストリングのsensor=falseという部分は、利用者の位置情報を求めるかどうかということで、falseに設定しておけば鬱陶しい確認ダイアログが出ないだろう。
今回は、id=”googlemapdiv”というdiv要素の中にマップを表示する場合で説明する。jQueryのreadyの中に、以下のように記述した。
$(function(){
.
.
var option = {
zoom: 17,//地図の縮尺初期設定(0〜19)
center: new google.maps.LatLng(imagelat,imagelong),
//地図の中心の座標 LatLng(緯度,経度)
//とりあえず今回はimagelat,imagelngというユーザ定義変数を使った
//もちろんこれより前に宣言されてないといけない
mapTypeId: google.maps.MapTypeId.ROADMAP,//地図の種別初期設定
zoomControl: true,//ズームGUIの表示非表示
streetViewControl: true,//ストリートビュー表示可能にするか
scaleControl: true,//ズームスライダーGUIの表示非表示
mapTypeControl: true//地図の種別切り替えGUIの表示非表示
};
var map = new google.maps.Map($("#googlemapdiv")[0],option);
var marker = new google.maps.Marker({
position : option.center,
map : map
});
}); |
$(function(){
.
.
var option = {
zoom: 17,//地図の縮尺初期設定(0〜19)
center: new google.maps.LatLng(imagelat,imagelong),
//地図の中心の座標 LatLng(緯度,経度)
//とりあえず今回はimagelat,imagelngというユーザ定義変数を使った
//もちろんこれより前に宣言されてないといけない
mapTypeId: google.maps.MapTypeId.ROADMAP,//地図の種別初期設定
zoomControl: true,//ズームGUIの表示非表示
streetViewControl: true,//ストリートビュー表示可能にするか
scaleControl: true,//ズームスライダーGUIの表示非表示
mapTypeControl: true//地図の種別切り替えGUIの表示非表示
};
var map = new google.maps.Map($("#googlemapdiv")[0],option);
var marker = new google.maps.Marker({
position : option.center,
map : map
});
});
コントロールの指定については必須ではない。google.maps.Mapメソッドに引数として地図の設置場所(DOMオブジェクトで指定)と、各種オプションを代入する。
Google Maps API用に緯度経度を10進法に変換する
さて、お膳立ても出来たので、単純にPHP側で成形した表のGPS情報の部分を取得し、Google Maps APIに渡せば良い、、とはいかない。
iPhone等で記録されるEXIFのGPSデータは、60進法で記録されているが、Google Maps APIが受け付けるのは10進法の数字なのだ。そこで、画像から取得した緯度経度を60→10進法に変換する処理を用意しないといけない。
60→10進法変換の方法だが、たとえば60進法で12.3という数字があった場合、10の位の1は10進法の60*1を表し、1の位の2は1*2、小数点第一位の3は60分の1*3を表している…といった数学の知識を、思い出してほしい。iPhoneなどが採用している60進法の表示方式では、”北緯36度46.5分0秒,東経137度54.52分0秒”といったように書くが、これは成形された表ではこのように表示されている。
EXIFでの緯度経度
GPSLatitudeRefやGPSLongitudeRefが東西南北(EWSN)を表している事は分かり易い。GPSLatitude 0が表すのは、緯度の”度”の部分。”36/1″という数字は、分母が1で分子が36ということ、つまり分数だ。同様に、GPSLatitude 1は100分の4650、つまり46.5分を表すといったように、面倒臭い書き方をしている。しかもこの値が60進法なのだ。
変換手順は、分数の文字列をなんとか少数に直して、それから10進法への変換を行うという形になる。
そして最後に10進法に直した後の値に、西経、南緯の場合マイナスをつけるという手順も必要だ。
//緯度を求める
var imagelat = parseFloat($("#GPSLatitude0").text().split("/")[0])/parseFloat($("#GPSLatitude0").text().split("/")[1]) + parseFloat($("#GPSLatitude1").text().split("/")[0])/(parseFloat($("#GPSLatitude1").text().split("/")[1])*60) + parseFloat($("#GPSLatitude2").text().split("/")[0])/(parseFloat($("#GPSLatitude2").text().split("/")[1])*3600);
if($("#GPSLatitudeRef").text() == "S"){
imagelat = "-" + imagelat;
}
//経度を求める
var imagelong = parseFloat($("#GPSLongitude0").text().split("/")[0])/parseFloat($("#GPSLongitude0").text().split("/")[1]) + parseFloat($("#GPSLongitude1").text().split("/")[0])/(parseFloat($("#GPSLongitude1").text().split("/")[1])*60) + parseFloat($("#GPSLongitude2").text().split("/")[0])/(parseFloat($("#GPSLongitude2").text().split("/")[1])*3600);
if($("#GPSLongitudeRef").text() == "W"){
imagelong = "-" + imagelong;
} |
//緯度を求める
var imagelat = parseFloat($("#GPSLatitude0").text().split("/")[0])/parseFloat($("#GPSLatitude0").text().split("/")[1]) + parseFloat($("#GPSLatitude1").text().split("/")[0])/(parseFloat($("#GPSLatitude1").text().split("/")[1])*60) + parseFloat($("#GPSLatitude2").text().split("/")[0])/(parseFloat($("#GPSLatitude2").text().split("/")[1])*3600);
if($("#GPSLatitudeRef").text() == "S"){
imagelat = "-" + imagelat;
}
//経度を求める
var imagelong = parseFloat($("#GPSLongitude0").text().split("/")[0])/parseFloat($("#GPSLongitude0").text().split("/")[1]) + parseFloat($("#GPSLongitude1").text().split("/")[0])/(parseFloat($("#GPSLongitude1").text().split("/")[1])*60) + parseFloat($("#GPSLongitude2").text().split("/")[0])/(parseFloat($("#GPSLongitude2").text().split("/")[1])*3600);
if($("#GPSLongitudeRef").text() == "W"){
imagelong = "-" + imagelong;
}
このようにして出てきた結果を、google.maps.LatLngメソッドの引数として与えてやれば良い。
ということで、EXIFViewerでの地図表示が可能になった。労力のわりに、いまや出来て当たり前の機能のように見えるからどうにも報われないのです。