]> git.street.me.uk Git - andy/viking.git/blob - src/geotag_exif.c
Fix translatable string with variable argument
[andy/viking.git] / src / geotag_exif.c
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  *
5  * Copyright (C) 2011, Rob Norris <rw_norris@hotmail.com>
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
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
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>
40 #include <glib/gstdio.h>
41 #include <libexif/exif-data.h>
42 #include "libjpeg/jpeg-data.h"
43
44 /**
45  * Attempt to get a single comment from the various exif fields
46  */
47 static 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  */
89 static 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
121 /**
122  * a_geotag_create_waypoint_from_file:
123  * @filename: The image file to process
124  * @vcmode:   The current location mode to use in the positioning of Waypoint
125  * @name:     Returns a name for the Waypoint (can be NULL)
126  *
127  * Returns: An allocated Waypoint or NULL if Waypoint could not be generated (e.g. no EXIF info)
128  *
129  */
130 VikWaypoint* a_geotag_create_waypoint_from_file ( const gchar *filename, VikCoordMode vcmode, gchar **name )
131 {
132         // Default return values (for failures)
133         *name = NULL;
134         VikWaypoint *wp = NULL;
135
136         // TODO use log?
137         //ExifLog *log = NULL;
138
139         // open image with libexif
140         ExifData *ed = exif_data_new_from_file ( filename );
141
142         // Detect EXIF load failure
143         if ( !ed )
144                 // return with no Waypoint
145                 return wp;
146
147         struct LatLon ll;
148
149         gchar str[128];
150         ExifEntry *ee;
151
152         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID);
153         // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0"
154         if ( ! ( ee && ee->components == 4 ) )
155                 goto MyReturn;
156         // Could test for these versions explicitly but may have byte order issues...
157         //if ( ! ( ee->data[0] == 2 && ee->data[2] == 0 && ee->data[3] == 0 ) )
158         //      goto MyReturn;
159
160         //
161         // Lat & Long is necessary to form a waypoint.
162         //
163         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE);
164         if ( ! ( ee && ee->components == 3 && ee->format == EXIF_FORMAT_RATIONAL ) )
165                 goto MyReturn;
166   
167         ll.lat = Rational2Double ( ee->data,
168                                                            exif_format_get_size(ee->format),
169                                                            exif_data_get_byte_order(ed) );
170
171         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE_REF);
172         if ( ee ) {
173                 exif_entry_get_value ( ee, str, 128 );
174                 if ( str[0] == 'S' )
175                         ll.lat = -ll.lat;
176         }
177
178         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE);
179         if ( ! ( ee && ee->components == 3 && ee->format == EXIF_FORMAT_RATIONAL ) )
180                 goto MyReturn;
181
182         ll.lon = Rational2Double ( ee->data,
183                                                            exif_format_get_size(ee->format),
184                                                            exif_data_get_byte_order(ed) );
185
186         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE_REF);
187         if ( ee ) {
188                 exif_entry_get_value ( ee, str, 128 );
189                 if ( str[0] == 'W' )
190                         ll.lon = -ll.lon;
191         }
192
193         //
194         // Not worried if none of the other fields exist, as can default the values to something
195         //
196
197         gdouble alt = VIK_DEFAULT_ALTITUDE;
198         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_ALTITUDE);
199         if ( ee && ee->components == 1 && ee->format == EXIF_FORMAT_RATIONAL ) {
200                 alt = Rational2Double ( ee->data,
201                                                                 0,
202                                                                 exif_data_get_byte_order(ed) );
203
204                 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_ALTITUDE_REF);
205                 if ( ee && ee->components == 1 && ee->format == EXIF_FORMAT_BYTE && ee->data[0] == 1 )
206                         alt = -alt;
207         }
208
209         // Name
210         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE);
211         if ( ee ) {
212                 exif_entry_get_value ( ee, str, 128 );
213                 *name = g_strdup ( str );
214         }
215
216         //
217         // Now create Waypoint with acquired information
218         //
219         wp = vik_waypoint_new();
220         wp->visible = TRUE;
221         // Set info from exif values
222         // Location
223         vik_coord_load_from_latlon ( &(wp->coord), vcmode, &ll );
224         // Altitude
225         wp->altitude = alt;
226
227         wp->comment = geotag_get_exif_comment ( ed );
228
229         vik_waypoint_set_image ( wp, filename );
230
231 MyReturn:
232         // Finished with EXIF
233         exif_data_free ( ed );
234
235         return wp;
236 }
237
238 /**
239  * a_geotag_waypoint_positioned:
240  * @filename: The image file to process
241  * @coord:    The location for positioning the Waypoint
242  * @name:     Returns a name for the Waypoint (can be NULL)
243  * @waypoint: An existing waypoint to update (can be NULL to generate a new waypoint)
244  *
245  * Returns: An allocated waypoint if the input waypoint is NULL,
246  *  otherwise the passed in waypoint is updated
247  *
248  *  Here EXIF processing is used to get non position related information (i.e. just the comment)
249  *
250  */
251 VikWaypoint* a_geotag_waypoint_positioned ( const gchar *filename, VikCoord coord, gdouble alt, gchar **name, VikWaypoint *wp )
252 {
253         *name = NULL;
254         if ( wp == NULL ) {
255                 // Need to create waypoint
256                 wp = vik_waypoint_new();
257                 wp->visible = TRUE;
258         }
259         wp->coord = coord;
260         wp->altitude = alt;
261
262         ExifData *ed = exif_data_new_from_file ( filename );
263
264         // Set info from exif values
265         if ( ed ) {
266                 wp->comment = geotag_get_exif_comment ( ed );
267
268                 gchar str[128];
269                 ExifEntry *ee;
270                 // Name
271                 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE);
272                 if ( ee ) {
273                         exif_entry_get_value ( ee, str, 128 );
274                         *name = g_strdup ( str );
275                 }
276
277                 // Finished with EXIF
278                 exif_data_free ( ed );
279         }
280
281         vik_waypoint_set_image ( wp, filename );
282
283         return wp;
284 }
285
286 /**
287  * a_geotag_get_exif_date_from_file:
288  * @filename: The image file to process
289  * @has_GPS_info: Returns whether the file has existing GPS information
290  *
291  * Returns: An allocated string with the date and time in EXIF_DATE_FORMAT, otherwise NULL if some kind of failure
292  *
293  *  Here EXIF processing is used to get time information
294  *
295  */
296 gchar* a_geotag_get_exif_date_from_file ( const gchar *filename, gboolean *has_GPS_info )
297 {
298         gchar* datetime = NULL;
299         *has_GPS_info = FALSE;
300
301         ExifData *ed = exif_data_new_from_file ( filename );
302
303         // Detect EXIF load failure
304         if ( !ed )
305                 return datetime;
306
307         // Also allows us to remember what directory we where using
308         gchar *dir = g_path_get_dirname ( filename );
309         if ( dir ) {
310                 if ( g_chdir ( dir ) ) {
311                         g_warning ( "Could not change directory to %s", dir );
312                 }
313                 g_free (dir);
314         }
315
316         gchar str[128];
317         ExifEntry *ee;
318
319         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL);
320         if ( ee ) {
321                 exif_entry_get_value ( ee, str, 128 );
322                 datetime = g_strdup ( str );
323         }
324
325         // Check GPS Info
326
327         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_VERSION_ID);
328         // Confirm this has a GPS Id - normally "2.0.0.0" or "2.2.0.0"
329         if ( ee && ee->components == 4 )
330                 *has_GPS_info = TRUE;
331
332         // Check other basic GPS fields exist too
333         // I have encountered some images which have just the EXIF_TAG_GPS_VERSION_ID but nothing else
334         // So to confirm check more EXIF GPS TAGS:
335         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE);
336         if ( !ee )
337                 *has_GPS_info = FALSE;
338         ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE);
339         if ( !ee )
340                 *has_GPS_info = FALSE;
341
342         exif_data_free ( ed );
343
344         return datetime;
345 }
346
347
348 /**! If the entry doesn't exist, create it.
349  * Based on exif command line action_create_value function in exif 0.6.20
350  */
351 static ExifEntry* my_exif_create_value (ExifData *ed, ExifTag tag, ExifIfd ifd)
352 {
353         ExifEntry *e = exif_content_get_entry (ed->ifd[ifd], tag);
354         if ( !e ) {
355             e = exif_entry_new ();
356             exif_content_add_entry (ed->ifd[ifd], e);
357
358                 exif_entry_initialize (e, tag);
359
360                 // exif_entry_initialize doesn't seem to do much, especially for the GPS tags
361                 //   so have to setup fields ourselves:
362                 e->tag = tag;
363
364                 if ( tag == EXIF_TAG_GPS_VERSION_ID ) {
365                         e->format = EXIF_FORMAT_BYTE;
366                         e->components = 4;
367                         e->size = sizeof (char) * e->components;
368                         if ( e->data )
369                                 g_free (e->data);
370                         e->data = g_malloc (e->size);
371                 }
372                 if ( tag == EXIF_TAG_GPS_MAP_DATUM ||
373                          tag == EXIF_TAG_GPS_LATITUDE_REF || tag == EXIF_TAG_GPS_LONGITUDE_REF ||
374                          tag == EXIF_TAG_GPS_PROCESSING_METHOD ) {
375                         e->format = EXIF_FORMAT_ASCII;
376                         // NB Allocation is handled later on when the actual string used is known
377                 }
378                 if ( tag == EXIF_TAG_GPS_LATITUDE || tag == EXIF_TAG_GPS_LONGITUDE ) {
379                         e->format = EXIF_FORMAT_RATIONAL;
380                         e->components = 3;
381                         e->size = sizeof (ExifRational) * e->components;
382                         if ( e->data )
383                                 g_free (e->data);
384                         e->data = g_malloc (e->size);
385                 }
386                 if ( tag == EXIF_TAG_GPS_ALTITUDE ) {
387                         e->format = EXIF_FORMAT_RATIONAL;
388                         e->components = 1;
389                         e->size = sizeof (ExifRational) * e->components;
390                         if ( e->data )
391                                 g_free (e->data);
392                         e->data = g_malloc (e->size);
393                 }
394                 if ( tag == EXIF_TAG_GPS_ALTITUDE_REF ) {
395                         e->components = 1;
396                         e->size = sizeof (char) * e->components;
397                         if ( e->data )
398                                 g_free (e->data);
399                         e->data = g_malloc (e->size);
400                 }
401             /* The entry has been added to the IFD, so we can unref it */
402             //exif_entry_unref(e);
403                 // Crashes later on, when saving to jpeg if the above unref is enabled!!
404                 // ?Some other malloc problem somewhere?
405         }
406         return e;
407 }
408
409 /** Heavily based on convert_arg_to_entry from exif command line tool.
410  *  But without ExifLog, exitting, use of g_* io functions
411  *   and can take a gdouble value instead of a string
412  */
413 static void convert_to_entry (const char *set_value, gdouble gdvalue, ExifEntry *e, ExifByteOrder o)
414 {
415         unsigned int i, numcomponents;
416         char *value_p = NULL;
417         char *buf = NULL;
418         /*
419          * ASCII strings are handled separately,
420          * since they don't require any conversion.
421          */
422         if (e->format == EXIF_FORMAT_ASCII ||
423             e->tag == EXIF_TAG_USER_COMMENT) {
424                 if (e->data) g_free (e->data);
425                 e->components = strlen (set_value) + 1;
426                 if (e->tag == EXIF_TAG_USER_COMMENT)
427                         e->components += 8 - 1;
428                 e->size = sizeof (char) * e->components;
429                 e->data = g_malloc (e->size);
430                 if (!e->data) {
431                         g_warning (_("Not enough memory."));
432                         return;
433                 }
434                 if (e->tag == EXIF_TAG_USER_COMMENT) {
435                         /* assume ASCII charset */
436                         /* TODO: get this from the current locale */
437                         memcpy ((char *) e->data, "ASCII\0\0\0", 8);
438                         memcpy ((char *) e->data + 8, set_value,
439                                         strlen (set_value));
440                 } else
441                         strcpy ((char *) e->data, set_value);
442                 return;
443         }
444
445         /*
446          * Make sure we can handle this entry
447          */
448         if ((e->components == 0) && *set_value) {
449                 g_warning (_("Setting a value for this tag is unsupported!"));
450                 return;
451         }
452
453         gboolean use_string = (set_value != NULL);
454         if ( use_string ) {
455                 /* Copy the string so we can modify it */
456                 buf = g_strdup (set_value);
457                 if (!buf)
458                         return;
459                 value_p = strtok (buf, " ");
460         }
461
462         numcomponents = e->components;
463         for (i = 0; i < numcomponents; ++i) {
464                 unsigned char s;
465
466                 if ( use_string ) {
467                         if (!value_p) {
468                                 g_warning (_("Too few components specified (need %d, found %d)\n"), numcomponents, i);
469                                 return;
470                         }
471                         if (!isdigit(*value_p) && (*value_p != '+') && (*value_p != '-')) {
472                                 g_warning (_("Numeric value expected\n"));
473                                 return;
474                         }
475                 }
476
477                 s = exif_format_get_size (e->format);
478                 switch (e->format) {
479                 case EXIF_FORMAT_ASCII:
480                         g_warning (_("This shouldn't happen!"));
481                         return;
482                         break;
483                 case EXIF_FORMAT_SHORT:
484                         exif_set_short (e->data + (s * i), o, atoi (value_p));
485                         break;
486                 case EXIF_FORMAT_SSHORT:
487                         exif_set_sshort (e->data + (s * i), o, atoi (value_p));
488                         break;
489                 case EXIF_FORMAT_RATIONAL: {
490                         ExifRational er;
491
492                         double val = 0.0 ;
493                         if ( use_string && value_p )
494                                 val = fabs (atol (value_p));
495                         else
496                                 val = fabs (gdvalue);
497
498                         if ( i == 0 ) {
499                                 // One (or first) part rational
500
501                                 // Sneak peek into tag as location tags need rounding down to give just the degrees part
502                                 if ( e->tag == EXIF_TAG_GPS_LATITUDE || e->tag == EXIF_TAG_GPS_LONGITUDE ) {
503                                         er.numerator = (ExifLong) floor ( val );
504                                         er.denominator = 1.0;
505                                 }
506                                 else {
507                                         // I don't see any point in doing anything too complicated here,
508                                         //   such as trying to work out the 'best' denominator
509                                         // For the moment use KISS principle.
510                                         // Fix a precision of 1/100 metre as that's more than enough for GPS accuracy especially altitudes!
511                                         er.denominator = 100.0;
512                                         er.numerator = (ExifLong) (val * er.denominator);
513                                 }
514                         }
515
516                         // Now for Location 3 part rationals do Mins and Seconds format
517
518                         // Rounded down minutes
519                         if ( i == 1 ) {
520                                 er.denominator = 1.0;
521                                 er.numerator = (ExifLong) ( (int) floor ( ( val - floor (val) ) * 60.0 ) );
522                         }
523
524                         // Finally seconds
525                         if ( i == 2 ) {
526                                 er.denominator = 100.0;
527
528                                 // Fractional minute.
529                                 double FracPart = ((val - floor(val)) * 60) - (double)(int) floor ( ( val - floor (val) ) * 60.0 );
530                                 er.numerator = (ExifLong) ( (int)floor(FracPart * 6000) ); // Convert to seconds.
531                         }
532                         exif_set_rational (e->data + (s * i), o, er );
533                         break;
534                 }
535                 case EXIF_FORMAT_LONG:
536                         exif_set_long (e->data + (s * i), o, atol (value_p));
537                         break;
538                 case EXIF_FORMAT_SLONG:
539                         exif_set_slong (e->data + (s * i), o, atol (value_p));
540                         break;
541                 case EXIF_FORMAT_BYTE:
542                 case EXIF_FORMAT_SBYTE:
543                 case EXIF_FORMAT_UNDEFINED: /* treat as byte array */
544                         e->data[s * i] = atoi (value_p);
545                         break;
546                 case EXIF_FORMAT_FLOAT:
547                 case EXIF_FORMAT_DOUBLE:
548                 case EXIF_FORMAT_SRATIONAL:
549                 default:
550                         g_warning (_("Not yet implemented!"));
551                         return;
552                 }
553                 
554                 if ( use_string )
555                         value_p = strtok (NULL, " ");
556
557         }
558
559         g_free (buf);
560
561         if ( use_string )
562                 if ( value_p )
563                         g_warning (_("Warning; Too many components specified!"));
564 }
565
566 /**
567  * a_geotag_write_exif_gps:
568  * @filename: The image file to save information in
569  * @coord:    The location
570  * @alt:      The elevation
571  *
572  * Returns: A value indicating success: 0, or some other value for failure
573  *
574  */
575 gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble alt, gboolean no_change_mtime )
576 {
577         gint result = 0; // OK so far...
578
579         // Save mtime for later use
580         struct stat stat_save;
581         if ( no_change_mtime )
582                 stat ( filename, &stat_save );
583
584         /*
585           Appears libexif doesn't actually support writing EXIF data directly to files
586           Thus embed command line exif writing method within Viking
587           (for example this is done by Enlightment - http://www.enlightenment.org/ )
588           This appears to be JPEG only, but is probably 99% of our use case
589           Alternatively consider using libexiv2 and C++...
590         */
591
592         // Actual EXIF settings here...
593         JPEGData *jdata;
594
595         /* Parse the JPEG file. */
596         jdata = jpeg_data_new ();
597         jpeg_data_load_file (jdata, filename);
598
599         // Get current values
600         ExifData *ed = exif_data_new_from_file ( filename );
601         if ( !ed )
602                 ed = exif_data_new ();
603
604         // Update ExifData with our new settings
605         ExifEntry *ee;
606         //
607         // 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
608         // (unless of course there is some bug in the setting of the ID, that prevents subsequent tags)
609         //
610
611         ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE, EXIF_IFD_GPS);
612         convert_to_entry ( NULL, alt, ee, exif_data_get_byte_order(ed) );
613
614         // byte 0 meaning "sea level" or 1 if the value is negative.
615         ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE_REF, EXIF_IFD_GPS);
616         convert_to_entry ( alt < 0.0 ? "1" : "0", 0.0, ee, exif_data_get_byte_order(ed) );
617
618         ee = my_exif_create_value (ed, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_IFD_GPS);
619         // see http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/GPS.html
620         convert_to_entry ( "MANUAL", 0.0, ee, exif_data_get_byte_order(ed) );
621
622         ee = my_exif_create_value (ed, EXIF_TAG_GPS_MAP_DATUM, EXIF_IFD_GPS);
623         convert_to_entry ( "WGS-84", 0.0, ee, exif_data_get_byte_order(ed) );
624
625         struct LatLon ll;
626     vik_coord_to_latlon ( &coord, &ll );
627
628         ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE_REF, EXIF_IFD_GPS);
629         // N or S
630         convert_to_entry ( ll.lat < 0.0 ? "S" : "N", 0.0, ee, exif_data_get_byte_order(ed) );
631
632         ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE, EXIF_IFD_GPS);
633         convert_to_entry ( NULL, ll.lat, ee, exif_data_get_byte_order(ed) );
634
635         ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_IFD_GPS);
636         // E or W
637         convert_to_entry ( ll.lon < 0.0 ? "W" : "E", 0.0, ee, exif_data_get_byte_order(ed) );
638
639         ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE, EXIF_IFD_GPS);
640         convert_to_entry ( NULL, ll.lon, ee, exif_data_get_byte_order(ed) );
641
642         ee = my_exif_create_value (ed, EXIF_TAG_GPS_VERSION_ID, EXIF_IFD_GPS);
643         //convert_to_entry ( "2 0 0 0", 0.0, ee, exif_data_get_byte_order(ed) );
644         convert_to_entry ( "2 2 0 0", 0.0, ee, exif_data_get_byte_order(ed) );
645
646         jpeg_data_set_exif_data (jdata, ed);
647
648         if ( jdata ) {
649                 /* Save the modified image. */
650                 result = jpeg_data_save_file (jdata, filename);
651
652                 // Convert result from 1 for success, 0 for failure into our scheme
653                 result = !result;
654                 
655                 jpeg_data_unref (jdata);
656         }
657         else {
658                 // Epic fail - file probably not a JPEG
659                 result = 2;
660         }
661
662         if ( no_change_mtime ) {
663                 // Restore mtime, using the saved value
664                 struct stat stat_tmp;
665                 struct utimbuf utb;
666                 stat ( filename, &stat_tmp );
667                 utb.actime = stat_tmp.st_atime;
668                 utb.modtime = stat_save.st_mtime;
669                 utime ( filename, &utb );
670         }
671
672         return result;
673 }