]> git.street.me.uk Git - andy/viking.git/commitdiff
SF Bugs#112: Enable using libgexiv2 for writing geotag image data to void XMP data...
authorRob Norris <rw_norris@hotmail.com>
Mon, 17 Nov 2014 22:46:41 +0000 (22:46 +0000)
committerRob Norris <rw_norris@hotmail.com>
Sun, 14 Dec 2014 17:20:28 +0000 (17:20 +0000)
Use libgexiv2 which is the C interface to Exiv2 C++ library.
Requires installation of the package named libgexiv2-dev or similar.

If libgexiv2 is not available,
 then it's possible to run configure with --with-libexif to force the use of the libexif
 (and then 'libjpeg' code to write EXIF data only to JPEGs but may lose XMP data)

configure.ac
src/Makefile.am
src/dialog.c
src/geotag_exif.c
viking.spec.in
win32/configure_and_make.bat

index 0d7d4278d94e6c9b52bb4328db1dbdaf847c21c5..8d934266cf0923c5729c5a117673eba38796f4ae 100644 (file)
@@ -228,6 +228,8 @@ case $ac_cv_enable_geocaches in
 esac
 AM_CONDITIONAL([GEOCACHES], [test x$ac_cv_enable_geocaches = xyes])
 
+# Geotagging
+AC_ARG_WITH(libexif, AC_HELP_STRING([--with-libexif], [Force usage of libexif instead of libgexiv2]))
 AC_ARG_ENABLE(geotag, AC_HELP_STRING([--enable-geotag],
              [enable Geotag Support (default is enable).]),
               [ac_cv_enable_geotag=$enableval],
@@ -236,11 +238,21 @@ AC_CACHE_CHECK([whether to enable Geotag Support],
                [ac_cv_enable_geotag], [ac_cv_enable_geotag=yes])
 case $ac_cv_enable_geotag in
   yes)
-    AC_CHECK_LIB(exif,exif_loader_new,,AC_MSG_ERROR([libexif is needed for Geotag features[,] but is not found. The feature can be disabled with --disable-geotag]))
+    AS_IF([test x$with_libexif = xyes],
+      AC_CHECK_HEADER([libexif/exif-data.h],[],AC_MSG_ERROR([exif-data.h is needed but not found - you will need to install package 'libexif-dev' or similar]))
+      AC_CHECK_LIB(exif,exif_loader_new,, AC_MSG_ERROR([libexif is not found but it has been forcibly required])),
+      # gexiv2.h relies on glib so a simple compile check fails.
+      #AC_CHECK_HEADER([gexiv2/gexiv2.h],,AC_MSG_ERROR([Error msg...]))
+      AC_CHECK_LIB(gexiv2,gexiv2_metadata_new,, AC_MSG_ERROR([libgexiv2 is needed but not found - you will need to install package 'libgexiv2-dev' or similar. The feature can be disabled with --disable-geotag]))
+      )
     AC_DEFINE(VIK_CONFIG_GEOTAG, [], [GEOTAG STUFF])
     ;;
 esac
 AM_CONDITIONAL([GEOTAG], [test x$ac_cv_enable_geotag = xyes])
