写真に埋め込めれているGPS(位置情報)をもとに、Google MAPに打点するプログラム。さらに、あとから、GPS情報を写真に埋め込むプログラムも作成した。 下記のサイトが参考になった。

EXIF情報の読み、書き

EXIF情報の読み、書き-2

PHPでExifを操作する

Exif データにアクセスするコードを自作してみる

EXIFのTAG

Windowsで、写真のプロパティの詳細を見るとタイトルとコメントが同じになっている?

このタイトルとコメントはどうやって作成するのか? 疑問に思い、調べていくうちに、PERLのExifToolで作成できることがわかった。 その前に、JavaScript,PHPによるEXIFの操作を調べてみる。

EXIFをJavascriptで読む

function exifread2() {
  var oImg=document.getElementById("img1");

  var div = document.getElementById( "pan1" );
  
  EXIF.getData(oImg, function(){
     var ucd = Array();
    ss=EXIF.getTag(oImg,"ExposureTime");
    
   if (ss) {
      str1="メーカー:                    "+ EXIF.getTag(oImg,"Make")+"
"; str1+="モデル: "+ EXIF.getTag(oImg,"Model")+"
";    str1+="作者: "+ EXIF.getTag(oImg,"Artist")+"
"; str1+="撮影日: "+ EXIF.getTag(oImg,"DateTimeOriginal")+"
"; date = EXIF.getTag(oImg,"DateTimeOriginal"); str1+="幅: "+ EXIF.getTag(oImg,"XResolution")+" "; str1+="高: "+ EXIF.getTag(oImg,"YResolution")+"
";    str1+="Copyright: "+ EXIF.getTag(oImg,"Copyright")+"
"; ucd = EXIF.getTag(oImg,"UserComment"); if(ucd) { var jsonString = Utf8.decodeArray(ucd); jsonString = jsonString.replace(/ASCII/,""); str1+="コメント: "+ jsonString +"
"; } div.innerHTML += str1 +"
";  } else {str1 = "EXIF情報はありません!"; div.innerHTML += "
"+str1 +"
";} }); EXIF.getData(oImg, function(){ Lat=EXIF.getTag(oImg,"GPSLatitude"); // alert("GPS "+Lat); if (Lat) { Lon=EXIF.getTag(oImg,"GPSLongitude"); NS=EXIF.getTag(oImg,"GPSLatitudeRef"); EW=EXIF.getTag(oImg,"GPSLongitudeRef"); LatDeg=Lat.shift();LatMin=Lat.shift();LatSec=Lat.shift(); LonDeg=Lon.shift();LonMin=Lon.shift();LonSec=Lon.shift(); NDeg=(LatSec/60+LatMin)/60+LatDeg; EDeg=(LonSec/60+LonMin)/60+LonDeg; if (NS=="S") NDeg=-NDeg; if (EW=="W") EDeg=-EDeg; str2="緯度: "+ NDeg+"
"; str2+="経度: "+ EDeg+"
"; div.innerHTML += "
"+str2 +"
"; mapInit1(NDeg,EDeg,str1); var content = str1;  document.getElementById("LAT").value= NDeg; document.getElementById("LON").value= EDeg; // alert("TEST " +imgfile + " " + imgdata); } else {alert("位置情報はありません.");str2= "GPS情報はありません!"; var jyusyo = document.getElementById("JYUSYO").value; alert ("JYUSYO : " +jyusyo); drawMap(jyusyo); var LAT= document.getElementById("LAT").value; var LON= document.getElementById("LON").value; mapInit2(LAT,LON); div.innerHTML += "
"+str2 +"
"; } });
下記関数は、ASCIIコードをUTF-8に変換する関数。
function Uint8ToBase64(u8Arr){
  var CHUNK_SIZE = 0x8000; //arbitrary number
  var index = 0;
  var length = u8Arr.length;
  var result = '';
  var slice;
  while (index < length) {
    slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length)); 
    result += String.fromCharCode.apply(null, slice);
    index += CHUNK_SIZE;
  }
  return btoa(result);
}

EXIFをPHPで読み、書き

中心となるソースはPelJpegで、それはhttp://lsolesen.github.io/pel/にある。
getExif(); 
$gps = degree2dms( $lat , $lon );
$lat_a = explode( "." , $gps["lat"] );
$lon_a = explode( "." , $gps["lon"] );
if ($app1 != null) {
    $jpeg = new PelJpeg($filename);  
   $ifd0 = $jpeg->getExif()->getTiff()->getIfd();
    $ifd0->getSubIfd(PelIfd::GPS);
    $gps = new PelIfd(PelIfd::GPS);
    $ifd0->addSubIfd($gps);
    $gps->addEntry(new PelEntryRational(PelTag::GPS_LATITUDE, array($lat_a[0], 1), array($lat_a[1], 1), array($lat_a[2], 100)));
    $gps->addEntry(new PelEntryAscii(   PelTag::GPS_LATITUDE_REF, 'N'));
    $gps->addEntry(new PelEntryRational(PelTag::GPS_LONGITUDE, array($lon_a[0], 1), array($lon_a[1], 1), array($lon_a[2], 100)));
    $gps->addEntry(new PelEntryAscii(   PelTag::GPS_LONGITUDE_REF, 'E'));   
     $exif = $ifd0->getSubIfd(PelIfd::EXIF); 
   
    $entry = $exif->getEntry(PelTag::USER_COMMENT); 
    if (!$entry) { 
      $entry = new PelEntryUserComment(mb_convert_encoding($text,"UTF-8","auto")); 


      $exif->addEntry($entry); 
    } else { 
      $entry->setValue(mb_convert_encoding($text,"UTF-8","auto")); 
    }  
     $ofile= '/home/shino/public_html/exif/image/'. $fname; 
    $result = file_put_contents( $ofile  , $jpeg->getBytes() );
//    $ffname  = mb_convert_encoding( $fname,"SJIS","auto");

    $a = "

"); echo($a); echo($ffname); echo(">New MAP(added GPS)"); echo("

"); } else { echo("EXIF追加(撮影日時、緯度・経度、コメント)"); $input = $filename; $ofile= '/home/shino/public_html/exif/image/'. $fname; $output = $ofile; //$output = "/home/shino/public_html/exif/image/tmpimage.jpg"; $comment = $text; $latitude =$lat; $longtitude = $lon; addexif($input,$output,$comment,$latitude,$longtitude,$date_time); // $ffname = mb_convert_encoding( $fname,"UTF-8","auto"); $a = "

"); echo($a); echo($ffname); echo(">New MAP(added GPS)"); echo("

"); } function addexif($input,$output,$comment,$latitude,$longtitude,$date_time) { //$input = $filename; //$output = $ofile; $description ="EXIF ADDED"; //$comment = $text; $model = ""; //$latitude =$lat; //$longitude = $lon; $altitude ="11111"; //$date_time ="2015-12-26 06:30:10"; addGpsInfo($input, $output, $description, $comment, $model, $longtitude, $latitude, $altitude, $date_time); } function addGpsInfo($input, $output, $description, $comment, $model, $longitude, $latitude, $altitude, $date_time) { /* Load the given image into a PelJpeg object */ $jpeg = new PelJpeg($input); /* * Create and add empty Exif data to the image (this throws away any * old Exif data in the image). */ $exif = new PelExif(); $jpeg->setExif($exif); /* * Create and add TIFF data to the Exif data (Exif data is actually * stored in a TIFF format). */ $tiff = new PelTiff(); $exif->setTiff($tiff); /* * Create first Image File Directory and associate it with the TIFF * data. */ $ifd0 = new PelIfd(PelIfd::IFD0); $tiff->setIfd($ifd0); /* * Create a sub-IFD for holding GPS information. GPS data must be * below the first IFD. */ $gps_ifd = new PelIfd(PelIfd::GPS); $ifd0->addSubIfd($gps_ifd); /* * The USER_COMMENT tag must be put in a Exif sub-IFD under the * first IFD. */ $exif_ifd = new PelIfd(PelIfd::EXIF); $exif_ifd->addEntry(new PelEntryUserComment($comment)); //$date = new PelEntryTime(PelTag::DATE_TIME, $date_time); //$exif_ifd->addEntry(date); $ifd0->addSubIfd($exif_ifd); $inter_ifd = new PelIfd(PelIfd::INTEROPERABILITY); $ifd0->addSubIfd($inter_ifd); $ifd0->addEntry(new PelEntryAscii(PelTag::MODEL, $model)); $ifd0->addEntry(new PelEntryAscii(PelTag::DATE_TIME, $date_time)); $ifd0->addEntry(new PelEntryAscii(PelTag::IMAGE_DESCRIPTION, $description)); $gps_ifd->addEntry(new PelEntryByte(PelTag::GPS_VERSION_ID, 2, 2, 0, 0)); /* * Use the convertDecimalToDMS function to convert the latitude from * something like 12.34� to 12� 20' 42" */ list ($hours, $minutes, $seconds) = convertDecimalToDMS($latitude); /* We interpret a negative latitude as being south. */ $latitude_ref = ($latitude < 0) ? 'S' : 'N'; $gps_ifd->addEntry(new PelEntryAscii(PelTag::GPS_LATITUDE_REF, $latitude_ref)); $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_LATITUDE, $hours, $minutes, $seconds)); /* The longitude works like the latitude. */ list ($hours, $minutes, $seconds) = convertDecimalToDMS($longitude); $longitude_ref = ($longitude < 0) ? 'W' : 'E'; $gps_ifd->addEntry(new PelEntryAscii(PelTag::GPS_LONGITUDE_REF, $longitude_ref)); $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_LONGITUDE, $hours, $minutes, $seconds)); $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_ALTITUDE, array( abs($altitude), 1 ))); $gps_ifd->addEntry(new PelEntryByte(PelTag::GPS_ALTITUDE_REF, (int) ($altitude < 0))); /* Finally we store the data in the output file. */ file_put_contents($output, $jpeg->getBytes()); } function convertDecimalToDMS($degree) { if ($degree > 180 || $degree < - 180) { return null; } $degree = abs($degree); // make sure number is positive // (no distinction here for N/S // or W/E). $seconds = $degree * 3600; // Total number of seconds. $degrees = floor($degree); // Number of whole degrees. $seconds -= $degrees * 3600; // Subtract the number of seconds // taken by the degrees. $minutes = floor($seconds / 60); // Number of whole minutes. $seconds -= $minutes * 60; // Subtract the number of seconds // taken by the minutes. $seconds = round($seconds * 100, 0); // Round seconds with a 1/100th // second precision. return array( array( $degrees, 1 ), array( $minutes, 1 ), array( $seconds, 100 ) ); } //角度を度分秒表記に変換 function degree2dms( $lat , $lon ){ $lat_lon = array( "lat" => $lat, "lon" => $lon, ); foreach( $lat_lon as $key => $degree ){ $d = floor($degree); $m = floor(($degree-$d)*60); $s = floor(($degree-$d-$m/60)*3600); $u = floor(($degree-$d-$m/60-$s/3600)*360000); $s = $s * 100 + $u ; $dms = "$d.$m.$s"; $gps[$key] = $dms; } return( $gps ); } ?> 

PerによるEXIFの操作

ExifToolのDebianへのインストール
To install ExifTool on Debian, Ubuntu or Linux Mint:
$ sudo apt-get install libimage-exiftool-perl 
$date="2016:1:26 18:10:20
$uc = "Hello World"
$lat ="36.5"
$lng ="136.5"
$title ="TITLE"
$s ="Subject"
$artist ="作成者"
$cr="Copyright"

$param = '"/usr/local/bin/exiftool"'.' 
-DateTimeOriginal="'.$date.'" 
-UserComment="'.$uc.'" 
-GPSLatitude="'.$lat.'" 
-GPSLongitude="'.$lng.'" 
-Title="'.$title.'" 
-XPSubject="'.$s.'" 
-Artist="'.$artist.'" 
-Copyright="'.$cr.'"
  '.  $outfile;

$r=system($param);