]> git.street.me.uk Git - andy/viking.git/blame - src/geotag_exif.c
Add capability to save layer defaults from outside the layer defaults code.
[andy/viking.git] / src / geotag_exif.c
CommitLineData
f75d0233
RN
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2/*
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 *
d4ec7b44 5 * Copyright (C) 2011-2014, Rob Norris <rw_norris@hotmail.com>
f75d0233
RN
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/*
24 * This uses EXIF information from images to create waypoints at those positions
f75d0233
RN
25 *
26 * For the implementation I have chosen to use libexif, which keeps Viking a pure C program
27 * For an alternative implementation (a la gpscorrelate), one could use libeviv2 but it appears to be C++ only.
28 */
29#include <string.h>
30#include "geotag_exif.h"
31#include "globals.h"
32#include "file.h"
33
b3eb3b98
RN
34#include <sys/stat.h>
35#include <utime.h>
36#include <stdlib.h>
37#include <ctype.h>
38#include <math.h>
39#include <glib/gi18n.h>
d9599cb1 40#include <glib/gstdio.h>
f75d0233 41#include <libexif/exif-data.h>
b3eb3b98 42#include "libjpeg/jpeg-data.h"
f75d0233
RN
43
44/**
45 * Attempt to get a single comment from the various exif fields
46 */
47static gchar* geotag_get_exif_comment ( ExifData *ed )
48{
49 gchar str[128];
50 ExifEntry *ee;
51 //
52 // Try various options to create a comment
53 //
54 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_IMAGE_DESCRIPTION);
55 if ( ee ) {
56 exif_entry_get_value ( ee, str, 128 );
57 return g_strdup ( str );
58 }
59
60 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_COMMENT);
61 if ( ee ) {
62 exif_entry_get_value ( ee, str, 128 );
63 return g_strdup ( str );
64 }
65
66 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_SUBJECT);
67 if ( ee ) {
68 exif_entry_get_value ( ee, str, 128 );
69 return g_strdup ( str );
70 }
71
72 // Consider using these for existing GPS info??
73 //#define EXIF_TAG_GPS_TIME_STAMP 0x0007
74 //#define EXIF_TAG_GPS_DATE_STAMP 0x001d
75 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL);
76 if ( ee ) {
77 exif_entry_get_value ( ee, str, 128 );
78 return g_strdup ( str );
79 }
80
81 // Otherwise nothing found
82 return NULL;
83}
84
85/**
86 * Handles 3 part location Rationals
87 * Handles 1 part rational (must specify 0 for the offset)
88 */
89static gdouble Rational2Double ( unsigned char *data, int offset, ExifByteOrder order )
90{
91 // Explaination from GPS Correlate 'exif-gps.cpp' v 1.6.1
92 // What we are trying to do here is convert the three rationals:
93 // dd/v mm/v ss/v
94 // To a decimal
95 // dd.dddddd...
96 // dd/v is easy: result = dd/v.
97 // mm/v is harder:
98 // mm
99 // -- / 60 = result.
100 // v
101 // ss/v is sorta easy.
102 // ss
103 // -- / 3600 = result
104 // v
105 // Each part is added to the final number.
106 gdouble ans;
107 ExifRational er;
108 er = exif_get_rational (data, order);
109 ans = (gdouble)er.numerator / (gdouble)er.denominator;
110 if (offset <= 0)
111 return ans;
112
113 er = exif_get_rational (data+(1*offset), order);
114 ans = ans + ( ( (gdouble)er.numerator / (gdouble)er.denominator ) / 60.0 );
115 er = exif_get_rational (data+(2*offset), order);
116 ans = ans + ( ( (gdouble)er.numerator / (gdouble)er.denominator ) / 3600.0 );
117
118 return ans;
119}
120
d4ec7b44
RN
121static struct LatLon get_latlon ( ExifData *ed )
122{
123 struct LatLon ll = { 0.0, 0.0 };
124 const struct LatLon ll0 = { 0.0, 0.0 };
125
126 gchar str[128];
127 ExifEntry *ee;
128 //
129 // Lat & Long is necessary to form a waypoint.
130 //
131 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE);
132 if ( ! ( ee && ee->components == 3 && ee->format == EXIF_FORMAT_RATIONAL ) )
133 return ll0;
134
135 ll.lat = Rational2Double ( ee->data,
136 exif_format_get_size(ee->format),
137 exif_data_get_byte_order(ed) );
138
139 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE_REF);
140 if ( ee ) {
141 exif_entry_get_value ( ee, str, 128 );
142 if ( str[0] == 'S' )
143 ll.lat = -ll.lat;
144 }
145
146 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE);
147 if ( ! ( ee && ee->components == 3 && ee->format == EXIF_FORMAT_RATIONAL ) )
148 return ll0;
149
150 ll.lon = Rational2Double ( ee->data,
151 exif_format_get_size(ee->format),
152 exif_data_get_byte_order(ed) );
153
154 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE_REF);
155 if ( ee ) {
156 exif_entry_get_value ( ee, str, 128 );
157 if ( str[0] == 'W' )
158 ll.lon = -ll.lon;
159 }
160
161 return ll;
162}
163
164/**
165 * a_geotag_get_position:
166 *
167 * @filename: The (JPG) file with EXIF information in it
168 *
169 * Returns: The position in LatLon format.
170 * It will be 0,0 if some kind of failure occurs.
171 */
172struct LatLon a_geotag_get_position ( const gchar *filename )
173{
174 struct LatLon ll = { 0.0, 0.0 };
175
176 // open image with libexif
177 ExifData *ed = exif_data_new_from_file ( filename );
178
179 // Detect EXIF load failure
180 if ( !ed )
181 return ll;
182
183 ExifEntry *ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID);
184 // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0"
185 if ( ! ( ee && ee->components == 4 ) )
186 goto MyReturn0;
187
188 ll = get_latlon ( ed );
189
190MyReturn0:
191 // Finished with EXIF
192 exif_data_free ( ed );
193
194 return ll;
195}
196
f75d0233
RN
197/**
198 * a_geotag_create_waypoint_from_file:
199 * @filename: The image file to process
200 * @vcmode: The current location mode to use in the positioning of Waypoint
201 * @name: Returns a name for the Waypoint (can be NULL)
202 *
203 * Returns: An allocated Waypoint or NULL if Waypoint could not be generated (e.g. no EXIF info)
204 *
205 */
206VikWaypoint* a_geotag_create_waypoint_from_file ( const gchar *filename, VikCoordMode vcmode, gchar **name )
207{
208 // Default return values (for failures)
209 *name = NULL;
210 VikWaypoint *wp = NULL;
211
212 // TODO use log?
213 //ExifLog *log = NULL;
214
215 // open image with libexif
216 ExifData *ed = exif_data_new_from_file ( filename );
217
218 // Detect EXIF load failure
219 if ( !ed )
220 // return with no Waypoint
221 return wp;
222
223 struct LatLon ll;
224
225 gchar str[128];
226 ExifEntry *ee;
227
228 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID);
229 // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0"
230 if ( ! ( ee && ee->components == 4 ) )
231 goto MyReturn;
232 // Could test for these versions explicitly but may have byte order issues...
233 //if ( ! ( ee->data[0] == 2 && ee->data[2] == 0 && ee->data[3] == 0 ) )
234 // goto MyReturn;
235
d4ec7b44 236 ll = get_latlon ( ed );
f75d0233 237
d4ec7b44
RN
238 // Hopefully won't have valid images at 0,0!
239 if ( ll.lat == 0.0 && ll.lon == 0.0 )
f75d0233
RN
240 goto MyReturn;
241
f75d0233
RN
242 //
243 // Not worried if none of the other fields exist, as can default the values to something
244 //
245
246 gdouble alt = VIK_DEFAULT_ALTITUDE;
247 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_ALTITUDE);
248 if ( ee && ee->components == 1 && ee->format == EXIF_FORMAT_RATIONAL ) {
249 alt = Rational2Double ( ee->data,
250 0,
251 exif_data_get_byte_order(ed) );
252
253 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_ALTITUDE_REF);
254 if ( ee && ee->components == 1 && ee->format == EXIF_FORMAT_BYTE && ee->data[0] == 1 )
255 alt = -alt;
256 }
257
258 // Name
259 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE);
260 if ( ee ) {
261 exif_entry_get_value ( ee, str, 128 );
262 *name = g_strdup ( str );
263 }
264
265 //
266 // Now create Waypoint with acquired information
267 //
268 wp = vik_waypoint_new();
269 wp->visible = TRUE;
270 // Set info from exif values
271 // Location
272 vik_coord_load_from_latlon ( &(wp->coord), vcmode, &ll );
273 // Altitude
274 wp->altitude = alt;
275
276 wp->comment = geotag_get_exif_comment ( ed );
277
278 vik_waypoint_set_image ( wp, filename );
279
280MyReturn:
281 // Finished with EXIF
282 exif_data_free ( ed );
283
284 return wp;
285}
286
287/**
cbac0d22 288 * a_geotag_waypoint_positioned:
f75d0233
RN
289 * @filename: The image file to process
290 * @coord: The location for positioning the Waypoint
291 * @name: Returns a name for the Waypoint (can be NULL)
cbac0d22 292 * @waypoint: An existing waypoint to update (can be NULL to generate a new waypoint)
f75d0233 293 *
cbac0d22
RN
294 * Returns: An allocated waypoint if the input waypoint is NULL,
295 * otherwise the passed in waypoint is updated
f75d0233
RN
296 *
297 * Here EXIF processing is used to get non position related information (i.e. just the comment)
298 *
299 */
cbac0d22 300VikWaypoint* a_geotag_waypoint_positioned ( const gchar *filename, VikCoord coord, gdouble alt, gchar **name, VikWaypoint *wp )
f75d0233
RN
301{
302 *name = NULL;
cbac0d22
RN
303 if ( wp == NULL ) {
304 // Need to create waypoint
305 wp = vik_waypoint_new();
306 wp->visible = TRUE;
307 }
f75d0233
RN
308 wp->coord = coord;
309 wp->altitude = alt;
310
311 ExifData *ed = exif_data_new_from_file ( filename );
312
313 // Set info from exif values
314 if ( ed ) {
315 wp->comment = geotag_get_exif_comment ( ed );
316
317 gchar str[128];
318 ExifEntry *ee;
319 // Name
320 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE);
321 if ( ee ) {
322 exif_entry_get_value ( ee, str, 128 );
323 *name = g_strdup ( str );
324 }
325
326 // Finished with EXIF
327 exif_data_free ( ed );
328 }
329
330 vik_waypoint_set_image ( wp, filename );
331
f75d0233
RN
332 return wp;
333}
b3eb3b98
RN
334
335/**
336 * a_geotag_get_exif_date_from_file:
337 * @filename: The image file to process
338 * @has_GPS_info: Returns whether the file has existing GPS information
339 *
340 * Returns: An allocated string with the date and time in EXIF_DATE_FORMAT, otherwise NULL if some kind of failure
341 *
342 * Here EXIF processing is used to get time information
343 *
344 */
345gchar* a_geotag_get_exif_date_from_file ( const gchar *filename, gboolean *has_GPS_info )
346{
347 gchar* datetime = NULL;
06949e05 348 *has_GPS_info = FALSE;
b3eb3b98
RN
349
350 ExifData *ed = exif_data_new_from_file ( filename );
351
352 // Detect EXIF load failure
353 if ( !ed )
354 return datetime;
355
356 gchar str[128];
357 ExifEntry *ee;
358
359 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL);
360 if ( ee ) {
361 exif_entry_get_value ( ee, str, 128 );
362 datetime = g_strdup ( str );
363 }
364
365 // Check GPS Info
06949e05 366
b3eb3b98
RN
367 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID);
368 // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0"
369 if ( ee && ee->components == 4 )
370 *has_GPS_info = TRUE;
371
06949e05
RN
372 // Check other basic GPS fields exist too
373 // I have encountered some images which have just the EXIF_TAG_GPS_VERSION_ID but nothing else
374 // So to confirm check more EXIF GPS TAGS:
375 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE);
376 if ( !ee )
377 *has_GPS_info = FALSE;
378 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE);
379 if ( !ee )
380 *has_GPS_info = FALSE;
381
b3eb3b98
RN
382 exif_data_free ( ed );
383
384 return datetime;
385}
386
387
388/**! If the entry doesn't exist, create it.
389 * Based on exif command line action_create_value function in exif 0.6.20
390 */
391static ExifEntry* my_exif_create_value (ExifData *ed, ExifTag tag, ExifIfd ifd)
392{
393 ExifEntry *e = exif_content_get_entry (ed->ifd[ifd], tag);
394 if ( !e ) {
395 e = exif_entry_new ();
396 exif_content_add_entry (ed->ifd[ifd], e);
397
398 exif_entry_initialize (e, tag);
399
400 // exif_entry_initialize doesn't seem to do much, especially for the GPS tags
401 // so have to setup fields ourselves:
402 e->tag = tag;
403
404 if ( tag == EXIF_TAG_GPS_VERSION_ID ) {
405 e->format = EXIF_FORMAT_BYTE;
406 e->components = 4;
407 e->size = sizeof (char) * e->components;
408 if ( e->data )
409 g_free (e->data);
410 e->data = g_malloc (e->size);
411 }
412 if ( tag == EXIF_TAG_GPS_MAP_DATUM ||
413 tag == EXIF_TAG_GPS_LATITUDE_REF || tag == EXIF_TAG_GPS_LONGITUDE_REF ||
414 tag == EXIF_TAG_GPS_PROCESSING_METHOD ) {
415 e->format = EXIF_FORMAT_ASCII;
416 // NB Allocation is handled later on when the actual string used is known
417 }
418 if ( tag == EXIF_TAG_GPS_LATITUDE || tag == EXIF_TAG_GPS_LONGITUDE ) {
419 e->format = EXIF_FORMAT_RATIONAL;
420 e->components = 3;
421 e->size = sizeof (ExifRational) * e->components;
422 if ( e->data )
423 g_free (e->data);
424 e->data = g_malloc (e->size);
425 }
426 if ( tag == EXIF_TAG_GPS_ALTITUDE ) {
427 e->format = EXIF_FORMAT_RATIONAL;
428 e->components = 1;
429 e->size = sizeof (ExifRational) * e->components;
430 if ( e->data )
431 g_free (e->data);
432 e->data = g_malloc (e->size);
433 }
434 if ( tag == EXIF_TAG_GPS_ALTITUDE_REF ) {
435 e->components = 1;
436 e->size = sizeof (char) * e->components;
437 if ( e->data )
438 g_free (e->data);
439 e->data = g_malloc (e->size);
440 }
441 /* The entry has been added to the IFD, so we can unref it */
442 //exif_entry_unref(e);
443 // Crashes later on, when saving to jpeg if the above unref is enabled!!
444 // ?Some other malloc problem somewhere?
445 }
446 return e;
447}
448
449/** Heavily based on convert_arg_to_entry from exif command line tool.
450 * But without ExifLog, exitting, use of g_* io functions
451 * and can take a gdouble value instead of a string
452 */
453static void convert_to_entry (const char *set_value, gdouble gdvalue, ExifEntry *e, ExifByteOrder o)
454{
455 unsigned int i, numcomponents;
456 char *value_p = NULL;
457 char *buf = NULL;
458 /*
459 * ASCII strings are handled separately,
460 * since they don't require any conversion.
461 */
462 if (e->format == EXIF_FORMAT_ASCII ||
463 e->tag == EXIF_TAG_USER_COMMENT) {
464 if (e->data) g_free (e->data);
465 e->components = strlen (set_value) + 1;
466 if (e->tag == EXIF_TAG_USER_COMMENT)
467 e->components += 8 - 1;
468 e->size = sizeof (char) * e->components;
469 e->data = g_malloc (e->size);
470 if (!e->data) {
471 g_warning (_("Not enough memory."));
472 return;
473 }
474 if (e->tag == EXIF_TAG_USER_COMMENT) {
475 /* assume ASCII charset */
476 /* TODO: get this from the current locale */
477 memcpy ((char *) e->data, "ASCII\0\0\0", 8);
478 memcpy ((char *) e->data + 8, set_value,
479 strlen (set_value));
480 } else
481 strcpy ((char *) e->data, set_value);
482 return;
483 }
484
485 /*
486 * Make sure we can handle this entry
487 */
488 if ((e->components == 0) && *set_value) {
489 g_warning (_("Setting a value for this tag is unsupported!"));
490 return;
491 }
492
493 gboolean use_string = (set_value != NULL);
494 if ( use_string ) {
495 /* Copy the string so we can modify it */
496 buf = g_strdup (set_value);
497 if (!buf)
498 return;
499 value_p = strtok (buf, " ");
500 }
501
502 numcomponents = e->components;
503 for (i = 0; i < numcomponents; ++i) {
504 unsigned char s;
505
506 if ( use_string ) {
507 if (!value_p) {
508 g_warning (_("Too few components specified (need %d, found %d)\n"), numcomponents, i);
509 return;
510 }
511 if (!isdigit(*value_p) && (*value_p != '+') && (*value_p != '-')) {
512 g_warning (_("Numeric value expected\n"));
513 return;
514 }
515 }
516
517 s = exif_format_get_size (e->format);
518 switch (e->format) {
519 case EXIF_FORMAT_ASCII:
520 g_warning (_("This shouldn't happen!"));
521 return;
522 break;
523 case EXIF_FORMAT_SHORT:
524 exif_set_short (e->data + (s * i), o, atoi (value_p));
525 break;
526 case EXIF_FORMAT_SSHORT:
527 exif_set_sshort (e->data + (s * i), o, atoi (value_p));
528 break;
529 case EXIF_FORMAT_RATIONAL: {
530 ExifRational er;
531
532 double val = 0.0 ;
533 if ( use_string && value_p )
534 val = fabs (atol (value_p));
535 else
536 val = fabs (gdvalue);
537
538 if ( i == 0 ) {
539 // One (or first) part rational
540
541 // Sneak peek into tag as location tags need rounding down to give just the degrees part
542 if ( e->tag == EXIF_TAG_GPS_LATITUDE || e->tag == EXIF_TAG_GPS_LONGITUDE ) {
543 er.numerator = (ExifLong) floor ( val );
544 er.denominator = 1.0;
545 }
546 else {
547 // I don't see any point in doing anything too complicated here,
548 // such as trying to work out the 'best' denominator
549 // For the moment use KISS principle.
550 // Fix a precision of 1/100 metre as that's more than enough for GPS accuracy especially altitudes!
551 er.denominator = 100.0;
552 er.numerator = (ExifLong) (val * er.denominator);
553 }
554 }
555
556 // Now for Location 3 part rationals do Mins and Seconds format
557
558 // Rounded down minutes
559 if ( i == 1 ) {
560 er.denominator = 1.0;
561 er.numerator = (ExifLong) ( (int) floor ( ( val - floor (val) ) * 60.0 ) );
562 }
563
564 // Finally seconds
565 if ( i == 2 ) {
566 er.denominator = 100.0;
567
568 // Fractional minute.
569 double FracPart = ((val - floor(val)) * 60) - (double)(int) floor ( ( val - floor (val) ) * 60.0 );
570 er.numerator = (ExifLong) ( (int)floor(FracPart * 6000) ); // Convert to seconds.
571 }
572 exif_set_rational (e->data + (s * i), o, er );
573 break;
574 }
575 case EXIF_FORMAT_LONG:
576 exif_set_long (e->data + (s * i), o, atol (value_p));
577 break;
578 case EXIF_FORMAT_SLONG:
579 exif_set_slong (e->data + (s * i), o, atol (value_p));
580 break;
581 case EXIF_FORMAT_BYTE:
582 case EXIF_FORMAT_SBYTE:
583 case EXIF_FORMAT_UNDEFINED: /* treat as byte array */
584 e->data[s * i] = atoi (value_p);
585 break;
586 case EXIF_FORMAT_FLOAT:
587 case EXIF_FORMAT_DOUBLE:
588 case EXIF_FORMAT_SRATIONAL:
589 default:
590 g_warning (_("Not yet implemented!"));
591 return;
592 }
593
594 if ( use_string )
595 value_p = strtok (NULL, " ");
596
597 }
598
599 g_free (buf);
600
601 if ( use_string )
602 if ( value_p )
603 g_warning (_("Warning; Too many components specified!"));
604}
605
606/**
607 * a_geotag_write_exif_gps:
608 * @filename: The image file to save information in
609 * @coord: The location
610 * @alt: The elevation
611 *
612 * Returns: A value indicating success: 0, or some other value for failure
613 *
614 */
615gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble alt, gboolean no_change_mtime )
616{
617 gint result = 0; // OK so far...
618
619 // Save mtime for later use
620 struct stat stat_save;
621 if ( no_change_mtime )
622 stat ( filename, &stat_save );
623
624 /*
625 Appears libexif doesn't actually support writing EXIF data directly to files
626 Thus embed command line exif writing method within Viking
627 (for example this is done by Enlightment - http://www.enlightenment.org/ )
628 This appears to be JPEG only, but is probably 99% of our use case
629 Alternatively consider using libexiv2 and C++...
630 */
631
632 // Actual EXIF settings here...
633 JPEGData *jdata;
634
635 /* Parse the JPEG file. */
636 jdata = jpeg_data_new ();
637 jpeg_data_load_file (jdata, filename);
638
639 // Get current values
640 ExifData *ed = exif_data_new_from_file ( filename );
641 if ( !ed )
642 ed = exif_data_new ();
643
644 // Update ExifData with our new settings
645 ExifEntry *ee;
646 //
647 // I don't understand it, but when saving the 'ed' nothing gets set after putting in the GPS ID tag - so it must come last
648 // (unless of course there is some bug in the setting of the ID, that prevents subsequent tags)
649 //
650
651 ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE, EXIF_IFD_GPS);
652 convert_to_entry ( NULL, alt, ee, exif_data_get_byte_order(ed) );
653
654 // byte 0 meaning "sea level" or 1 if the value is negative.
655 ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE_REF, EXIF_IFD_GPS);
656 convert_to_entry ( alt < 0.0 ? "1" : "0", 0.0, ee, exif_data_get_byte_order(ed) );
657
658 ee = my_exif_create_value (ed, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_IFD_GPS);
659 // see http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/GPS.html
660 convert_to_entry ( "MANUAL", 0.0, ee, exif_data_get_byte_order(ed) );
661
662 ee = my_exif_create_value (ed, EXIF_TAG_GPS_MAP_DATUM, EXIF_IFD_GPS);
663 convert_to_entry ( "WGS-84", 0.0, ee, exif_data_get_byte_order(ed) );
664
665 struct LatLon ll;
666 vik_coord_to_latlon ( &coord, &ll );
667
668 ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE_REF, EXIF_IFD_GPS);
669 // N or S
670 convert_to_entry ( ll.lat < 0.0 ? "S" : "N", 0.0, ee, exif_data_get_byte_order(ed) );
671
672 ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE, EXIF_IFD_GPS);
673 convert_to_entry ( NULL, ll.lat, ee, exif_data_get_byte_order(ed) );
674
675 ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_IFD_GPS);
676 // E or W
677 convert_to_entry ( ll.lon < 0.0 ? "W" : "E", 0.0, ee, exif_data_get_byte_order(ed) );
678
679 ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE, EXIF_IFD_GPS);
680 convert_to_entry ( NULL, ll.lon, ee, exif_data_get_byte_order(ed) );
681
682 ee = my_exif_create_value (ed, EXIF_TAG_GPS_VERSION_ID, EXIF_IFD_GPS);
683 //convert_to_entry ( "2 0 0 0", 0.0, ee, exif_data_get_byte_order(ed) );
684 convert_to_entry ( "2 2 0 0", 0.0, ee, exif_data_get_byte_order(ed) );
685
686 jpeg_data_set_exif_data (jdata, ed);
687
688 if ( jdata ) {
689 /* Save the modified image. */
690 result = jpeg_data_save_file (jdata, filename);
691
692 // Convert result from 1 for success, 0 for failure into our scheme
693 result = !result;
694
695 jpeg_data_unref (jdata);
696 }
697 else {
698 // Epic fail - file probably not a JPEG
699 result = 2;
700 }
701
702 if ( no_change_mtime ) {
703 // Restore mtime, using the saved value
704 struct stat stat_tmp;
705 struct utimbuf utb;
706 stat ( filename, &stat_tmp );
707 utb.actime = stat_tmp.st_atime;
708 utb.modtime = stat_save.st_mtime;
709 utime ( filename, &utb );
710 }
711
712 return result;
713}