]> git.street.me.uk Git - andy/viking.git/blob - src/datasource_osm_my_traces.c
Add option to auto connect to GPSD rather than having to manually control
[andy/viking.git] / src / datasource_osm_my_traces.c
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) 2012-2015, 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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <glib/gprintf.h>
27 #include <glib/gi18n.h>
28 #include <glib/gstdio.h>
29
30 #include <expat.h>
31
32 #include "viking.h"
33 #include "gpx.h"
34 #include "acquire.h"
35 #include "osm-traces.h"
36 #include "datasource_gps.h"
37 #include "bbox.h"
38
39 /**
40  * See http://wiki.openstreetmap.org/wiki/API_v0.6#GPS_Traces
41  */
42 #define DS_OSM_TRACES_GPX_URL_FMT "api.openstreetmap.org/api/0.6/gpx/%d/data"
43 #define DS_OSM_TRACES_GPX_FILES "api.openstreetmap.org/api/0.6/user/gpx_files"
44
45 typedef struct {
46         GtkWidget *user_entry;
47         GtkWidget *password_entry;
48         // NB actual user and password values are stored in oms-traces.c
49         VikViewport *vvp;
50 } datasource_osm_my_traces_t;
51
52 static gpointer datasource_osm_my_traces_init ( acq_vik_t *avt );
53 static void datasource_osm_my_traces_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data );
54 static void datasource_osm_my_traces_get_process_options ( gpointer user_data, ProcessOptions *po, DownloadFileOptions *options, const gchar *notused1, const gchar *notused2 );
55 static gboolean datasource_osm_my_traces_process ( VikTrwLayer *vtl, ProcessOptions *process_options, BabelStatusFunc status_cb, acq_dialog_widgets_t *adw, DownloadFileOptions *options_unused );
56 static void datasource_osm_my_traces_cleanup ( gpointer data );
57
58 VikDataSourceInterface vik_datasource_osm_my_traces_interface = {
59   N_("OSM My Traces"),
60   N_("OSM My Traces"),
61   VIK_DATASOURCE_MANUAL_LAYER_MANAGEMENT, // we'll do this ourselves
62   VIK_DATASOURCE_INPUTTYPE_NONE,
63   TRUE,
64   TRUE,
65   FALSE, // Don't use thread method
66   (VikDataSourceInitFunc)                   datasource_osm_my_traces_init,
67   (VikDataSourceCheckExistenceFunc)     NULL,
68   (VikDataSourceAddSetupWidgetsFunc)datasource_osm_my_traces_add_setup_widgets,
69   (VikDataSourceGetProcessOptionsFunc) datasource_osm_my_traces_get_process_options,
70   (VikDataSourceProcessFunc)           datasource_osm_my_traces_process,
71   (VikDataSourceProgressFunc)           NULL,
72   (VikDataSourceAddProgressWidgetsFunc) NULL,
73   (VikDataSourceCleanupFunc)            datasource_osm_my_traces_cleanup,
74   (VikDataSourceOffFunc)            NULL,
75
76   NULL,
77   0,
78   NULL,
79   NULL,
80   0
81 };
82
83 static gpointer datasource_osm_my_traces_init ( acq_vik_t *avt )
84 {
85   datasource_osm_my_traces_t *data = g_malloc(sizeof(*data));
86   // Reuse GPS functions
87   // Haven't been able to get the thread method to work reliably (or get progress feedback)
88   // So thread version is disabled ATM
89   /*
90   if ( vik_datasource_osm_my_traces_interface.is_thread ) {
91           vik_datasource_osm_my_traces_interface.progress_func = datasource_gps_progress;
92           vik_datasource_osm_my_traces_interface.add_progress_widgets_func = datasource_gps_add_progress_widgets;
93   }
94   */
95   return data;
96 }
97
98 static void datasource_osm_my_traces_add_setup_widgets ( GtkWidget *dialog, VikViewport *vvp, gpointer user_data )
99 {
100         datasource_osm_my_traces_t *data = (datasource_osm_my_traces_t *)user_data;
101
102         GtkWidget *user_label;
103         GtkWidget *password_label;
104         user_label = gtk_label_new(_("Username:"));
105         data->user_entry = gtk_entry_new();
106
107         gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), user_label, FALSE, FALSE, 0 );
108         gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), data->user_entry, FALSE, FALSE, 0 );
109         gtk_widget_set_tooltip_markup ( GTK_WIDGET(data->user_entry), _("The email or username used to login to OSM") );
110
111         password_label = gtk_label_new ( _("Password:") );
112         data->password_entry = gtk_entry_new ();
113
114         gtk_widget_set_tooltip_markup ( GTK_WIDGET(data->password_entry), _("The password used to login to OSM") );
115
116         osm_login_widgets (data->user_entry, data->password_entry);
117
118         /* Packing all widgets */
119         GtkBox *box = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
120         gtk_box_pack_start ( box, password_label, FALSE, FALSE, 0 );
121         gtk_box_pack_start ( box, data->password_entry, FALSE, FALSE, 0 );
122         gtk_widget_show_all ( dialog );
123
124         /* Keep reference to viewport */
125         data->vvp = vvp;
126 }
127
128 static void datasource_osm_my_traces_get_process_options ( gpointer user_data, ProcessOptions *po, DownloadFileOptions *options, const gchar *notused1, const gchar *notused2 )
129 {
130         datasource_osm_my_traces_t *data = (datasource_osm_my_traces_t*) user_data;
131
132     /* overwrite authentication info */
133         osm_set_login ( gtk_entry_get_text ( GTK_ENTRY(data->user_entry) ),
134                         gtk_entry_get_text ( GTK_ENTRY(data->password_entry) ) );
135
136         // If going to use the values passed back into the process function parameters then they need to be set.
137         // But ATM we aren't
138         options = NULL;
139 }
140
141 typedef enum {
142         tt_unknown = 0,
143         tt_osm,
144         tt_gpx_file,
145         tt_gpx_file_desc,
146         tt_gpx_file_tag,
147 } xtag_type;
148
149 typedef struct {
150         xtag_type tag_type;              /* enum from above for this tag */
151         const char *tag_name;           /* xpath-ish tag name */
152 } xtag_mapping;
153
154 typedef struct {
155         guint id;
156         gchar *name;
157         gchar *vis;
158         gchar *desc;
159         struct LatLon ll;
160         gboolean in_current_view; // Is the track LatLon start within the current viewport
161                               // This is useful in deciding whether to download a track or not
162         // ATM Only used for display - may want to convert to a time_t for other usage
163         gchar *timestamp;
164         // user made up tags - not being used yet - would be nice to sort/select on these but display will get complicated
165         // GList *tag_list;
166 } gpx_meta_data_t;
167
168 static gpx_meta_data_t *new_gpx_meta_data_t()
169 {
170         gpx_meta_data_t *ret;
171
172         ret = (gpx_meta_data_t *)g_malloc(sizeof(gpx_meta_data_t));
173         ret->id = 0;
174         ret->name = NULL;
175         ret->vis  = NULL;
176         ret->desc = NULL;
177         ret->ll.lat = 0.0;
178         ret->ll.lon = 0.0;
179         ret->in_current_view = FALSE;
180         ret->timestamp = NULL;
181
182         return ret;
183 }
184
185 static void free_gpx_meta_data ( gpx_meta_data_t *data, gpointer userdata )
186 {
187         g_free(data->name);
188         g_free(data->vis);
189         g_free(data->desc);
190         g_free(data->timestamp);
191 }
192
193 static void free_gpx_meta_data_list (GList *list)
194 {
195         g_list_foreach (list, (GFunc)free_gpx_meta_data, NULL);
196         g_list_free (list);
197 }
198
199 static gpx_meta_data_t *copy_gpx_meta_data_t (gpx_meta_data_t *src)
200 {
201         gpx_meta_data_t *dest = new_gpx_meta_data_t();
202
203         dest->id = src->id;
204         dest->name = g_strdup(src->name);
205         dest->vis  = g_strdup(src->vis);
206         dest->desc = g_strdup(src->desc);
207         dest->ll.lat = src->ll.lat;
208         dest->ll.lon = src->ll.lon;
209         dest->in_current_view = src->in_current_view;
210         dest->timestamp = g_strdup(src->timestamp);
211
212         return dest;
213 }
214
215 typedef struct {
216         //GString *xpath;
217         GString *c_cdata;
218         xtag_type current_tag;
219         gpx_meta_data_t *current_gpx_meta_data;
220         GList *list_of_gpx_meta_data;
221 } xml_data;
222
223 // Same as the gpx.c function
224 static const char *get_attr ( const char **attr, const char *key )
225 {
226   while ( *attr ) {
227     if ( strcmp(*attr,key) == 0 )
228       return *(attr + 1);
229     attr += 2;
230   }
231   return NULL;
232 }
233
234 // ATM don't care about actual path as tags are all unique
235 static xtag_mapping xtag_path_map[] = {
236         { tt_osm,           "osm" },
237         { tt_gpx_file,      "gpx_file" },
238         { tt_gpx_file_desc, "description" },
239         { tt_gpx_file_tag,  "tag" },
240 };
241
242 static xtag_type get_tag ( const char *t )
243 {
244         xtag_mapping *tm;
245         for (tm = xtag_path_map; tm->tag_type != 0; tm++)
246                 if (0 == strcmp(tm->tag_name, t))
247                         return tm->tag_type;
248         return tt_unknown;
249 }
250
251 static void gpx_meta_data_start ( xml_data *xd, const char *el, const char **attr )
252 {
253         const gchar *tmp;
254         gchar buf[G_ASCII_DTOSTR_BUF_SIZE];
255         buf[0] = '\0';
256
257         // Don't need to build a path - we can use the tag directly
258         //g_string_append_c ( xd->xpath, '/' );
259         //g_string_append ( xd->xpath, el );
260         //xd->current_tag = get_tag ( xd->xpath->str );
261         xd->current_tag = get_tag ( el );
262         switch ( xd->current_tag ) {
263         case tt_gpx_file:
264                 if ( xd->current_gpx_meta_data )
265                         free_gpx_meta_data ( xd->current_gpx_meta_data, NULL );
266                 xd->current_gpx_meta_data = new_gpx_meta_data_t();
267
268                 if ( ( tmp = get_attr ( attr, "id" ) ) )
269                         xd->current_gpx_meta_data->id = atoi ( tmp );
270
271                 if ( ( tmp = get_attr ( attr, "name" ) ) )
272                         xd->current_gpx_meta_data->name = g_strdup ( tmp );
273
274                 if ( ( tmp = get_attr ( attr, "lat" ) ) ) {
275                         g_strlcpy ( buf, tmp, sizeof (buf) );
276                         xd->current_gpx_meta_data->ll.lat = g_ascii_strtod ( buf, NULL );
277                 }
278
279                 if ( ( tmp = get_attr ( attr, "lon" ) ) ) {
280                         g_strlcpy ( buf, tmp, sizeof (buf) );
281                         xd->current_gpx_meta_data->ll.lon = g_ascii_strtod ( buf, NULL );
282                 }
283
284                 if ( ( tmp = get_attr ( attr, "visibility" ) ) )
285                         xd->current_gpx_meta_data->vis = g_strdup ( tmp );
286
287                 if ( ( tmp = get_attr ( attr, "timestamp" ) ) )
288                         xd->current_gpx_meta_data->timestamp = g_strdup ( tmp );
289
290                 g_string_erase ( xd->c_cdata, 0, -1 ); // clear the cdata buffer
291                 break;
292         case tt_gpx_file_desc:
293         case tt_gpx_file_tag:
294                 g_string_erase ( xd->c_cdata, 0, -1 ); // clear the cdata buffer
295                 break;
296         default:
297                 g_string_erase ( xd->c_cdata, 0, -1 ); // clear the cdata buffer
298                 break;
299         }
300 }
301
302 static void gpx_meta_data_end ( xml_data *xd, const char *el )
303 {
304         //g_string_truncate ( xd->xpath, xd->xpath->len - strlen(el) - 1 );
305         //switch ( xd->current_tag ) {
306         switch ( get_tag ( el ) ) {
307         case tt_gpx_file: {
308                 // End of the individual file metadata, thus save what we have read in to the list
309                 // Copy it so we can reference it
310                 gpx_meta_data_t *current = copy_gpx_meta_data_t ( xd->current_gpx_meta_data );
311                 // Stick in the list
312                 xd->list_of_gpx_meta_data = g_list_prepend(xd->list_of_gpx_meta_data, current);
313                 g_string_erase ( xd->c_cdata, 0, -1 );
314                 break;
315         }
316         case tt_gpx_file_desc:
317                 // Store the description:
318                 if ( xd->current_gpx_meta_data ) {
319                         // NB Limit description size as it's displayed on a single line
320                         // Hopefully this will prevent the dialog getting too wide...
321                         xd->current_gpx_meta_data->desc = g_strndup ( xd->c_cdata->str, 63 );
322                 }
323                 g_string_erase ( xd->c_cdata, 0, -1 );
324                 break;
325         case tt_gpx_file_tag:
326                 // One day do something with this...
327                 g_string_erase ( xd->c_cdata, 0, -1 );
328                 break;
329         default:
330                 break;
331         }
332 }
333
334 static void gpx_meta_data_cdata ( xml_data *xd, const XML_Char *s, int len )
335 {
336         switch ( xd->current_tag ) {
337     case tt_gpx_file_desc:
338     case tt_gpx_file_tag:
339                 g_string_append_len ( xd->c_cdata, s, len );
340                 break;
341         default: break;  // ignore cdata from other things
342         }
343 }
344
345 static gboolean read_gpx_files_metadata_xml ( gchar *tmpname, xml_data *xd )
346 {
347         FILE *ff = g_fopen (tmpname, "r");
348         if ( !ff )
349                 return FALSE;
350
351         XML_Parser parser = XML_ParserCreate(NULL);
352         enum XML_Status status = XML_STATUS_ERROR;
353
354         XML_SetElementHandler(parser, (XML_StartElementHandler) gpx_meta_data_start, (XML_EndElementHandler) gpx_meta_data_end);
355         XML_SetUserData(parser, xd);
356         XML_SetCharacterDataHandler(parser, (XML_CharacterDataHandler) gpx_meta_data_cdata);
357
358         gchar buf[4096];
359
360         int done=0, len;
361         while (!done) {
362                 len = fread(buf, 1, sizeof(buf)-7, ff);
363                 done = feof(ff) || !len;
364                 status = XML_Parse(parser, buf, len, done);
365         }
366
367         XML_ParserFree (parser);
368
369         fclose  ( ff );
370
371         return status != XML_STATUS_ERROR;
372 }
373
374 static GList *select_from_list (GtkWindow *parent, GList *list, const gchar *title, const gchar *msg )
375 {
376         GtkTreeIter iter;
377         GtkCellRenderer *renderer;
378         GtkWidget *view;
379         gchar *latlon_string;
380         int column_runner;
381
382         GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
383                                                                                                          parent,
384                                                                                                          GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
385                                                                                                          GTK_STOCK_CANCEL,
386                                                                                                          GTK_RESPONSE_REJECT,
387                                                                                                          GTK_STOCK_OK,
388                                                                                                          GTK_RESPONSE_ACCEPT,
389                                                                                                          NULL);
390         /* When something is selected then OK */
391         gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
392         GtkWidget *response_w = NULL;
393 #if GTK_CHECK_VERSION (2, 20, 0)
394         /* Default to not apply - as initially nothing is selected! */
395         response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_REJECT );
396 #endif
397         GtkWidget *label = gtk_label_new ( msg );
398         GtkTreeStore *store = gtk_tree_store_new ( 6, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN );
399         GList *list_runner = list;
400         while (list_runner) {
401                 gpx_meta_data_t *gpx_meta_data = (gpx_meta_data_t *)list_runner->data;
402                 // To keep display compact three digits of precision for lat/lon should be plenty
403                 latlon_string = g_strdup_printf("(%.3f,%.3f)", gpx_meta_data->ll.lat, gpx_meta_data->ll.lon);
404                 gtk_tree_store_append(store, &iter, NULL);
405                 gtk_tree_store_set ( store, &iter,
406                                      0, gpx_meta_data->name,
407                                      1, gpx_meta_data->desc,
408                                      2, gpx_meta_data->timestamp,
409                                      3, latlon_string,
410                                      4, gpx_meta_data->vis,
411                                      5, gpx_meta_data->in_current_view,
412                                      -1 );
413                 list_runner = g_list_next ( list_runner );
414                 g_free ( latlon_string );
415         }
416
417         view = gtk_tree_view_new();
418         renderer = gtk_cell_renderer_text_new();
419         column_runner = 0;
420         GtkTreeViewColumn *column;
421
422         column = gtk_tree_view_column_new_with_attributes ( _("Name"), renderer, "text", column_runner, NULL);
423         gtk_tree_view_column_set_sort_column_id (column, column_runner);
424         gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
425
426         column_runner++;
427         column = gtk_tree_view_column_new_with_attributes ( _("Description"), renderer, "text", column_runner, NULL);
428         gtk_tree_view_column_set_sort_column_id (column, column_runner);
429         gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
430
431         column_runner++;
432         column = gtk_tree_view_column_new_with_attributes ( _("Time"), renderer, "text", column_runner, NULL);
433         gtk_tree_view_column_set_sort_column_id (column, column_runner);
434         gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
435
436         column_runner++;
437         column = gtk_tree_view_column_new_with_attributes ( _("Lat/Lon"), renderer, "text", column_runner, NULL);
438         gtk_tree_view_column_set_sort_column_id (column, column_runner);
439         gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
440
441         column_runner++;
442         column = gtk_tree_view_column_new_with_attributes ( _("Privacy"), renderer, "text", column_runner, NULL); // AKA Visibility
443         gtk_tree_view_column_set_sort_column_id (column, column_runner);
444         gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
445
446         GtkCellRenderer *renderer_toggle = gtk_cell_renderer_toggle_new ();
447         g_object_set (G_OBJECT (renderer_toggle), "activatable", FALSE, NULL); // No user action - value is just for display
448         column_runner++;
449         column = gtk_tree_view_column_new_with_attributes ( _("Within Current View"), renderer_toggle, "active", column_runner, NULL);
450         gtk_tree_view_column_set_sort_column_id (column, column_runner);
451         gtk_tree_view_append_column (GTK_TREE_VIEW (view), column);
452
453         gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
454         gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(view)), GTK_SELECTION_MULTIPLE );
455         g_object_unref(store);
456
457         GtkWidget *scrolledwindow = gtk_scrolled_window_new ( NULL, NULL );
458         gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW(scrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC );
459         gtk_container_add ( GTK_CONTAINER(scrolledwindow), view );
460
461         gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), label, FALSE, FALSE, 0);
462         gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), scrolledwindow, TRUE, TRUE, 0);
463
464         // Ensure a reasonable number of items are shown, but let the width be automatically sized
465         gtk_widget_set_size_request ( dialog, -1, 400) ;
466         gtk_widget_show_all ( dialog );
467
468         if ( response_w )
469                 gtk_widget_grab_focus ( response_w );
470
471         while ( gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT ) {
472
473                 // Possibily not the fastest method but we don't have thousands of entries to process...
474                 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
475                 GList *selected = NULL;
476
477                 //  because we don't store the full data in the gtk model, we have to scan & look it up
478                 if ( gtk_tree_model_get_iter_first( GTK_TREE_MODEL(store), &iter) ) {
479                         do {
480                                 if ( gtk_tree_selection_iter_is_selected ( selection, &iter ) ) {
481                                         // For every selected item,
482                                         // compare the name from the displayed view to every gpx entry to find the gpx this selection represents
483                                         gchar* name;
484                                         gtk_tree_model_get (GTK_TREE_MODEL(store), &iter, 0, &name, -1 );
485                                         // I believe the name of these items to be always unique
486                                         list_runner = list;
487                                         while (list_runner) {
488                                                 if ( !strcmp ( ((gpx_meta_data_t*)list_runner->data)->name, name ) ) {
489                                                         gpx_meta_data_t *copied = copy_gpx_meta_data_t (list_runner->data);
490                                                         selected = g_list_prepend (selected, copied);
491                                                         break;
492                                                 }
493                                                 list_runner = g_list_next ( list_runner );
494                                         }
495                                         g_free ( name );
496                                 }
497                         }
498                         while ( gtk_tree_model_iter_next ( GTK_TREE_MODEL(store), &iter ) );
499                 }
500
501                 if ( selected ) {
502                         gtk_widget_destroy ( dialog );
503                         return selected;
504                 }
505                 a_dialog_error_msg(parent, _("Nothing was selected"));
506         }
507         gtk_widget_destroy ( dialog );
508         return NULL;
509 }
510
511 static void none_found ( GtkWindow *gw )
512 {
513         GtkWidget *dialog = NULL;
514
515         dialog = gtk_dialog_new_with_buttons ( "", gw, 0, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL );
516         gtk_window_set_title(GTK_WINDOW(dialog), _("GPS Traces"));
517
518         GtkWidget *search_label = gtk_label_new(_("None found!"));
519         gtk_box_pack_start ( GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), search_label, FALSE, FALSE, 5 );
520         gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
521         gtk_widget_show_all(dialog);
522
523         gtk_dialog_run ( GTK_DIALOG(dialog) );
524         gtk_widget_destroy(dialog);
525 }
526
527 /**
528  * For each track - mark whether the start is in within the viewport
529  */
530 static void set_in_current_view_property ( VikTrwLayer *vtl, datasource_osm_my_traces_t *data, GList *gl )
531 {
532         gdouble min_lat, max_lat, min_lon, max_lon;
533         /* get Viewport bounding box */
534         vik_viewport_get_min_max_lat_lon ( data->vvp, &min_lat, &max_lat, &min_lon, &max_lon );
535
536         LatLonBBox bbox;
537         bbox.north = max_lat;
538         bbox.east = max_lon;
539         bbox.south = min_lat;
540         bbox.west = min_lon;
541
542         GList *iterator = gl;
543         while ( iterator ) {
544                 gpx_meta_data_t* gmd = (gpx_meta_data_t*)iterator->data;
545                 // Convert point position into a 'fake' bounding box
546                 // TODO - probably should have function to see if point is within bounding box
547                 //   rather than constructing this fake bounding box for the test
548                 LatLonBBox gmd_bbox;
549                 gmd_bbox.north = gmd->ll.lat;
550                 gmd_bbox.east = gmd->ll.lon;
551                 gmd_bbox.south = gmd->ll.lat;
552                 gmd_bbox.west = gmd->ll.lon;
553
554                 if ( BBOX_INTERSECT ( bbox, gmd_bbox ) )
555                         gmd->in_current_view = TRUE;
556
557                 iterator = g_list_next ( iterator );
558         }
559 }
560
561 static gboolean datasource_osm_my_traces_process ( VikTrwLayer *vtl, ProcessOptions *process_options, BabelStatusFunc status_cb, acq_dialog_widgets_t *adw, DownloadFileOptions *options_unused )
562 {
563         //datasource_osm_my_traces_t *data = (datasource_osm_my_traces_t *)adw->user_data;
564
565         gboolean result;
566
567         gchar *user_pass = osm_get_login();
568
569         // Support .zip + bzip2 files directly
570         DownloadFileOptions options = { FALSE, FALSE, NULL, 2, NULL, user_pass, a_try_decompress_file }; // Allow a couple of redirects
571
572         gchar *tmpname = a_download_uri_to_tmp_file ( DS_OSM_TRACES_GPX_FILES, &options );
573         if ( !tmpname )
574                 return FALSE;
575
576         xml_data *xd = g_malloc ( sizeof (xml_data) );
577         //xd->xpath = g_string_new ( "" );
578         xd->c_cdata = g_string_new ( "" );
579         xd->current_tag = tt_unknown;
580         xd->current_gpx_meta_data = new_gpx_meta_data_t();
581         xd->list_of_gpx_meta_data = NULL;
582
583         result = read_gpx_files_metadata_xml ( tmpname, xd );
584         // Test already downloaded metadata file: eg:
585         //result = read_gpx_files_metadata_xml ( "/tmp/viking-download.GI47PW", xd );
586
587         if ( tmpname ) {
588                 (void)util_remove ( tmpname );
589                 g_free ( tmpname );
590         }
591
592         if ( ! result ) {
593                 g_free ( xd );
594                 return FALSE;
595         }
596
597         if ( g_list_length ( xd->list_of_gpx_meta_data ) == 0 ) {
598                 if (!vik_datasource_osm_my_traces_interface.is_thread)
599                         none_found ( GTK_WINDOW(adw->vw) );
600                 g_free ( xd );
601                 return FALSE;
602         }
603
604         xd->list_of_gpx_meta_data = g_list_reverse ( xd->list_of_gpx_meta_data );
605
606         set_in_current_view_property ( vtl, adw->user_data, xd->list_of_gpx_meta_data );
607
608     if (vik_datasource_osm_my_traces_interface.is_thread) gdk_threads_enter();
609         GList *selected = select_from_list ( GTK_WINDOW(adw->vw), xd->list_of_gpx_meta_data, "Select GPS Traces", "Select the GPS traces you want to add." );
610     if (vik_datasource_osm_my_traces_interface.is_thread) gdk_threads_leave();
611
612         // If non thread - show program is 'doing something...'
613         if ( !vik_datasource_osm_my_traces_interface.is_thread )
614                 vik_window_set_busy_cursor ( adw->vw );
615
616         // If passed in on an existing layer - we will create everything into that.
617         //  thus with many differing gpx's - this will combine all waypoints into this single layer!
618         // Hence the preference is to create multiple layers
619         //  and so this creation of the layers must be managed here
620
621         gboolean create_new_layer = ( !vtl );
622
623         // Only update the screen on the last layer acquired
624         VikTrwLayer *vtl_last = vtl;
625         gboolean got_something = FALSE;
626
627         GList *selected_iterator = selected;
628         while ( selected_iterator ) {
629
630                 VikTrwLayer *vtlX = vtl;
631
632                 if ( create_new_layer ) {
633                         // Have data but no layer - so create one
634                         vtlX = VIK_TRW_LAYER ( vik_layer_create ( VIK_LAYER_TRW, adw->vvp, FALSE ) );
635                         if ( ((gpx_meta_data_t*)selected_iterator->data)->name )
636                                 vik_layer_rename ( VIK_LAYER ( vtlX ), ((gpx_meta_data_t*)selected_iterator->data)->name );
637                         else
638                                 vik_layer_rename ( VIK_LAYER ( vtlX ), _("My OSM Traces") );
639                 }
640
641                 result = FALSE;
642                 gint gpx_id = ((gpx_meta_data_t*)selected_iterator->data)->id;
643                 if ( gpx_id ) {
644                         gchar *url = g_strdup_printf ( DS_OSM_TRACES_GPX_URL_FMT, gpx_id );
645
646                         // NB download type is GPX (or a compressed version)
647                         ProcessOptions my_po = *process_options;
648                         my_po.url = url;
649                         result = a_babel_convert_from ( vtlX, &my_po, status_cb, adw, &options );
650                         // TODO investigate using a progress bar:
651                         // http://developer.gnome.org/gtk/2.24/GtkProgressBar.html
652
653                         got_something = got_something || result;
654                         if ( !result ) {
655                                 // Report errors to the status bar
656                                 gchar* msg = g_strdup_printf ( _("Unable to get trace: %s"), url );
657                                 vik_window_statusbar_update ( adw->vw, msg, VIK_STATUSBAR_INFO );
658                                 g_free (msg);
659                         }
660                         g_free ( url );
661                 }
662
663                 if ( result ) {
664                         // Can use the layer
665                         vik_aggregate_layer_add_layer ( vik_layers_panel_get_top_layer (adw->vlp), VIK_LAYER(vtlX), TRUE );
666                         // Move to area of the track
667                         vik_layer_post_read ( VIK_LAYER(vtlX), vik_window_viewport(adw->vw), TRUE );
668                         vik_trw_layer_auto_set_view ( vtlX, vik_window_viewport(adw->vw) );
669                         vtl_last = vtlX;
670                 }
671                 else if ( create_new_layer ) {
672                         // Layer not needed as no data has been acquired
673                         g_object_unref ( vtlX );
674                 }
675
676                 selected_iterator = g_list_next ( selected_iterator );
677         }
678
679         // Free memory
680         if ( xd->current_gpx_meta_data )
681                 free_gpx_meta_data ( xd->current_gpx_meta_data, NULL );
682         g_free ( xd->current_gpx_meta_data );
683         free_gpx_meta_data_list ( xd->list_of_gpx_meta_data );
684         free_gpx_meta_data_list ( selected );
685         g_free ( xd );
686         g_free ( user_pass );
687
688         // Would prefer to keep the update in acquire.c,
689         //  however since we may create the layer - need to do the update here
690         if ( got_something )
691                 vik_layer_emit_update ( VIK_LAYER(vtl_last) );
692
693         // ATM The user is only informed if all getting *all* of the traces failed
694         if ( selected )
695                 result = got_something;
696         else
697                 // Process was cancelled but need to return that it proceeded as expected
698                 result = TRUE;
699
700         if ( !vik_datasource_osm_my_traces_interface.is_thread )
701                 vik_window_clear_busy_cursor ( adw->vw );
702
703         return result;
704 }
705
706 static void datasource_osm_my_traces_cleanup ( gpointer data )
707 {
708         g_free ( data );
709 }