]> git.street.me.uk Git - andy/viking.git/blob - src/geotag_exif.c
Fix crashing on invoking the Customize Toolbar from the preferences dialog.
[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-2014, 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 static 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  */
172 struct 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
190 MyReturn0:
191         // Finished with EXIF
192         exif_data_free ( ed );
193
194         return ll;
195 }
196
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  */
206 VikWaypoint* 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
236         ll = get_latlon ( ed );
237
238         // Hopefully won't have valid images at 0,0!
239         if ( ll.lat == 0.0 && ll.lon == 0.0 )
240                 goto MyReturn;
241
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
280 MyReturn:
281         // Finished with EXIF
282         exif_data_free ( ed );
283
284         return wp;
285 }
286
287 /**
288  * a_geotag_waypoint_positioned:
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)
292  * @waypoint: An existing waypoint to update (can be NULL to generate a new waypoint)
293  *
294  * Returns: An allocated waypoint if the input waypoint is NULL,
295  *  otherwise the passed in waypoint is updated
296  *
297  *  Here EXIF processing is used to get non position related information (i.e. just the comment)
298  *
299  */
300 VikWaypoint* a_geotag_waypoint_positioned ( const gchar *filename, VikCoord coord, gdouble alt, gchar **name, VikWaypoint *wp )
301 {
302         *name = NULL;
303         if ( wp == NULL ) {
304                 // Need to create waypoint
305                 wp = vik_waypoint_new();
306                 wp->visible = TRUE;
307         }
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
332         return wp;
333 }
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  */
345 gchar* a_geotag_get_exif_date_from_file ( const gchar *filename, gboolean *has_GPS_info )
346 {
347         gchar* datetime = NULL;
348         *has_GPS_info = FALSE;
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
366
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
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
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  */
391 static 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  */
453 static 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  */
615 gint 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 }