]> git.street.me.uk Git - andy/viking.git/blame - src/viktrwlayer_geotag.c
[DOC] Mention map tilesize configuration.
[andy/viking.git] / src / viktrwlayer_geotag.c
CommitLineData
b3eb3b98
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 * Similar to the track and trackpoint properties dialogs,
24 * this is made a separate file for ease of grouping related stuff together
25 */
26#ifdef HAVE_CONFIG_H
27#include "config.h"
28#endif
29#include <math.h>
30#include <time.h>
31#include <string.h>
32#include <stdlib.h>
33#include <gtk/gtk.h>
34#include <glib/gi18n.h>
35#include "viking.h"
36#include "vikfilelist.h"
37#include "geotag_exif.h"
38#include "thumbnails.h"
404328d1 39#include "background.h"
b3eb3b98 40
404328d1 41// Function taken from GPSCorrelate 1.6.1
b3eb3b98
RN
42// ConvertToUnixTime Copyright 2005 Daniel Foote. GPL2+
43
44#define EXIF_DATE_FORMAT "%d:%d:%d %d:%d:%d"
45
46time_t ConvertToUnixTime(char* StringTime, char* Format, int TZOffsetHours, int TZOffsetMinutes)
47{
48 /* Read the time using the specified format.
49 * The format and string being read from must
50 * have the most significant time on the left,
51 * and the least significant on the right:
52 * ie, Year on the left, seconds on the right. */
53
54 /* Sanity check... */
55 if (StringTime == NULL || Format == NULL)
56 {
57 return 0;
58 }
59
60 /* Define and set up our structure. */
61 struct tm Time;
62 Time.tm_wday = 0;
63 Time.tm_yday = 0;
64 Time.tm_isdst = -1;
65
66 /* Read out the time from the string using our format. */
67 sscanf(StringTime, Format, &Time.tm_year, &Time.tm_mon,
68 &Time.tm_mday, &Time.tm_hour,
69 &Time.tm_min, &Time.tm_sec);
70
71 /* Adjust the years for the mktime function to work. */
72 Time.tm_year -= 1900;
73 Time.tm_mon -= 1;
74
75 /* Add our timezone offset to the time.
76 * We don't check to see if it overflowed anything;
77 * mktime does this and fixes it for us. */
78 /* Note also that we SUBTRACT these times. We want the
79 * result to be in UTC. */
80
81 Time.tm_hour -= TZOffsetHours;
82 Time.tm_min -= TZOffsetMinutes;
83
84 /* Calculate and return the unix time. */
85 return mktime(&Time);
86}
87
88// GPSCorrelate END
89
90typedef struct {
91 GtkWidget *dialog;
92 VikFileList *files;
93 VikTrwLayer *vtl; // to pass on
41f4abac 94 VikWaypoint *wpt; // Use specified waypoint or otherwise the track(s) if NULL
b3eb3b98
RN
95 VikTrack *track; // Use specified track or all tracks if NULL
96 GtkCheckButton *create_waypoints_b;
cbac0d22
RN
97 GtkLabel *overwrite_waypoints_l; // Referenced so the sensitivity can be changed
98 GtkCheckButton *overwrite_waypoints_b;
b3eb3b98
RN
99 GtkCheckButton *write_exif_b;
100 GtkLabel *overwrite_gps_exif_l; // Referenced so the sensitivity can be changed
101 GtkCheckButton *overwrite_gps_exif_b;
102 GtkLabel *no_change_mtime_l; // Referenced so the sensitivity can be changed
103 GtkCheckButton *no_change_mtime_b;
104 GtkCheckButton *interpolate_segments_b;
105 GtkEntry *time_zone_b; // TODO consider a more user friendly tz widget eg libtimezonemap or similar
106 GtkEntry *time_offset_b;
107} GeoTagWidgets;
108
109static GeoTagWidgets *geotag_widgets_new()
110{
111 GeoTagWidgets *widgets = g_malloc0(sizeof(GeoTagWidgets));
112 return widgets;
113}
114
115static void geotag_widgets_free ( GeoTagWidgets *widgets )
116{
117 // Need to free VikFileList??
118 g_free(widgets);
119}
120
121typedef struct {
122 gboolean create_waypoints;
cbac0d22 123 gboolean overwrite_waypoints;
b3eb3b98
RN
124 gboolean write_exif;
125 gboolean overwrite_gps_exif;
126 gboolean no_change_mtime;
127 gboolean interpolate_segments;
128 gint time_offset;
129 gint TimeZoneHours;
130 gint TimeZoneMins;
131} option_values_t;
132
133typedef struct {
134 VikTrwLayer *vtl;
135 gchar *image;
41f4abac 136 VikWaypoint *wpt; // Use specified waypoint or otherwise the track(s) if NULL
b3eb3b98
RN
137 VikTrack *track; // Use specified track or all tracks if NULL
138 // User options...
139 option_values_t ov;
404328d1 140 GList *files;
b3eb3b98
RN
141 time_t PhotoTime;
142 // Store answer from interpolation for an image
143 gboolean found_match;
144 VikCoord coord;
145 gdouble altitude;
146 // If anything has changed
147 gboolean redraw;
148} geotag_options_t;
149
21f0a3ca
RN
150#define VIK_SETTINGS_GEOTAG_CREATE_WAYPOINT "geotag_create_waypoints"
151#define VIK_SETTINGS_GEOTAG_OVERWRITE_WAYPOINTS "geotag_overwrite_waypoints"
152#define VIK_SETTINGS_GEOTAG_WRITE_EXIF "geotag_write_exif"
153#define VIK_SETTINGS_GEOTAG_OVERWRITE_GPS_EXIF "geotag_overwrite_gps"
154#define VIK_SETTINGS_GEOTAG_NO_CHANGE_MTIME "geotag_no_change_mtime"
155#define VIK_SETTINGS_GEOTAG_INTERPOLATE_SEGMENTS "geotag_interpolate_segments"
156#define VIK_SETTINGS_GEOTAG_TIME_OFFSET "geotag_time_offset"
157#define VIK_SETTINGS_GEOTAG_TIME_OFFSET_HOURS "geotag_time_offset_hours"
158#define VIK_SETTINGS_GEOTAG_TIME_OFFSET_MINS "geotag_time_offset_mins"
159
160static void save_default_values ( option_values_t default_values )
161{
162 a_settings_set_boolean ( VIK_SETTINGS_GEOTAG_CREATE_WAYPOINT, default_values.create_waypoints );
163 a_settings_set_boolean ( VIK_SETTINGS_GEOTAG_OVERWRITE_WAYPOINTS, default_values.overwrite_waypoints );
164 a_settings_set_boolean ( VIK_SETTINGS_GEOTAG_WRITE_EXIF, default_values.write_exif );
165 a_settings_set_boolean ( VIK_SETTINGS_GEOTAG_OVERWRITE_GPS_EXIF, default_values.overwrite_gps_exif );
166 a_settings_set_boolean ( VIK_SETTINGS_GEOTAG_NO_CHANGE_MTIME, default_values.no_change_mtime );
167 a_settings_set_boolean ( VIK_SETTINGS_GEOTAG_INTERPOLATE_SEGMENTS, default_values.interpolate_segments );
168 a_settings_set_integer ( VIK_SETTINGS_GEOTAG_TIME_OFFSET, default_values.time_offset );
169 a_settings_set_integer ( VIK_SETTINGS_GEOTAG_TIME_OFFSET_HOURS, default_values.TimeZoneHours );
170 a_settings_set_integer ( VIK_SETTINGS_GEOTAG_TIME_OFFSET_MINS, default_values.TimeZoneMins );
171}
172
173static option_values_t get_default_values ( )
174{
175 option_values_t default_values;
176 if ( ! a_settings_get_boolean ( VIK_SETTINGS_GEOTAG_CREATE_WAYPOINT, &default_values.create_waypoints ) )
177 default_values.create_waypoints = TRUE;
178 if ( ! a_settings_get_boolean ( VIK_SETTINGS_GEOTAG_OVERWRITE_WAYPOINTS, &default_values.overwrite_waypoints ) )
179 default_values.overwrite_waypoints = TRUE;
180 if ( ! a_settings_get_boolean ( VIK_SETTINGS_GEOTAG_WRITE_EXIF, &default_values.write_exif ) )
181 default_values.write_exif = TRUE;
182 if ( ! a_settings_get_boolean ( VIK_SETTINGS_GEOTAG_OVERWRITE_GPS_EXIF, &default_values.overwrite_gps_exif ) )
183 default_values.overwrite_gps_exif = FALSE;
184 if ( ! a_settings_get_boolean ( VIK_SETTINGS_GEOTAG_NO_CHANGE_MTIME, &default_values.no_change_mtime ) )
185 default_values.no_change_mtime = TRUE;
186 if ( ! a_settings_get_boolean ( VIK_SETTINGS_GEOTAG_INTERPOLATE_SEGMENTS, &default_values.interpolate_segments ) )
187 default_values.interpolate_segments = TRUE;
188 if ( ! a_settings_get_integer ( VIK_SETTINGS_GEOTAG_TIME_OFFSET, &default_values.time_offset ) )
189 default_values.time_offset = 0;
190 if ( ! a_settings_get_integer ( VIK_SETTINGS_GEOTAG_TIME_OFFSET_HOURS, &default_values.TimeZoneHours ) )
191 default_values.TimeZoneHours = 0;
192 if ( ! a_settings_get_integer ( VIK_SETTINGS_GEOTAG_TIME_OFFSET_MINS, &default_values.TimeZoneMins ) )
193 default_values.TimeZoneMins = 0;
194 return default_values;
195}
b3eb3b98
RN
196
197/**
198 * Correlate the image against the specified track
199 */
89a068d8 200static void trw_layer_geotag_track ( const gpointer id, VikTrack *track, geotag_options_t *options )
b3eb3b98
RN
201{
202 // If already found match then don't need to check this track
203 if ( options->found_match )
204 return;
205
206 VikTrackpoint *trkpt;
207 VikTrackpoint *trkpt_next;
208
5e610fc3
RN
209 GList *mytrkpt;
210 for ( mytrkpt = track->trackpoints; mytrkpt; mytrkpt = mytrkpt->next ) {
b3eb3b98
RN
211
212 // Do something for this trackpoint...
213
214 trkpt = VIK_TRACKPOINT(mytrkpt->data);
215
216 // is it exactly this point?
217 if ( options->PhotoTime == trkpt->timestamp ) {
218 options->coord = trkpt->coord;
219 options->altitude = trkpt->altitude;
220 options->found_match = TRUE;
221 break;
222 }
223
224 // Now need two trackpoints, hence check next is available
225 if ( !mytrkpt->next ) break;
226 trkpt_next = VIK_TRACKPOINT(mytrkpt->next->data);
227
228 // TODO need to use 'has_timestamp' property
229 if ( trkpt->timestamp == trkpt_next->timestamp ) continue;
230 if ( trkpt->timestamp > trkpt_next->timestamp ) continue;
231
232 // When interpolating between segments, no need for any special segment handling
233 if ( !options->ov.interpolate_segments )
234 // Don't check between segments
235 if ( trkpt_next->newsegment )
236 // Simply move on to consider next point
237 continue;
238
239 // Too far
240 if ( trkpt->timestamp > options->PhotoTime ) break;
241
242 // Is is between this and the next point?
243 if ( (options->PhotoTime > trkpt->timestamp) && (options->PhotoTime < trkpt_next->timestamp) ) {
244 options->found_match = TRUE;
245 // Interpolate
246 /* Calculate the "scale": a decimal giving the relative distance
247 * in time between the two points. Ie, a number between 0 and 1 -
248 * 0 is the first point, 1 is the next point, and 0.5 would be
249 * half way. */
250 gdouble scale = (gdouble)trkpt_next->timestamp - (gdouble)trkpt->timestamp;
251 scale = ((gdouble)options->PhotoTime - (gdouble)trkpt->timestamp) / scale;
252
253 struct LatLon ll_result, ll1, ll2;
254
255 vik_coord_to_latlon ( &(trkpt->coord), &ll1 );
256 vik_coord_to_latlon ( &(trkpt_next->coord), &ll2 );
257
258 ll_result.lat = ll1.lat + ((ll2.lat - ll1.lat) * scale);
259
260 // NB This won't cope with going over the 180 degrees longitude boundary
261 ll_result.lon = ll1.lon + ((ll2.lon - ll1.lon) * scale);
262
263 // set coord
264 vik_coord_load_from_latlon ( &(options->coord), VIK_COORD_LATLON, &ll_result );
265
266 // Interpolate elevation
267 options->altitude = trkpt->altitude + ((trkpt_next->altitude - trkpt->altitude) * scale);
268 break;
269 }
41f4abac
RN
270 }
271}
272
273/**
274 * Simply align the images the waypoint position
275 */
276static void trw_layer_geotag_waypoint ( geotag_options_t *options )
277{
278 // Write EXIF if specified - although a fairly useless process if you've turned it off!
279 if ( options->ov.write_exif ) {
280 gboolean has_gps_exif = FALSE;
281 gchar* datetime = a_geotag_get_exif_date_from_file ( options->image, &has_gps_exif );
282 // If image already has gps info - don't attempt to change it unless forced
283 if ( options->ov.overwrite_gps_exif || !has_gps_exif ) {
284 gint ans = a_geotag_write_exif_gps ( options->image, options->wpt->coord, options->wpt->altitude, options->ov.no_change_mtime );
285 if ( ans != 0 ) {
286 gchar *message = g_strdup_printf ( _("Failed updating EXIF on %s"), options->image );
287 vik_window_statusbar_update ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(options->vtl)), message, VIK_STATUSBAR_INFO );
288 g_free ( message );
289 }
290 }
291 g_free ( datetime );
b3eb3b98
RN
292 }
293}
294
295/**
296 * Correlate the image to any track within the TrackWaypoint layer
297 */
298static void trw_layer_geotag_process ( geotag_options_t *options )
299{
404328d1 300 if ( !options->vtl || !IS_VIK_LAYER(options->vtl) )
b3eb3b98
RN
301 return;
302
303 if ( !options->image )
304 return;
305
41f4abac
RN
306 if ( options->wpt ) {
307 trw_layer_geotag_waypoint ( options );
308 return;
309 }
310
b3eb3b98
RN
311 gboolean has_gps_exif = FALSE;
312 gchar* datetime = a_geotag_get_exif_date_from_file ( options->image, &has_gps_exif );
313
314 if ( datetime ) {
315
316 // If image already has gps info - don't attempt to change it.
317 if ( !options->ov.overwrite_gps_exif && has_gps_exif ) {
318 if ( options->ov.create_waypoints ) {
319 // Create waypoint with file information
320 gchar *name = NULL;
321 VikWaypoint *wp = a_geotag_create_waypoint_from_file ( options->image, vik_trw_layer_get_coord_mode (options->vtl), &name );
0fb2c85d
RN
322 if ( !wp ) {
323 // Couldn't create Waypoint
324 g_free ( datetime );
325 return;
326 }
b3eb3b98
RN
327 if ( !name )
328 name = g_strdup ( a_file_basename ( options->image ) );
cbac0d22
RN
329
330 gboolean updated_waypoint = FALSE;
331
332 if ( options->ov.overwrite_waypoints ) {
333 VikWaypoint *current_wp = vik_trw_layer_get_waypoint ( options->vtl, name );
334 if ( current_wp ) {
335 // Existing wp found, so set new position, comment and image
5263679f 336 (void)a_geotag_waypoint_positioned ( options->image, wp->coord, wp->altitude, &name, current_wp );
cbac0d22
RN
337 updated_waypoint = TRUE;
338 }
339 }
340
341 if ( !updated_waypoint ) {
342 vik_trw_layer_filein_add_waypoint ( options->vtl, name, wp );
343 }
344
b3eb3b98
RN
345 g_free ( name );
346
347 // Mark for redraw
348 options->redraw = TRUE;
349 }
0847b808 350 g_free ( datetime );
b3eb3b98
RN
351 return;
352 }
353
354 options->PhotoTime = ConvertToUnixTime ( datetime, EXIF_DATE_FORMAT, options->ov.TimeZoneHours, options->ov.TimeZoneMins);
355 g_free ( datetime );
356
357 // Apply any offset
358 options->PhotoTime = options->PhotoTime + options->ov.time_offset;
359
360 options->found_match = FALSE;
361
362 if ( options->track ) {
363 // Single specified track
89a068d8 364 // NB Doesn't care about track id
b3eb3b98
RN
365 trw_layer_geotag_track ( NULL, options->track, options );
366 }
367 else {
368 // Try all tracks
369 GHashTable *tracks = vik_trw_layer_get_tracks ( options->vtl );
370 if ( g_hash_table_size (tracks) > 0 ) {
371 g_hash_table_foreach ( tracks, (GHFunc) trw_layer_geotag_track, options );
372 }
373 }
374
375 // Match found ?
376 if ( options->found_match ) {
377
378 if ( options->ov.create_waypoints ) {
379
cbac0d22
RN
380 gboolean updated_waypoint = FALSE;
381
382 if ( options->ov.overwrite_waypoints ) {
b3eb3b98 383
cbac0d22
RN
384 // Update existing WP
385 // Find a WP with current name
386 gchar *name = NULL;
387 name = g_strdup ( a_file_basename ( options->image ) );
388 VikWaypoint *wp = vik_trw_layer_get_waypoint ( options->vtl, name );
389 if ( wp ) {
390 // Found, so set new position, comment and image
5263679f 391 (void)a_geotag_waypoint_positioned ( options->image, options->coord, options->altitude, &name, wp );
cbac0d22
RN
392 updated_waypoint = TRUE;
393 }
394 g_free ( name );
395 }
396
397 if ( !updated_waypoint ) {
398 // Create waypoint with found position
399 gchar *name = NULL;
400 VikWaypoint *wp = a_geotag_waypoint_positioned ( options->image, options->coord, options->altitude, &name, NULL );
401 if ( !name )
402 name = g_strdup ( a_file_basename ( options->image ) );
403 vik_trw_layer_filein_add_waypoint ( options->vtl, name, wp );
404 g_free ( name );
405 }
406
b3eb3b98
RN
407 // Mark for redraw
408 options->redraw = TRUE;
409 }
410
411 // Write EXIF if specified
412 if ( options->ov.write_exif ) {
41f4abac
RN
413 gint ans = a_geotag_write_exif_gps ( options->image, options->coord, options->altitude, options->ov.no_change_mtime );
414 if ( ans != 0 ) {
415 gchar *message = g_strdup_printf ( _("Failed updating EXIF on %s"), options->image );
416 vik_window_statusbar_update ( VIK_WINDOW(VIK_GTK_WINDOW_FROM_LAYER(options->vtl)), message, VIK_STATUSBAR_INFO );
417 g_free ( message );
418 }
b3eb3b98
RN
419 }
420 }
421 }
422}
423
404328d1
RN
424/*
425 * Tidy up
426 */
427static void trw_layer_geotag_thread_free ( geotag_options_t *gtd )
428{
429 if ( gtd->files )
430 g_list_free ( gtd->files );
431 g_free ( gtd );
432}
433
434/**
435 * Run geotagging process in a separate thread
436 */
437static int trw_layer_geotag_thread ( geotag_options_t *options, gpointer threaddata )
438{
439 guint total = g_list_length(options->files), done = 0;
440
441 // TODO decide how to report any issues to the user ...
442
443 // Foreach file attempt to geotag it
444 while ( options->files ) {
445 options->image = (gchar *) ( options->files->data );
446 trw_layer_geotag_process ( options );
447 options->files = options->files->next;
448
449 // Update thread progress and detect stop requests
450 int result = a_background_thread_progress ( threaddata, ((gdouble) ++done) / total );
451 if ( result != 0 )
452 return -1; /* Abort thread */
453 }
454
455 if ( options->redraw ) {
456 if ( IS_VIK_LAYER(options->vtl) ) {
2cec1c4e 457 trw_layer_calculate_bounds_waypoints ( options->vtl );
404328d1 458 // Ensure any new images get shown
e1f7a7a5 459 trw_layer_verify_thumbnails ( options->vtl );
404328d1 460 // Force redraw as verify only redraws if there are new thumbnails (they may already exist)
da121f9b 461 vik_layer_emit_update ( VIK_LAYER(options->vtl) ); // NB Update from background
404328d1
RN
462 }
463 }
464
465 return 0;
466}
467
b3eb3b98
RN
468/**
469 * Parse user input from dialog response
470 */
471static void trw_layer_geotag_response_cb ( GtkDialog *dialog, gint resp, GeoTagWidgets *widgets )
472{
473 switch (resp) {
474 case GTK_RESPONSE_DELETE_EVENT: /* received delete event (not from buttons) */
475 case GTK_RESPONSE_REJECT:
476 break;
477 default: {
478 //GTK_RESPONSE_ACCEPT:
479 // Get options
404328d1
RN
480 geotag_options_t *options = g_malloc ( sizeof(geotag_options_t) );
481 options->vtl = widgets->vtl;
41f4abac 482 options->wpt = widgets->wpt;
404328d1 483 options->track = widgets->track;
b3eb3b98 484 // Values extracted from the widgets:
404328d1 485 options->ov.create_waypoints = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->create_waypoints_b) );
cbac0d22 486 options->ov.overwrite_waypoints = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->overwrite_waypoints_b) );
404328d1
RN
487 options->ov.write_exif = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->write_exif_b) );
488 options->ov.overwrite_gps_exif = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->overwrite_gps_exif_b) );
489 options->ov.no_change_mtime = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->no_change_mtime_b) );
490 options->ov.interpolate_segments = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->interpolate_segments_b) );
491 options->ov.TimeZoneHours = 0;
492 options->ov.TimeZoneMins = 0;
b3eb3b98
RN
493 const gchar* TZString = gtk_entry_get_text(GTK_ENTRY(widgets->time_zone_b));
494 /* Check the string. If there is a colon, then (hopefully) it's a time in xx:xx format.
495 * If not, it's probably just a +/-xx format. In all other cases,
496 * it will be interpreted as +/-xx, which, if given a string, returns 0. */
497 if (strstr(TZString, ":")) {
498 /* Found colon. Split into two. */
404328d1
RN
499 sscanf(TZString, "%d:%d", &options->ov.TimeZoneHours, &options->ov.TimeZoneMins);
500 if (options->ov.TimeZoneHours < 0)
501 options->ov.TimeZoneMins *= -1;
b3eb3b98
RN
502 } else {
503 /* No colon. Just parse. */
404328d1 504 options->ov.TimeZoneHours = atoi(TZString);
b3eb3b98 505 }
404328d1 506 options->ov.time_offset = atoi ( gtk_entry_get_text ( GTK_ENTRY(widgets->time_offset_b) ) );
b3eb3b98 507
404328d1 508 options->redraw = FALSE;
b3eb3b98
RN
509
510 // Save settings for reuse
21f0a3ca 511 save_default_values ( options->ov );
b3eb3b98 512
404328d1
RN
513 options->files = g_list_copy ( vik_file_list_get_files ( widgets->files ) );
514
515 gint len = g_list_length ( options->files );
516 gchar *tmp = g_strdup_printf ( _("Geotagging %d Images..."), len );
517
518 // Processing lots of files can take time - so run a background effort
c75da936
RN
519 a_background_thread ( BACKGROUND_POOL_LOCAL,
520 VIK_GTK_WINDOW_FROM_LAYER(options->vtl),
521 tmp,
522 (vik_thr_func) trw_layer_geotag_thread,
523 options,
524 (vik_thr_free_func) trw_layer_geotag_thread_free,
525 NULL,
526 len );
404328d1
RN
527
528 g_free ( tmp );
b3eb3b98
RN
529
530 break;
531 }
532 }
533 geotag_widgets_free ( widgets );
534 gtk_widget_destroy ( GTK_WIDGET(dialog) );
535}
536
537/**
538 * Handle widget sensitivities
539 */
540static void write_exif_b_cb ( GtkWidget *gw, GeoTagWidgets *gtw )
541{
542 // Overwriting & file modification times are irrelevant if not going to write EXIF!
543 if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(gtw->write_exif_b) ) ) {
544 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_gps_exif_b), TRUE );
545 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_gps_exif_l), TRUE );
546 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->no_change_mtime_b), TRUE );
547 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->no_change_mtime_l), TRUE );
548 }
549 else {
550 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_gps_exif_b), FALSE );
551 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_gps_exif_l), FALSE );
552 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->no_change_mtime_b), FALSE );
553 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->no_change_mtime_l), FALSE );
554 }
555}
556
cbac0d22
RN
557static void create_waypoints_b_cb ( GtkWidget *gw, GeoTagWidgets *gtw )
558{
559 // Overwriting waypoints are irrelevant if not going to create them!
560 if ( gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(gtw->create_waypoints_b) ) ) {
561 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_waypoints_b), TRUE );
562 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_waypoints_l), TRUE );
563 }
564 else {
565 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_waypoints_b), FALSE );
566 gtk_widget_set_sensitive ( GTK_WIDGET(gtw->overwrite_waypoints_l), FALSE );
567 }
568}
b3eb3b98
RN
569
570/**
571 * trw_layer_geotag_dialog:
572 * @parent: The Window of the calling process
573 * @vtl: The VikTrwLayer to use for correlating images to tracks
574 * @track: Optional - The particular track to use (if specified) for correlating images
575 * @track_name: Optional - The name of specified track to use
576 */
41f4abac
RN
577void trw_layer_geotag_dialog ( GtkWindow *parent,
578 VikTrwLayer *vtl,
579 VikWaypoint *wpt,
580 VikTrack *track )
b3eb3b98
RN
581{
582 GeoTagWidgets *widgets = geotag_widgets_new();
583
584 widgets->dialog = gtk_dialog_new_with_buttons ( _("Geotag Images"),
585 parent,
586 GTK_DIALOG_DESTROY_WITH_PARENT,
587 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
588 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
589 NULL );
8a2df21a
RN
590 GtkFileFilter *filter = gtk_file_filter_new ();
591 gtk_file_filter_set_name ( filter, _("JPG") );
592 gtk_file_filter_add_mime_type ( filter, "image/jpeg");
593
594 widgets->files = VIK_FILE_LIST(vik_file_list_new ( _("Images"), filter ));
b3eb3b98 595 widgets->vtl = vtl;
41f4abac 596 widgets->wpt = wpt;
b3eb3b98
RN
597 widgets->track = track;
598 widgets->create_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
cbac0d22
RN
599 widgets->overwrite_waypoints_l = GTK_LABEL ( gtk_label_new ( _("Overwrite Existing Waypoints:") ) );
600 widgets->overwrite_waypoints_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
b3eb3b98
RN
601 widgets->write_exif_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
602 widgets->overwrite_gps_exif_l = GTK_LABEL ( gtk_label_new ( _("Overwrite Existing GPS Information:") ) );
603 widgets->overwrite_gps_exif_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
604 widgets->no_change_mtime_l = GTK_LABEL ( gtk_label_new ( _("Keep File Modification Timestamp:") ) );
605 widgets->no_change_mtime_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
606 widgets->interpolate_segments_b = GTK_CHECK_BUTTON ( gtk_check_button_new () );
607 widgets->time_zone_b = GTK_ENTRY ( gtk_entry_new () );
608 widgets->time_offset_b = GTK_ENTRY ( gtk_entry_new () );
609
610 gtk_entry_set_width_chars ( widgets->time_zone_b, 7);
611 gtk_entry_set_width_chars ( widgets->time_offset_b, 7);
612
21f0a3ca
RN
613 // Defaults
614 option_values_t default_values = get_default_values ();
615
b3eb3b98 616 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(widgets->create_waypoints_b), default_values.create_waypoints );
cbac0d22 617 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(widgets->overwrite_waypoints_b), default_values.overwrite_waypoints );
b3eb3b98
RN
618 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(widgets->write_exif_b), default_values.write_exif );
619 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(widgets->overwrite_gps_exif_b), default_values.overwrite_gps_exif );
620 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(widgets->no_change_mtime_b), default_values.no_change_mtime );
621 gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON(widgets->interpolate_segments_b), default_values.interpolate_segments );
622 gchar tmp_string[7];
623 snprintf (tmp_string, 7, "%+02d:%02d", default_values.TimeZoneHours, abs (default_values.TimeZoneMins) );
624 gtk_entry_set_text ( widgets->time_zone_b, tmp_string );
625 snprintf (tmp_string, 7, "%d", default_values.time_offset );
626 gtk_entry_set_text ( widgets->time_offset_b, tmp_string );
627
628 // Ensure sensitivities setup
629 write_exif_b_cb ( GTK_WIDGET(widgets->write_exif_b), widgets );
b3eb3b98
RN
630 g_signal_connect ( G_OBJECT(widgets->write_exif_b), "toggled", G_CALLBACK(write_exif_b_cb), widgets );
631
cbac0d22
RN
632 create_waypoints_b_cb ( GTK_WIDGET(widgets->create_waypoints_b), widgets );
633 g_signal_connect ( G_OBJECT(widgets->create_waypoints_b), "toggled", G_CALLBACK(create_waypoints_b_cb), widgets );
634
b3eb3b98 635 GtkWidget *cw_hbox = gtk_hbox_new ( FALSE, 0 );
41f4abac
RN
636 GtkWidget *create_waypoints_l = gtk_label_new ( _("Create Waypoints:") );
637 gtk_box_pack_start ( GTK_BOX(cw_hbox), create_waypoints_l, FALSE, FALSE, 5 );
b3eb3b98
RN
638 gtk_box_pack_start ( GTK_BOX(cw_hbox), GTK_WIDGET(widgets->create_waypoints_b), FALSE, FALSE, 5 );
639
cbac0d22
RN
640 GtkWidget *ow_hbox = gtk_hbox_new ( FALSE, 0 );
641 gtk_box_pack_start ( GTK_BOX(ow_hbox), GTK_WIDGET(widgets->overwrite_waypoints_l), FALSE, FALSE, 5 );
642 gtk_box_pack_start ( GTK_BOX(ow_hbox), GTK_WIDGET(widgets->overwrite_waypoints_b), FALSE, FALSE, 5 );
643
b3eb3b98
RN
644 GtkWidget *we_hbox = gtk_hbox_new ( FALSE, 0 );
645 gtk_box_pack_start ( GTK_BOX(we_hbox), gtk_label_new ( _("Write EXIF:") ), FALSE, FALSE, 5 );
646 gtk_box_pack_start ( GTK_BOX(we_hbox), GTK_WIDGET(widgets->write_exif_b), FALSE, FALSE, 5 );
647
648 GtkWidget *og_hbox = gtk_hbox_new ( FALSE, 0 );
649 gtk_box_pack_start ( GTK_BOX(og_hbox), GTK_WIDGET(widgets->overwrite_gps_exif_l), FALSE, FALSE, 5 );
650 gtk_box_pack_start ( GTK_BOX(og_hbox), GTK_WIDGET(widgets->overwrite_gps_exif_b), FALSE, FALSE, 5 );
651
652 GtkWidget *fm_hbox = gtk_hbox_new ( FALSE, 0 );
653 gtk_box_pack_start ( GTK_BOX(fm_hbox), GTK_WIDGET(widgets->no_change_mtime_l), FALSE, FALSE, 5 );
654 gtk_box_pack_start ( GTK_BOX(fm_hbox), GTK_WIDGET(widgets->no_change_mtime_b), FALSE, FALSE, 5 );
655
656 GtkWidget *is_hbox = gtk_hbox_new ( FALSE, 0 );
41f4abac
RN
657 GtkWidget *interpolate_segments_l = gtk_label_new ( _("Interpolate Between Track Segments:") );
658 gtk_box_pack_start ( GTK_BOX(is_hbox), interpolate_segments_l, FALSE, FALSE, 5 );
b3eb3b98
RN
659 gtk_box_pack_start ( GTK_BOX(is_hbox), GTK_WIDGET(widgets->interpolate_segments_b), FALSE, FALSE, 5 );
660
661 GtkWidget *to_hbox = gtk_hbox_new ( FALSE, 0 );
41f4abac
RN
662 GtkWidget *time_offset_l = gtk_label_new ( _("Image Time Offset (Seconds):") );
663 gtk_box_pack_start ( GTK_BOX(to_hbox), time_offset_l, FALSE, FALSE, 5 );
b3eb3b98
RN
664 gtk_box_pack_start ( GTK_BOX(to_hbox), GTK_WIDGET(widgets->time_offset_b), FALSE, FALSE, 5 );
665 gtk_widget_set_tooltip_text ( GTK_WIDGET(widgets->time_offset_b), _("The number of seconds to ADD to the photos time to make it match the GPS data. Calculate this with (GPS - Photo). Can be negative or positive. Useful to adjust times when a camera's timestamp was incorrect.") );
666
667 GtkWidget *tz_hbox = gtk_hbox_new ( FALSE, 0 );
41f4abac
RN
668 GtkWidget *time_zone_l = gtk_label_new ( _("Image Timezone:") );
669 gtk_box_pack_start ( GTK_BOX(tz_hbox), time_zone_l, FALSE, FALSE, 5 );
b3eb3b98
RN
670 gtk_box_pack_start ( GTK_BOX(tz_hbox), GTK_WIDGET(widgets->time_zone_b), FALSE, FALSE, 5 );
671 gtk_widget_set_tooltip_text ( GTK_WIDGET(widgets->time_zone_b), _("The timezone that was used when the images were created. For example, if a camera is set to AWST or +8:00 hours. Enter +8:00 here so that the correct adjustment to the images' time can be made. GPS data is always in UTC.") );
672
673 gchar *track_string = NULL;
41f4abac
RN
674 if ( widgets->wpt ) {
675 track_string = g_strdup_printf ( _("Using waypoint: %s"), wpt->name );
676 // Control sensitivities
677 gtk_widget_set_sensitive ( GTK_WIDGET(widgets->create_waypoints_b), FALSE );
678 gtk_widget_set_sensitive ( GTK_WIDGET(create_waypoints_l), FALSE );
679 gtk_widget_set_sensitive ( GTK_WIDGET(widgets->overwrite_waypoints_b), FALSE );
680 gtk_widget_set_sensitive ( GTK_WIDGET(widgets->overwrite_waypoints_l), FALSE );
681 gtk_widget_set_sensitive ( GTK_WIDGET(widgets->interpolate_segments_b), FALSE );
682 gtk_widget_set_sensitive ( GTK_WIDGET(interpolate_segments_l), FALSE );
683 gtk_widget_set_sensitive ( GTK_WIDGET(widgets->time_offset_b), FALSE );
684 gtk_widget_set_sensitive ( GTK_WIDGET(time_offset_l), FALSE );
685 gtk_widget_set_sensitive ( GTK_WIDGET(widgets->time_zone_b), FALSE );
686 gtk_widget_set_sensitive ( GTK_WIDGET(time_zone_l), FALSE );
687 }
688 else if ( widgets->track )
689 track_string = g_strdup_printf ( _("Using track: %s"), track->name );
b3eb3b98
RN
690 else
691 track_string = g_strdup_printf ( _("Using all tracks in: %s"), VIK_LAYER(widgets->vtl)->name );
692
9b082b39
RN
693 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), gtk_label_new ( track_string ), FALSE, FALSE, 5 );
694
695 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), GTK_WIDGET(widgets->files), TRUE, TRUE, 0 );
696
697 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), cw_hbox, FALSE, FALSE, 0);
698 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), ow_hbox, FALSE, FALSE, 0);
699 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), we_hbox, FALSE, FALSE, 0);
700 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), og_hbox, FALSE, FALSE, 0);
701 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), fm_hbox, FALSE, FALSE, 0);
702 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), is_hbox, FALSE, FALSE, 0);
703 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), to_hbox, FALSE, FALSE, 0);
704 gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(widgets->dialog))), tz_hbox, FALSE, FALSE, 0);
b3eb3b98
RN
705
706 g_signal_connect ( widgets->dialog, "response", G_CALLBACK(trw_layer_geotag_response_cb), widgets );
707
708 gtk_dialog_set_default_response ( GTK_DIALOG(widgets->dialog), GTK_RESPONSE_REJECT );
709
710 gtk_widget_show_all ( widgets->dialog );
711
712 g_free ( track_string );
713}