+AM_CONDITIONAL([GEXIV2], [test x$ac_cv_lib_gexiv2_gexiv2_metadata_new = xyes] )
+# Tested with gexiv2 0.10.2, but probably would work with older versions; may be all of them...
+#AM_COND_IF([GEXIV2], [PKG_CHECK_MODULES([GEXIV2], [gexiv2 >= 0.6.1])])
+AM_CONDITIONAL([LIBEXIF], [test x$ac_cv_lib_exif_exif_loader_new = xyes] )
 
 AC_ARG_ENABLE(dem24k, AC_HELP_STRING([--enable-dem24k],
              [enable USGS 24k DEM (default is disable) download source. Requires dem24k.pl script in path.]),
@@ -420,7 +432,7 @@ echo "Open Street Map                  : $ac_cv_enable_openstreetmap"
 echo "BlueMarble                       : $ac_cv_enable_bluemarble"
 echo "Geonames                         : $ac_cv_enable_geonames"
 echo "Geocaches Acquire                : $ac_cv_enable_geocaches"
-echo "Geotag Support                   : $ac_cv_enable_geotag"
+echo "Geotag Support                   : $ac_cv_enable_geotag (libgexiv2=$ac_cv_lib_gexiv2_gexiv2_metadata_new libexif=$ac_cv_lib_exif_exif_loader_new)"
 echo "USGS 24k DEM                     : $ac_cv_enable_dem24k"
 echo "Realtime GPS Tracking            : $ac_cv_enable_realtimegpstracking"
 echo "bzip2 Support                    : $ac_cv_enable_bzip2"
index d17999d3679b3add87cb67ebfc21adb7d632515e..167b391f6b542c7ca6261b7b04fdc64fb0a65b46 100644 (file)
@@ -198,7 +198,12 @@ if GEOTAG
 libviking_a_SOURCES += \
        datasource_geotag.c \
        geotag_exif.c geotag_exif.h \
-       viktrwlayer_geotag.c viktrwlayer_geotag.h \
+       viktrwlayer_geotag.c viktrwlayer_geotag.h
+endif
+
+# libexif doesn't have write support of EXIF info - so reused this code from command line exif tool
+if LIBEXIF
+libviking_a_SOURCES += \
        libjpeg/jpeg-data.c libjpeg/jpeg-data.h \
        libjpeg/jpeg-marker.c libjpeg/jpeg-marker.h
 endif
index aaa77bbee7ae360cef3a8bec347f9c99d1a5a49d..cc1438ae827dc7c4a04570367dce435d2c7fb5f3 100644 (file)
@@ -636,6 +636,9 @@ void a_dialog_about ( GtkWindow *parent )
 #ifdef HAVE_LIBGPS
     "libgps",
 #endif
+#ifdef HAVE_LIBGEXIV2
+    "libgexiv2",
+#endif
 #ifdef HAVE_LIBEXIF
     "libexif",
 #endif
index ca3adf900f3e643aa4fc2382412e0894ea765df0..8d1005b14c3032243450bfd653dc447591169372 100644 (file)
 /*
  * This uses EXIF information from images to create waypoints at those positions
  *
- * For the implementation I have chosen to use libexif, which keeps Viking a pure C program
- * For an alternative implementation (a la gpscorrelate), one could use libeviv2 but it appears to be C++ only.
+ * The intial implementation uses libexif, which keeps Viking a pure C program.
+ * Now libgexiv2 is available (in C as a wrapper around the more powerful libexiv2 [C++]) so this is the preferred build.
+ *  The attentative reader will notice the use of gexiv2 is a lot simpler as well.
+ * For the time being the libexif code + build is still made available.
  */
 #include <string.h>
 #include "geotag_exif.h"
+#include "config.h"
 #include "globals.h"
 #include "file.h"
 
 #include <math.h>
 #include <glib/gi18n.h>
 #include <glib/gstdio.h>
+#ifdef HAVE_LIBGEXIV2
+#include <gexiv2/gexiv2.h>
+#endif
+#ifdef HAVE_LIBEXIF
 #include <libexif/exif-data.h>
 #include "libjpeg/jpeg-data.h"
+#endif
 
+#ifdef HAVE_LIBGEXIV2
+/**
+ * Attempt to get a single comment from the various exif fields
+ */
+static gchar* geotag_get_exif_comment ( GExiv2Metadata *gemd )
+{
+       //
+       // Try various options to create a comment
+       //
+       if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.ImageDescription" ) )
+               return g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.ImageDescription" ) );
+
+       if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.XPComment" ) )
+               return g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.XPComment" ) );
+
+       if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.XPSubject" ) )
+               return g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.XPSubject" ) );
+
+       if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.DateTimeOriginal" ) )
+               return g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.DateTimeOriginal" ) );
+
+       // Otherwise nothing found
+       return NULL;
+}
+#endif
+
+#ifdef HAVE_LIBEXIF
 /**
  * Attempt to get a single comment from the various exif fields
  */
@@ -77,7 +112,7 @@ static gchar* geotag_get_exif_comment ( ExifData *ed )
                exif_entry_get_value ( ee, str, 128 );
                return g_strdup ( str );
        }
-       
+
        // Otherwise nothing found
        return NULL;
 }
@@ -160,6 +195,7 @@ static struct LatLon get_latlon ( ExifData *ed )
 
        return ll;
 }
