Add option to hide tracks
[gpx-viewer.git] / src / gpx-viewer.c
blobe6af82371e86fb49a6063e8c94671128e50d9c43
1 /* Gpx Viewer
2 * Copyright (C) 2009-2009 Qball Cow <qball@sarine.nl>
3 * Project homepage: http://blog.sarine.nl/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <gtk/gtk.h>
21 #include <stdlib.h>
22 #include <champlain/champlain.h>
23 #include <champlain-gtk/champlain-gtk.h>
24 #include <clutter-gtk/gtk-clutter-embed.h>
25 #include <goffice/goffice.h>
26 #include <goffice/gtk/go-graph-widget.h>
27 #include <goffice/graph/gog-styled-object.h>
28 #include <goffice/graph/gog-plot.h>
29 #include <goffice/graph/gog-series.h>
30 #include <goffice/graph/gog-label.h>
31 #include <goffice/graph/gog-grid.h>
32 #include <goffice/utils/go-marker.h>
33 #include <goffice/graph/gog-data-set.h>
34 #include <goffice/data/go-data-simple.h>
35 #include <goffice/app/go-plugin.h>
36 #include <goffice/app/go-plugin-loader-module.h>
37 #include <config.h>
38 #include "gpx-parser.h"
39 /* List of gpx files */
40 GList *files = NULL;
42 GtkWidget *champlain_view = NULL;
43 GtkBuilder *builder = NULL;
44 GogPlot *plot = NULL;
46 /* Clutter collors */
47 ClutterColor waypoint = { 0xf3, 0x94, 0x07, 0xff };
48 ClutterColor highlight_track_color = { 0xf3, 0x94, 0x07, 0xff };
49 ClutterColor normal_track_color = { 0x00, 0x00, 0xff, 0x66 };
51 typedef struct Route {
52 GpxFile *file;
53 GpxTrack *track;
54 ChamplainPolygon *polygon;
55 gboolean visible;
56 } Route;
57 /* List of routes */
58 GList *routes = NULL;
59 /* The currently active route */
60 Route *active_route = NULL;
62 void on_destroy(void)
64 printf("Quit...\n");
65 gtk_main_quit();
67 gtk_widget_destroy(
68 GTK_WIDGET(gtk_builder_get_object(builder, "gpx_viewer_window")));
69 g_object_unref(builder);
72 void interface_update_heading(GtkBuilder *builder, GpxTrack *track)
74 time_t temp;
75 gdouble gtemp;
76 GtkWidget *label = NULL;
77 /* Duration */
78 label = (GtkWidget *)gtk_builder_get_object(builder, "duration_label");
79 temp = gpx_track_get_total_time(track);
80 if(temp > 0){
81 int hour = temp/3600;
82 int minutes = ((temp%3600)/60);
83 int seconds = (temp%60);
84 GString *string = g_string_new("");
85 if(hour > 0) {
86 g_string_append_printf(string, "%i %s", hour, (hour == 1)?"hour":"hours");
89 if(minutes > 0) {
90 if(hour > 0) g_string_append(string, ", ");
91 g_string_append_printf(string, "%i %s", minutes, (minutes == 1)?"minute":"minutes");
94 if(seconds > 0) {
95 if(minutes > 0) g_string_append(string, ", ");
96 g_string_append_printf(string, "%i %s", seconds, (seconds == 1)?"second":"seconds");
99 gtk_label_set_text(GTK_LABEL(label), string->str);
100 g_string_free(string, TRUE);
101 }else{
102 gtk_label_set_text(GTK_LABEL(label), "n/a");
104 /* Distance */
105 label = (GtkWidget *)gtk_builder_get_object(builder, "distance_label");
106 gtemp = track->total_distance;
107 if(gtemp > 0)
109 gchar *string = g_strdup_printf("%.2f km", gtemp);
110 gtk_label_set_text(GTK_LABEL(label), string);
111 g_free(string);
112 }else{
113 gtk_label_set_text(GTK_LABEL(label), "n/a");
116 /* Average */
117 label = (GtkWidget *)gtk_builder_get_object(builder, "average_label");
118 gtemp = gpx_track_get_track_average(track);
119 if(gtemp > 0)
121 gchar *string = g_strdup_printf("%.2f km/h", gtemp);
122 gtk_label_set_text(GTK_LABEL(label), string);
123 g_free(string);
124 }else{
125 gtk_label_set_text(GTK_LABEL(label), "n/a");
128 /* Moving Average */
129 label = (GtkWidget *)gtk_builder_get_object(builder, "moving_average_label");
130 gtemp = gpx_track_calculate_moving_average(track, &temp);
131 if(gtemp > 0)
133 gchar *string = g_strdup_printf("%.2f km/h", gtemp);
134 gtk_label_set_text(GTK_LABEL(label), string);
135 g_free(string);
136 }else{
137 gtk_label_set_text(GTK_LABEL(label), "n/a");
140 label = (GtkWidget *)gtk_builder_get_object(builder, "moving_average_time_label");
141 if(gtemp > 0)
143 int hour = temp/3600;
144 int minutes = ((temp%3600)/60);
145 int seconds = (temp%60);
146 GString *string = g_string_new("");
147 if(hour > 0) {
148 g_string_append_printf(string, "%i %s", hour, (hour == 1)?"hour":"hours");
151 if(minutes > 0) {
152 if(hour > 0) g_string_append(string, ", ");
153 g_string_append_printf(string, "%i %s", minutes, (minutes == 1)?"minute":"minutes");
156 if(seconds > 0) {
157 if(minutes > 0) g_string_append(string, ", ");
158 g_string_append_printf(string, "%i %s", seconds, (seconds == 1)?"second":"seconds");
161 gtk_label_set_text(GTK_LABEL(label), string->str);
162 g_string_free(string, TRUE);
163 }else{
164 gtk_label_set_text(GTK_LABEL(label), "n/a");
166 /* Max speed*/
167 label = (GtkWidget *)gtk_builder_get_object(builder, "max_speed_label");
168 gtemp = track->max_speed;
169 if(gtemp > 0)
171 gchar *string = g_strdup_printf("%.2f km/h", gtemp);
172 gtk_label_set_text(GTK_LABEL(label), string);
173 g_free(string);
174 }else{
175 gtk_label_set_text(GTK_LABEL(label), "n/a");
178 /* GPS Points */
179 label = (GtkWidget *)gtk_builder_get_object(builder, "num_points_label");
180 gtemp = (double)g_list_length(track->points);
181 if(gtemp > 0)
183 gchar *string = g_strdup_printf("%.0f points", gtemp);
184 gtk_label_set_text(GTK_LABEL(label), string);
185 g_free(string);
186 }else{
187 gtk_label_set_text(GTK_LABEL(label), "n/a");
193 void interface_map_plot_route(ChamplainView *view,struct Route *route)
195 GpxPoint *p = NULL;
196 GList *iter;
197 route->polygon = champlain_polygon_new ();
198 for(iter = g_list_first(route->track->points); iter; iter = iter->next)
200 p = iter->data;
201 champlain_polygon_append_point(route->polygon, p->lat_dec, p->lon_dec);
203 champlain_polygon_set_stroke_width (route->polygon, 5.0);
204 champlain_polygon_set_stroke_color (route->polygon, &normal_track_color);
205 champlain_view_add_polygon (CHAMPLAIN_VIEW (view), route->polygon);
207 void interface_map_make_waypoints(ChamplainView *view)
209 GList *iter;
210 ChamplainLayer *layer = champlain_layer_new();
211 for(iter = g_list_first(files);iter != NULL; iter = g_list_next(iter))
213 GpxFile *file = iter->data;
214 GList *it = g_list_first(file->waypoints);
215 while(it)
217 GpxPoint *p = it->data;
218 ClutterActor *marker = champlain_marker_new_with_text(p->name, "Seric 12", NULL, NULL);
219 champlain_base_marker_set_position(CHAMPLAIN_BASE_MARKER(marker), p->lat_dec, p->lon_dec);
220 champlain_marker_set_color (CHAMPLAIN_MARKER (marker), &waypoint);
221 clutter_container_add(CLUTTER_CONTAINER(layer), CLUTTER_ACTOR(marker),NULL);
222 clutter_actor_show(CLUTTER_ACTOR(layer));
223 it = g_list_next(it);
226 champlain_view_add_layer(view, layer);
229 void route_set_visible(GtkCheckButton *button, gpointer user_data)
231 gboolean active = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
232 if(active_route)
234 if(active_route->visible != active)
236 active_route->visible = active;
237 if(active) {
238 champlain_polygon_show(active_route->polygon);
239 }else{
240 champlain_polygon_hide(active_route->polygon);
245 void routes_combo_changed_cb(GtkComboBox *box, gpointer user_data)
247 GtkTreeModel *model = gtk_combo_box_get_model(box);
248 GtkTreeIter iter;
249 if(gtk_combo_box_get_active_iter(box, &iter))
251 Route *route = NULL;
252 gtk_tree_model_get(model, &iter, 1, &route, -1);
253 if(active_route)
255 champlain_polygon_set_stroke_color (active_route->polygon, &normal_track_color);
256 if(active_route->visible)
257 champlain_polygon_show(active_route->polygon);
260 if(route)
262 ChamplainView *view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view));
263 champlain_polygon_set_stroke_color (route->polygon, &highlight_track_color);
265 if(route->visible)
266 champlain_polygon_show(route->polygon);
268 interface_update_heading(builder, route->track);
271 GSList const *list;
272 GogSeries *series;
273 GOData *data;
274 GError *error;
275 GogLabel *label;
276 list = gog_plot_get_series(plot);
277 if(list) series = list->data;
278 else
279 series = gog_plot_new_series (plot);
280 unsigned int items = g_list_length(route->track->points);
281 double *values = g_malloc0(items*sizeof(double));
282 char **index = g_malloc0(items*sizeof(char *));
283 GList *iter;
284 items = 0;
285 GpxPoint *first = route->track->points->data;
286 for(iter = g_list_first(route->track->points);iter; iter = iter->next)
288 GpxPoint *p = iter->data;
289 values[items] =p->speed;
290 unsigned int item = (int)(gpx_point_get_time(p)-gpx_point_get_time(first));
291 index[items] = g_strdup_printf("%02u:%02u", item/3600, (item%3600)/60);
292 items++;
295 data = go_data_vector_str_new((const char * const *) index, items, NULL);
296 gog_series_set_dim(series, 0, data, &error);
297 data = go_data_vector_val_new(values, items, NULL);
298 gog_series_set_dim(series, 1, data, &error);
300 if(route->track->top && route->track->bottom)
302 champlain_view_ensure_visible(view,
303 route->track->top->lat_dec, route->track->top->lon_dec,
304 route->track->bottom->lat_dec,route->track->bottom->lon_dec,
305 FALSE);
308 active_route = route;
310 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "route_visible_check_button")),
311 active_route->visible);
314 void map_zoom_changed (ChamplainView *view,
315 GParamSpec *gobject,
316 GtkSpinButton *spinbutton)
318 gint zoom;
319 g_object_get(G_OBJECT(view), "zoom-level", &zoom, NULL);
320 gtk_spin_button_set_value(spinbutton, zoom);
321 printf("zoom changed: %i\n", zoom);
323 void map_zoom_level_change_value_cb(GtkSpinButton *spin, gpointer user_data)
325 ChamplainView *view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view));
326 int current = champlain_view_get_zoom_level(view);
327 int new = gtk_spin_button_get_value_as_int(spin);
328 printf("changed: %i %i\n", current, new);
329 if(current != new)
331 champlain_view_set_zoom_level(view, new);
335 /* Create the interface */
336 void create_interface(void)
338 time_t temp;
339 GError *error = NULL;
340 const gchar * const *dirs = g_get_system_data_dirs();
341 int i=0;
342 GList *fiter;
343 builder = gtk_builder_new();
344 int found_ui = 0;
346 gchar *path = g_build_filename(DATA_DIR, "gpx-viewer.ui", NULL);
347 if(gtk_builder_add_from_file(builder,path, NULL)){
348 found_ui = 1;
350 printf("path: %s\n", path);
351 g_free(path);
354 if(!found_ui) {
355 g_error("Failed to create ui: %s\n", error->message);
357 /* Create map view */
358 champlain_view = gtk_champlain_embed_new();
359 gtk_widget_set_size_request (champlain_view, 640, 280);
360 gtk_paned_pack1(GTK_PANED(gtk_builder_get_object(builder, "main_view_pane")), champlain_view, TRUE, TRUE);
361 /* graph */
363 GtkWidget *wid = go_graph_widget_new(NULL);
364 GogGraph *graph = go_graph_widget_get_graph(GO_GRAPH_WIDGET(wid));
365 GogChart *chart;
366 chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET(wid));
367 plot = gog_plot_new_by_name("GogAreaPlot");
368 gog_object_add_by_name(GOG_OBJECT(chart), "Plot",GOG_OBJECT(plot));
369 GogObject *grid = g_object_new(GOG_GRID_TYPE, NULL);
370 gog_object_add_by_name(GOG_OBJECT(chart), "Grid", grid);
373 /* Add a title */
374 GogLabel *label = (GogLabel *) g_object_new (GOG_LABEL_TYPE, NULL);
375 GOData *data = go_data_scalar_str_new ("Speed vs. Time", FALSE);
376 gog_dataset_set_dim (GOG_DATASET (label), 0, data, NULL);
377 gog_object_add_by_name (GOG_OBJECT (graph), "Title", GOG_OBJECT (label));
378 /* Create a series for the plot and populate it with some simple data */
379 gtk_paned_pack2(GTK_PANED(gtk_builder_get_object(builder, "main_view_pane")), wid,FALSE, TRUE);
380 gtk_paned_set_position(GTK_PANED(gtk_builder_get_object(builder, "main_view_pane")),200);
383 /* show the interface */
384 gtk_widget_show_all(
385 GTK_WIDGET(gtk_builder_get_object(builder, "gpx_viewer_window")));
387 ChamplainView *view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view));
388 g_object_set (G_OBJECT (view), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC,
389 "zoom-level", 5, NULL);
391 interface_map_make_waypoints(view);
393 double lon1= 1000, lon2=-1000,lat1=1000, lat2=-1000;
394 for(fiter = g_list_first(files); fiter; fiter = g_list_next(fiter))
396 GpxFile *file = fiter->data;
397 GpxTrack *track = file->tracks->data;
398 GList *iter = g_list_first(file->tracks);
399 /* Plot all tracks, and get total bounding box */
400 GtkTreeIter liter;
401 GtkTreeModel *model = (GtkTreeModel *)gtk_builder_get_object(builder, "routes_store");
402 while(iter)
404 struct Route *route= g_new0(Route, 1);
405 route->file = file;
406 route->track = iter->data;
407 route->visible = TRUE;
409 /* draw the track */
410 interface_map_plot_route(view, route);
411 if(track->top && track->top->lat_dec < lat1) lat1 = track->top->lat_dec;
412 if(track->top && track->top->lon_dec < lon1) lon1 = track->top->lon_dec;
414 if(track->bottom && track->bottom->lat_dec > lat2) lat2 = track->bottom->lat_dec;
415 if(track->bottom && track->bottom->lon_dec > lon2) lon2 = track->bottom->lon_dec;
417 gtk_list_store_append(GTK_LIST_STORE(model), &liter);
418 gtk_list_store_set(GTK_LIST_STORE(model), &liter, 0, (route->track->name)?route->track->name:"n/a", 1, route, -1);
420 routes = g_list_append(routes, route);
422 iter = g_list_next(iter);
425 /* Set up the zoom widget */
427 GtkWidget *sp = GTK_WIDGET(gtk_builder_get_object(builder, "map_zoom_level"));
428 int current;
429 champlain_view_set_min_zoom_level(view,1);
430 champlain_view_set_max_zoom_level(view,18);
431 current = champlain_view_get_zoom_level(view);
432 gtk_spin_button_set_value(GTK_SPIN_BUTTON(sp), (double)current);
434 g_signal_connect (view, "notify::zoom-level", G_CALLBACK (map_zoom_changed),
435 sp);
438 gtk_builder_connect_signals(builder,NULL);
439 /* Try to center the track on map correctly */
440 if(lon1 < 1000.0 && lon2 < 1000.0)
442 champlain_view_set_zoom_level(view,8);
443 champlain_view_ensure_visible(view,
444 lat1, lon1,
445 lat2,lon2,
446 FALSE);
450 int main (int argc, char **argv)
453 int i =0;
454 g_thread_init (NULL);
455 gtk_clutter_init (&argc, &argv);
456 /* Initialize office plugins */
457 libgoffice_init();
458 /* Initialize plugins manager */
459 go_plugins_init (NULL, NULL, NULL, NULL, TRUE, GO_PLUGIN_LOADER_MODULE_TYPE);
460 /* If no file(s) given, ask for it */
461 if(argc < 2)
463 GtkWidget *dialog;
464 GtkBuilder *fbuilder = gtk_builder_new();
465 /* Show dialog */
466 gchar *path = g_build_filename(DATA_DIR, "gpx-viewer-file-chooser.ui", NULL);
467 if(!gtk_builder_add_from_file(fbuilder,path, NULL)){
468 g_error("Failed to load gpx-viewer.ui");
470 g_free(path);
471 /* update filter */
473 GtkFileFilter *filter = (GtkFileFilter *)gtk_builder_get_object(fbuilder, "gpx_viewer_file_chooser_filter");
474 gtk_file_filter_add_pattern(filter, "*.gpx");
477 dialog = GTK_WIDGET(gtk_builder_get_object(fbuilder, "gpx_viewer_file_chooser"));
478 switch(gtk_dialog_run(GTK_DIALOG(dialog)))
480 case 1:
482 GSList *iter, *choosen_files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
483 for(iter =choosen_files; iter; iter = g_slist_next(iter))
485 /* Try to open the gpx file */
486 GpxFile *file = gpx_file_new((gchar *)iter->data);
487 files = g_list_append(files, file);
489 g_slist_foreach(choosen_files, (GFunc)g_free, NULL);
490 g_slist_free(choosen_files);
492 break;
494 gtk_widget_destroy(dialog);
495 g_object_unref(fbuilder);
498 for(i =1; i < argc; i++)
500 /* Try to open the gpx file */
501 GpxFile *file = gpx_file_new(argv[i]);
502 files = g_list_append(files, file);
505 create_interface();
508 gtk_main();
509 /* Cleanup office */
510 libgoffice_shutdown();
511 /* Destroy the track */
512 g_list_foreach(files, (GFunc)g_object_unref, NULL);
513 g_list_free(files);
515 return EXIT_SUCCESS;