それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

写真からGPS情報を抜き出して、地図にマッピングする

写真からGPSを抜き出して地図にマッピングしたら便利だった。

iPhotoJavaScript 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

identify でexif

コレを使って、ファイルをまとめて処理

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" />

ただし、これでは、写真の向きを考慮しないので、、

a

天地がひっくり返っちゃう。

向きを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を自動化すると写真の多さに、タイムアウトでまくりで苦労しました。

その他の記事はこちら

takuya-1st.hatenablog.jp