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