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