Example #1
0
/**
 * a_geotag_write_exif_gps:
 * @filename: The image file to save information in
 * @coord:    The location
 * @alt:      The elevation
 *
 * Returns: A value indicating success: 0, or some other value for failure
 *
 */
gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble alt, gboolean no_change_mtime )
{
	gint result = 0; // OK so far...

	// Save mtime for later use
	struct stat stat_save;
	if ( no_change_mtime )
		stat ( filename, &stat_save );

	/*
	  Appears libexif doesn't actually support writing EXIF data directly to files
	  Thus embed command line exif writing method within Viking
	  (for example this is done by Enlightment - http://www.enlightenment.org/ )
	  This appears to be JPEG only, but is probably 99% of our use case
	  Alternatively consider using libexiv2 and C++...
	*/

	// Actual EXIF settings here...
	JPEGData *jdata;

	/* Parse the JPEG file. */
	jdata = jpeg_data_new ();
	jpeg_data_load_file (jdata, filename);

	// Get current values
	ExifData *ed = exif_data_new_from_file ( filename );
	if ( !ed )
		ed = exif_data_new ();

	// Update ExifData with our new settings
	ExifEntry *ee;
	//
	// 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
	// (unless of course there is some bug in the setting of the ID, that prevents subsequent tags)
	//

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE, EXIF_IFD_GPS);
	convert_to_entry ( NULL, alt, ee, exif_data_get_byte_order(ed) );

	// byte 0 meaning "sea level" or 1 if the value is negative.
	ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE_REF, EXIF_IFD_GPS);
	convert_to_entry ( alt < 0.0 ? "1" : "0", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_IFD_GPS);
	// see http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/GPS.html
	convert_to_entry ( "MANUAL", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_MAP_DATUM, EXIF_IFD_GPS);
	convert_to_entry ( "WGS-84", 0.0, ee, exif_data_get_byte_order(ed) );

	struct LatLon ll;
    vik_coord_to_latlon ( &coord, &ll );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE_REF, EXIF_IFD_GPS);
	// N or S
	convert_to_entry ( ll.lat < 0.0 ? "S" : "N", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE, EXIF_IFD_GPS);
	convert_to_entry ( NULL, ll.lat, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_IFD_GPS);
	// E or W
	convert_to_entry ( ll.lon < 0.0 ? "W" : "E", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE, EXIF_IFD_GPS);
	convert_to_entry ( NULL, ll.lon, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_VERSION_ID, EXIF_IFD_GPS);
	//convert_to_entry ( "2 0 0 0", 0.0, ee, exif_data_get_byte_order(ed) );
	convert_to_entry ( "2 2 0 0", 0.0, ee, exif_data_get_byte_order(ed) );

	jpeg_data_set_exif_data (jdata, ed);

	if ( jdata ) {
		/* Save the modified image. */
		result = jpeg_data_save_file (jdata, filename);

		// Convert result from 1 for success, 0 for failure into our scheme
		result = !result;
		
		jpeg_data_unref (jdata);
	}
	else {
		// Epic fail - file probably not a JPEG
		result = 2;
	}

	if ( no_change_mtime ) {
		// Restore mtime, using the saved value
		struct stat stat_tmp;
		struct utimbuf utb;
		stat ( filename, &stat_tmp );
		utb.actime = stat_tmp.st_atime;
		utb.modtime = stat_save.st_mtime;
		utime ( filename, &utb );
	}

	return result;
}
Example #2
0
/**
 * a_geotag_write_exif_gps:
 * @filename: The image file to save information in
 * @coord:    The location
 * @alt:      The elevation
 * @direction:     The image direction (if NAN then these direction fields are not written)
 * @direction_ref: The image direction value type
 *
 * Returns: A value indicating success: 0, or some other value for failure
 *
 */