+#endif
 
 /**
  * a_geotag_get_position:
@@ -173,6 +209,20 @@ struct LatLon a_geotag_get_position ( const gchar *filename )
 {
        struct LatLon ll = { 0.0, 0.0 };
 
+#ifdef HAVE_LIBGEXIV2
+       GExiv2Metadata *gemd = gexiv2_metadata_new ();
+       if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) {
+               gdouble lat;
+               gdouble lon;
+               gdouble alt;
+               if ( gexiv2_metadata_get_gps_info ( gemd, &lon, &lat, &alt ) ) {
+                       ll.lat = lat;
+                       ll.lon = lon;
+               }
+       }
+       gexiv2_metadata_free  ( gemd );
+#else
+#ifdef HAVE_LIBEXIF
        // open image with libexif
        ExifData *ed = exif_data_new_from_file ( filename );
 
@@ -190,6 +240,8 @@ struct LatLon a_geotag_get_position ( const gchar *filename )
 MyReturn0:
        // Finished with EXIF
        exif_data_free ( ed );
+#endif
+#endif
 
        return ll;
 }
@@ -209,6 +261,38 @@ VikWaypoint* a_geotag_create_waypoint_from_file ( const gchar *filename, VikCoor
        *name = NULL;
        VikWaypoint *wp = NULL;
 
+#ifdef HAVE_LIBGEXIV2
+       GExiv2Metadata *gemd = gexiv2_metadata_new ();
+       if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) {
+               gdouble lat;
+               gdouble lon;
+               gdouble alt;
+               if ( gexiv2_metadata_get_gps_info ( gemd, &lon, &lat, &alt ) ) {
+                       struct LatLon ll;
+                       ll.lat = lat;
+                       ll.lon = lon;
+
+                       //
+                       // Now create Waypoint with acquired information
+                       //
+                       wp = vik_waypoint_new();
+                       wp->visible = TRUE;
+                       // Set info from exif values
+                       // Location
+                       vik_coord_load_from_latlon ( &(wp->coord), vcmode, &ll );
+                       // Altitude
+                       wp->altitude = alt;
+
+                       if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.XPTitle" ) )
+                               *name = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.XPTitle" ) );
+                       wp->comment = geotag_get_exif_comment ( gemd );
+
+                       vik_waypoint_set_image ( wp, filename );
+               }
+       }
+       gexiv2_metadata_free ( gemd );
+#else
+#ifdef HAVE_LIBEXIF
        // TODO use log?
        //ExifLog *log = NULL;
 
@@ -280,6 +364,8 @@ VikWaypoint* a_geotag_create_waypoint_from_file ( const gchar *filename, VikCoor
 MyReturn:
        // Finished with EXIF
        exif_data_free ( ed );
+#endif
+#endif
 
        return wp;
 }
@@ -308,6 +394,16 @@ VikWaypoint* a_geotag_waypoint_positioned ( const gchar *filename, VikCoord coor
        wp->coord = coord;
        wp->altitude = alt;
 
+#ifdef HAVE_LIBGEXIV2
+       GExiv2Metadata *gemd = gexiv2_metadata_new ();
+       if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) {
+                       wp->comment = geotag_get_exif_comment ( gemd );
+                       if ( gexiv2_metadata_has_tag ( gemd, "Exif.Image.XPTitle" ) )
+                               *name = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.XPTitle" ) );
+       }
+       gexiv2_metadata_free ( gemd );
+#else
+#ifdef HAVE_LIBEXIF
        ExifData *ed = exif_data_new_from_file ( filename );
 
        // Set info from exif values
@@ -326,6 +422,8 @@ VikWaypoint* a_geotag_waypoint_positioned ( const gchar *filename, VikCoord coor
                // Finished with EXIF
                exif_data_free ( ed );
        }
+#endif
+#endif
 
        vik_waypoint_set_image ( wp, filename );
 
@@ -347,6 +445,21 @@ gchar* a_geotag_get_exif_date_from_file ( const gchar *filename, gboolean *has_G
        gchar* datetime = NULL;
        *has_GPS_info = FALSE;
 
+#ifdef HAVE_LIBGEXIV2
+       GExiv2Metadata *gemd = gexiv2_metadata_new ();
+       if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) {
+               gdouble lat, lon;
+               *has_GPS_info = ( gexiv2_metadata_get_gps_longitude(gemd,&lon) && gexiv2_metadata_get_gps_latitude(gemd,&lat) );
+
+               // Prefer 'Photo' version over 'Image'
+               if ( gexiv2_metadata_has_tag ( gemd, "Exif.Photo.DateTimeOriginal" ) )
+                       datetime = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Photo.DateTimeOriginal" ) );
+               else
+                       datetime = g_strdup ( gexiv2_metadata_get_tag_interpreted_string ( gemd, "Exif.Image.DateTimeOriginal" ) );
+       }
+       gexiv2_metadata_free ( gemd );
+#else
+#ifdef HAVE_LIBEXIF
        ExifData *ed = exif_data_new_from_file ( filename );
 
        // Detect EXIF load failure
@@ -380,11 +493,13 @@ gchar* a_geotag_get_exif_date_from_file ( const gchar *filename, gboolean *has_G
                *has_GPS_info = FALSE;
 
        exif_data_free ( ed );
-
+#endif
+#endif
        return datetime;
 }
 
 
+#ifdef HAVE_LIBEXIF
 /**! If the entry doesn't exist, create it.
  * Based on exif command line action_create_value function in exif 0.6.20
  */
