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>
26 #include <glib/gi18n.h>
27 #include "speed-plot.h"
29 /* List of gpx files */
32 GtkWidget
*champlain_view
= NULL
;
33 GtkBuilder
*builder
= NULL
;
34 GpxGraph
*gpx_graph
=NULL
;
35 ChamplainLayer
*marker_layer
= NULL
;
37 ClutterColor waypoint
= { 0xf3, 0x94, 0x07, 0xff };
38 ClutterColor highlight_track_color
= { 0xf3, 0x94, 0x07, 0xff };
39 ClutterColor normal_track_color
= { 0x00, 0x00, 0xff, 0x66 };
41 typedef struct Route
{
44 ChamplainPolygon
*polygon
;
49 /* The currently active route */
50 Route
*active_route
= NULL
;
58 GTK_WIDGET(gtk_builder_get_object(builder
, "gpx_viewer_window")));
59 g_object_unref(builder
);
62 * Update on track changes
64 static void interface_update_heading(GtkBuilder
*builder
, GpxTrack
*track
)
68 GtkWidget
*label
= NULL
;
70 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "duration_label");
71 temp
= gpx_track_get_total_time(track
);
74 int minutes
= ((temp
%3600)/60);
75 int seconds
= (temp
%60);
76 GString
*string
= g_string_new("");
78 g_string_append_printf(string
, "%i %s", hour
, (hour
== 1)?"hour":"hours");
82 if(hour
> 0) g_string_append(string
, ", ");
83 g_string_append_printf(string
, "%i %s", minutes
, (minutes
== 1)?"minute":"minutes");
87 if(minutes
> 0) g_string_append(string
, ", ");
88 g_string_append_printf(string
, "%i %s", seconds
, (seconds
== 1)?"second":"seconds");
91 gtk_label_set_text(GTK_LABEL(label
), string
->str
);
92 g_string_free(string
, TRUE
);
94 gtk_label_set_text(GTK_LABEL(label
), "n/a");
97 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "distance_label");
98 gtemp
= track
->total_distance
;
101 gchar
*string
= g_strdup_printf("%.2f km", gtemp
);
102 gtk_label_set_text(GTK_LABEL(label
), string
);
105 gtk_label_set_text(GTK_LABEL(label
), "n/a");
109 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "average_label");
110 gtemp
= gpx_track_get_track_average(track
);
113 gchar
*string
= g_strdup_printf("%.2f km/h", gtemp
);
114 gtk_label_set_text(GTK_LABEL(label
), string
);
117 gtk_label_set_text(GTK_LABEL(label
), "n/a");
121 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "moving_average_label");
122 gtemp
= gpx_track_calculate_moving_average(track
, &temp
);
125 gchar
*string
= g_strdup_printf("%.2f km/h", gtemp
);
126 gtk_label_set_text(GTK_LABEL(label
), string
);
129 gtk_label_set_text(GTK_LABEL(label
), "n/a");
132 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "moving_average_time_label");
135 int hour
= temp
/3600;
136 int minutes
= ((temp
%3600)/60);
137 int seconds
= (temp
%60);
138 GString
*string
= g_string_new("");
140 g_string_append_printf(string
, "%i %s", hour
, (hour
== 1)?"hour":"hours");
144 if(hour
> 0) g_string_append(string
, ", ");
145 g_string_append_printf(string
, "%i %s", minutes
, (minutes
== 1)?"minute":"minutes");
149 if(minutes
> 0) g_string_append(string
, ", ");
150 g_string_append_printf(string
, "%i %s", seconds
, (seconds
== 1)?"second":"seconds");
153 gtk_label_set_text(GTK_LABEL(label
), string
->str
);
154 g_string_free(string
, TRUE
);
156 gtk_label_set_text(GTK_LABEL(label
), "n/a");
159 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "max_speed_label");
160 gtemp
= track
->max_speed
;
163 gchar
*string
= g_strdup_printf("%.2f km/h", gtemp
);
164 gtk_label_set_text(GTK_LABEL(label
), string
);
167 gtk_label_set_text(GTK_LABEL(label
), "n/a");
171 label
= (GtkWidget
*)gtk_builder_get_object(builder
, "num_points_label");
172 gtemp
= (double)g_list_length(track
->points
);
175 gchar
*string
= g_strdup_printf("%.0f points", gtemp
);
176 gtk_label_set_text(GTK_LABEL(label
), string
);
179 gtk_label_set_text(GTK_LABEL(label
), "n/a");
183 static void interface_map_plot_route(ChamplainView
*view
,struct Route
*route
)
187 route
->polygon
= champlain_polygon_new ();
188 for(iter
= g_list_first(route
->track
->points
); iter
; iter
= iter
->next
)
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 static void interface_map_make_waypoints(ChamplainView
*view
)
200 if(marker_layer
== NULL
)
202 marker_layer
= champlain_layer_new();
203 champlain_view_add_layer(view
, marker_layer
);
205 for(iter
= g_list_first(files
);iter
!= NULL
; iter
= g_list_next(iter
))
207 GpxFile
*file
= iter
->data
;
208 GList
*it
= g_list_first(file
->waypoints
);
211 GpxPoint
*p
= it
->data
;
212 ClutterActor
*marker
= champlain_marker_new_with_text(p
->name
, "Seric 12", NULL
, NULL
);
213 champlain_base_marker_set_position(CHAMPLAIN_BASE_MARKER(marker
), p
->lat_dec
, p
->lon_dec
);
214 champlain_marker_set_color (CHAMPLAIN_MARKER (marker
), &waypoint
);
215 clutter_container_add(CLUTTER_CONTAINER(marker_layer
), CLUTTER_ACTOR(marker
),NULL
);
216 clutter_actor_show(CLUTTER_ACTOR(marker_layer
));
217 it
= g_list_next(it
);
222 void route_set_visible(GtkCheckButton
*button
, gpointer user_data
)
224 gboolean active
= gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button
));
227 if(active_route
->visible
!= active
)
229 active_route
->visible
= active
;
231 champlain_polygon_show(active_route
->polygon
);
233 champlain_polygon_hide(active_route
->polygon
);
238 void routes_combo_changed_cb(GtkComboBox
*box
, gpointer user_data
)
240 GtkTreeModel
*model
= gtk_combo_box_get_model(box
);
242 if(gtk_combo_box_get_active_iter(box
, &iter
))
245 gtk_tree_model_get(model
, &iter
, 1, &route
, -1);
248 champlain_polygon_set_stroke_color (active_route
->polygon
, &normal_track_color
);
249 if(active_route
->visible
)
250 champlain_polygon_show(active_route
->polygon
);
255 ChamplainView
*view
= gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view
));
256 champlain_polygon_set_stroke_color (route
->polygon
, &highlight_track_color
);
259 champlain_polygon_show(route
->polygon
);
261 interface_update_heading(builder
, route
->track
);
263 if(route
->track
->top
&& route
->track
->bottom
)
265 champlain_view_ensure_visible(view
,
266 route
->track
->top
->lat_dec
, route
->track
->top
->lon_dec
,
267 route
->track
->bottom
->lat_dec
,route
->track
->bottom
->lon_dec
,
270 gpx_graph_set_track(gpx_graph
, route
->track
);
272 active_route
= route
;
274 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder
, "route_visible_check_button")),
275 active_route
->visible
);
279 static void smooth_factor_changed (GpxGraph
*graph
,
281 GtkSpinButton
*spinbutton
)
284 g_object_get(G_OBJECT(graph
), "smooth-factor", &zoom
, NULL
);
285 gtk_spin_button_set_value(spinbutton
, zoom
);
286 printf("smooth changed: %i\n", zoom
);
289 void smooth_factor_change_value_cb(GtkSpinButton
*spin
, gpointer user_data
)
291 int current
= gpx_graph_get_smooth_factor(gpx_graph
);
292 int new = gtk_spin_button_get_value_as_int(spin
);
293 printf("changed: %i %i\n", current
, new);
296 gpx_graph_set_smooth_factor(gpx_graph
, new);
299 static void map_zoom_changed (ChamplainView
*view
,
301 GtkSpinButton
*spinbutton
)
304 g_object_get(G_OBJECT(view
), "zoom-level", &zoom
, NULL
);
305 gtk_spin_button_set_value(spinbutton
, zoom
);
306 printf("zoom changed: %i\n", zoom
);
308 void map_zoom_level_change_value_cb(GtkSpinButton
*spin
, gpointer user_data
)
310 ChamplainView
*view
= gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view
));
311 int current
= champlain_view_get_zoom_level(view
);
312 int new = gtk_spin_button_get_value_as_int(spin
);
313 printf("changed: %i %i\n", current
, new);
316 champlain_view_set_zoom_level(view
, new);
320 #define MARKER_SIZE 10
321 static gboolean
graph_point_remove(ClutterActor
*marker
)
323 clutter_actor_destroy(marker
);
326 static void graph_point_clicked(double lat_dec
, double lon_dec
)
328 ChamplainView
*view
= gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view
));
329 if(marker_layer
== NULL
)
331 marker_layer
= champlain_layer_new();
332 champlain_view_add_layer(view
, marker_layer
);
335 GtkIconInfo
*ii
= gtk_icon_theme_lookup_icon(gtk_icon_theme_get_default(),
339 ClutterActor
*marker
= NULL
; if(ii
) {
340 gchar
*path2
= gtk_icon_info_get_filename(ii
);
343 marker
= champlain_marker_new_from_file(path2
, NULL
);
344 champlain_marker_set_draw_background (CHAMPLAIN_MARKER (marker
), FALSE
);
349 marker
= champlain_marker_new ();
351 /* Create the marker */
352 champlain_base_marker_set_position(CHAMPLAIN_BASE_MARKER(marker
), lat_dec
, lon_dec
);
353 champlain_marker_set_color (CHAMPLAIN_MARKER (marker
), &waypoint
);
354 clutter_container_add(CLUTTER_CONTAINER(marker_layer
), CLUTTER_ACTOR(marker
),NULL
);
355 clutter_actor_show(CLUTTER_ACTOR(marker_layer
));
356 g_timeout_add_seconds(1, graph_point_remove
, marker
);
359 /* Create the interface */
360 static void create_interface(void)
363 GError
*error
= NULL
;
364 const gchar
* const *dirs
= g_get_system_data_dirs();
367 builder
= gtk_builder_new();
368 gchar
*path
= g_build_filename(DATA_DIR
, "gpx-viewer.ui", NULL
);
369 if(!gtk_builder_add_from_file(builder
,path
, NULL
)){
370 g_error("Failed to create ui: %s\n", error
->message
);
372 printf("path: %s\n", path
);
375 /* Create map view */
376 champlain_view
= gtk_champlain_embed_new();
377 gtk_widget_set_size_request (champlain_view
, 640, 280);
378 gtk_paned_pack1(GTK_PANED(gtk_builder_get_object(builder
, "main_view_pane")), champlain_view
, TRUE
, TRUE
);
380 gpx_graph
= gpx_graph_new();
381 gtk_paned_pack2(GTK_PANED(gtk_builder_get_object(builder
, "main_view_pane")), GTK_WIDGET(gpx_graph
),FALSE
, TRUE
);
382 gtk_paned_set_position(GTK_PANED(gtk_builder_get_object(builder
, "main_view_pane")),200);
384 /* show the interface */
386 GTK_WIDGET(gtk_builder_get_object(builder
, "gpx_viewer_window")));
388 ChamplainView
*view
= gtk_champlain_embed_get_view (GTK_CHAMPLAIN_EMBED (champlain_view
));
389 g_object_set (G_OBJECT (view
), "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC
,
390 "zoom-level", 5, NULL
);
392 interface_map_make_waypoints(view
);
394 double lon1
= 1000, lon2
=-1000,lat1
=1000, lat2
=-1000;
395 for(fiter
= g_list_first(files
); fiter
; fiter
= g_list_next(fiter
))
397 GpxFile
*file
= fiter
->data
;
398 GpxTrack
*track
= file
->tracks
->data
;
399 GList
*iter
= g_list_first(file
->tracks
);
400 /* Plot all tracks, and get total bounding box */
402 GtkTreeModel
*model
= (GtkTreeModel
*)gtk_builder_get_object(builder
, "routes_store");
405 struct Route
*route
= g_new0(Route
, 1);
407 route
->track
= iter
->data
;
408 route
->visible
= TRUE
;
411 interface_map_plot_route(view
, route
);
412 if(track
->top
&& track
->top
->lat_dec
< lat1
) lat1
= track
->top
->lat_dec
;
413 if(track
->top
&& track
->top
->lon_dec
< lon1
) lon1
= track
->top
->lon_dec
;
415 if(track
->bottom
&& track
->bottom
->lat_dec
> lat2
) lat2
= track
->bottom
->lat_dec
;
416 if(track
->bottom
&& track
->bottom
->lon_dec
> lon2
) lon2
= track
->bottom
->lon_dec
;
418 gtk_list_store_append(GTK_LIST_STORE(model
), &liter
);
419 gtk_list_store_set(GTK_LIST_STORE(model
), &liter
, 0, (route
->track
->name
)?route
->track
->name
:"n/a", 1, route
, -1);
421 routes
= g_list_append(routes
, route
);
423 iter
= g_list_next(iter
);
426 /* Set up the zoom widget */
428 GtkWidget
*sp
= GTK_WIDGET(gtk_builder_get_object(builder
, "map_zoom_level"));
430 champlain_view_set_min_zoom_level(view
,1);
431 champlain_view_set_max_zoom_level(view
,18);
432 current
= champlain_view_get_zoom_level(view
);
433 gtk_spin_button_set_value(GTK_SPIN_BUTTON(sp
), (double)current
);
435 g_signal_connect (view
, "notify::zoom-level", G_CALLBACK (map_zoom_changed
),
438 /* Set up the smooth widget */
440 GtkWidget
*sp
= GTK_WIDGET(gtk_builder_get_object(builder
, "smooth_factor"));
442 current
= gpx_graph_get_smooth_factor(gpx_graph
);
443 gtk_spin_button_set_value(GTK_SPIN_BUTTON(sp
), (double)current
);
445 g_signal_connect (gpx_graph
, "notify::smooth-factor", G_CALLBACK (smooth_factor_changed
),
448 g_signal_connect (gpx_graph
, "point-clicked", G_CALLBACK(graph_point_clicked
), NULL
);
449 gtk_builder_connect_signals(builder
,NULL
);
450 /* Try to center the track on map correctly */
451 if(lon1
< 1000.0 && lon2
< 1000.0)
453 champlain_view_set_zoom_level(view
,8);
454 champlain_view_ensure_visible(view
,
459 gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_builder_get_object(builder
, "routes_combo")), 0);
462 int main (int argc
, char **argv
)
466 GOptionContext
*context
= NULL
;
467 GError
*error
= NULL
;
468 context
= g_option_context_new (_("GPX Viewer"));
471 g_option_context_add_group (context
, gtk_get_option_group (TRUE
));
472 g_option_context_parse (context
, &argc
, &argv
, &error
);
473 g_option_context_free(context
);
476 g_log(NULL
, G_LOG_LEVEL_ERROR
, "Failed to parse commandline options: %s", error
->message
);
481 g_thread_init (NULL
);
482 gtk_clutter_init (&argc
, &argv
);
483 /* If no file(s) given, ask for it */
487 GtkBuilder
*fbuilder
= gtk_builder_new();
489 gchar
*path
= g_build_filename(DATA_DIR
, "gpx-viewer-file-chooser.ui", NULL
);
490 if(!gtk_builder_add_from_file(fbuilder
,path
, NULL
)){
491 g_error("Failed to load gpx-viewer.ui");
496 GtkFileFilter
*filter
= (GtkFileFilter
*)gtk_builder_get_object(fbuilder
, "gpx_viewer_file_chooser_filter");
497 gtk_file_filter_add_pattern(filter
, "*.gpx");
500 dialog
= GTK_WIDGET(gtk_builder_get_object(fbuilder
, "gpx_viewer_file_chooser"));
501 switch(gtk_dialog_run(GTK_DIALOG(dialog
)))
505 GSList
*iter
, *choosen_files
= gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(dialog
));
506 for(iter
=choosen_files
; iter
; iter
= g_slist_next(iter
))
508 /* Try to open the gpx file */
509 GpxFile
*file
= gpx_file_new((gchar
*)iter
->data
);
510 files
= g_list_append(files
, file
);
512 g_slist_foreach(choosen_files
, (GFunc
)g_free
, NULL
);
513 g_slist_free(choosen_files
);
517 gtk_widget_destroy(dialog
);
518 g_object_unref(fbuilder
);
519 if(files
== NULL
) return EXIT_SUCCESS
;
522 for(i
=1; i
< argc
; i
++)
524 /* Try to open the gpx file */
525 GpxFile
*file
= gpx_file_new(argv
[i
]);
526 files
= g_list_append(files
, file
);
534 /* Destroy the files*/
535 g_list_foreach(files
, (GFunc
)g_object_unref
, NULL
);