4 * PEL: PHP Exif Library.
5 * A library with support for reading and
6 * writing all Exif headers in JPEG and TIFF images using PHP.
8 * Copyright (C) 2007 Martin Geisler.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program in the file COPYING; if not, write to the
22 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
23 * Boston, MA 02110-1301 USA
27 * Contributed by Andac Aydin (aandac@gmx.de).
28 * This example shows how one can add GPS information to a JPEG image.
29 * Any Exif information in the image will be overwritten by the new
31 * This example includes two functions:
32 * - convertDecimalTpDMS() converts decimal GPS coordinates (how you use them
33 * in Google Maps for example) to the conventional coordinate-system
35 * - addGpsInfo() adds several Exif tags to your JPEG file.
37 require_once '../autoload.php';
39 use lsolesen\pel\PelJpeg;
40 use lsolesen\pel\PelTiff;
43 * Convert a decimal degree into degrees, minutes, and seconds.
46 * int the degree in the form 123.456. Must be in the interval
49 * @return array a triple with the degrees, minutes, and seconds. Each
50 * value is an array itself, suitable for passing to a
51 * PelEntryRational. If the degree is outside the allowed interval,
52 * null is returned instead.
54 function convertDecimalToDMS($degree)
56 if ($degree > 180 || $degree < - 180) {
60 $degree = abs($degree); // make sure number is positive
61 // (no distinction here for N/S
64 $seconds = $degree * 3600; // Total number of seconds.
66 $degrees = floor($degree); // Number of whole degrees.
67 $seconds -= $degrees * 3600; // Subtract the number of seconds
68 // taken by the degrees.
70 $minutes = floor($seconds / 60); // Number of whole minutes.
71 $seconds -= $minutes * 60; // Subtract the number of seconds
72 // taken by the minutes.
74 $seconds = round($seconds * 100, 0); // Round seconds with a 1/100th
94 * Add GPS information to an image basic metadata.
99 * string the input filename.
102 * string the output filename. An updated copy of the input
103 * image is saved here.
106 * string image description.
109 * string user comment.
112 * string camera model.
115 * float longitude expressed as a fractional number of degrees,
116 * e.g. 12.345�. Negative values denotes degrees west of Greenwich.
119 * float latitude expressed as for longitude. Negative values
120 * denote degrees south of equator.
123 * float the altitude, negative values express an altitude
127 * string the date and time.
129 function addGpsInfo($input, $output, $description, $comment, $model, $longitude, $latitude, $altitude, $date_time)
131 /* Load the given image into a PelJpeg object */
132 $jpeg = new PelJpeg($input);
135 * Create and add empty Exif data to the image (this throws away any
136 * old Exif data in the image).
138 $exif = new PelExif();
139 $jpeg->setExif($exif);
142 * Create and add TIFF data to the Exif data (Exif data is actually
143 * stored in a TIFF format).
145 $tiff = new PelTiff();
146 $exif->setTiff($tiff);
149 * Create first Image File Directory and associate it with the TIFF
152 $ifd0 = new PelIfd(PelIfd::IFD0);
153 $tiff->setIfd($ifd0);
156 * Create a sub-IFD for holding GPS information. GPS data must be
157 * below the first IFD.
159 $gps_ifd = new PelIfd(PelIfd::GPS);
160 $ifd0->addSubIfd($gps_ifd);
163 * The USER_COMMENT tag must be put in a Exif sub-IFD under the
166 $exif_ifd = new PelIfd(PelIfd::EXIF);
167 $exif_ifd->addEntry(new PelEntryUserComment($comment));
168 $ifd0->addSubIfd($exif_ifd);
170 $inter_ifd = new PelIfd(PelIfd::INTEROPERABILITY);
171 $ifd0->addSubIfd($inter_ifd);
173 $ifd0->addEntry(new PelEntryAscii(PelTag::MODEL, $model));
174 $ifd0->addEntry(new PelEntryAscii(PelTag::DATE_TIME, $date_time));
175 $ifd0->addEntry(new PelEntryAscii(PelTag::IMAGE_DESCRIPTION, $description));
177 $gps_ifd->addEntry(new PelEntryByte(PelTag::GPS_VERSION_ID, 2, 2, 0, 0));
180 * Use the convertDecimalToDMS function to convert the latitude from
181 * something like 12.34� to 12� 20' 42"
183 list ($hours, $minutes, $seconds) = convertDecimalToDMS($latitude);
185 /* We interpret a negative latitude as being south. */
186 $latitude_ref = ($latitude < 0) ? 'S' : 'N';
188 $gps_ifd->addEntry(new PelEntryAscii(PelTag::GPS_LATITUDE_REF, $latitude_ref));
189 $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_LATITUDE, $hours, $minutes, $seconds));
191 /* The longitude works like the latitude. */
192 list ($hours, $minutes, $seconds) = convertDecimalToDMS($longitude);
193 $longitude_ref = ($longitude < 0) ? 'W' : 'E';
195 $gps_ifd->addEntry(new PelEntryAscii(PelTag::GPS_LONGITUDE_REF, $longitude_ref));
196 $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_LONGITUDE, $hours, $minutes, $seconds));
199 * Add the altitude. The absolute value is stored here, the sign is
200 * stored in the GPS_ALTITUDE_REF tag below.
202 $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_ALTITUDE, array(
207 * The reference is set to 1 (true) if the altitude is below sea
208 * level, or 0 (false) otherwise.
210 $gps_ifd->addEntry(new PelEntryByte(PelTag::GPS_ALTITUDE_REF, (int) ($altitude < 0)));
212 /* Finally we store the data in the output file. */
213 file_put_contents($output, $jpeg->getBytes());