gint a_geotag_write_exif_gps ( const gchar *filename, VikCoord coord, gdouble alt,
                               gdouble direction, VikWaypointImageDirectionRef direction_ref,
                               gboolean no_change_mtime )
{
	gint result = 0; // OK so far...

	// Save mtime for later use
	struct stat stat_save;
	if ( no_change_mtime )
		if ( stat ( filename, &stat_save ) != 0 )
			g_warning ( "%s couldn't read: %s", __FUNCTION__, filename );

#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 {
			if ( !isnan(direction) ) {
				gint nom = (gint)round(direction * 10.0);
				gboolean set_d = gexiv2_metadata_set_exif_tag_rational ( gemd, EXIF_GPS_IMGDIR, nom, 10 );
				if ( !set_d ) result = 1; // Failed
				gboolean set_r = gexiv2_metadata_set_tag_string ( gemd, EXIF_GPS_IMGDIR_REF, direction_ref == WP_IMAGE_DIRECTION_REF_TRUE ? "T" : "M" );
				if ( !set_r ) result = 1; // Failed
			}
			// Still OK to save - no fails yet
			if ( result == 0 ) {
				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 );
				}
			}
		}
	}
	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
	  (for example this is done by Enlightment - http://www.enlightenment.org/ )
	  This appears to be JPEG only, but is probably 99% of our use case
	  Alternatively consider using libexiv2 and C++...
	*/

	// Actual EXIF settings here...
	JPEGData *jdata;

	/* Parse the JPEG file. */
	jdata = jpeg_data_new ();
	jpeg_data_load_file (jdata, filename);

	// Get current values
	ExifData *ed = exif_data_new_from_file ( filename );
	if ( !ed )
		ed = exif_data_new ();

	// Update ExifData with our new settings
	ExifEntry *ee;
	//
	// 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
	// (unless of course there is some bug in the setting of the ID, that prevents subsequent tags)
	//

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE, EXIF_IFD_GPS);
	convert_to_entry ( NULL, alt, ee, exif_data_get_byte_order(ed) );

	// byte 0 meaning "sea level" or 1 if the value is negative.
	ee = my_exif_create_value (ed, EXIF_TAG_GPS_ALTITUDE_REF, EXIF_IFD_GPS);
	convert_to_entry ( alt < 0.0 ? "1" : "0", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_PROCESSING_METHOD, EXIF_IFD_GPS);
	// see http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/GPS.html
	convert_to_entry ( "MANUAL", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_MAP_DATUM, EXIF_IFD_GPS);
	convert_to_entry ( "WGS-84", 0.0, ee, exif_data_get_byte_order(ed) );

	struct LatLon ll;
    vik_coord_to_latlon ( &coord, &ll );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE_REF, EXIF_IFD_GPS);
	// N or S
	convert_to_entry ( ll.lat < 0.0 ? "S" : "N", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LATITUDE, EXIF_IFD_GPS);
	convert_to_entry ( NULL, ll.lat, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE_REF, EXIF_IFD_GPS);
	// E or W
	convert_to_entry ( ll.lon < 0.0 ? "W" : "E", 0.0, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_LONGITUDE, EXIF_IFD_GPS);
	convert_to_entry ( NULL, ll.lon, ee, exif_data_get_byte_order(ed) );

	ee = my_exif_create_value (ed, EXIF_TAG_GPS_VERSION_ID, EXIF_IFD_GPS);
	//convert_to_entry ( "2 0 0 0", 0.0, ee, exif_data_get_byte_order(ed) );
	convert_to_entry ( "2 2 0 0", 0.0, ee, exif_data_get_byte_order(ed) );

	jpeg_data_set_exif_data (jdata, ed);

	if ( jdata ) {
		/* Save the modified image. */
		result = jpeg_data_save_file (jdata, filename);

		// Convert result from 1 for success, 0 for failure into our scheme
		result = !result;
		
		jpeg_data_unref (jdata);
	}
	else {
		// Epic fail - file probably not a JPEG
		result = 2;
	}

	exif_data_free ( ed );
#endif
#endif

	if ( no_change_mtime ) {
		// Restore mtime, using the saved value
		struct stat stat_tmp;
		struct utimbuf utb;
		(void)stat ( filename, &stat_tmp );
		utb.actime = stat_tmp.st_atime;
		utb.modtime = stat_save.st_mtime;
		// Not security critical, thus potential Time of Check Time of Use race condition is not bad
		// coverity[toctou]
		if ( g_utime ( filename, &utb ) != 0 )
			g_warning ( "%s couldn't set time on: %s", __FUNCTION__, filename );
	}

	return result;
}