写真からGPSを抜き出して地図にマッピングしたら便利だった。
iPhoto をJavaScript for OSX で地図にマッピングするすることをしたんだ。
iPhoto で写真を扱う - GPS情報を取り出す - それマグで!
結構楽しかったので、コマンドで作ってみた
JS関係なくコマンドで呼び出したほうが便利そうだったので。コマンドで作った
作業の進め方 - 画像を一覧する - 画像の一覧を { lon , lat , orientation , file_name }に変換 - 画像を地図上にマッピング
画像の一覧は find コマンドで
find /path/to/album/ -type f > photo.list
画像からEXIF情報を取得するのは、identify コマンドで
identify -format " \'{ \"lat\" : \"%[Exif:GPSLatitude]\" , \"lon\" : \"%[Exif:GPSLongitude]\" , \"lat_ref\" : \"%[Exif:GPSLatitudeRef]\", \"lon_ref\" : \"%[Exif:GPSLongitudeRef]\", \"orientation\" : \"%[exif:Orientation]\" }\' \n" IMG_1234.jpg
コレを使って、ファイルをまとめて処理
cat photo.list | xargs -I@ identify -format " \'{ \"lat\" : \"%[Exif:GPSLatitude]\" , \"lon\" : \"%[Exif:GPSLongitude]\" , \"lat_ref\" : \"%[Exif:GPSLatitudeRef]\", \"lon_ref\" : \"%[Exif:GPSLongitudeRef]\", \"orientation\" : \"%[exif:Orientation]\" \"path\" : \"@\" }\' \n" @
出力はこんなの
takuya@rena:~/Desktop$ identify -format " \'{ > \"lat\" : \"%[Exif:GPSLatitude]\" , > \"lon\" : \"%[Exif:GPSLongitude]\" , > \"lat_ref\" : \"%[Exif:GPSLatitudeRef]\", > \"lon_ref\" : \"%[Exif:GPSLongitudeRef]\", > \"orientation\" : \"%[exif:Orientation]\" > }\' \n" /Users/takuya/Desktop/IMG_4901.jpg '{ "lat" : "31/1, 42/1, 1812/100" , "lon" : "131/1, 27/1, 4295/100" , "lat_ref" : "N", "lon_ref" : "E", "orientation" : "" # ⇐あとで使います }
GPS の緯度経度をFloatに置換
緯度経度高度情報はそのままではGoogle Mapsに放り込めないので、
"lat" : "31/1, 42/1, 1812/100" , "lon" : "131/1, 27/1, 4295/100" ,
小数点に変換する関数を作った
function gps_string_to_float(str){ xtude = str.split(",") tud = xtude.map( function(e) { e=e.split('/') ; return parseFloat(e[0])/parseFloat(e[1]) } ) tud = tud.map(function(e,i){return e/Math.pow(60,i);}).reduce(function(a,b){return a+b}) return tud }
コレを使って、、、
> gps_string_to_float("31/1, 42/1, 1812/100")
31.705033333333333
Floatに変換。精度は、良くないけれど、そこまで求めるのは・・・
画像をファイルから読み込むようにHTMLを書く
<img src="file:///Users/takuya/Pictures/iPhoto.library/masters/2014/12/IMG_XXX.jpg" />
ただし、これでは、写真の向きを考慮しないので、、
天地がひっくり返っちゃう。
向きをCSSで置換する
向きをJSで変換してやる。exifを取得した時にOrientation を記憶しておいたので、それを活用します。
//Orientation =- 3 //180°回転 style = "rotateX( 0deg) rotateY( 0deg) rotateZ( 180deg)"
このように、transします。
最後にコレをMap.htmlに入れる。
データを入れると
<!DOCTYPE html "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>Google Maps v3 JavaScript API サンプル</title> <style> body{ margin:0px; padding:0px; } </style> <script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> var locations = [ [3x.xxxxx222222222,13x.xxxxx8333333334,"6","/Users/takuya/Pictures/iPhoto Library.photolibrary/Masters/2013/12/29/20131229-003257/IMG_0058.JPG"], //(略) ] function exif_orientation_to_css(orientation){ style=""; switch( orientation ){ case 0: //未定義 break; case 1: //通常 style = "rotateX( 0deg) rotateY( 0deg) rotateZ( 0deg)" break; case 2: //左右反転 style = "rotateX( 0deg) rotateY(180deg) rotateZ( 0deg)" break; case 3: //180°回転 style = "rotateX( 0deg) rotateY( 0deg) rotateZ( 180deg)" break; case 4: //上下反転 style = "rotateX(180deg) rotateY( 0deg) rotateZ( 0deg)" break; case 5: //反時計回りに90°回転 上下反転 style = "rotateX(180deg) rotateY( 0deg) rotateZ( -90deg)" break; case 6: //時計回りに90°回転 style = "rotateX( 0deg) rotateY( 0deg) rotateZ( 90deg)" break; case 7: //時計回りに90°回転 上下反転 style = "rotateX(180deg) rotateY( 0deg) rotateZ( 90deg)" break; case 8: //反時計回りに90°回転 style = "rotateX( 0deg) rotateY( 0deg) rotateZ( -90deg)" break; } return style; } var map; function initialize() { var latlng = new google.maps.LatLng(3x.xxxx329999996, 13x.xxx58505); var opts = { zoom: 13 , center: latlng, mapTypeId: google.maps.MapTypeId.ROADMAP }; map = new google.maps.Map (document.getElementById("map_canvas"), opts); } function add_pin( latitude,logitude , orientation, path){ var marker = new google.maps.Marker({ position: new google.maps.LatLng(latitude,logitude), title:"Hello World!" }); var rotation = ""//exif_orientation_to_css( parseInt( orientation ) ) var infowindow = new google.maps.InfoWindow({ content: '<img src="file://'+path+ '"" style="max-height:200px;transform:'+rotation+'" >' }); google.maps.event.addListener(marker, 'click', function() { infowindow.open(map,marker); }); marker.setMap(map) } document.addEventListener( "DOMContentLoaded", function(){ initialize(); locations.forEach(function(e){ //console.log(e) add_pin(e[0],e[1],e[2],e[3]) }) } ) </script> </head> <body> <div id="map_canvas" style="width:100%; height:100%"><div> </body> </html>
表示される。
コレで、正しく表示される。
あとは、この処理をPython なり nodeなりで・・・
わたしはRubyで書きましたけど。
定期的地図を更新しておいておくと楽しい。
PostGISと連携させて、位置情報を元にした絞り込みを出来るともっと便利かもしれない。
試した見た結果 xargs / find -exe でマルっとすると。。。
iPhotoライブラリに10k 枚ほど写真があるのですが、xargsや find -exe でマルっと処理すると大変なことになったんので、
バッチ処理の基本。小さな処理を何度も起動するに戻って、idneity コマンドを起動してデータ変換して終了するコマンドを繰り返す必要があった。
iPhotoを自動化すると写真の多さに、タイムアウトでまくりで苦労しました。