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