A simple PHP script to add image description and geolocation EXIF data to an image. The script makes use of the PHP EXIF Library (PEL) by Isolesen, which can be obtained from http://lsolesen.github.io/pel/.
PHP Code
<?php
set_include_path('lsolesen-pel-9f52634' . PATH_SEPARATOR . get_include_path());
require_once('src/PelJpeg.php');
$pelJpeg = new PelJpeg('in_filename.jpg');
$pelExif = $pelJpeg->getExif();
if ($pelExif == null) {
$pelExif = new PelExif();
$pelJpeg->setExif($pelExif);
}
$pelTiff = $pelExif->getTiff();
if ($pelTiff == null) {
$pelTiff = new PelTiff();
$pelExif->setTiff($pelTiff);
}
$pelIfd0 = $pelTiff->getIfd();
if ($pelIfd0 == null) {
$pelIfd0 = new PelIfd(PelIfd::IFD0);
$pelTiff->setIfd($pelIfd0);
}
$pelIfd0->addEntry(new PelEntryAscii(
PelTag::IMAGE_DESCRIPTION, 'This is the image description'));
$pelSubIfdGps = new PelIfd(PelIfd::GPS);
$pelIfd0->addSubIfd($pelSubIfdGps);
setGeolocation($pelSubIfdGps, 13.37, 1.337);
$pelJpeg->saveFile('out_filename.jpg');
function setGeolocation(
$pelSubIfdGps, $latitudeDegreeDecimal, $longitudeDegreeDecimal) {
$latitudeRef = ($latitudeDegreeDecimal >= 0) ? 'N' : 'S';
$latitudeDegreeMinuteSecond
= degreeDecimalToDegreeMinuteSecond(abs($latitudeDegreeDecimal));
$longitudeRef= ($longitudeDegreeDecimal >= 0) ? 'E' : 'W';
$longitudeDegreeMinuteSecond
= degreeDecimalToDegreeMinuteSecond(abs($longitudeDegreeDecimal));
$pelSubIfdGps->addEntry(new PelEntryAscii(
PelTag::GPS_LATITUDE_REF, $latitudeRef));
$pelSubIfdGps->addEntry(new PelEntryRational(
PelTag::GPS_LATITUDE,
array($latitudeDegreeMinuteSecond['degree'], 1),
array($latitudeDegreeMinuteSecond['minute'], 1),
array(round($latitudeDegreeMinuteSecond['second'] * 1000), 1000)));
$pelSubIfdGps->addEntry(new PelEntryAscii(
PelTag::GPS_LONGITUDE_REF, $longitudeRef));
$pelSubIfdGps->addEntry(new PelEntryRational(
PelTag::GPS_LONGITUDE,
array($longitudeDegreeMinuteSecond['degree'], 1),
array($longitudeDegreeMinuteSecond['minute'], 1),
array(round($longitudeDegreeMinuteSecond['second'] * 1000), 1000)));
}
function degreeDecimalToDegreeMinuteSecond($degreeDecimal) {
$degree = floor($degreeDecimal);
$remainder = $degreeDecimal - $degree;
$minute = floor($remainder * 60);
$remainder = ($remainder * 60) - $minute;
$second = $remainder * 60;
return array('degree' => $degree, 'minute' => $minute, 'second' => $second);
}
?>
Explanations
set_include_path('lsolesen-pel-9f52634' . PATH_SEPARATOR . get_include_path());
require_once('src/PelJpeg.php');
Import the necessary code. For this script, only PelJpeg.php from the Isolesen PEL library is needed.
$pelJpeg = new PelJpeg('in_filename.jpg');
Load the image file to be editied, in this case, in_filename.jpg.
$pelExif = $pelJpeg->getExif();
if ($pelExif == null) {
$pelExif = new PelExif();
$pelJpeg->setExif($pelExif);
}
$pelTiff = $pelExif->getTiff();
if ($pelTiff == null) {
$pelTiff = new PelTiff();
$pelExif->setTiff($pelTiff);
}
$pelIfd0 = $pelTiff->getIfd();
if ($pelIfd0 == null) {
$pelIfd0 = new PelIfd(PelIfd::IFD0);
$pelTiff->setIfd($pelIfd0);
}
Get the EXIF and the root image file directory (IFD) from the image.
$pelIfd0->addEntry(new PelEntryAscii(
PelTag::IMAGE_DESCRIPTION, 'This is the image description'));
Add the description (in this case, the description is “This is the image description“.)
$pelSubIfdGps = new PelIfd(PelIfd::GPS);
$pelIfd0->addSubIfd($pelSubIfdGps);
Add a sub-IFD for the geolocation data.
setGeolocation($pelSubIfdGps, 13.37, 1.337);
Set the geolocation data (in this case, with a latitude of 13.37 and a longitude of 1.337). The setGeolocation function is explained below.
$pelJpeg->saveFile('out_filename.jpg');
Save the image which contains the additional EXIF data. In this case, the image is saved to out_filename.jpg.
function setGeolocation(
$pelSubIfdGps, $latitudeDegreeDecimal, $longitudeDegreeDecimal) {
$latitudeRef = ($latitudeDegreeDecimal >= 0) ? 'N' : 'S';
$latitudeDegreeMinuteSecond
= degreeDecimalToDegreeMinuteSecond(abs($latitudeDegreeDecimal));
$longitudeRef= ($longitudeDegreeDecimal >= 0) ? 'E' : 'W';
$longitudeDegreeMinuteSecond
= degreeDecimalToDegreeMinuteSecond(abs($longitudeDegreeDecimal));
$pelSubIfdGps->addEntry(new PelEntryAscii(
PelTag::GPS_LATITUDE_REF, $latitudeRef));
$pelSubIfdGps->addEntry(new PelEntryRational(
PelTag::GPS_LATITUDE,
array($latitudeDegreeMinuteSecond['degree'], 1),
array($latitudeDegreeMinuteSecond['minute'], 1),
array(round($latitudeDegreeMinuteSecond['second'] * 1000), 1000)));
$pelSubIfdGps->addEntry(new PelEntryAscii(
PelTag::GPS_LONGITUDE_REF, $longitudeRef));
$pelSubIfdGps->addEntry(new PelEntryRational(
PelTag::GPS_LONGITUDE,
array($longitudeDegreeMinuteSecond['degree'], 1),
array($longitudeDegreeMinuteSecond['minute'], 1),
array(round($longitudeDegreeMinuteSecond['second'] * 1000), 1000)));
}
The function for setting the geolocation data. This function is needed because the library uses the degree-minute-second format for the latitude and longitude values, so the input decimal values have to be converted beforehand. Also, the library uses positive latitude and longitude values with a direction reference (N or S for latitude, and E or W for longitude), instead of positive and negative values.
function degreeDecimalToDegreeMinuteSecond($degreeDecimal) {
$degree = floor($degreeDecimal);
$remainder = $degreeDecimal - $degree;
$minute = floor($remainder * 60);
$remainder = ($remainder * 60) - $minute;
$second = $remainder * 60;
}
The function for converting a decimal degree value to the degree-minute-second format.
Note: php is needed to run the script, and it may not be present in a default installation. It can be installed through the package manager.
References
[1] http://lsolesen.github.io/pel/
[2] http://www.wikipedia.org/wiki/Exchangeable_image_file_format