Add GPL header in files
[gpx-viewer.git] / src / gpx-viewer.c
blobafd9e2819c6c3dca8494c9e2a75df5c524158ada
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"
40 GList *files = NULL;
41 GtkWidget *champlain_view = NULL;
42 GtkBuilder *builder = NULL;
43 GogPlot *plot = NULL;
45 ClutterColor orange = { 0xf3, 0x94, 0x07, 0xff };
46 ClutterColor normal_track_color = { 0x00, 0x00, 0xff, 0x66 };
48 typedef struct Route {
49 GpxTrack *track;
50 ChamplainPolygon *polygon;
51 } Route;
52 GList *routes = NULL;
53 Route *active_route = NULL;
55 void on_destroy(void)
57 printf("Quit...\n");
58 gtk_main_quit();
59 g_object_unref(builder);
62 void interface_update_heading(GtkBuilder *builder, GpxTrack *track)
64 time_t temp;
65 gdouble gtemp;
66 GtkWidget *label = NULL;
67 /* Duration */
68 label = (GtkWidget *)gtk_builder_get_object(builder, "duration_label");
69 temp = gpx_track_get_total_time(track);
70 if(temp > 0){
71 int hour = temp/3600;
72 int minutes = ((temp%3600)/60);
73 int seconds = (temp%60);
74 GString *string = g_string_new("");
75 if(hour > 0) {
76 g_string_append_printf(string, "%i %s", hour, (hour == 1)?"hour":"hours");
79 if(minutes > 0) {
80 if(hour > 0) g_string_append(string, ", ");
81 g_string_append_printf(string, "%i %s", minutes, (minutes == 1)?"minute":"minutes");
84 if(seconds > 0) {
85 if(minutes > 0) g_string_append(string, ", ");
86 g_string_append_printf(string, "%i %s", seconds, (seconds == 1)?"second":"seconds");
89 gtk_label_set_text(GTK_LABEL(label), string->str);
90 g_string_free(string, TRUE);
91 }else{
92 gtk_label_set_text(GTK_LABEL(label), "n/a");
94 /* Distance */
95 label = (GtkWidget *)gtk_builder_get_object(builder, "distance_label");
96 gtemp = track->total_distance;
97 if(gtemp > 0)
99 gchar *string = g_strdup_printf("%.2f km", gtemp);
100 gtk_label_set_text(GTK_LABEL(label), string);
101 g_free(string);
102 }else{
103 gtk_label_set_text(GTK_LABEL(label), "n/a");
106 /* Average */
107 label = (GtkWidget *)gtk_builder_get_object(builder, "average_label");
108 gtemp = gpx_track_get_track_average(track);
109 if(gtemp > 0)
111 gchar *string = g_strdup_printf("%.2f km/h", gtemp);
112 gtk_label_set_text(GTK_LABEL(label), string);
113 g_free(string);
114 }else{
115 gtk_label_set_text(GTK_LABEL(label), "n/a");
118 /* Moving Average */
119 label = (GtkWidget *)gtk_builder_get_object(builder, "moving_average_label");
120 gtemp = gpx_track_calculate_moving_average(track, &temp);
121 if(gtemp > 0)
123 gchar *string = g_strdup_printf("%.2f km/h", gtemp);
124 gtk_label_set_text(GTK_LABEL(label), string);
125 g_free(string);
126 }else{
127 gtk_label_set_text(GTK_LABEL(label), "n/a");
130 label = (GtkWidget *)gtk_builder_get_object(builder, "moving_average_time_label");
131 if(gtemp > 0)
133 int hour = temp/3600;
134 int minutes = ((temp%3600)/60);
135 int seconds = (temp%60);
136 GString *string = g_string_new("");
137 if(hour > 0) {
138 g_string_append_printf(string, "%i %s", hour, (hour == 1)?"hour":"hours");
141 if(minutes > 0) {
142 if(hour > 0) g_string_append(string, ", ");
143 g_string_append_printf(string, "%i %s", minutes, (minutes == 1)?"minute":"minutes");
146 if(seconds > 0) {
147 if(minutes > 0) g_string_append(string, ", ");
148 g_string_append_printf(string, "%i %s", seconds, (seconds == 1)?"second":"seconds");
151 gtk_label_set_text(GTK_LABEL(label), string->str);
152 g_string_free(string, TRUE);
153 }else{
154 gtk_label_set_text(GTK_LABEL(label), "n/a");
156 /* Max speed*/
157 label = (GtkWidget *)gtk_builder_get_object(builder, "max_speed_label");
158 gtemp = track->max_speed;
159 if(gtemp > 0)
161 gchar *string = g_strdup_printf("%.2f km/h", gtemp);
162 gtk_label_set_text(GTK_LABEL(label), string);
163 g_free(string);
164 }else{
165 gtk_label_set_text(GTK_LABEL(label), "n/a");
168 /* GPS Points */
169 label = (GtkWidget *)gtk_builder_get_object(builder, "num_points_label");
170 gtemp = (double)g_list_length(track->points);
171 if(gtemp > 0)
173 gchar *string = g_strdup_printf("%.0f points", gtemp);
174 gtk_label_set_text(GTK_LABEL(label), string);
175 g_free(string);
176 }else{
177 gtk_label_set_text(GTK_LABEL(label), "n/a");
183 void interface_map_plot_route(ChamplainView *view,struct Route *route)
185 GpxPoint *p = NULL;
186 GList *iter;
187 route->polygon = champlain_polygon_new ();
188 for(iter = g_list_first(route->track->points); iter; iter = iter->next)
190 p = iter->data;
191 champlain_polygon_append_point(route->polygon, p->lat_dec, p->lon_dec);
193 champlain_polygon_set_stroke_width (route->polygon, 5.0);
194 champlain_polygon_set_stroke_color (route->polygon, &normal_track_color);
195 champlain_view_add_polygon (CHAMPLAIN_VIEW (view), route->polygon);
197 void interface_map_make_waypoints(ChamplainView *view)
199 GList *iter;
200 ChamplainLayer *layer = champlain_layer_new();
201 for(iter = g_list_first(files);iter != NULL; iter = g_list_next(iter))
203 GpxFile *file = iter->data;
204 GList *it = g_list_first(file->waypoints);
205 while(it)
207 GpxPoint *p = it->data;
208 ClutterActor *marker = champlain_marker_new_with_text(p->name, "Seric 12", NULL, NULL);
209 champlain_base_marker_set_position(CHAMPLAIN_BASE_MARKER(marker), p->lat_dec, p->lon_dec);
210 champlain_marker_set_color (CHAMPLAIN_MARKER (marker), &orange);
211 clutter_container_add(CLUTTER_CONTAINER(layer), CLUTTER_ACTOR(marker),NULL);
212 clutter_actor_show(CLUTTER_ACTOR(layer));
213 it = g_list_next(it);
216 champlain_view_add_layer(view, layer);
218 void routes_combo_changed_cb(GtkComboBox *box, gpointer user_data)
220 GtkTreeModel *model = gtk_combo_box_get_model(box);
221 GtkTreeIter iter;
222 if(gtk_combo_box_get_active_iter(box, &iter))
224 Route *route = NULL;
225 gtk_tree_model_get(model, &iter, 1, &route, -1);
226 if(active_route)
228 champlain_polygon_set_stroke_color (active_route->polygon, &normal_track_color);
229 champlain_polygon_show(active_route->polygon);
232 if(route)
234 ChamplainView *view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view));
235 champlain_polygon_set_stroke_color (route->polygon, &orange);
236 champlain_polygon_show(route->polygon);
238 interface_update_heading(builder, route->track);
241 GSList const *list;
242 GogSeries *series;
243 GOData *data;
244 GError *error;
245 GogLabel *label;
246 list = gog_plot_get_series(plot);
247 if(list) series = list->data;
248 else
249 series = gog_plot_new_series (plot);
250 unsigned int items = g_list_length(route->track->points);
251 double *values = g_malloc0(items*sizeof(double));
252 char **index = g_malloc0(items*sizeof(char *));
253 GList *iter;
254 items = 0;
255 GpxPoint *first = route->track->points->data;
256 for(iter = g_list_first(route->track->points);iter; iter = iter->next)
258 GpxPoint *p = iter->data;
259 values[items] =p->speed;
260 unsigned int item = (int)(gpx_point_get_time(p)-gpx_point_get_time(first));
261 index[items] = g_strdup_printf("%02u:%02u", item/3600, (item%3600)/60);
262 items++;
265 data = go_data_vector_str_new((const char * const *) index, items, NULL);
266 gog_series_set_dim(series, 0, data, &error);
267 data = go_data_vector_val_new(values, items, NULL);
268 gog_series_set_dim(series, 1, data, &error);
270 if(route->track->top && route->track->bottom)
272 champlain_view_ensure_visible(view,
273 route->track->top->lat_dec, route->track->top->lon_dec,
274 route->track->bottom->lat_dec,route->track->bottom->lon_dec,
275 FALSE);
278 active_route = route;
281 void map_zoom_changed (ChamplainView *view,
282 GParamSpec *gobject,
283 GtkSpinButton *spinbutton)
285 gint zoom;
286 g_object_get(G_OBJECT(view), "zoom-level", &zoom, NULL);
287 gtk_spin_button_set_value(spinbutton, zoom);
288 printf("zoom changed: %i\n", zoom);
290 void map_zoom_level_change_value_cb(GtkSpinButton *spin, gpointer user_data)
292 ChamplainView *view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view));
293 int current = champlain_view_get_zoom_level(view);
294 int new = gtk_spin_button_get_value_as_int(spin);
295 printf("changed: %i %i\n", current, new);
296 if(current != new)
298 champlain_view_set_zoom_level(view, new);
302 /* Create the interface */
303 void create_interface(void)
305 time_t temp;
306 GError *error = NULL;
307 const gchar * const *dirs = g_get_system_data_dirs();
308 int i=0;
309 GList *fiter;
310 builder = gtk_builder_new();
311 int found_ui = 0;
313 gchar *path = g_build_filename(DATA_DIR, "gpx-viewer", "gpx-viewer.ui", NULL);
314 if(gtk_builder_add_from_file(builder,path, NULL)){
315 found_ui = 1;
317 printf("path: %s\n", path);
318 g_free(path);
321 if(!found_ui) {
322 g_error("Failed to create ui: %s\n", error->message);
324 /* Create map view */
325 champlain_view = gtk_champlain_embed_new();
326 gtk_widget_set_size_request (champlain_view, 640, 280);
327 gtk_paned_pack1(GTK_PANED(gtk_builder_get_object(builder, "main_view_pane")), champlain_view, TRUE, TRUE);
328 /* graph */
330 GtkWidget *wid = go_graph_widget_new(NULL);
331 GogGraph *graph = go_graph_widget_get_graph(GO_GRAPH_WIDGET(wid));
332 GogChart *chart;
333 chart = go_graph_widget_get_chart (GO_GRAPH_WIDGET(wid));
334 plot = gog_plot_new_by_name("GogAreaPlot");
335 gog_object_add_by_name(GOG_OBJECT(chart), "Plot",GOG_OBJECT(plot));
336 GogObject *grid = g_object_new(GOG_GRID_TYPE, NULL);
337 gog_object_add_by_name(GOG_OBJECT(chart), "Grid", grid);
340 /* Add a title */
341 GogLabel *label = (GogLabel *) g_object_new (GOG_LABEL_TYPE, NULL);
342 GOData *data = go_data_scalar_str_new ("Speed vs. Time", FALSE);
343 gog_dataset_set_dim (GOG_DATASET (label), 0, data, NULL);
344 gog_object_add_by_name (GOG_OBJECT (graph), "Title", GOG_OBJECT (label));
345 /* Create a series for the plot and populate it with some simple data */
346 gtk_paned_pack2(GTK_PANED(gtk_builder_get_object(builder, "main_view_pane")), wid,FALSE, TRUE);
347 gtk_paned_set_position(GTK_PANED(gtk_builder_get_object(builder, "main_view_pane")),200);
350 /* show the interface */
351 gtk_widget_show_all(
352 GTK_WIDGET(gtk_builder_get_object(builder, "gpx_viewer_window")));
354 ChamplainView *view = gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view));
355 g_object_set (G_OBJECT (view), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC,
356 "zoom-level", 5, NULL);
358 interface_map_make_waypoints(view);
360 double lon1= 1000, lon2=-1000,lat1=1000, lat2=-1000;
361 for(fiter = g_list_first(files); fiter; fiter = g_list_next(fiter))
363 GpxFile *file = fiter->data;
364 GpxTrack *track = file->tracks->data;
365 GList *iter = g_list_first(file->tracks);
366 /* Plot all tracks, and get total bounding box */
367 GtkTreeIter liter;
368 GtkTreeModel *model = (GtkTreeModel *)gtk_builder_get_object(builder, "routes_store");
369 while(iter)
371 struct Route *route= g_new0(Route, 1);
372 route->track = iter->data;
374 /* draw the track */
375 interface_map_plot_route(view, route);
376 if(track->top && track->top->lat_dec < lat1) lat1 = track->top->lat_dec;
377 if(track->top && track->top->lon_dec < lon1) lon1 = track->top->lon_dec;
379 if(track->bottom && track->bottom->lat_dec > lat2) lat2 = track->bottom->lat_dec;
380 if(track->bottom && track->bottom->lon_dec > lon2) lon2 = track->bottom->lon_dec;
382 gtk_list_store_append(GTK_LIST_STORE(model), &liter);
383 gtk_list_store_set(GTK_LIST_STORE(model), &liter, 0, (route->track->name)?route->track->name:"n/a", 1, route, -1);
385 routes = g_list_append(routes, route);
387 iter = g_list_next(iter);
390 /* Set up the zoom widget */
392 GtkWidget *sp = GTK_WIDGET(gtk_builder_get_object(builder, "map_zoom_level"));
393 int min, max, current;
394 champlain_view_set_min_zoom_level(view,1);
395 champlain_view_set_max_zoom_level(view,18);
396 current = champlain_view_get_zoom_level(view);
397 gtk_spin_button_set_value(GTK_SPIN_BUTTON(sp), (double)current);
399 g_signal_connect (view, "notify::zoom-level", G_CALLBACK (map_zoom_changed),
400 sp);
403 gtk_builder_connect_signals(builder,NULL);
404 /* Try to center the track on map correctly */
405 if(lon1 < 1000.0 && lon2 < 1000.0)
407 printf("%.4f %.4f %.4f %.4f\n", lat1, lon1, lat2, lon2);
408 champlain_view_set_zoom_level(view,8);
409 champlain_view_ensure_visible(view,
410 lat1, lon1,
411 lat2,lon2,
412 FALSE);
416 int main (int argc, char **argv)
419 int i =0;
420 g_thread_init (NULL);
421 gtk_clutter_init (&argc, &argv);
422 /* Initialize office plugins */
423 libgoffice_init();
424 /* Initialize plugins manager */
425 go_plugins_init (NULL, NULL, NULL, NULL, TRUE, GO_PLUGIN_LOADER_MODULE_TYPE);
426 /* If no file(s) given, ask for it */
427 if(argc < 2)
429 GtkWidget *dialog;
430 GtkBuilder *fbuilder = gtk_builder_new();
431 /* Show dialog */
432 gchar *path = g_build_filename(DATA_DIR, "gpx-viewer", "gpx-viewer-file-chooser.ui", NULL);
433 if(!gtk_builder_add_from_file(fbuilder,path, NULL)){
434 g_error("Failed to load gpx-viewer.ui");
436 g_free(path);
437 /* update filter */
439 GtkFileFilter *filter = (GtkFileFilter *)gtk_builder_get_object(fbuilder, "gpx_viewer_file_chooser_filter");
440 gtk_file_filter_add_pattern(filter, "*.gpx");
443 dialog = GTK_WIDGET(gtk_builder_get_object(fbuilder, "gpx_viewer_file_chooser"));
444 switch(gtk_dialog_run(GTK_DIALOG(dialog)))
446 case 1:
448 GSList *iter, *choosen_files = gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog));
449 for(iter =choosen_files; iter; iter = g_slist_next(iter))
451 /* Try to open the gpx file */
452 GpxFile *file = gpx_file_new((gchar *)iter->data);
453 files = g_list_append(files, file);
455 g_slist_foreach(choosen_files, (GFunc)g_free, NULL);
456 g_slist_free(choosen_files);
458 break;
460 gtk_widget_destroy(dialog);
461 g_object_unref(fbuilder);
464 for(i =1; i < argc; i++)
466 /* Try to open the gpx file */
467 GpxFile *file = gpx_file_new(argv[i]);
468 files = g_list_append(files, file);
471 create_interface();
474 gtk_main();
475 /* Cleanup office */
476 libgoffice_shutdown();
477 /* Destroy the track */
478 g_list_foreach(files, (GFunc)g_object_unref, NULL);
479 g_list_free(files);
481 return EXIT_SUCCESS;