@@ -602,6 +717,7 @@ static void convert_to_entry (const char *set_value, gdouble gdvalue, ExifEntry
                if ( value_p )
                        g_warning (_("Warning; Too many components specified!"));
 }
+#endif
 
 /**
  * a_geotag_write_exif_gps:
@@ -621,6 +737,26 @@ gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble al
        if ( no_change_mtime )
                stat ( filename, &stat_save );
 
+#ifdef HAVE_LIBGEXIV2
+       GExiv2Metadata *gemd = gexiv2_metadata_new ();
+       if ( gexiv2_metadata_open_path ( gemd, filename, NULL ) ) {
+               struct LatLon ll;
+               vik_coord_to_latlon ( &coord, &ll );
+               if ( ! gexiv2_metadata_set_gps_info ( gemd, ll.lon, ll.lat, alt ) ) {
+                       result = 1; // Failed
+               }
+               else {
+                       GError *error = NULL;
+                       if ( ! gexiv2_metadata_save_file ( gemd, filename, &error ) ) {
+                               result = 2;
+                               g_warning ( "Write EXIF failure:%s" , error->message );
+                               g_error_free ( error );
+                       }
+               }
+       }
+       gexiv2_metadata_free ( gemd );
+#else
+#ifdef HAVE_LIBEXIF
        /*
          Appears libexif doesn't actually support writing EXIF data directly to files
          Thus embed command line exif writing method within Viking
@@ -699,6 +835,10 @@ gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble al
                result = 2;
        }
 
+       exif_data_free ( ed );
+#endif
+#endif
+
        if ( no_change_mtime ) {
                // Restore mtime, using the saved value
                struct stat stat_tmp;
@@ -709,7 +849,5 @@ gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble al
                utime ( filename, &utb );
        }
 
-       exif_data_free ( ed );
-
        return result;
 }
index 5c2035624465b205f683f678ae602c987d1c088c..71733b196119629f68f7c26cb1069621722a965e 100644 (file)
@@ -22,7 +22,7 @@ BuildRequires:        gpsd-devel
 BuildRequires: gettext perl(XML::Parser)
 BuildRequires: intltool
 BuildRequires: libxslt
-BuildRequires:  libexif-devel
+BuildRequires:  libgexiv2-devel
 BuildRequires:  libbz2-devel
 BuildRequires:  libmagic-devel
 BuildRequires:  libsqlite3-devel
index fe91504bfa14f24ea29a268ef21fbdd4192d32a9..4e194512a394f72de1ccf43c244101c687c7a1c1 100644 (file)
@@ -6,6 +6,7 @@
 ::\r
 set PATH=%PATH%;%SystemDrive%\Mingw\bin;%SystemDrive%\msys\1.0\bin\r
 pushd ..\r
-sh configure CFLAGS="-DWINDOWS -mwindows" LIBCURL=-lcurldll LIBS=-lzdll --disable-realtime-gps-tracking --disable-scrollkeeper --enable-windows\r
+:: ATM Don't have build method for libgexiv2, so use the fallback of libexif\r
+sh configure CFLAGS="-DWINDOWS -mwindows" LIBCURL=-lcurldll LIBS=-lzdll --with-libexif --disable-realtime-gps-tracking --disable-scrollkeeper --enable-windows\r
 popd\r
 make.bat\r