9443a925e2fd48313050063da1619dfb35d6d29b
[yaffs-website] / lsolesen / pel / examples / gps.php
1 <?php
2
3 /**
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.
7  *
8  * Copyright (C) 2007 Martin Geisler.
9  *
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.
14  *
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.
19  *
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
24  */
25
26 /*
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
30  * information.
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
34  * used in Exif data.
35  * - addGpsInfo() adds several Exif tags to your JPEG file.
36  */
37 require_once '../autoload.php';
38
39 use lsolesen\pel\PelJpeg;
40 use lsolesen\pel\PelTiff;
41
42 /**
43  * Convert a decimal degree into degrees, minutes, and seconds.
44  *
45  * @param
46  *            int the degree in the form 123.456. Must be in the interval
47  *            [-180, 180].
48  *
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.
53  */
54 function convertDecimalToDMS($degree)
55 {
56     if ($degree > 180 || $degree < - 180) {
57         return null;
58     }
59
60     $degree = abs($degree); // make sure number is positive
61                             // (no distinction here for N/S
62                             // or W/E).
63
64     $seconds = $degree * 3600; // Total number of seconds.
65
66     $degrees = floor($degree); // Number of whole degrees.
67     $seconds -= $degrees * 3600; // Subtract the number of seconds
68                                  // taken by the degrees.
69
70     $minutes = floor($seconds / 60); // Number of whole minutes.
71     $seconds -= $minutes * 60; // Subtract the number of seconds
72                                // taken by the minutes.
73
74     $seconds = round($seconds * 100, 0); // Round seconds with a 1/100th
75                                          // second precision.
76
77     return array(
78         array(
79             $degrees,
80             1
81         ),
82         array(
83             $minutes,
84             1
85         ),
86         array(
87             $seconds,
88             100
89         )
90     );
91 }
92
93 /**
94  * Add GPS information to an image basic metadata.
95  * Any old Exif data
96  * is discarded.
97  *
98  * @param
99  *            string the input filename.
100  *
101  * @param
102  *            string the output filename. An updated copy of the input
103  *            image is saved here.
104  *
105  * @param
106  *            string image description.
107  *
108  * @param
109  *            string user comment.
110  *
111  * @param
112  *            string camera model.
113  *
114  * @param
115  *            float longitude expressed as a fractional number of degrees,
116  *            e.g. 12.345�. Negative values denotes degrees west of Greenwich.
117  *
118  * @param
119  *            float latitude expressed as for longitude. Negative values
120  *            denote degrees south of equator.
121  *
122  * @param
123  *            float the altitude, negative values express an altitude
124  *            below sea level.
125  *
126  * @param
127  *            string the date and time.
128  */
129 function addGpsInfo($input, $output, $description, $comment, $model, $longitude, $latitude, $altitude, $date_time)
130 {
131     /* Load the given image into a PelJpeg object */
132     $jpeg = new PelJpeg($input);
133
134     /*
135      * Create and add empty Exif data to the image (this throws away any
136      * old Exif data in the image).
137      */
138     $exif = new PelExif();
139     $jpeg->setExif($exif);
140
141     /*
142      * Create and add TIFF data to the Exif data (Exif data is actually
143      * stored in a TIFF format).
144      */
145     $tiff = new PelTiff();
146     $exif->setTiff($tiff);
147
148     /*
149      * Create first Image File Directory and associate it with the TIFF
150      * data.
151      */
152     $ifd0 = new PelIfd(PelIfd::IFD0);
153     $tiff->setIfd($ifd0);
154
155     /*
156      * Create a sub-IFD for holding GPS information. GPS data must be
157      * below the first IFD.
158      */
159     $gps_ifd = new PelIfd(PelIfd::GPS);
160     $ifd0->addSubIfd($gps_ifd);
161
162     /*
163      * The USER_COMMENT tag must be put in a Exif sub-IFD under the
164      * first IFD.
165      */
166     $exif_ifd = new PelIfd(PelIfd::EXIF);
167     $exif_ifd->addEntry(new PelEntryUserComment($comment));
168     $ifd0->addSubIfd($exif_ifd);
169
170     $inter_ifd = new PelIfd(PelIfd::INTEROPERABILITY);
171     $ifd0->addSubIfd($inter_ifd);
172
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));
176
177     $gps_ifd->addEntry(new PelEntryByte(PelTag::GPS_VERSION_ID, 2, 2, 0, 0));
178
179     /*
180      * Use the convertDecimalToDMS function to convert the latitude from
181      * something like 12.34� to 12� 20' 42"
182      */
183     list ($hours, $minutes, $seconds) = convertDecimalToDMS($latitude);
184
185     /* We interpret a negative latitude as being south. */
186     $latitude_ref = ($latitude < 0) ? 'S' : 'N';
187
188     $gps_ifd->addEntry(new PelEntryAscii(PelTag::GPS_LATITUDE_REF, $latitude_ref));
189     $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_LATITUDE, $hours, $minutes, $seconds));
190
191     /* The longitude works like the latitude. */
192     list ($hours, $minutes, $seconds) = convertDecimalToDMS($longitude);
193     $longitude_ref = ($longitude < 0) ? 'W' : 'E';
194
195     $gps_ifd->addEntry(new PelEntryAscii(PelTag::GPS_LONGITUDE_REF, $longitude_ref));
196     $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_LONGITUDE, $hours, $minutes, $seconds));
197
198     /*
199      * Add the altitude. The absolute value is stored here, the sign is
200      * stored in the GPS_ALTITUDE_REF tag below.
201      */
202     $gps_ifd->addEntry(new PelEntryRational(PelTag::GPS_ALTITUDE, array(
203         abs($altitude),
204         1
205     )));
206     /*
207      * The reference is set to 1 (true) if the altitude is below sea
208      * level, or 0 (false) otherwise.
209      */
210     $gps_ifd->addEntry(new PelEntryByte(PelTag::GPS_ALTITUDE_REF, (int) ($altitude < 0)));
211
212     /* Finally we store the data in the output file. */
213     file_put_contents($output, $jpeg->getBytes());
214 }