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.
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>
38 #include "gpx-parser.h"
39 /* List of gpx files */
42 GtkWidget
*champlain_view
= NULL
;
43 GtkBuilder
*builder
= NULL
;
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
{
54 ChamplainPolygon
*polygon
;
59 /* The currently active route */
60 Route
*active_route
= NULL
;
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
)
76 GtkWidget
*label
= NULL
;
78 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "duration_label");
79 temp
= gpx_track_get_total_time(track
);
82 int minutes
= ((temp
%3600)/60);
83 int seconds
= (temp
%60);
84 GString
*string
= g_string_new("");
86 g_string_append_printf(string
, "%i %s", hour
, (hour
== 1)?"hour":"hours");
90 if(hour
> 0) g_string_append(string
, ", ");
91 g_string_append_printf(string
, "%i %s", minutes
, (minutes
== 1)?"minute":"minutes");
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
);
102 gtk_label_set_text(GTK_LABEL(label
), "n/a");
105 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "distance_label");
106 gtemp
= track
->total_distance
;
109 gchar
*string
= g_strdup_printf("%.2f km", gtemp
);
110 gtk_label_set_text(GTK_LABEL(label
), string
);
113 gtk_label_set_text(GTK_LABEL(label
), "n/a");
117 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "average_label");
118 gtemp
= gpx_track_get_track_average(track
);
121 gchar
*string
= g_strdup_printf("%.2f km/h", gtemp
);
122 gtk_label_set_text(GTK_LABEL(label
), string
);
125 gtk_label_set_text(GTK_LABEL(label
), "n/a");
129 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "moving_average_label");
130 gtemp
= gpx_track_calculate_moving_average(track
, &temp
);
133 gchar
*string
= g_strdup_printf("%.2f km/h", gtemp
);
134 gtk_label_set_text(GTK_LABEL(label
), string
);
137 gtk_label_set_text(GTK_LABEL(label
), "n/a");
140 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "moving_average_time_label");
143 int hour
= temp
/3600;
144 int minutes
= ((temp
%3600)/60);
145 int seconds
= (temp
%60);
146 GString
*string
= g_string_new("");
148 g_string_append_printf(string
, "%i %s", hour
, (hour
== 1)?"hour":"hours");
152 if(hour
> 0) g_string_append(string
, ", ");
153 g_string_append_printf(string
, "%i %s", minutes
, (minutes
== 1)?"minute":"minutes");
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
);
164 gtk_label_set_text(GTK_LABEL(label
), "n/a");
167 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "max_speed_label");
168 gtemp
= track
->max_speed
;
171 gchar
*string
= g_strdup_printf("%.2f km/h", gtemp
);
172 gtk_label_set_text(GTK_LABEL(label
), string
);
175 gtk_label_set_text(GTK_LABEL(label
), "n/a");
179 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "num_points_label");
180 gtemp
= (double)g_list_length(track
->points
);
183 gchar
*string
= g_strdup_printf("%.0f points", gtemp
);
184 gtk_label_set_text(GTK_LABEL(label
), string
);
187 gtk_label_set_text(GTK_LABEL(label
), "n/a");
193 void interface_map_plot_route(ChamplainView
*view
,struct Route
*route
)
197 route
->polygon
= champlain_polygon_new ();
198 for(iter
= g_list_first(route
->track
->points
); iter
; iter
= iter
->next
)
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
)
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
);
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
));
234 if(active_route
->visible
!= active
)
236 active_route
->visible
= active
;
238 champlain_polygon_show(active_route
->polygon
);
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
);
249 if(gtk_combo_box_get_active_iter(box
, &iter
))
252 gtk_tree_model_get(model
, &iter
, 1, &route
, -1);
255 champlain_polygon_set_stroke_color (active_route
->polygon
, &normal_track_color
);
256 if(active_route
->visible
)
257 champlain_polygon_show(active_route
->polygon
);
262 ChamplainView
*view
= gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view
));
263 champlain_polygon_set_stroke_color (route
->polygon
, &highlight_track_color
);
266 champlain_polygon_show(route
->polygon
);
268 interface_update_heading(builder
, route
->track
);
276 list
= gog_plot_get_series(plot
);
277 if(list
) series
= list
->data
;
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 *));
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);
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
,
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
,
316 GtkSpinButton
*spinbutton
)
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);
331 champlain_view_set_zoom_level(view
, new);
335 /* Create the interface */
336 void create_interface(void)
339 GError
*error
= NULL
;
340 const gchar
* const *dirs
= g_get_system_data_dirs();
343 builder
= gtk_builder_new();
346 gchar
*path
= g_build_filename(DATA_DIR
, "gpx-viewer.ui", NULL
);
347 if(gtk_builder_add_from_file(builder
,path
, NULL
)){
350 printf("path: %s\n", path
);
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
);
363 GtkWidget
*wid
= go_graph_widget_new(NULL
);
364 GogGraph
*graph
= go_graph_widget_get_graph(GO_GRAPH_WIDGET(wid
));
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
);
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 */
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 */
401 GtkTreeModel
*model
= (GtkTreeModel
*)gtk_builder_get_object(builder
, "routes_store");
404 struct Route
*route
= g_new0(Route
, 1);
406 route
->track
= iter
->data
;
407 route
->visible
= TRUE
;
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"));
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
),
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
,
450 int main (int argc
, char **argv
)
454 g_thread_init (NULL
);
455 gtk_clutter_init (&argc
, &argv
);
456 /* Initialize office plugins */
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 */
464 GtkBuilder
*fbuilder
= gtk_builder_new();
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");
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
)))
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
);
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
);
510 libgoffice_shutdown();
511 /* Destroy the track */
512 g_list_foreach(files
, (GFunc
)g_object_unref
, NULL
);