1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
3 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
5 * Copyright (C) 2011, Rob Norris <rw_norris@hotmail.com>
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.
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.
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
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
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.
33 #include "geotag_exif.h"
42 #include <glib/gi18n.h>
43 #include <libexif/exif-data.h>
44 #include "libjpeg/jpeg-data.h"
47 * Attempt to get a single comment from the various exif fields
49 static gchar* geotag_get_exif_comment ( ExifData *ed )
54 // Try various options to create a comment
56 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_IMAGE_DESCRIPTION);
58 exif_entry_get_value ( ee, str, 128 );
59 return g_strdup ( str );
62 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_COMMENT);
64 exif_entry_get_value ( ee, str, 128 );
65 return g_strdup ( str );
68 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_SUBJECT);
70 exif_entry_get_value ( ee, str, 128 );
71 return g_strdup ( str );
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);
79 exif_entry_get_value ( ee, str, 128 );
80 return g_strdup ( str );
83 // Otherwise nothing found
88 * Handles 3 part location Rationals
89 * Handles 1 part rational (must specify 0 for the offset)
91 static gdouble Rational2Double ( unsigned char *data, int offset, ExifByteOrder order )
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:
98 // dd/v is easy: result = dd/v.
103 // ss/v is sorta easy.
105 // -- / 3600 = result
107 // Each part is added to the final number.
110 er = exif_get_rational (data, order);
111 ans = (gdouble)er.numerator / (gdouble)er.denominator;
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 );
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)
129 * Returns: An allocated Waypoint or NULL if Waypoint could not be generated (e.g. no EXIF info)
132 VikWaypoint* a_geotag_create_waypoint_from_file ( const gchar *filename, VikCoordMode vcmode, gchar **name )
134 // Default return values (for failures)
136 VikWaypoint *wp = NULL;
139 //ExifLog *log = NULL;
141 // open image with libexif
142 ExifData *ed = exif_data_new_from_file ( filename );
144 // Detect EXIF load failure
146 // return with no Waypoint
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 ) )
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 ) )
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 ) )
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) )
175 // Lat & Long is necessary to form a waypoint.
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 ) )
181 ll.lat = Rational2Double ( ee->data,
182 exif_format_get_size(ee->format),
183 exif_data_get_byte_order(ed) );
185 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LATITUDE_REF);
187 exif_entry_get_value ( ee, str, 128 );
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 ) )
196 ll.lon = Rational2Double ( ee->data,
197 exif_format_get_size(ee->format),
198 exif_data_get_byte_order(ed) );
200 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_GPS], EXIF_TAG_GPS_LONGITUDE_REF);
202 exif_entry_get_value ( ee, str, 128 );
208 // Not worried if none of the other fields exist, as can default the values to something
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,
216 exif_data_get_byte_order(ed) );
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 )
224 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE);
226 exif_entry_get_value ( ee, str, 128 );
227 *name = g_strdup ( str );
231 // Now create Waypoint with acquired information
233 wp = vik_waypoint_new();
235 // Set info from exif values
237 vik_coord_load_from_latlon ( &(wp->coord), vcmode, &ll );
241 wp->comment = geotag_get_exif_comment ( ed );
243 vik_waypoint_set_image ( wp, filename );
246 // Finished with EXIF
247 exif_data_free ( ed );
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)
258 * Returns: An allocated Waypoint or NULL if Waypoint could not be generated
260 * Here EXIF processing is used to get non position related information (i.e. just the comment)
263 VikWaypoint* a_geotag_create_waypoint_positioned ( const gchar *filename, VikCoord coord, gdouble alt, gchar **name )
266 VikWaypoint *wp = vik_waypoint_new();
271 ExifData *ed = exif_data_new_from_file ( filename );
273 // Set info from exif values
275 wp->comment = geotag_get_exif_comment ( ed );
280 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_0], EXIF_TAG_XP_TITLE);
282 exif_entry_get_value ( ee, str, 128 );
283 *name = g_strdup ( str );
286 // Finished with EXIF
287 exif_data_free ( ed );
290 vik_waypoint_set_image ( wp, filename );
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
301 * Returns: An allocated string with the date and time in EXIF_DATE_FORMAT, otherwise NULL if some kind of failure
303 * Here EXIF processing is used to get time information
306 gchar* a_geotag_get_exif_date_from_file ( const gchar *filename, gboolean *has_GPS_info )
308 gchar* datetime = NULL;
310 ExifData *ed = exif_data_new_from_file ( filename );
312 // Detect EXIF load failure
319 ee = exif_content_get_entry (ed->ifd[EXIF_IFD_EXIF], EXIF_TAG_DATE_TIME_ORIGINAL);
321 exif_entry_get_value ( ee, str, 128 );
322 datetime = g_strdup ( str );
326 *has_GPS_info = FALSE;
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;
333 exif_data_free ( ed );
339 /**! If the entry doesn't exist, create it.
340 * Based on exif command line action_create_value function in exif 0.6.20
342 static ExifEntry* my_exif_create_value (ExifData *ed, ExifTag tag, ExifIfd ifd)
344 ExifEntry *e = exif_content_get_entry (ed->ifd[ifd], tag);
346 e = exif_entry_new ();
347 exif_content_add_entry (ed->ifd[ifd], e);
349 exif_entry_initialize (e, tag);
351 // exif_entry_initialize doesn't seem to do much, especially for the GPS tags
352 // so have to setup fields ourselves:
355 if ( tag == EXIF_TAG_GPS_VERSION_ID ) {
356 e->format = EXIF_FORMAT_BYTE;
358 e->size = sizeof (char) * e->components;
361 e->data = g_malloc (e->size);
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
369 if ( tag == EXIF_TAG_GPS_LATITUDE || tag == EXIF_TAG_GPS_LONGITUDE ) {
370 e->format = EXIF_FORMAT_RATIONAL;
372 e->size = sizeof (ExifRational) * e->components;
375 e->data = g_malloc (e->size);
377 if ( tag == EXIF_TAG_GPS_ALTITUDE ) {
378 e->format = EXIF_FORMAT_RATIONAL;
380 e->size = sizeof (ExifRational) * e->components;
383 e->data = g_malloc (e->size);
385 if ( tag == EXIF_TAG_GPS_ALTITUDE_REF ) {
387 e->size = sizeof (char) * e->components;
390 e->data = g_malloc (e->size);
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?
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
404 static void convert_to_entry (const char *set_value, gdouble gdvalue, ExifEntry *e, ExifByteOrder o)
406 unsigned int i, numcomponents;
407 char *value_p = NULL;
410 * ASCII strings are handled separately,
411 * since they don't require any conversion.
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);
422 g_warning (_("Not enough memory."));
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,
432 strcpy ((char *) e->data, set_value);
437 * Make sure we can handle this entry
439 if ((e->components == 0) && *set_value) {
440 g_warning (_("Setting a value for this tag is unsupported!"));
444 gboolean use_string = (set_value != NULL);
446 /* Copy the string so we can modify it */
447 buf = g_strdup (set_value);
450 value_p = strtok (buf, " ");
453 numcomponents = e->components;
454 for (i = 0; i < numcomponents; ++i) {
459 g_warning (_("Too few components specified (need %d, found %d)\n"), numcomponents, i);
462 if (!isdigit(*value_p) && (*value_p != '+') && (*value_p != '-')) {
463 g_warning (_("Numeric value expected\n"));
468 s = exif_format_get_size (e->format);
470 case EXIF_FORMAT_ASCII:
471 g_warning (_("This shouldn't happen!"));
474 case EXIF_FORMAT_SHORT:
475 exif_set_short (e->data + (s * i), o, atoi (value_p));
477 case EXIF_FORMAT_SSHORT:
478 exif_set_sshort (e->data + (s * i), o, atoi (value_p));
480 case EXIF_FORMAT_RATIONAL: {
484 if ( use_string && value_p )
485 val = fabs (atol (value_p));
487 val = fabs (gdvalue);
490 // One (or first) part rational
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;
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);
507 // Now for Location 3 part rationals do Mins and Seconds format
509 // Rounded down minutes
511 er.denominator = 1.0;
512 er.numerator = (ExifLong) ( (int) floor ( ( val - floor (val) ) * 60.0 ) );
517 er.denominator = 100.0;
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.
523 exif_set_rational (e->data + (s * i), o, er );
526 case EXIF_FORMAT_LONG:
527 exif_set_long (e->data + (s * i), o, atol (value_p));
529 case EXIF_FORMAT_SLONG:
530 exif_set_slong (e->data + (s * i), o, atol (value_p));
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);
537 case EXIF_FORMAT_FLOAT:
538 case EXIF_FORMAT_DOUBLE:
539 case EXIF_FORMAT_SRATIONAL:
541 g_warning (_("Not yet implemented!"));
546 value_p = strtok (NULL, " ");
554 g_warning (_("Warning; Too many components specified!"));
558 * a_geotag_write_exif_gps:
559 * @filename: The image file to save information in
560 * @coord: The location
561 * @alt: The elevation
563 * Returns: A value indicating success: 0, or some other value for failure
566 gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble alt, gboolean no_change_mtime )
568 gint result = 0; // OK so far...
570 // Save mtime for later use
571 struct stat stat_save;
572 if ( no_change_mtime )
573 stat ( filename, &stat_save );
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++...
583 // Actual EXIF settings here...
586 /* Parse the JPEG file. */
587 jdata = jpeg_data_new ();
588 jpeg_data_load_file (jdata, filename);
590 // Get current values
591 ExifData *ed = exif_data_new_from_file ( filename );
593 ed = exif_data_new ();
595 // Update ExifData with our new settings
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)
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) );
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) );
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) );
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) );
617 vik_coord_to_latlon ( &coord, &ll );
619 ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE_REF, EXIF_IFD_GPS);
621 convert_to_entry ( ll.lat < 0.0 ? "S" : "N", 0.0, ee, exif_data_get_byte_order(ed) );
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) );
626 ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_IFD_GPS);
628 convert_to_entry ( ll.lon < 0.0 ? "W" : "E", 0.0, ee, exif_data_get_byte_order(ed) );
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) );
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) );
637 jpeg_data_set_exif_data (jdata, ed);
640 /* Save the modified image. */
641 result = jpeg_data_save_file (jdata, filename);
643 // Convert result from 1 for success, 0 for failure into our scheme
646 jpeg_data_unref (jdata);
649 // Epic fail - file probably not a JPEG
653 if ( no_change_mtime ) {
654 // Restore mtime, using the saved value
655 struct stat stat_tmp;
657 stat ( filename, &stat_tmp );
658 utb.actime = stat_tmp.st_atime;
659 utb.modtime = stat_save.st_mtime;
660 utime ( filename, &utb );