1 /*
2 * Copyright (c) 2003-2005, Eric M. Johnston <emj@postal.net>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by Eric M. Johnston.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: exifgps.c,v 1.14 2007/12/15 20:57:10 ejohnst Exp $
33 */
34
35 /*
36 * Exif GPS information tags.
37 *
38 * Note: things aren't quite complete. Waiting on additional examples
39 * that include the tags marked unknown.
40 */
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <math.h>
46
47 #include "exif.h"
48 #include "exifint.h"
49
50 #define DEGREE "�"
51
52
53 /* Speed. */
54
55 static struct descrip gps_speed[] = {
56 { 'K', "km/h" },
57 { 'M', "mph" },
58 { 'N', "knots" },
59 { -1, "" },
60 };
61
62
63 /* Status. */
64
65 static struct descrip gps_status[] = {
66 { 'A', "Measurement In Progress" },
67 { 'V', "Measurement Interoperability" },
68 { -1, "Unknown" },
69 };
70
71
72 /* Distance. */
73
74 static struct descrip gps_dist[] = {
75 { 'K', "km" },
76 { 'M', "mi" },
77 { 'N', "knots" },
78 { -1, "" },
79 };
80
81
82 /* Differential correction. */
83
84 static struct descrip gps_diff[] = {
85 { 0, "No Correction" },
86 { 1, "Correction Applied" },
87 { -1, "Unknown" },
88 };
89
90
91 /* Bearing reference. */
92
93 static struct descrip gps_bear[] = {
94 { 'M', "Magnetic North" },
95 { 'T', "True North" },
96 { -1, "Unknown" },
97 };
98
99
100 /* GPS info version 2.2.0.0 tags. */
101
102 struct exiftag gpstags[] = {
103 { 0x0000, TIFF_BYTE, 4, ED_VRB,
104 "GPSVersionID", "GPS Info Version", NULL },
105 { 0x0001, TIFF_ASCII, 2, ED_VRB,
106 "GPSLatitudeRef", "Latitude Reference", NULL },
107 { 0x0002, TIFF_RTNL, 3, ED_IMG,
108 "GPSLatitude", "Latitude", NULL },
109 { 0x0003, TIFF_ASCII, 2, ED_VRB,
110 "GPSLongitudeRef", "Longitude Reference", NULL },
111 { 0x0004, TIFF_RTNL, 3, ED_IMG,
112 "GPSLongitude", "Longitude", NULL },
113 { 0x0005, TIFF_BYTE, 1, ED_VRB,
114 "GPSAltitudeRef", "Altitude Reference", NULL },
115 { 0x0006, TIFF_RTNL, 1, ED_IMG, /* meters */
116 "GPSAltitude", "Altitude", NULL },
117 { 0x0007, TIFF_RTNL, 3, ED_IMG,
118 "GPSTimeStamp", "Time (UTC)", NULL },
119 { 0x0008, TIFF_ASCII, 0, ED_IMG,
120 "GPSSatellites", "GPS Satellites", NULL },
121 { 0x0009, TIFF_ASCII, 2, ED_IMG,
122 "GPSStatus", "GPS Status", gps_status },
123 { 0x000a, TIFF_ASCII, 2, ED_IMG,
124 "GPSMeasureMode", "GPS Measurement Mode", NULL },
125 { 0x000b, TIFF_RTNL, 1, ED_UNK,
126 "GPSDOP", "GPS Degree of Precision", NULL },
127 { 0x000c, TIFF_ASCII, 2, ED_VRB,
128 "GPSSpeedRef", "GPS Speed Reference", gps_speed },
129 { 0x000d, TIFF_RTNL, 1, ED_UNK,
130 "GPSSpeed", "Movement Speed", NULL },
131 { 0x000e, TIFF_ASCII, 2, ED_VRB,
132 "GPSTrackRef", "GPS Direction Reference", gps_bear },
133 { 0x000f, TIFF_RTNL, 1, ED_UNK, /* degrees */
134 "GPSTrack", "Movement Direction", NULL },
135 { 0x0010, TIFF_ASCII, 2, ED_VRB,
136 "GPSImgDirectionRef", "GPS Image Direction Ref", gps_bear },
137 { 0x0011, TIFF_RTNL, 1, ED_UNK, /* degrees */
138 "GPSImgDirection", "Image Direction", NULL },
139 { 0x0012, TIFF_ASCII, 0, ED_IMG,
140 "GPSMapDatum", "Geodetic Survey Data", NULL },
141 { 0x0013, TIFF_ASCII, 2, ED_VRB,
142 "GPSDestLatitudeRef", "GPS Dest Latitude Ref", NULL },
143 { 0x0014, TIFF_RTNL, 3, ED_IMG,
144 "GPSDestLatitude", "Destination Latitude", NULL },
145 { 0x0015, TIFF_ASCII, 2, ED_VRB,
146 "GPSDestLongitudeRef", "GPS Dest Longitude Ref", NULL },
147 { 0x0016, TIFF_RTNL, 3, ED_IMG,
148 "GPSDestLongitude", "Destination Longitude", NULL },
149 { 0x0017, TIFF_ASCII, 2, ED_VRB,
150 "GPSDestBearingRef", "GPS Dest Bearing Ref", gps_bear },
151 { 0x0018, TIFF_RTNL, 1, ED_UNK, /* degrees */
152 "GPSDestBearing", "Destination Direction", NULL },
153 { 0x0019, TIFF_ASCII, 2, ED_VRB,
154 "GPSDestDistanceRef", "GPS Dest Distance Ref", gps_dist },
155 { 0x001a, TIFF_RTNL, 1, ED_UNK,
156 "GPSDestDistance", "Destination Distance", NULL },
157 { 0x001b, TIFF_UNDEF, 0, ED_IMG,
158 "GPSProcessingMethod", "GPS Processing Method", NULL },
159 { 0x001c, TIFF_UNDEF, 0, ED_IMG,
160 "GPSAreaInformation", "GPS Area", NULL },
161 { 0x001d, TIFF_ASCII, 11, ED_IMG,
162 "GPSDateStamp", "Date (UTC)", NULL },
163 { 0x001e, TIFF_SHORT, 1, ED_IMG,
164 "GPSDifferental", "GPS Differential Correction", gps_diff },
165 { 0xffff, TIFF_UNKN, 0, ED_UNK,
166 "Unknown", NULL, NULL },
167 };
168
169
170 /*
171 * Process GPS tags.
172 */
173 void
gpsprop(struct exifprop * prop,struct exiftags * t)174 gpsprop(struct exifprop *prop, struct exiftags *t)
175 {
176 u_int32_t i, n, d;
177 double deg, min, sec, alt;
178 char fmt[32], buf[16];
179 struct exifprop *tmpprop;
180 enum byteorder o = t->md.order;
181
182 switch (prop->tag) {
183
184 /* Version. */
185
186 case 0x0000:
187 exifstralloc(&prop->str, 8);
188
189 /* Convert the value back into a string. */
190
191 byte4exif(prop->value, (unsigned char *)buf, o);
192
193 for (i = 0; i < 4; i++) {
194 prop->str[i * 2] = '0' + buf[i];
195 prop->str[i * 2 + 1] = '.';
196 }
197 prop->str[7] = '\0';
198 break;
199
200 /*
201 * Reference values. The value is 2-count nul-terminated ASCII,
202 * not an offset to the ASCII string.
203 * XXX Shouldn't really be necessary now that short ASCII strings work.
204 */
205
206 case 0x0001:
207 case 0x0003:
208 case 0x0009:
209 case 0x000a:
210 case 0x000c:
211 case 0x000e:
212 case 0x0010:
213 case 0x0013:
214 case 0x0015:
215 case 0x0017:
216 case 0x0019:
217 /* Clean-up from any earlier processing. */
218
219 free(prop->str);
220 prop->str = NULL;
221
222 byte4exif(prop->value, (unsigned char *)buf, o);
223
224 for (i = 0; gpstags[i].tag < EXIF_T_UNKNOWN &&
225 gpstags[i].tag != prop->tag; i++);
226 if (gpstags[i].table)
227 prop->str = finddescr(gpstags[i].table,
228 (unsigned char)buf[0]);
229 else {
230 exifstralloc(&prop->str, 2);
231 prop->str[0] = buf[0];
232 }
233 break;
234
235 /*
236 * Coordinate values.
237 *
238 * This is really kind of a mess. The display behavior here is
239 * based on image samples from a Nikon D1X and a Fuji FinePix S1 Pro.
240 * The specification allows for fractional minutes (and no seconds).
241 * Not sure if there are any other combinations...
242 */
243
244 case 0x0002:
245 case 0x0004:
246 case 0x0014:
247 case 0x0016:
248 if (prop->count != 3) {
249 exifwarn("unexpected GPS coordinate values");
250 prop->lvl = ED_BAD;
251 break;
252 }
253
254 free(prop->str);
255 prop->str = NULL;
256 exifstralloc(&prop->str, 32);
257
258 /* Figure out the reference prefix. */
259
260 switch (prop->tag) {
261 case 0x0002:
262 tmpprop = findprop(t->props, gpstags, 0x0001);
263 break;
264 case 0x0004:
265 tmpprop = findprop(t->props, gpstags, 0x0003);
266 break;
267 case 0x0014:
268 tmpprop = findprop(t->props, gpstags, 0x0013);
269 break;
270 case 0x0016:
271 tmpprop = findprop(t->props, gpstags, 0x0015);
272 break;
273 default:
274 tmpprop = NULL;
275 }
276
277 /* Degrees. */
278
279 i = 0;
280 n = exif4byte(t->md.btiff + prop->value + i * 8, o);
281 d = exif4byte(t->md.btiff + prop->value + 4 + i * 8, o);
282
283 strcpy(fmt, "%s %.f%s ");
284 if (!n || !d) /* Punt. */
285 deg = 0.0;
286 else {
287 deg = (double)n / (double)d;
288 if (d != 1)
289 sprintf(fmt, "%%s %%.%df%%s ",
290 (int)log10((double)d));
291 }
292
293 /* Minutes. */
294
295 i++;
296 n = exif4byte(t->md.btiff + prop->value + i * 8, o);
297 d = exif4byte(t->md.btiff + prop->value + 4 + i * 8, o);
298
299 if (!n || !d) { /* Punt. */
300 min = 0.0;
301 strcat(fmt, "%.f'");
302 } else {
303 min = (double)n / (double)d;
304 if (d != 1) {
305 sprintf(buf, "%%.%df'", (int)log10((double)d));
306 strcat(fmt, buf);
307 } else
308 strcat(fmt, "%.f'");
309 }
310
311 /*
312 * Seconds. We'll assume if minutes are fractional, we
313 * should just ignore seconds.
314 */
315
316 i++;
317 n = exif4byte(t->md.btiff + prop->value + i * 8, o);
318 d = exif4byte(t->md.btiff + prop->value + 4 + i * 8, o);
319
320 if (!n || !d) { /* Assume no seconds. */
321 snprintf(prop->str, 31, fmt, tmpprop && tmpprop->str ?
322 tmpprop->str : "", deg, DEGREE, min);
323 break;
324 } else {
325 sec = (double)n / (double)d;
326 if (d != 1) {
327 sprintf(buf, " %%.%df", (int)log10((double)d));
328 strcat(fmt, buf);
329 } else
330 strcat(fmt, " %.f");
331 }
332 snprintf(prop->str, 31, fmt, tmpprop && tmpprop->str ?
333 tmpprop->str : "", deg, DEGREE, min, sec);
334 break;
335
336 /* Altitude. */
337
338 case 0x0006:
339 n = exif4byte(t->md.btiff + prop->value, o);
340 d = exif4byte(t->md.btiff + prop->value + 4, o);
341
342 /* Look up reference. Non-zero means negative altitude. */
343
344 tmpprop = findprop(t->props, gpstags, 0x0005);
345 if (tmpprop && tmpprop->value)
346 n *= -1;
347
348 if (!n || !d)
349 alt = 0.0;
350 else
351 alt = (double)n / (double)d;
352
353 /* Should already have a 32-byte buffer from parsetag(). */
354
355 snprintf(prop->str, 31, "%.2f m", alt);
356 prop->str[31] = '\0';
357 break;
358
359 /* Time. */
360
361 case 0x0007:
362 /* Should already have a 32-byte buffer from parsetag(). */
363
364 prop->str[0] = '\0';
365 for (i = 0; i < prop->count; i++) {
366 n = exif4byte(t->md.btiff + prop->value + i * 8, o);
367 d = exif4byte(t->md.btiff + prop->value + 4 + i * 8, o);
368
369 if (!d) break;
370
371 if (!i)
372 sprintf(fmt, "%%02.%df", (int)log10((double)d));
373 else
374 sprintf(fmt, ":%%02.%df",
375 (int)log10((double)d));
376
377 snprintf(buf, 8, fmt, (double)n / (double)d);
378 strcat(prop->str, buf);
379 }
380 break;
381 }
382 }
383