2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5 * Copyright (C) 2005-2007, Alex Foobarian <foobarian@gmail.com>
6 * Copyright (C) 2007-2008, Quy Tonthat <qtonthat@gmail.com>
7 * Copyright (C) 2012-2014, Rob Norris <rw_norris@hotmail.com>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 #include <glib/gi18n.h>
39 #include "viktrwlayer.h"
40 #include "viktrwlayer_propwin.h"
43 #include "vikviewport.h" /* ugh */
45 #include <gdk-pixbuf/gdk-pixdata.h>
48 PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE,
49 PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE,
50 PROPWIN_GRAPH_TYPE_SPEED_TIME,
51 PROPWIN_GRAPH_TYPE_DISTANCE_TIME,
52 PROPWIN_GRAPH_TYPE_ELEVATION_TIME,
53 PROPWIN_GRAPH_TYPE_SPEED_DISTANCE,
54 PROPWIN_GRAPH_TYPE_END,
55 } VikPropWinGraphType_t;
57 /* (Hopefully!) Human friendly altitude grid sizes - note no fixed 'ratio' just numbers that look nice...*/
58 static const gdouble chunksa[] = {2.0, 5.0, 10.0, 15.0, 20.0,
59 25.0, 40.0, 50.0, 75.0, 100.0,
60 150.0, 200.0, 250.0, 375.0, 500.0,
61 750.0, 1000.0, 2000.0, 5000.0, 10000.0, 100000.0};
63 /* (Hopefully!) Human friendly gradient grid sizes - note no fixed 'ratio' just numbers that look nice...*/
64 static const gdouble chunksg[] = {1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
65 12.0, 15.0, 20.0, 25.0, 30.0, 35.0, 40.0, 45.0, 50.0, 75.0,
66 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
67 750.0, 1000.0, 10000.0, 100000.0};
68 // Normally gradients should range up to couple hundred precent at most,
69 // however there are possibilities of having points with no altitude after a point with a big altitude
70 // (such as places with invalid DEM values in otherwise mountainous regions) - thus giving huge negative gradients.
72 /* (Hopefully!) Human friendly grid sizes - note no fixed 'ratio' just numbers that look nice...*/
73 /* As need to cover walking speeds - have many low numbers (but also may go up to airplane speeds!) */
74 static const gdouble chunkss[] = {1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
75 15.0, 20.0, 25.0, 40.0, 50.0, 75.0,
76 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
77 750.0, 1000.0, 10000.0};
79 /* (Hopefully!) Human friendly distance grid sizes - note no fixed 'ratio' just numbers that look nice...*/
80 static const gdouble chunksd[] = {0.1, 0.2, 0.5, 1.0, 2.0, 3.0, 4.0, 5.0, 8.0, 10.0,
81 15.0, 20.0, 25.0, 40.0, 50.0, 75.0,
82 100.0, 150.0, 200.0, 250.0, 375.0, 500.0,
83 750.0, 1000.0, 10000.0};
85 // Time chunks in seconds
86 static const time_t chunkst[] = {
103 // Local show settings to restore on dialog opening
104 static gboolean show_dem = TRUE;
105 static gboolean show_alt_gps_speed = TRUE;
106 static gboolean show_gps_speed = TRUE;
107 static gboolean show_gradient_gps_speed = TRUE;
108 static gboolean show_dist_speed = FALSE;
109 static gboolean show_elev_speed = FALSE;
110 static gboolean show_elev_dem = FALSE;
111 static gboolean show_sd_gps_speed = TRUE;
113 typedef struct _propsaved {
118 typedef struct _propwidgets {
119 gboolean configure_dialog;
126 gint profile_width_old;
127 gint profile_height_old;
128 gint profile_width_offset;
129 gint profile_height_offset;
131 GtkWidget *w_comment;
132 GtkWidget *w_description;
133 GtkWidget *w_track_length;
134 GtkWidget *w_tp_count;
135 GtkWidget *w_segment_count;
136 GtkWidget *w_duptp_count;
137 GtkWidget *w_max_speed;
138 GtkWidget *w_avg_speed;
139 GtkWidget *w_mvg_speed;
140 GtkWidget *w_avg_dist;
141 GtkWidget *w_elev_range;
142 GtkWidget *w_elev_gain;
143 GtkWidget *w_time_start;
144 GtkWidget *w_time_end;
145 GtkWidget *w_time_dur;
147 GtkWidget *w_namelabel;
148 GtkWidget *w_number_distlabels;
149 GtkWidget *w_cur_dist; /*< Current distance */
150 GtkWidget *w_cur_elevation;
151 GtkWidget *w_cur_gradient_dist; /*< Current distance on gradient graph */
152 GtkWidget *w_cur_gradient_gradient; /*< Current gradient on gradient graph */
153 GtkWidget *w_cur_time; /*< Current track time */
154 GtkWidget *w_cur_time_real; /*< Actual time as on a clock */
155 GtkWidget *w_cur_speed;
156 GtkWidget *w_cur_dist_dist; /*< Current distance on distance graph */
157 GtkWidget *w_cur_dist_time; /*< Current track time on distance graph */
158 GtkWidget *w_cur_dist_time_real; // Clock time
159 GtkWidget *w_cur_elev_elev;
160 GtkWidget *w_cur_elev_time; // Track time
161 GtkWidget *w_cur_elev_time_real; // Clock time
162 GtkWidget *w_cur_speed_dist;
163 GtkWidget *w_cur_speed_speed;
164 GtkWidget *w_show_dem;
165 GtkWidget *w_show_alt_gps_speed;
166 GtkWidget *w_show_gps_speed;
167 GtkWidget *w_show_gradient_gps_speed;
168 GtkWidget *w_show_dist_speed;
169 GtkWidget *w_show_elev_speed;
170 GtkWidget *w_show_elev_dem;
171 GtkWidget *w_show_sd_gps_speed;
172 gdouble track_length;
173 gdouble track_length_inc_gaps;
174 PropSaved elev_graph_saved_img;
175 PropSaved gradient_graph_saved_img;
176 PropSaved speed_graph_saved_img;
177 PropSaved dist_graph_saved_img;
178 PropSaved elev_time_graph_saved_img;
179 PropSaved speed_dist_graph_saved_img;
181 GtkWidget *gradient_box;
182 GtkWidget *speed_box;
184 GtkWidget *elev_time_box;
185 GtkWidget *speed_dist_box;
187 gdouble *ats; // altitudes in time
188 gdouble min_altitude;
189 gdouble max_altitude;
190 gdouble draw_min_altitude;
191 gdouble draw_min_altitude_time;
192 gint cia; // Chunk size Index into Altitudes
193 gint ciat; // Chunk size Index into Altitudes / Time
194 // NB cia & ciat are normally same value but sometimes not due to differing methods of altitude array creation
195 // thus also have draw_min_altitude for each altitude graph type
197 gdouble min_gradient;
198 gdouble max_gradient;
199 gdouble draw_min_gradient;
200 gint cig; // Chunk size Index into Gradients
202 gdouble *speeds_dist;
205 gdouble draw_min_speed;
206 gdouble max_speed_dist;
207 gint cis; // Chunk size Index into Speeds
208 gint cisd; // Chunk size Index into Speed/Distance
210 gint cid; // Chunk size Index into Distance
211 VikTrackpoint *marker_tp;
212 gboolean is_marker_drawn;
213 VikTrackpoint *blob_tp;
214 gboolean is_blob_drawn;
216 gchar *tz; // TimeZone at track's location
219 static PropWidgets *prop_widgets_new()
221 PropWidgets *widgets = g_malloc0(sizeof(PropWidgets));
226 static void prop_widgets_free(PropWidgets *widgets)
228 if (widgets->elev_graph_saved_img.img)
229 g_object_unref(widgets->elev_graph_saved_img.img);
230 if (widgets->gradient_graph_saved_img.img)
231 g_object_unref(widgets->gradient_graph_saved_img.img);
232 if (widgets->speed_graph_saved_img.img)
233 g_object_unref(widgets->speed_graph_saved_img.img);
234 if (widgets->dist_graph_saved_img.img)
235 g_object_unref(widgets->dist_graph_saved_img.img);
236 if (widgets->elev_time_graph_saved_img.img)
237 g_object_unref(widgets->elev_time_graph_saved_img.img);
238 if (widgets->speed_dist_graph_saved_img.img)
239 g_object_unref(widgets->speed_dist_graph_saved_img.img);
240 if (widgets->altitudes)
241 g_free(widgets->altitudes);
242 if (widgets->gradients)
243 g_free(widgets->gradients);
245 g_free(widgets->speeds);
246 if (widgets->distances)
247 g_free(widgets->distances);
249 g_free(widgets->ats);
250 if (widgets->speeds_dist)
251 g_free(widgets->speeds_dist);
255 static void minmax_array(const gdouble *array, gdouble *min, gdouble *max, gboolean NO_ALT_TEST, gint PROFILE_WIDTH)
260 for ( i=0; i < PROFILE_WIDTH; i++ ) {
261 if ( NO_ALT_TEST || (array[i] != VIK_DEFAULT_ALTITUDE) ) {
262 if ( array[i] > *max )
264 if ( array[i] < *min )
274 * get_new_min_and_chunk_index:
275 * Returns via pointers:
276 * the new minimum value to be used for the graph
277 * the index in to the chunk sizes array (ci = Chunk Index)
279 static void get_new_min_and_chunk_index (gdouble mina, gdouble maxa, const gdouble *chunks, size_t chunky, gdouble *new_min, gint *ci)
281 /* Get unitized chunk */
282 /* Find suitable chunk index */
284 gdouble diff_chunk = (maxa - mina)/LINES;
286 /* Loop through to find best match */
287 while (diff_chunk > chunks[*ci]) {
289 /* Last Resort Check */
290 if ( *ci == chunky ) {
291 // Use previous value and exit loop
297 /* Ensure adjusted minimum .. maximum covers mina->maxa */
299 // Now work out adjusted minimum point to the nearest lowest chunk divisor value
300 // When negative ensure logic uses lowest value
302 *new_min = (gdouble) ( (gint)((mina - chunks[*ci]) / chunks[*ci]) * chunks[*ci] );
304 *new_min = (gdouble) ( (gint)(mina / chunks[*ci]) * chunks[*ci] );
306 // Range not big enough - as new minimum has lowered
307 if ((*new_min + (chunks[*ci] * LINES) < maxa)) {
308 // Next chunk should cover it
309 if ( *ci < chunky-1 ) {
311 // Remember to adjust the minimum too...
313 *new_min = (gdouble) ( (gint)((mina - chunks[*ci]) / chunks[*ci]) * chunks[*ci] );
315 *new_min = (gdouble) ( (gint)(mina / chunks[*ci]) * chunks[*ci] );
320 static guint get_time_chunk_index (time_t duration)
323 time_t myduration = duration / LINES;
325 // Search nearest chunk index
327 guint last_chunk = G_N_ELEMENTS(chunkst);
329 // Loop through to find best match
330 while (myduration > chunkst[ci]) {
333 if ( ci == last_chunk )
336 // Use previous value
346 static guint get_distance_chunk_index (gdouble length)
349 gdouble mylength = length / LINES;
351 // Search nearest chunk index
353 guint last_chunk = G_N_ELEMENTS(chunksd);
355 // Loop through to find best match
356 while (mylength > chunksd[ci]) {
359 if ( ci == last_chunk )
362 // Use previous value
369 static VikTrackpoint *set_center_at_graph_position(gdouble event_x,
378 VikTrackpoint *trackpoint;
379 gdouble x = event_x - img_width / 2 + PROFILE_WIDTH / 2 - MARGIN_X / 2;
382 if (x > PROFILE_WIDTH)
386 trackpoint = vik_track_get_closest_tp_by_percentage_time ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
388 trackpoint = vik_track_get_closest_tp_by_percentage_dist ( tr, (gdouble) x / PROFILE_WIDTH, NULL );
391 VikCoord coord = trackpoint->coord;
393 vik_viewport_set_center_coord ( vik_layers_panel_get_viewport(vlp), &coord, TRUE );
394 vik_layers_panel_emit_update ( vlp );
397 /* since vlp not set, vvp should be valid instead! */
399 vik_viewport_set_center_coord ( vvp, &coord, TRUE );
400 vik_layer_emit_update ( VIK_LAYER(vtl) );
407 * Returns whether the marker was drawn or not and whether the blob was drawn or not
409 static void save_image_and_draw_graph_marks (GtkWidget *image,
414 PropSaved *saved_img,
417 gboolean *marker_drawn,
418 gboolean *blob_drawn)
420 GdkPixmap *pix = NULL;
421 /* the pixmap = margin + graph area */
422 gtk_image_get_pixmap(GTK_IMAGE(image), &pix, NULL);
424 /* Restore previously saved image */
425 if (saved_img->saved) {
426 gdk_draw_image(GDK_DRAWABLE(pix), gc, saved_img->img, 0, 0, 0, 0, MARGIN_X+PROFILE_WIDTH, MARGIN_Y+PROFILE_HEIGHT);
427 saved_img->saved = FALSE;
430 // ATM always save whole image - as anywhere could have changed
432 gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN_X+PROFILE_WIDTH, MARGIN_Y+PROFILE_HEIGHT);
434 saved_img->img = gdk_drawable_copy_to_image(GDK_DRAWABLE(pix), saved_img->img, 0, 0, 0, 0, MARGIN_X+PROFILE_WIDTH, MARGIN_Y+PROFILE_HEIGHT);
435 saved_img->saved = TRUE;
437 if ((marker_x >= MARGIN_X) && (marker_x < (PROFILE_WIDTH + MARGIN_X))) {
438 gdk_draw_line (GDK_DRAWABLE(pix), gc, marker_x, MARGIN_Y, marker_x, PROFILE_HEIGHT + MARGIN_Y);
439 *marker_drawn = TRUE;
442 *marker_drawn = FALSE;
444 // Draw a square blob to indicate where we are on track for this graph
445 if ( (blob_x >= MARGIN_X) && (blob_x < (PROFILE_WIDTH + MARGIN_X)) && (blob_y < PROFILE_HEIGHT+MARGIN_Y) ) {
446 gdk_draw_rectangle (GDK_DRAWABLE(pix), gc, TRUE, blob_x-3, blob_y-3, 6, 6);
452 // Anywhere on image could have changed
453 if (*marker_drawn || *blob_drawn)
454 gtk_widget_queue_draw(image);
458 * Return the percentage of how far a trackpoint is a long a track via the time method
460 static gdouble tp_percentage_by_time ( VikTrack *tr, VikTrackpoint *trackpoint )
463 if (trackpoint == NULL)
465 time_t t_start, t_end, t_total;
466 t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
467 t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
468 t_total = t_end - t_start;
469 pc = (gdouble)(trackpoint->timestamp - t_start)/t_total;
474 * Return the percentage of how far a trackpoint is a long a track via the distance method
476 static gdouble tp_percentage_by_distance ( VikTrack *tr, VikTrackpoint *trackpoint, gdouble track_length )
479 if (trackpoint == NULL)
483 for (iter = tr->trackpoints->next; iter != NULL; iter = iter->next) {
484 dist += vik_coord_diff(&(VIK_TRACKPOINT(iter->data)->coord),
485 &(VIK_TRACKPOINT(iter->prev->data)->coord));
486 /* Assuming trackpoint is not a copy */
487 if (trackpoint == VIK_TRACKPOINT(iter->data))
491 pc = dist/track_length;
495 static void track_graph_click( GtkWidget *event_box, GdkEventButton *event, PropWidgets *widgets, VikPropWinGraphType_t graph_type )
497 gboolean is_time_graph =
498 ( graph_type == PROPWIN_GRAPH_TYPE_SPEED_TIME ||
499 graph_type == PROPWIN_GRAPH_TYPE_DISTANCE_TIME ||
500 graph_type == PROPWIN_GRAPH_TYPE_ELEVATION_TIME );
502 GtkAllocation allocation;
503 gtk_widget_get_allocation ( event_box, &allocation );
505 VikTrackpoint *trackpoint = set_center_at_graph_position(event->x, allocation.width, widgets->vtl, widgets->vlp, widgets->vvp, widgets->tr, is_time_graph, widgets->profile_width);
506 // Unable to get the point so give up
507 if ( trackpoint == NULL ) {
508 gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets->dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, FALSE);
512 widgets->marker_tp = trackpoint;
516 GtkWidget *window = gtk_widget_get_toplevel(GTK_WIDGET(event_box));
517 GtkWidget *graph_box;
518 PropSaved *graph_saved_img;
521 // Attempt to redraw marker on all graph types
523 for ( graphite = PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE;
524 graphite < PROPWIN_GRAPH_TYPE_END;
527 // Switch commonal variables to particular graph type
530 case PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE:
531 graph_box = widgets->elev_box;
532 graph_saved_img = &widgets->elev_graph_saved_img;
533 is_time_graph = FALSE;
535 case PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE:
536 graph_box = widgets->gradient_box;
537 graph_saved_img = &widgets->gradient_graph_saved_img;
538 is_time_graph = FALSE;
540 case PROPWIN_GRAPH_TYPE_SPEED_TIME:
541 graph_box = widgets->speed_box;
542 graph_saved_img = &widgets->speed_graph_saved_img;
543 is_time_graph = TRUE;
545 case PROPWIN_GRAPH_TYPE_DISTANCE_TIME:
546 graph_box = widgets->dist_box;
547 graph_saved_img = &widgets->dist_graph_saved_img;
548 is_time_graph = TRUE;
550 case PROPWIN_GRAPH_TYPE_ELEVATION_TIME:
551 graph_box = widgets->elev_time_box;
552 graph_saved_img = &widgets->elev_time_graph_saved_img;
553 is_time_graph = TRUE;
555 case PROPWIN_GRAPH_TYPE_SPEED_DISTANCE:
556 graph_box = widgets->speed_dist_box;
557 graph_saved_img = &widgets->speed_dist_graph_saved_img;
558 is_time_graph = FALSE;
562 // Commonal method of redrawing marker
565 child = gtk_container_get_children(GTK_CONTAINER(graph_box));
566 image = GTK_WIDGET(child->data);
569 pc = tp_percentage_by_time ( widgets->tr, trackpoint );
571 pc = tp_percentage_by_distance ( widgets->tr, trackpoint, widgets->track_length_inc_gaps );
574 gdouble marker_x = (pc * widgets->profile_width) + MARGIN_X;
575 save_image_and_draw_graph_marks(image,
577 gtk_widget_get_style(window)->black_gc,
578 -1, // Don't draw blob on clicks
581 widgets->profile_width,
582 widgets->profile_height,
583 &widgets->is_marker_drawn,
584 &widgets->is_blob_drawn);
590 gtk_dialog_set_response_sensitive(GTK_DIALOG(widgets->dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, widgets->is_marker_drawn);
593 static gboolean track_profile_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
595 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_ELEVATION_DISTANCE);
596 return TRUE; /* don't call other (further) callbacks */
599 static gboolean track_gradient_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
601 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_GRADIENT_DISTANCE);
602 return TRUE; /* don't call other (further) callbacks */
605 static gboolean track_vt_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
607 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_SPEED_TIME);
608 return TRUE; /* don't call other (further) callbacks */
611 static gboolean track_dt_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
613 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_DISTANCE_TIME);
614 return TRUE; /* don't call other (further) callbacks */
617 static gboolean track_et_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
619 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_ELEVATION_TIME);
620 return TRUE; /* don't call other (further) callbacks */
623 static gboolean track_sd_click( GtkWidget *event_box, GdkEventButton *event, gpointer ptr )
625 track_graph_click(event_box, event, ptr, PROPWIN_GRAPH_TYPE_SPEED_DISTANCE);
626 return TRUE; /* don't call other (further) callbacks */
630 * Calculate y position for blob on elevation graph
632 static gint blobby_altitude ( gdouble x_blob, PropWidgets *widgets )
634 gint ix = (gint)x_blob;
635 // Ensure ix is inbounds
636 if (ix == widgets->profile_width)
639 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->altitudes[ix]-widgets->draw_min_altitude)/(chunksa[widgets->cia]*LINES);
645 * Calculate y position for blob on gradient graph
647 static gint blobby_gradient ( gdouble x_blob, PropWidgets *widgets )
649 gint ix = (gint)x_blob;
650 // Ensure ix is inbounds
651 if (ix == widgets->profile_width)
654 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->gradients[ix]-widgets->draw_min_gradient)/(chunksg[widgets->cig]*LINES);
660 * Calculate y position for blob on speed graph
662 static gint blobby_speed ( gdouble x_blob, PropWidgets *widgets )
664 gint ix = (gint)x_blob;
665 // Ensure ix is inbounds
666 if (ix == widgets->profile_width)
669 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->speeds[ix]-widgets->draw_min_speed)/(chunkss[widgets->cis]*LINES);
675 * Calculate y position for blob on distance graph
677 static gint blobby_distance ( gdouble x_blob, PropWidgets *widgets )
679 gint ix = (gint)x_blob;
680 // Ensure ix is inbounds
681 if (ix == widgets->profile_width)
684 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->distances[ix])/(chunksd[widgets->cid]*LINES);
685 //NB min distance is always 0, so no need to subtract that from this ______/
691 * Calculate y position for blob on elevation/time graph
693 static gint blobby_altitude_time ( gdouble x_blob, PropWidgets *widgets )
695 gint ix = (gint)x_blob;
696 // Ensure ix is inbounds
697 if (ix == widgets->profile_width)
700 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->ats[ix]-widgets->draw_min_altitude_time)/(chunksa[widgets->ciat]*LINES);
705 * Calculate y position for blob on speed/dist graph
707 static gint blobby_speed_dist ( gdouble x_blob, PropWidgets *widgets )
709 gint ix = (gint)x_blob;
710 // Ensure ix is inbounds
711 if (ix == widgets->profile_width)
714 gint y_blob = widgets->profile_height-widgets->profile_height*(widgets->speeds_dist[ix]-widgets->draw_min_speed)/(chunkss[widgets->cisd]*LINES);
720 void track_profile_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
722 int mouse_x, mouse_y;
723 GdkModifierType state;
726 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
730 GtkAllocation allocation;
731 gtk_widget_get_allocation ( event_box, &allocation );
733 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
736 if (x > widgets->profile_width)
737 x = widgets->profile_width;
739 gdouble meters_from_start;
740 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
741 if (trackpoint && widgets->w_cur_dist) {
742 static gchar tmp_buf[20];
743 vik_units_distance_t dist_units = a_vik_get_units_distance ();
744 switch (dist_units) {
745 case VIK_UNITS_DISTANCE_KILOMETRES:
746 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
748 case VIK_UNITS_DISTANCE_MILES:
749 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
751 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
752 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(meters_from_start) );
755 g_critical("Houston, we've had a problem. distance=%d", dist_units);
757 gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist), tmp_buf);
760 // Show track elevation for this position - to the nearest whole number
761 if (trackpoint && widgets->w_cur_elevation) {
762 static gchar tmp_buf[20];
763 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
764 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d ft", (int)VIK_METERS_TO_FEET(trackpoint->altitude));
766 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d m", (int)trackpoint->altitude);
767 gtk_label_set_text(GTK_LABEL(widgets->w_cur_elevation), tmp_buf);
770 widgets->blob_tp = trackpoint;
772 if ( widgets->altitudes == NULL )
775 GtkWidget *window = gtk_widget_get_toplevel (event_box);
776 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
777 GtkWidget *image = GTK_WIDGET(child->data);
779 gint y_blob = blobby_altitude (x, widgets);
781 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
782 if (widgets->is_marker_drawn) {
783 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
785 marker_x = (pc * widgets->profile_width) + MARGIN_X;
789 save_image_and_draw_graph_marks (image,
791 gtk_widget_get_style(window)->black_gc,
794 &widgets->elev_graph_saved_img,
795 widgets->profile_width,
796 widgets->profile_height,
797 &widgets->is_marker_drawn,
798 &widgets->is_blob_drawn);
803 void track_gradient_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
805 int mouse_x, mouse_y;
806 GdkModifierType state;
809 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
813 GtkAllocation allocation;
814 gtk_widget_get_allocation ( event_box, &allocation );
816 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
819 if (x > widgets->profile_width)
820 x = widgets->profile_width;
822 gdouble meters_from_start;
823 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
824 if (trackpoint && widgets->w_cur_gradient_dist) {
825 static gchar tmp_buf[20];
826 vik_units_distance_t dist_units = a_vik_get_units_distance ();
827 switch (dist_units) {
828 case VIK_UNITS_DISTANCE_KILOMETRES:
829 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
831 case VIK_UNITS_DISTANCE_MILES:
832 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
834 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
835 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(meters_from_start) );
838 g_critical("Houston, we've had a problem. distance=%d", dist_units);
840 gtk_label_set_text(GTK_LABEL(widgets->w_cur_gradient_dist), tmp_buf);
843 // Show track gradient for this position - to the nearest whole number
844 if (trackpoint && widgets->w_cur_gradient_gradient) {
845 static gchar tmp_buf[20];
847 double gradient = widgets->gradients[(int) x];
849 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d%%", (int)gradient);
850 gtk_label_set_text(GTK_LABEL(widgets->w_cur_gradient_gradient), tmp_buf);
853 widgets->blob_tp = trackpoint;
855 if ( widgets->gradients == NULL )
858 GtkWidget *window = gtk_widget_get_toplevel (event_box);
859 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
860 GtkWidget *image = GTK_WIDGET(child->data);
862 gint y_blob = blobby_gradient (x, widgets);
864 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
865 if (widgets->is_marker_drawn) {
866 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
868 marker_x = (pc * widgets->profile_width) + MARGIN_X;
872 save_image_and_draw_graph_marks (image,
874 gtk_widget_get_style(window)->black_gc,
877 &widgets->gradient_graph_saved_img,
878 widgets->profile_width,
879 widgets->profile_height,
880 &widgets->is_marker_drawn,
881 &widgets->is_blob_drawn);
887 static void time_label_update (GtkWidget *widget, time_t seconds_from_start)
889 static gchar tmp_buf[20];
890 guint h = seconds_from_start/3600;
891 guint m = (seconds_from_start - h*3600)/60;
892 guint s = seconds_from_start - (3600*h) - (60*m);
893 g_snprintf(tmp_buf, sizeof(tmp_buf), "%02d:%02d:%02d", h, m, s);
894 gtk_label_set_text(GTK_LABEL(widget), tmp_buf);
898 static void real_time_label_update ( PropWidgets *widgets, GtkWidget *widget, VikTrackpoint *trackpoint)
900 static gchar tmp_buf[64];
901 if ( trackpoint->has_timestamp ) {
902 // Alternatively could use %c format but I prefer a slightly more compact form here
903 // The full date can of course be seen on the Statistics tab
904 strftime (tmp_buf, sizeof(tmp_buf), "%X %x %Z", localtime(&(trackpoint->timestamp)));
907 g_snprintf (tmp_buf, sizeof(tmp_buf), _("No Data"));
908 gtk_label_set_text(GTK_LABEL(widget), tmp_buf);
911 void track_vt_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
913 int mouse_x, mouse_y;
914 GdkModifierType state;
917 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
921 GtkAllocation allocation;
922 gtk_widget_get_allocation ( event_box, &allocation );
923 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
926 if (x > widgets->profile_width)
927 x = widgets->profile_width;
929 time_t seconds_from_start;
930 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
931 if (trackpoint && widgets->w_cur_time) {
932 time_label_update ( widgets->w_cur_time, seconds_from_start );
935 if (trackpoint && widgets->w_cur_time_real) {
936 real_time_label_update ( widgets, widgets->w_cur_time_real, trackpoint );
940 // Ensure ix is inbounds
941 if (ix == widgets->profile_width)
944 // Show track speed for this position
945 if (trackpoint && widgets->w_cur_speed) {
946 static gchar tmp_buf[20];
947 // Even if GPS speed available (trackpoint->speed), the text will correspond to the speed map shown
948 // No conversions needed as already in appropriate units
949 vik_units_speed_t speed_units = a_vik_get_units_speed ();
950 switch (speed_units) {
951 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
952 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f kph"), widgets->speeds[ix]);
954 case VIK_UNITS_SPEED_MILES_PER_HOUR:
955 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f mph"), widgets->speeds[ix]);
957 case VIK_UNITS_SPEED_KNOTS:
958 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f knots"), widgets->speeds[ix]);
961 // VIK_UNITS_SPEED_METRES_PER_SECOND:
962 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f m/s"), widgets->speeds[ix]);
965 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed), tmp_buf);
968 widgets->blob_tp = trackpoint;
970 if ( widgets->speeds == NULL )
973 GtkWidget *window = gtk_widget_get_toplevel (event_box);
974 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
975 GtkWidget *image = GTK_WIDGET(child->data);
977 gint y_blob = blobby_speed (x, widgets);
979 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
980 if (widgets->is_marker_drawn) {
981 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
983 marker_x = (pc * widgets->profile_width) + MARGIN_X;
987 save_image_and_draw_graph_marks (image,
989 gtk_widget_get_style(window)->black_gc,
992 &widgets->speed_graph_saved_img,
993 widgets->profile_width,
994 widgets->profile_height,
995 &widgets->is_marker_drawn,
996 &widgets->is_blob_drawn);
1002 * Update labels and blob marker on mouse moves in the distance/time graph
1004 void track_dt_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
1006 int mouse_x, mouse_y;
1007 GdkModifierType state;
1010 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
1014 GtkAllocation allocation;
1015 gtk_widget_get_allocation ( event_box, &allocation );
1017 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
1020 if (x > widgets->profile_width)
1021 x = widgets->profile_width;
1023 time_t seconds_from_start;
1024 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
1025 if (trackpoint && widgets->w_cur_dist_time) {
1026 time_label_update ( widgets->w_cur_dist_time, seconds_from_start );
1029 if (trackpoint && widgets->w_cur_dist_time_real) {
1030 real_time_label_update ( widgets, widgets->w_cur_dist_time_real, trackpoint );
1034 // Ensure ix is inbounds
1035 if (ix == widgets->profile_width)
1038 if (trackpoint && widgets->w_cur_dist_dist) {
1039 static gchar tmp_buf[20];
1040 switch ( a_vik_get_units_distance () ) {
1041 case VIK_UNITS_DISTANCE_MILES:
1042 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", widgets->distances[ix]);
1044 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1045 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", widgets->distances[ix]);
1048 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", widgets->distances[ix]);
1051 gtk_label_set_text(GTK_LABEL(widgets->w_cur_dist_dist), tmp_buf);
1054 widgets->blob_tp = trackpoint;
1056 if ( widgets->distances == NULL )
1059 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1060 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1061 GtkWidget *image = GTK_WIDGET(child->data);
1063 gint y_blob = blobby_distance (x, widgets);
1065 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1066 if (widgets->is_marker_drawn) {
1067 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
1069 marker_x = (pc * widgets->profile_width) + MARGIN_X;
1073 save_image_and_draw_graph_marks (image,
1075 gtk_widget_get_style(window)->black_gc,
1078 &widgets->dist_graph_saved_img,
1079 widgets->profile_width,
1080 widgets->profile_height,
1081 &widgets->is_marker_drawn,
1082 &widgets->is_blob_drawn);
1088 * Update labels and blob marker on mouse moves in the elevation/time graph
1090 void track_et_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
1092 int mouse_x, mouse_y;
1093 GdkModifierType state;
1096 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
1100 GtkAllocation allocation;
1101 gtk_widget_get_allocation ( event_box, &allocation );
1103 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
1106 if (x > widgets->profile_width)
1107 x = widgets->profile_width;
1109 time_t seconds_from_start;
1110 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, (gdouble) x / widgets->profile_width, &seconds_from_start );
1111 if (trackpoint && widgets->w_cur_elev_time) {
1112 time_label_update ( widgets->w_cur_elev_time, seconds_from_start );
1115 if (trackpoint && widgets->w_cur_elev_time_real) {
1116 real_time_label_update ( widgets, widgets->w_cur_elev_time_real, trackpoint );
1120 // Ensure ix is inbounds
1121 if (ix == widgets->profile_width)
1124 if (trackpoint && widgets->w_cur_elev_elev) {
1125 static gchar tmp_buf[20];
1126 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
1127 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d ft", (int)VIK_METERS_TO_FEET(trackpoint->altitude));
1129 g_snprintf(tmp_buf, sizeof(tmp_buf), "%d m", (int)trackpoint->altitude);
1130 gtk_label_set_text(GTK_LABEL(widgets->w_cur_elev_elev), tmp_buf);
1133 widgets->blob_tp = trackpoint;
1135 if ( widgets->ats == NULL )
1138 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1139 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1140 GtkWidget *image = GTK_WIDGET(child->data);
1142 gint y_blob = blobby_altitude_time (x, widgets);
1144 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1145 if (widgets->is_marker_drawn) {
1146 gdouble pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
1148 marker_x = (pc * widgets->profile_width) + MARGIN_X;
1152 save_image_and_draw_graph_marks (image,
1154 gtk_widget_get_style(window)->black_gc,
1157 &widgets->elev_time_graph_saved_img,
1158 widgets->profile_width,
1159 widgets->profile_height,
1160 &widgets->is_marker_drawn,
1161 &widgets->is_blob_drawn);
1166 void track_sd_move( GtkWidget *event_box, GdkEventMotion *event, PropWidgets *widgets )
1168 int mouse_x, mouse_y;
1169 GdkModifierType state;
1172 gdk_window_get_pointer (event->window, &mouse_x, &mouse_y, &state);
1176 GtkAllocation allocation;
1177 gtk_widget_get_allocation ( event_box, &allocation );
1179 gdouble x = mouse_x - allocation.width / 2 + widgets->profile_width / 2 - MARGIN_X / 2;
1182 if (x > widgets->profile_width)
1183 x = widgets->profile_width;
1185 gdouble meters_from_start;
1186 VikTrackpoint *trackpoint = vik_track_get_closest_tp_by_percentage_dist ( widgets->tr, (gdouble) x / widgets->profile_width, &meters_from_start );
1187 if (trackpoint && widgets->w_cur_speed_dist) {
1188 static gchar tmp_buf[20];
1189 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1190 switch (dist_units) {
1191 case VIK_UNITS_DISTANCE_KILOMETRES:
1192 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", meters_from_start/1000.0);
1194 case VIK_UNITS_DISTANCE_MILES:
1195 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(meters_from_start) );
1197 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1198 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(meters_from_start) );
1201 g_critical("Houston, we've had a problem. distance=%d", dist_units);
1203 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed_dist), tmp_buf);
1207 // Ensure ix is inbounds
1208 if (ix == widgets->profile_width)
1211 if ( widgets->speeds_dist == NULL )
1214 // Show track speed for this position
1215 if (widgets->w_cur_speed_speed) {
1216 static gchar tmp_buf[20];
1217 // Even if GPS speed available (trackpoint->speed), the text will correspond to the speed map shown
1218 // No conversions needed as already in appropriate units
1219 vik_units_speed_t speed_units = a_vik_get_units_speed ();
1220 switch (speed_units) {
1221 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1222 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f kph"), widgets->speeds_dist[ix]);
1224 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1225 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f mph"), widgets->speeds_dist[ix]);
1227 case VIK_UNITS_SPEED_KNOTS:
1228 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f knots"), widgets->speeds_dist[ix]);
1231 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1232 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%.1f m/s"), widgets->speeds_dist[ix]);
1235 gtk_label_set_text(GTK_LABEL(widgets->w_cur_speed_speed), tmp_buf);
1238 widgets->blob_tp = trackpoint;
1240 GtkWidget *window = gtk_widget_get_toplevel (event_box);
1241 GList *child = gtk_container_get_children(GTK_CONTAINER(event_box));
1242 GtkWidget *image = GTK_WIDGET(child->data);
1244 gint y_blob = blobby_speed_dist (x, widgets);
1246 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
1247 if (widgets->is_marker_drawn) {
1248 gdouble pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
1250 marker_x = (pc * widgets->profile_width) + MARGIN_X;
1254 save_image_and_draw_graph_marks (image,
1256 gtk_widget_get_style(window)->black_gc,
1259 &widgets->speed_dist_graph_saved_img,
1260 widgets->profile_width,
1261 widgets->profile_height,
1262 &widgets->is_marker_drawn,
1263 &widgets->is_blob_drawn);
1269 * Draws DEM points and a respresentative speed on the supplied pixmap
1270 * (which is the elevations graph)
1272 static void draw_dem_alt_speed_dist(VikTrack *tr,
1278 gdouble max_speed_in,
1287 gdouble max_speed = 0;
1288 gdouble total_length = vik_track_get_length_including_gaps(tr);
1290 // Calculate the max speed factor
1292 max_speed = max_speed_in * 110 / 100;
1295 gint h2 = height + MARGIN_Y; // Adjust height for x axis labelling offset
1296 gint achunk = chunksa[cia]*LINES;
1298 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
1300 dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1301 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1302 x = (width * dist)/total_length + margin;
1304 gint16 elev = a_dems_get_elev_by_coord(&(VIK_TRACKPOINT(iter->data)->coord), VIK_DEM_INTERPOL_BEST);
1306 if ( elev != VIK_DEM_INVALID_ELEVATION ) {
1307 // Convert into height units
1308 if (a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET)
1309 elev = VIK_METERS_TO_FEET(elev);
1310 // No conversion needed if already in metres
1312 // consider chunk size
1313 int y_alt = h2 - ((height * elev)/achunk );
1314 gdk_draw_rectangle(GDK_DRAWABLE(pix), alt_gc, TRUE, x-2, y_alt-2, 4, 4);
1318 // This is just a speed indicator - no actual values can be inferred by user
1319 if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
1320 int y_speed = h2 - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
1321 gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
1330 * A common way to draw the grid with y axis labels
1333 static void draw_grid_y ( GtkWidget *window, GtkWidget *image, PropWidgets *widgets, GdkPixmap *pix, gchar *ss, gint i )
1335 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
1337 pango_layout_set_alignment (pl, PANGO_ALIGN_RIGHT);
1338 pango_layout_set_font_description (pl, gtk_widget_get_style(window)->font_desc);
1340 gchar *label_markup = g_strdup_printf ( "<span size=\"small\">%s</span>", ss );
1341 pango_layout_set_markup ( pl, label_markup, -1 );
1342 g_free ( label_markup );
1345 pango_layout_get_pixel_size ( pl, &w, &h );
1347 gdk_draw_layout ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->fg_gc[0],
1349 CLAMP((int)i*widgets->profile_height/LINES - h/2 + MARGIN_Y, 0, widgets->profile_height-h+MARGIN_Y),
1351 g_object_unref ( G_OBJECT ( pl ) );
1353 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[0],
1354 MARGIN_X, MARGIN_Y + widgets->profile_height/LINES * i,
1355 MARGIN_X + widgets->profile_width, MARGIN_Y + widgets->profile_height/LINES * i );
1361 * A common way to draw the grid with x axis labels for time graphs
1364 static void draw_grid_x_time ( GtkWidget *window, GtkWidget *image, PropWidgets *widgets, GdkPixmap *pix, guint ii, guint tt, guint xx )
1366 gchar *label_markup = NULL;
1373 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", tt/60, _("mins") );
1380 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60), _("h") );
1386 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60*24), _("d") );
1391 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60*24*7), _("w") );
1395 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", (gdouble)tt/(60*60*24*28), _("M") );
1400 if ( label_markup ) {
1402 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
1403 pango_layout_set_font_description (pl, gtk_widget_get_style(window)->font_desc);
1405 pango_layout_set_markup ( pl, label_markup, -1 );
1406 g_free ( label_markup );
1408 pango_layout_get_pixel_size ( pl, &ww, &hh );
1410 gdk_draw_layout ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->fg_gc[0],
1411 MARGIN_X+xx-ww/2, MARGIN_Y/2-hh/2, pl );
1412 g_object_unref ( G_OBJECT ( pl ) );
1415 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[0],
1416 MARGIN_X+xx, MARGIN_Y, MARGIN_X+xx, MARGIN_Y+widgets->profile_height );
1420 * draw_grid_x_distance:
1422 * A common way to draw the grid with x axis labels for distance graphs
1425 static void draw_grid_x_distance ( GtkWidget *window, GtkWidget *image, PropWidgets *widgets, GdkPixmap *pix, guint ii, gdouble dd, guint xx, vik_units_distance_t dist_units )
1427 gchar *label_markup = NULL;
1428 switch ( dist_units ) {
1429 case VIK_UNITS_DISTANCE_MILES:
1431 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", (guint)dd, _("miles") );
1433 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", dd, _("miles") );
1435 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1437 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", (guint)dd, _("NM") );
1439 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", dd, _("NM") );
1442 // VIK_UNITS_DISTANCE_KILOMETRES:
1444 label_markup = g_strdup_printf ( "<span size=\"small\">%d %s</span>", (guint)dd, _("km") );
1446 label_markup = g_strdup_printf ( "<span size=\"small\">%.1f %s</span>", dd, _("km") );
1450 if ( label_markup ) {
1451 PangoLayout *pl = gtk_widget_create_pango_layout (GTK_WIDGET(image), NULL);
1452 pango_layout_set_font_description (pl, gtk_widget_get_style(window)->font_desc);
1454 pango_layout_set_markup ( pl, label_markup, -1 );
1455 g_free ( label_markup );
1457 pango_layout_get_pixel_size ( pl, &ww, &hh );
1459 gdk_draw_layout ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->fg_gc[0],
1460 MARGIN_X+xx-ww/2, MARGIN_Y/2-hh/2, pl );
1461 g_object_unref ( G_OBJECT ( pl ) );
1464 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[0],
1465 MARGIN_X+xx, MARGIN_Y, MARGIN_X+xx, MARGIN_Y+widgets->profile_height );
1469 * clear the images (scale texts & actual graph)
1471 static void clear_images (GdkPixmap *pix, GtkWidget *window, PropWidgets *widgets)
1473 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->bg_gc[0],
1474 TRUE, 0, 0, widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y);
1475 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->mid_gc[0],
1476 TRUE, 0, 0, widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y);
1482 static void draw_distance_divisions ( GtkWidget *window, GtkWidget *image, GdkPixmap *pix, PropWidgets *widgets, vik_units_distance_t dist_units )
1484 // Set to display units from length in metres.
1485 gdouble length = widgets->track_length_inc_gaps;
1486 switch (dist_units) {
1487 case VIK_UNITS_DISTANCE_MILES:
1488 length = VIK_METERS_TO_MILES(length);
1490 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1491 length = VIK_METERS_TO_NAUTICAL_MILES(length);
1495 length = length/1000.0;
1498 guint index = get_distance_chunk_index ( length );
1499 gdouble dist_per_pixel = length/widgets->profile_width;
1501 for (guint i=1; chunksd[index]*i <= length; i++) {
1502 draw_grid_x_distance ( window, image, widgets, pix, index, chunksd[index]*i, (guint)(chunksd[index]*i/dist_per_pixel), dist_units );
1507 * Draw just the height profile image
1509 static void draw_elevations (GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1516 // Free previous allocation
1517 if ( widgets->altitudes )
1518 g_free ( widgets->altitudes );
1520 widgets->altitudes = vik_track_make_elevation_map ( tr, widgets->profile_width );
1522 if ( widgets->altitudes == NULL )
1525 // Convert into appropriate units
1526 vik_units_height_t height_units = a_vik_get_units_height ();
1527 if ( height_units == VIK_UNITS_HEIGHT_FEET ) {
1528 // Convert altitudes into feet units
1529 for ( i = 0; i < widgets->profile_width; i++ ) {
1530 widgets->altitudes[i] = VIK_METERS_TO_FEET(widgets->altitudes[i]);
1533 // Otherwise leave in metres
1535 minmax_array(widgets->altitudes, &widgets->min_altitude, &widgets->max_altitude, TRUE, widgets->profile_width);
1537 get_new_min_and_chunk_index (widgets->min_altitude, widgets->max_altitude, chunksa, G_N_ELEMENTS(chunksa), &widgets->draw_min_altitude, &widgets->cia);
1540 gdouble mina = widgets->draw_min_altitude;
1542 GtkWidget *window = gtk_widget_get_toplevel (widgets->elev_box);
1543 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
1545 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1547 no_alt_info = gdk_gc_new ( gtk_widget_get_window(window) );
1548 gdk_color_parse ( "yellow", &color );
1549 gdk_gc_set_rgb_fg_color ( no_alt_info, &color);
1551 // Reset before redrawing
1552 clear_images (pix, window, widgets);
1555 for (i=0; i<=LINES; i++) {
1558 switch (height_units) {
1559 case VIK_UNITS_HEIGHT_METRES:
1560 sprintf(s, "%8dm", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
1562 case VIK_UNITS_HEIGHT_FEET:
1563 // NB values already converted into feet
1564 sprintf(s, "%8dft", (int)(mina + (LINES-i)*chunksa[widgets->cia]));
1568 g_critical("Houston, we've had a problem. height=%d", height_units);
1571 draw_grid_y ( window, image, widgets, pix, s, i );
1574 draw_distance_divisions ( window, image, pix, widgets, a_vik_get_units_distance() );
1576 /* draw elevations */
1577 guint height = MARGIN_Y+widgets->profile_height;
1578 for ( i = 0; i < widgets->profile_width; i++ )
1579 if ( widgets->altitudes[i] == VIK_DEFAULT_ALTITUDE )
1580 gdk_draw_line ( GDK_DRAWABLE(pix), no_alt_info,
1581 i + MARGIN_X, MARGIN_Y, i + MARGIN_X, height );
1583 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1584 i + MARGIN_X, height, i + MARGIN_X, height-widgets->profile_height*(widgets->altitudes[i]-mina)/(chunksa[widgets->cia]*LINES) );
1586 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_dem)) ||
1587 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)) ) {
1589 GdkGC *dem_alt_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1590 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1592 gdk_color_parse ( "green", &color );
1593 gdk_gc_set_rgb_fg_color ( dem_alt_gc, &color);
1595 gdk_color_parse ( "red", &color );
1596 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1598 // Ensure somekind of max speed when not set
1599 if ( widgets->max_speed < 0.01 )
1600 widgets->max_speed = vik_track_get_max_speed(tr);
1602 draw_dem_alt_speed_dist(tr,
1607 widgets->max_altitude - mina,
1610 widgets->profile_width,
1611 widgets->profile_height,
1613 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_dem)),
1614 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)));
1616 g_object_unref ( G_OBJECT(dem_alt_gc) );
1617 g_object_unref ( G_OBJECT(gps_speed_gc) );
1621 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
1623 g_object_unref ( G_OBJECT(pix) );
1624 g_object_unref ( G_OBJECT(no_alt_info) );
1628 * Draws representative speed on the supplied pixmap
1629 * (which is the gradients graph)
1631 static void draw_speed_dist(VikTrack *tr,
1634 gdouble max_speed_in,
1641 gdouble max_speed = 0;
1642 gdouble total_length = vik_track_get_length_including_gaps(tr);
1644 // Calculate the max speed factor
1646 max_speed = max_speed_in * 110 / 100;
1649 for (iter = tr->trackpoints->next; iter; iter = iter->next) {
1651 dist += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
1652 &(VIK_TRACKPOINT(iter->prev->data)->coord) );
1653 x = (width * dist)/total_length + MARGIN_X;
1655 // This is just a speed indicator - no actual values can be inferred by user
1656 if (!isnan(VIK_TRACKPOINT(iter->data)->speed)) {
1657 int y_speed = height - (height * VIK_TRACKPOINT(iter->data)->speed)/max_speed;
1658 gdk_draw_rectangle(GDK_DRAWABLE(pix), speed_gc, TRUE, x-2, y_speed-2, 4, 4);
1665 * Draw just the gradient image
1667 static void draw_gradients (GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1671 // Free previous allocation
1672 if ( widgets->gradients )
1673 g_free ( widgets->gradients );
1675 widgets->gradients = vik_track_make_gradient_map ( tr, widgets->profile_width );
1677 if ( widgets->gradients == NULL )
1680 minmax_array(widgets->gradients, &widgets->min_gradient, &widgets->max_gradient, TRUE, widgets->profile_width);
1682 get_new_min_and_chunk_index (widgets->min_gradient, widgets->max_gradient, chunksg, G_N_ELEMENTS(chunksg), &widgets->draw_min_gradient, &widgets->cig);
1685 gdouble mina = widgets->draw_min_gradient;
1687 GtkWidget *window = gtk_widget_get_toplevel (widgets->gradient_box);
1688 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
1690 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1692 // Reset before redrawing
1693 clear_images (pix, window, widgets);
1696 for (i=0; i<=LINES; i++) {
1699 sprintf(s, "%8d%%", (int)(mina + (LINES-i)*chunksg[widgets->cig]));
1701 draw_grid_y ( window, image, widgets, pix, s, i );
1704 draw_distance_divisions ( window, image, pix, widgets, a_vik_get_units_distance() );
1706 /* draw gradients */
1707 guint height = widgets->profile_height + MARGIN_Y;
1708 for ( i = 0; i < widgets->profile_width; i++ )
1709 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1710 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->gradients[i]-mina)/(chunksg[widgets->cig]*LINES) );
1712 if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_gradient_gps_speed)) ) {
1713 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1716 gdk_color_parse ( "red", &color );
1717 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1719 // Ensure somekind of max speed when not set
1720 if ( widgets->max_speed < 0.01 )
1721 widgets->max_speed = vik_track_get_max_speed(tr);
1727 widgets->profile_width,
1728 widgets->profile_height,
1730 gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed)));
1732 g_object_unref ( G_OBJECT(gps_speed_gc) );
1736 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
1738 g_object_unref ( G_OBJECT(pix) );
1741 static void draw_time_lines ( GtkWidget *window, GtkWidget *image, GdkPixmap *pix, PropWidgets *widgets )
1743 guint index = get_time_chunk_index ( widgets->duration );
1744 gdouble time_per_pixel = (gdouble)(widgets->duration)/widgets->profile_width;
1746 // If stupidly long track in time - don't bother trying to draw grid lines
1747 if ( widgets->duration > chunkst[G_N_ELEMENTS(chunkst)-1]*LINES*LINES )
1750 for (guint i=1; chunkst[index]*i <= widgets->duration; i++) {
1751 draw_grid_x_time ( window, image, widgets, pix, index, chunkst[index]*i, (guint)(chunkst[index]*i/time_per_pixel) );
1756 * Draw just the speed (velocity)/time image
1758 static void draw_vt ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets)
1762 // Free previous allocation
1763 if ( widgets->speeds )
1764 g_free ( widgets->speeds );
1766 widgets->speeds = vik_track_make_speed_map ( tr, widgets->profile_width );
1767 if ( widgets->speeds == NULL )
1770 widgets->duration = vik_track_get_duration ( tr );
1771 // Negative time or other problem
1772 if ( widgets->duration <= 0 )
1775 // Convert into appropriate units
1776 vik_units_speed_t speed_units = a_vik_get_units_speed ();
1777 switch (speed_units) {
1778 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1779 for ( i = 0; i < widgets->profile_width; i++ ) {
1780 widgets->speeds[i] = VIK_MPS_TO_KPH(widgets->speeds[i]);
1783 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1784 for ( i = 0; i < widgets->profile_width; i++ ) {
1785 widgets->speeds[i] = VIK_MPS_TO_MPH(widgets->speeds[i]);
1788 case VIK_UNITS_SPEED_KNOTS:
1789 for ( i = 0; i < widgets->profile_width; i++ ) {
1790 widgets->speeds[i] = VIK_MPS_TO_KNOTS(widgets->speeds[i]);
1794 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1795 // No need to convert as already in m/s
1799 GtkWidget *window = gtk_widget_get_toplevel (widgets->speed_box);
1800 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
1802 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1804 minmax_array(widgets->speeds, &widgets->min_speed, &widgets->max_speed, FALSE, widgets->profile_width);
1805 if (widgets->min_speed < 0.0)
1806 widgets->min_speed = 0; /* splines sometimes give negative speeds */
1808 /* Find suitable chunk index */
1809 get_new_min_and_chunk_index (widgets->min_speed, widgets->max_speed, chunkss, G_N_ELEMENTS(chunkss), &widgets->draw_min_speed, &widgets->cis);
1812 gdouble mins = widgets->draw_min_speed;
1814 // Reset before redrawing
1815 clear_images (pix, window, widgets);
1818 for (i=0; i<=LINES; i++) {
1821 // NB: No need to convert here anymore as numbers are in the appropriate units
1822 switch (speed_units) {
1823 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1824 sprintf(s, "%8dkm/h", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1826 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1827 sprintf(s, "%8dmph", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1829 case VIK_UNITS_SPEED_METRES_PER_SECOND:
1830 sprintf(s, "%8dm/s", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1832 case VIK_UNITS_SPEED_KNOTS:
1833 sprintf(s, "%8dknots", (int)(mins + (LINES-i)*chunkss[widgets->cis]));
1837 g_critical("Houston, we've had a problem. speed=%d", speed_units);
1840 draw_grid_y ( window, image, widgets, pix, s, i );
1843 draw_time_lines ( window, image, pix, widgets );
1846 guint height = widgets->profile_height + MARGIN_Y;
1847 for ( i = 0; i < widgets->profile_width; i++ )
1848 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1849 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->speeds[i]-mins)/(chunkss[widgets->cis]*LINES) );
1851 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_gps_speed)) ) {
1853 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1855 gdk_color_parse ( "red", &color );
1856 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
1858 time_t beg_time = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
1859 time_t dur = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp - beg_time;
1862 for (iter = tr->trackpoints; iter; iter = iter->next) {
1863 gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
1864 if (isnan(gps_speed))
1866 switch (speed_units) {
1867 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
1868 gps_speed = VIK_MPS_TO_KPH(gps_speed);
1870 case VIK_UNITS_SPEED_MILES_PER_HOUR:
1871 gps_speed = VIK_MPS_TO_MPH(gps_speed);
1873 case VIK_UNITS_SPEED_KNOTS:
1874 gps_speed = VIK_MPS_TO_KNOTS(gps_speed);
1877 // VIK_UNITS_SPEED_METRES_PER_SECOND:
1878 // No need to convert as already in m/s
1881 int x = MARGIN_X + widgets->profile_width * (VIK_TRACKPOINT(iter->data)->timestamp - beg_time) / dur;
1882 int y = height - widgets->profile_height*(gps_speed - mins)/(chunkss[widgets->cis]*LINES);
1883 gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
1885 g_object_unref ( G_OBJECT(gps_speed_gc) );
1889 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
1891 g_object_unref ( G_OBJECT(pix) );
1895 * Draw just the distance/time image
1897 static void draw_dt ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
1901 // Free previous allocation
1902 if ( widgets->distances )
1903 g_free ( widgets->distances );
1905 widgets->distances = vik_track_make_distance_map ( tr, widgets->profile_width );
1906 if ( widgets->distances == NULL )
1909 // Convert into appropriate units
1910 vik_units_distance_t dist_units = a_vik_get_units_distance ();
1911 switch ( dist_units ) {
1912 case VIK_UNITS_DISTANCE_MILES:
1913 for ( i = 0; i < widgets->profile_width; i++ ) {
1914 widgets->distances[i] = VIK_METERS_TO_MILES(widgets->distances[i]);
1917 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1918 for ( i = 0; i < widgets->profile_width; i++ ) {
1919 widgets->distances[i] = VIK_METERS_TO_NAUTICAL_MILES(widgets->distances[i]);
1923 // Metres - but want in kms
1924 for ( i = 0; i < widgets->profile_width; i++ ) {
1925 widgets->distances[i] = widgets->distances[i]/1000.0;
1930 widgets->duration = vik_track_get_duration ( widgets->tr );
1931 // Negative time or other problem
1932 if ( widgets->duration <= 0 )
1935 GtkWidget *window = gtk_widget_get_toplevel (widgets->dist_box);
1936 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
1938 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
1940 // easy to work out min / max of distance!
1942 // mind = 0.0; - Thus not used
1944 switch ( dist_units ) {
1945 case VIK_UNITS_DISTANCE_MILES:
1946 maxd = VIK_METERS_TO_MILES(vik_track_get_length_including_gaps (tr));
1948 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1949 maxd = VIK_METERS_TO_NAUTICAL_MILES(vik_track_get_length_including_gaps (tr));
1952 maxd = vik_track_get_length_including_gaps (tr) / 1000.0;
1956 /* Find suitable chunk index */
1957 gdouble dummy = 0.0; // expect this to remain the same! (not that it's used)
1958 get_new_min_and_chunk_index (0, maxd, chunksd, G_N_ELEMENTS(chunksd), &dummy, &widgets->cid);
1960 // Reset before redrawing
1961 clear_images (pix, window, widgets);
1964 for (i=0; i<=LINES; i++) {
1967 switch ( dist_units ) {
1968 case VIK_UNITS_DISTANCE_MILES:
1969 sprintf(s, _("%.1f miles"), ((LINES-i)*chunksd[widgets->cid]));
1971 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
1972 sprintf(s, _("%.1f NM"), ((LINES-i)*chunksd[widgets->cid]));
1975 sprintf(s, _("%.1f km"), ((LINES-i)*chunksd[widgets->cid]));
1979 draw_grid_y ( window, image, widgets, pix, s, i );
1982 draw_time_lines ( window, image, pix, widgets );
1985 guint height = widgets->profile_height + MARGIN_Y;
1986 for ( i = 0; i < widgets->profile_width; i++ )
1987 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
1988 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->distances[i])/(chunksd[widgets->cid]*LINES) );
1990 // Show speed indicator
1991 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_dist_speed)) ) {
1992 GdkGC *dist_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
1994 gdk_color_parse ( "red", &color );
1995 gdk_gc_set_rgb_fg_color ( dist_speed_gc, &color);
1997 gdouble max_speed = 0;
1998 max_speed = widgets->max_speed * 110 / 100;
2000 // This is just an indicator - no actual values can be inferred by user
2001 for ( i = 0; i < widgets->profile_width; i++ ) {
2002 int y_speed = widgets->profile_height - (widgets->profile_height * widgets->speeds[i])/max_speed;
2003 gdk_draw_rectangle(GDK_DRAWABLE(pix), dist_speed_gc, TRUE, i+MARGIN_X-2, y_speed-2, 4, 4);
2005 g_object_unref ( G_OBJECT(dist_speed_gc) );
2009 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
2011 g_object_unref ( G_OBJECT(pix) );
2016 * Draw just the elevation/time image
2018 static void draw_et ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets )
2022 // Free previous allocation
2024 g_free ( widgets->ats );
2026 widgets->ats = vik_track_make_elevation_time_map ( tr, widgets->profile_width );
2028 if ( widgets->ats == NULL )
2031 // Convert into appropriate units
2032 vik_units_height_t height_units = a_vik_get_units_height ();
2033 if ( height_units == VIK_UNITS_HEIGHT_FEET ) {
2034 // Convert altitudes into feet units
2035 for ( i = 0; i < widgets->profile_width; i++ ) {
2036 widgets->ats[i] = VIK_METERS_TO_FEET(widgets->ats[i]);
2039 // Otherwise leave in metres
2041 minmax_array(widgets->ats, &widgets->min_altitude, &widgets->max_altitude, TRUE, widgets->profile_width);
2043 get_new_min_and_chunk_index (widgets->min_altitude, widgets->max_altitude, chunksa, G_N_ELEMENTS(chunksa), &widgets->draw_min_altitude_time, &widgets->ciat);
2046 gdouble mina = widgets->draw_min_altitude_time;
2048 widgets->duration = vik_track_get_duration ( widgets->tr );
2049 // Negative time or other problem
2050 if ( widgets->duration <= 0 )
2053 GtkWidget *window = gtk_widget_get_toplevel (widgets->elev_time_box);
2054 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2056 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
2058 // Reset before redrawing
2059 clear_images (pix, window, widgets);
2062 for (i=0; i<=LINES; i++) {
2065 switch (height_units) {
2066 case VIK_UNITS_HEIGHT_METRES:
2067 sprintf(s, "%8dm", (int)(mina + (LINES-i)*chunksa[widgets->ciat]));
2069 case VIK_UNITS_HEIGHT_FEET:
2070 // NB values already converted into feet
2071 sprintf(s, "%8dft", (int)(mina + (LINES-i)*chunksa[widgets->ciat]));
2075 g_critical("Houston, we've had a problem. height=%d", height_units);
2078 draw_grid_y ( window, image, widgets, pix, s, i );
2081 draw_time_lines ( window, image, pix, widgets );
2083 /* draw elevations */
2084 guint height = widgets->profile_height + MARGIN_Y;
2085 for ( i = 0; i < widgets->profile_width; i++ )
2086 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
2087 i + MARGIN_X, height, i + MARGIN_X, height-widgets->profile_height*(widgets->ats[i]-mina)/(chunksa[widgets->ciat]*LINES) );
2090 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_elev_dem)) ) {
2092 GdkGC *dem_alt_gc = gdk_gc_new ( gtk_widget_get_window(window) );
2093 gdk_color_parse ( "green", &color );
2094 gdk_gc_set_rgb_fg_color ( dem_alt_gc, &color);
2096 gint h2 = widgets->profile_height + MARGIN_Y; // Adjust height for x axis labelling offset
2097 gint achunk = chunksa[widgets->ciat]*LINES;
2099 for ( i = 0; i < widgets->profile_width; i++ ) {
2100 // This could be slow doing this each time...
2101 VikTrackpoint *tp = vik_track_get_closest_tp_by_percentage_time ( widgets->tr, ((gdouble)i/(gdouble)widgets->profile_width), NULL );
2103 gint16 elev = a_dems_get_elev_by_coord(&(tp->coord), VIK_DEM_INTERPOL_SIMPLE);
2105 if ( elev != VIK_DEM_INVALID_ELEVATION ) {
2106 // Convert into height units
2107 if ( a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
2108 elev = VIK_METERS_TO_FEET(elev);
2109 // No conversion needed if already in metres
2111 // consider chunk size
2112 int y_alt = h2 - ((widgets->profile_height * elev)/achunk );
2113 gdk_draw_rectangle(GDK_DRAWABLE(pix), dem_alt_gc, TRUE, i+MARGIN_X-2, y_alt-2, 4, 4);
2117 g_object_unref ( G_OBJECT(dem_alt_gc) );
2121 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_elev_speed)) ) {
2123 // This is just an indicator - no actual values can be inferred by user
2124 GdkGC *elev_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
2125 gdk_color_parse ( "red", &color );
2126 gdk_gc_set_rgb_fg_color ( elev_speed_gc, &color);
2128 gdouble max_speed = widgets->max_speed * 110 / 100;
2130 for ( i = 0; i < widgets->profile_width; i++ ) {
2131 int y_speed = widgets->profile_height - (widgets->profile_height * widgets->speeds[i])/max_speed;
2132 gdk_draw_rectangle(GDK_DRAWABLE(pix), elev_speed_gc, TRUE, i+MARGIN_X-2, y_speed-2, 4, 4);
2135 g_object_unref ( G_OBJECT(elev_speed_gc) );
2139 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
2141 g_object_unref ( G_OBJECT(pix) );
2145 * Draw just the speed/distance image
2147 static void draw_sd ( GtkWidget *image, VikTrack *tr, PropWidgets *widgets)
2152 // Free previous allocation
2153 if ( widgets->speeds_dist )
2154 g_free ( widgets->speeds_dist );
2156 widgets->speeds_dist = vik_track_make_speed_dist_map ( tr, widgets->profile_width );
2157 if ( widgets->speeds_dist == NULL )
2160 // Convert into appropriate units
2161 vik_units_speed_t speed_units = a_vik_get_units_speed ();
2162 switch (speed_units) {
2163 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2164 for ( i = 0; i < widgets->profile_width; i++ ) {
2165 widgets->speeds_dist[i] = VIK_MPS_TO_KPH(widgets->speeds_dist[i]);
2168 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2169 for ( i = 0; i < widgets->profile_width; i++ ) {
2170 widgets->speeds_dist[i] = VIK_MPS_TO_MPH(widgets->speeds_dist[i]);
2173 case VIK_UNITS_SPEED_KNOTS:
2174 for ( i = 0; i < widgets->profile_width; i++ ) {
2175 widgets->speeds_dist[i] = VIK_MPS_TO_KNOTS(widgets->speeds_dist[i]);
2179 // VIK_UNITS_SPEED_METRES_PER_SECOND:
2180 // No need to convert as already in m/s
2184 GtkWidget *window = gtk_widget_get_toplevel (widgets->speed_dist_box);
2185 GdkPixmap *pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2187 gtk_image_set_from_pixmap ( GTK_IMAGE(image), pix, NULL );
2189 // OK to resuse min_speed here
2190 minmax_array(widgets->speeds_dist, &widgets->min_speed, &widgets->max_speed_dist, FALSE, widgets->profile_width);
2191 if (widgets->min_speed < 0.0)
2192 widgets->min_speed = 0; /* splines sometimes give negative speeds */
2194 /* Find suitable chunk index */
2195 get_new_min_and_chunk_index (widgets->min_speed, widgets->max_speed_dist, chunkss, G_N_ELEMENTS(chunkss), &widgets->draw_min_speed, &widgets->cisd);
2198 mins = widgets->draw_min_speed;
2200 // Reset before redrawing
2201 clear_images (pix, window, widgets);
2204 for (i=0; i<=LINES; i++) {
2207 // NB: No need to convert here anymore as numbers are in the appropriate units
2208 switch (speed_units) {
2209 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2210 sprintf(s, "%8dkm/h", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2212 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2213 sprintf(s, "%8dmph", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2215 case VIK_UNITS_SPEED_METRES_PER_SECOND:
2216 sprintf(s, "%8dm/s", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2218 case VIK_UNITS_SPEED_KNOTS:
2219 sprintf(s, "%8dknots", (int)(mins + (LINES-i)*chunkss[widgets->cisd]));
2223 g_critical("Houston, we've had a problem. speed=%d", speed_units);
2226 draw_grid_y ( window, image, widgets, pix, s, i );
2229 draw_distance_divisions ( window, image, pix, widgets, a_vik_get_units_distance() );
2232 guint height = widgets->profile_height + MARGIN_Y;
2233 for ( i = 0; i < widgets->profile_width; i++ )
2234 gdk_draw_line ( GDK_DRAWABLE(pix), gtk_widget_get_style(window)->dark_gc[3],
2235 i + MARGIN_X, height, i + MARGIN_X, height - widgets->profile_height*(widgets->speeds_dist[i]-mins)/(chunkss[widgets->cisd]*LINES) );
2238 if ( gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widgets->w_show_sd_gps_speed)) ) {
2240 GdkGC *gps_speed_gc = gdk_gc_new ( gtk_widget_get_window(window) );
2242 gdk_color_parse ( "red", &color );
2243 gdk_gc_set_rgb_fg_color ( gps_speed_gc, &color);
2245 gdouble dist = vik_track_get_length_including_gaps(tr);
2246 gdouble dist_tp = 0.0;
2248 GList *iter = tr->trackpoints;
2249 for (iter = iter->next; iter; iter = iter->next) {
2250 gdouble gps_speed = VIK_TRACKPOINT(iter->data)->speed;
2251 if (isnan(gps_speed))
2253 switch (speed_units) {
2254 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
2255 gps_speed = VIK_MPS_TO_KPH(gps_speed);
2257 case VIK_UNITS_SPEED_MILES_PER_HOUR:
2258 gps_speed = VIK_MPS_TO_MPH(gps_speed);
2260 case VIK_UNITS_SPEED_KNOTS:
2261 gps_speed = VIK_MPS_TO_KNOTS(gps_speed);
2264 // VIK_UNITS_SPEED_METRES_PER_SECOND:
2265 // No need to convert as already in m/s
2268 dist_tp += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) );
2269 int x = MARGIN_X + (widgets->profile_width * dist_tp / dist);
2270 int y = height - widgets->profile_height*(gps_speed - mins)/(chunkss[widgets->cisd]*LINES);
2271 gdk_draw_rectangle(GDK_DRAWABLE(pix), gps_speed_gc, TRUE, x-2, y-2, 4, 4);
2273 g_object_unref ( G_OBJECT(gps_speed_gc) );
2277 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc, FALSE, MARGIN_X, MARGIN_Y, widgets->profile_width-1, widgets->profile_height-1);
2279 g_object_unref ( G_OBJECT(pix) );
2286 static void draw_all_graphs ( GtkWidget *widget, PropWidgets *widgets, gboolean resized )
2288 // Draw graphs even if they are not visible
2290 GList *child = NULL;
2291 GtkWidget *image = NULL;
2292 GtkWidget *window = gtk_widget_get_toplevel(widget);
2294 gdouble pc_blob = NAN;
2297 if (widgets->elev_box != NULL) {
2299 // Saved image no longer any good as we've resized, so we remove it here
2300 if (resized && widgets->elev_graph_saved_img.img) {
2301 g_object_unref(widgets->elev_graph_saved_img.img);
2302 widgets->elev_graph_saved_img.img = NULL;
2303 widgets->elev_graph_saved_img.saved = FALSE;
2306 child = gtk_container_get_children(GTK_CONTAINER(widgets->elev_box));
2307 draw_elevations (GTK_WIDGET(child->data), widgets->tr, widgets );
2309 image = GTK_WIDGET(child->data);
2312 // Ensure marker or blob are redrawn if necessary
2313 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2315 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
2316 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
2318 if (widgets->is_blob_drawn) {
2319 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
2320 if (!isnan(pc_blob)) {
2321 x_blob = (pc_blob * widgets->profile_width);
2323 y_blob = blobby_altitude (x_blob, widgets);
2326 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2328 marker_x = (pc * widgets->profile_width) + MARGIN_X;
2331 save_image_and_draw_graph_marks (image,
2333 gtk_widget_get_style(window)->black_gc,
2336 &widgets->elev_graph_saved_img,
2337 widgets->profile_width,
2338 widgets->profile_height,
2339 &widgets->is_marker_drawn,
2340 &widgets->is_blob_drawn);
2345 if (widgets->gradient_box != NULL) {
2347 // Saved image no longer any good as we've resized, so we remove it here
2348 if (resized && widgets->gradient_graph_saved_img.img) {
2349 g_object_unref(widgets->gradient_graph_saved_img.img);
2350 widgets->gradient_graph_saved_img.img = NULL;
2351 widgets->gradient_graph_saved_img.saved = FALSE;
2354 child = gtk_container_get_children(GTK_CONTAINER(widgets->gradient_box));
2355 draw_gradients (GTK_WIDGET(child->data), widgets->tr, widgets );
2357 image = GTK_WIDGET(child->data);
2360 // Ensure marker or blob are redrawn if necessary
2361 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2363 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
2364 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
2366 if (widgets->is_blob_drawn) {
2367 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
2368 if (!isnan(pc_blob)) {
2369 x_blob = (pc_blob * widgets->profile_width);
2371 y_blob = blobby_gradient (x_blob, widgets);
2374 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2376 marker_x = (pc * widgets->profile_width) + MARGIN_X;
2379 save_image_and_draw_graph_marks (image,
2381 gtk_widget_get_style(window)->black_gc,
2384 &widgets->gradient_graph_saved_img,
2385 widgets->profile_width,
2386 widgets->profile_height,
2387 &widgets->is_marker_drawn,
2388 &widgets->is_blob_drawn);
2393 if (widgets->speed_box != NULL) {
2395 // Saved image no longer any good as we've resized
2396 if (resized && widgets->speed_graph_saved_img.img) {
2397 g_object_unref(widgets->speed_graph_saved_img.img);
2398 widgets->speed_graph_saved_img.img = NULL;
2399 widgets->speed_graph_saved_img.saved = FALSE;
2402 child = gtk_container_get_children(GTK_CONTAINER(widgets->speed_box));
2403 draw_vt (GTK_WIDGET(child->data), widgets->tr, widgets );
2405 image = GTK_WIDGET(child->data);
2408 // Ensure marker or blob are redrawn if necessary
2409 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2411 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
2413 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
2415 if (widgets->is_blob_drawn) {
2416 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
2417 if (!isnan(pc_blob)) {
2418 x_blob = (pc_blob * widgets->profile_width);
2421 y_blob = blobby_speed (x_blob, widgets);
2424 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2426 marker_x = (pc * widgets->profile_width) + MARGIN_X;
2429 save_image_and_draw_graph_marks (image,
2431 gtk_widget_get_style(window)->black_gc,
2434 &widgets->speed_graph_saved_img,
2435 widgets->profile_width,
2436 widgets->profile_height,
2437 &widgets->is_marker_drawn,
2438 &widgets->is_blob_drawn);
2443 if (widgets->dist_box != NULL) {
2445 // Saved image no longer any good as we've resized
2446 if (resized && widgets->dist_graph_saved_img.img) {
2447 g_object_unref(widgets->dist_graph_saved_img.img);
2448 widgets->dist_graph_saved_img.img = NULL;
2449 widgets->dist_graph_saved_img.saved = FALSE;
2452 child = gtk_container_get_children(GTK_CONTAINER(widgets->dist_box));
2453 draw_dt (GTK_WIDGET(child->data), widgets->tr, widgets );
2455 image = GTK_WIDGET(child->data);
2458 // Ensure marker or blob are redrawn if necessary
2459 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2461 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
2463 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
2465 if (widgets->is_blob_drawn) {
2466 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
2467 if (!isnan(pc_blob)) {
2468 x_blob = (pc_blob * widgets->profile_width);
2471 y_blob = blobby_distance (x_blob, widgets);
2474 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2476 marker_x = (pc * widgets->profile_width) + MARGIN_X;
2479 save_image_and_draw_graph_marks (image,
2481 gtk_widget_get_style(window)->black_gc,
2484 &widgets->dist_graph_saved_img,
2485 widgets->profile_width,
2486 widgets->profile_height,
2487 &widgets->is_marker_drawn,
2488 &widgets->is_blob_drawn);
2492 // Draw Elevations in timely manner
2493 if (widgets->elev_time_box != NULL) {
2495 // Saved image no longer any good as we've resized
2496 if (resized && widgets->elev_time_graph_saved_img.img) {
2497 g_object_unref(widgets->elev_time_graph_saved_img.img);
2498 widgets->elev_time_graph_saved_img.img = NULL;
2499 widgets->elev_time_graph_saved_img.saved = FALSE;
2502 child = gtk_container_get_children(GTK_CONTAINER(widgets->elev_time_box));
2503 draw_et (GTK_WIDGET(child->data), widgets->tr, widgets );
2505 image = GTK_WIDGET(child->data);
2508 // Ensure marker or blob are redrawn if necessary
2509 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2511 pc = tp_percentage_by_time ( widgets->tr, widgets->marker_tp );
2513 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
2515 if (widgets->is_blob_drawn) {
2516 pc_blob = tp_percentage_by_time ( widgets->tr, widgets->blob_tp );
2517 if (!isnan(pc_blob)) {
2518 x_blob = (pc_blob * widgets->profile_width);
2520 y_blob = blobby_altitude_time (x_blob, widgets);
2523 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2525 marker_x = (pc * widgets->profile_width) + MARGIN_X;
2528 save_image_and_draw_graph_marks (image,
2530 gtk_widget_get_style(window)->black_gc,
2533 &widgets->elev_time_graph_saved_img,
2534 widgets->profile_width,
2535 widgets->profile_height,
2536 &widgets->is_marker_drawn,
2537 &widgets->is_blob_drawn);
2541 // Draw speed distances
2542 if (widgets->speed_dist_box != NULL) {
2544 // Saved image no longer any good as we've resized, so we remove it here
2545 if (resized && widgets->speed_dist_graph_saved_img.img) {
2546 g_object_unref(widgets->speed_dist_graph_saved_img.img);
2547 widgets->speed_dist_graph_saved_img.img = NULL;
2548 widgets->speed_dist_graph_saved_img.saved = FALSE;
2551 child = gtk_container_get_children(GTK_CONTAINER(widgets->speed_dist_box));
2552 draw_sd (GTK_WIDGET(child->data), widgets->tr, widgets );
2554 image = GTK_WIDGET(child->data);
2557 // Ensure marker or blob are redrawn if necessary
2558 if (widgets->is_marker_drawn || widgets->is_blob_drawn) {
2560 pc = tp_percentage_by_distance ( widgets->tr, widgets->marker_tp, widgets->track_length_inc_gaps );
2561 gdouble x_blob = -MARGIN_X - 1.0; // i.e. Don't draw unless we get a valid value
2563 if (widgets->is_blob_drawn) {
2564 pc_blob = tp_percentage_by_distance ( widgets->tr, widgets->blob_tp, widgets->track_length_inc_gaps );
2565 if (!isnan(pc_blob)) {
2566 x_blob = (pc_blob * widgets->profile_width);
2568 y_blob = blobby_speed_dist (x_blob, widgets);
2571 gdouble marker_x = -1.0; // i.e. Don't draw unless we get a valid value
2573 marker_x = (pc * widgets->profile_width) + MARGIN_X;
2576 save_image_and_draw_graph_marks (image,
2578 gtk_widget_get_style(window)->black_gc,
2581 &widgets->speed_dist_graph_saved_img,
2582 widgets->profile_width,
2583 widgets->profile_height,
2584 &widgets->is_marker_drawn,
2585 &widgets->is_blob_drawn);
2592 * Configure/Resize the profile & speed/time images
2594 static gboolean configure_event ( GtkWidget *widget, GdkEventConfigure *event, PropWidgets *widgets )
2596 if (widgets->configure_dialog) {
2597 // Determine size offsets between dialog size and size for images
2598 // Only on the initialisation of the dialog
2599 widgets->profile_width_offset = event->width - widgets->profile_width;
2600 widgets->profile_height_offset = event->height - widgets->profile_height;
2601 widgets->configure_dialog = FALSE;
2603 // Without this the settting, the dialog will only grow in vertical size - one can not then make it smaller!
2604 gtk_widget_set_size_request ( widget, widgets->profile_width+widgets->profile_width_offset, widgets->profile_height+widgets->profile_height_offset );
2606 // Allow resizing back down to a minimal size (especially useful if the initial size has been made bigger after restoring from the saved settings)
2607 GdkGeometry geom = { 600+widgets->profile_width_offset, 300+widgets->profile_height_offset, 0, 0, 0, 0, 0, 0, 0, 0, GDK_GRAVITY_STATIC };
2608 gdk_window_set_geometry_hints ( gtk_widget_get_window(widget), &geom, GDK_HINT_MIN_SIZE );
2611 widgets->profile_width_old = widgets->profile_width;
2612 widgets->profile_height_old = widgets->profile_height;
2615 // Now adjust From Dialog size to get image size
2616 widgets->profile_width = event->width - widgets->profile_width_offset;
2617 widgets->profile_height = event->height - widgets->profile_height_offset;
2619 // ATM we receive configure_events when the dialog is moved and so no further action is necessary
2620 if ( !widgets->configure_dialog &&
2621 (widgets->profile_width_old == widgets->profile_width) && (widgets->profile_height_old == widgets->profile_height) )
2625 draw_all_graphs ( widget, widgets, TRUE );
2631 * Create height profile widgets including the image and callbacks
2633 GtkWidget *vik_trw_layer_create_profile ( GtkWidget *window, PropWidgets *widgets, gdouble *min_alt, gdouble *max_alt)
2637 GtkWidget *eventbox;
2640 widgets->altitudes = vik_track_make_elevation_map ( widgets->tr, widgets->profile_width );
2642 if ( widgets->altitudes == NULL ) {
2643 *min_alt = *max_alt = VIK_DEFAULT_ALTITUDE;
2647 minmax_array(widgets->altitudes, min_alt, max_alt, TRUE, widgets->profile_width);
2649 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2650 image = gtk_image_new_from_pixmap ( pix, NULL );
2652 g_object_unref ( G_OBJECT(pix) );
2654 eventbox = gtk_event_box_new ();
2655 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_profile_click), widgets );
2656 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_profile_move), widgets );
2657 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2658 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_STRUCTURE_MASK);
2664 * Create height profile widgets including the image and callbacks
2666 GtkWidget *vik_trw_layer_create_gradient ( GtkWidget *window, PropWidgets *widgets)
2670 GtkWidget *eventbox;
2673 widgets->gradients = vik_track_make_gradient_map ( widgets->tr, widgets->profile_width );
2675 if ( widgets->gradients == NULL ) {
2679 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2680 image = gtk_image_new_from_pixmap ( pix, NULL );
2682 g_object_unref ( G_OBJECT(pix) );
2684 eventbox = gtk_event_box_new ();
2685 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_gradient_click), widgets );
2686 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_gradient_move), widgets );
2687 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2688 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_STRUCTURE_MASK);
2694 * Create speed/time widgets including the image and callbacks
2696 GtkWidget *vik_trw_layer_create_vtdiag ( GtkWidget *window, PropWidgets *widgets)
2700 GtkWidget *eventbox;
2703 widgets->speeds = vik_track_make_speed_map ( widgets->tr, widgets->profile_width );
2704 if ( widgets->speeds == NULL )
2707 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2708 image = gtk_image_new_from_pixmap ( pix, NULL );
2711 /* XXX this can go out, it's just a helpful dev tool */
2714 GdkGC **colors[8] = { gtk_widget_get_style(window)->bg_gc,
2715 gtk_widget_get_style(window)->fg_gc,
2716 gtk_widget_get_style(window)->light_gc,
2717 gtk_widget_get_style(window)->dark_gc,
2718 gtk_widget_get_style(window)->mid_gc,
2719 gtk_widget_get_style(window)->text_gc,
2720 gtk_widget_get_style(window)->base_gc,
2721 gtk_widget_get_style(window)->text_aa_gc };
2722 for (i=0; i<5; i++) {
2723 for (j=0; j<8; j++) {
2724 gdk_draw_rectangle(GDK_DRAWABLE(pix), colors[j][i],
2725 TRUE, i*20, j*20, 20, 20);
2726 gdk_draw_rectangle(GDK_DRAWABLE(pix), gtk_widget_get_style(window)->black_gc,
2727 FALSE, i*20, j*20, 20, 20);
2733 g_object_unref ( G_OBJECT(pix) );
2735 eventbox = gtk_event_box_new ();
2736 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_vt_click), widgets );
2737 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_vt_move), widgets );
2738 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2739 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2745 * Create distance / time widgets including the image and callbacks
2747 GtkWidget *vik_trw_layer_create_dtdiag ( GtkWidget *window, PropWidgets *widgets)
2751 GtkWidget *eventbox;
2754 widgets->distances = vik_track_make_distance_map ( widgets->tr, widgets->profile_width );
2755 if ( widgets->distances == NULL )
2758 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2759 image = gtk_image_new_from_pixmap ( pix, NULL );
2761 g_object_unref ( G_OBJECT(pix) );
2763 eventbox = gtk_event_box_new ();
2764 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_dt_click), widgets );
2765 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_dt_move), widgets );
2766 //g_signal_connect_swapped ( G_OBJECT(eventbox), "destroy", G_CALLBACK(g_free), widgets );
2767 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2768 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2774 * Create elevation / time widgets including the image and callbacks
2776 GtkWidget *vik_trw_layer_create_etdiag ( GtkWidget *window, PropWidgets *widgets)
2780 GtkWidget *eventbox;
2783 widgets->ats = vik_track_make_elevation_time_map ( widgets->tr, widgets->profile_width );
2784 if ( widgets->ats == NULL )
2787 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2788 image = gtk_image_new_from_pixmap ( pix, NULL );
2790 g_object_unref ( G_OBJECT(pix) );
2792 eventbox = gtk_event_box_new ();
2793 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_et_click), widgets );
2794 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_et_move), widgets );
2795 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2796 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2802 * Create speed/distance widgets including the image and callbacks
2804 GtkWidget *vik_trw_layer_create_sddiag ( GtkWidget *window, PropWidgets *widgets)
2808 GtkWidget *eventbox;
2811 widgets->speeds_dist = vik_track_make_speed_dist_map ( widgets->tr, widgets->profile_width );
2812 if ( widgets->speeds_dist == NULL )
2815 pix = gdk_pixmap_new( gtk_widget_get_window(window), widgets->profile_width+MARGIN_X, widgets->profile_height+MARGIN_Y, -1 );
2816 image = gtk_image_new_from_pixmap ( pix, NULL );
2818 g_object_unref ( G_OBJECT(pix) );
2820 eventbox = gtk_event_box_new ();
2821 g_signal_connect ( G_OBJECT(eventbox), "button_press_event", G_CALLBACK(track_sd_click), widgets );
2822 g_signal_connect ( G_OBJECT(eventbox), "motion_notify_event", G_CALLBACK(track_sd_move), widgets );
2823 gtk_container_add ( GTK_CONTAINER(eventbox), image );
2824 gtk_widget_set_events (eventbox, GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
2830 #define VIK_SETTINGS_TRACK_PROFILE_WIDTH "track_profile_display_width"
2831 #define VIK_SETTINGS_TRACK_PROFILE_HEIGHT "track_profile_display_height"
2833 static void save_values ( PropWidgets *widgets )
2836 a_settings_set_integer ( VIK_SETTINGS_TRACK_PROFILE_WIDTH, widgets->profile_width );
2837 a_settings_set_integer ( VIK_SETTINGS_TRACK_PROFILE_HEIGHT, widgets->profile_height );
2839 // Just for this session ATM
2840 show_dem = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_dem) );
2841 show_alt_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_alt_gps_speed) );
2842 show_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_gps_speed) );
2843 show_gradient_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_gradient_gps_speed) );
2844 show_dist_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_dist_speed) );
2845 show_elev_dem = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_elev_dem) );
2846 show_elev_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_elev_speed) );
2847 show_sd_gps_speed = gtk_toggle_button_get_active ( GTK_TOGGLE_BUTTON(widgets->w_show_sd_gps_speed) );
2850 static void destroy_cb ( GtkDialog *dialog, PropWidgets *widgets )
2852 save_values(widgets);
2853 prop_widgets_free(widgets);
2856 static void propwin_response_cb( GtkDialog *dialog, gint resp, PropWidgets *widgets )
2858 VikTrack *tr = widgets->tr;
2859 VikTrwLayer *vtl = widgets->vtl;
2860 gboolean keep_dialog = FALSE;
2862 /* FIXME: check and make sure the track still exists before doing anything to it */
2863 /* Note: destroying diaglog (eg, parent window exit) won't give "response" */
2865 case GTK_RESPONSE_DELETE_EVENT: /* received delete event (not from buttons) */
2866 case GTK_RESPONSE_REJECT:
2868 case GTK_RESPONSE_ACCEPT:
2869 vik_track_set_comment(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_comment)));
2870 vik_track_set_description(tr, gtk_entry_get_text(GTK_ENTRY(widgets->w_description)));
2871 gtk_color_button_get_color ( GTK_COLOR_BUTTON(widgets->w_color), &(tr->color) );
2872 tr->draw_name_mode = gtk_combo_box_get_active ( GTK_COMBO_BOX(widgets->w_namelabel) );
2873 tr->max_number_dist_labels = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(widgets->w_number_distlabels) );
2874 trw_layer_update_treeview ( widgets->vtl, widgets->tr );
2875 vik_layer_emit_update ( VIK_LAYER(vtl) );
2877 case VIK_TRW_LAYER_PROPWIN_REVERSE:
2878 vik_track_reverse(tr);
2879 vik_layer_emit_update ( VIK_LAYER(vtl) );
2881 case VIK_TRW_LAYER_PROPWIN_DEL_DUP:
2882 vik_track_remove_dup_points(tr); // NB ignore the returned answer
2883 // As we could have seen the nuber of dulplicates that would be deleted in the properties statistics tab,
2884 // choose not to inform the user unnecessarily
2886 /* above operation could have deleted current_tp or last_tp */
2887 trw_layer_cancel_tps_of_track ( vtl, tr );
2888 vik_layer_emit_update ( VIK_LAYER(vtl) );
2890 case VIK_TRW_LAYER_PROPWIN_SPLIT:
2892 /* get new tracks, add them and then the delete old one. old can still exist on clipboard. */
2895 VikTrack **tracks = vik_track_split_into_segments(tr, &ntracks);
2898 for ( i = 0; i < ntracks; i++ )
2901 new_tr_name = trw_layer_new_unique_sublayer_name ( vtl,
2902 widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
2904 if ( widgets->tr->is_route )
2905 vik_trw_layer_add_route ( vtl, new_tr_name, tracks[i] );
2907 vik_trw_layer_add_track ( vtl, new_tr_name, tracks[i] );
2908 vik_track_calculate_bounds ( tracks[i] );
2910 g_free ( new_tr_name );
2916 /* Don't let track destroy this dialog */
2917 vik_track_clear_property_dialog(tr);
2918 if ( widgets->tr->is_route )
2919 vik_trw_layer_delete_route ( vtl, tr );
2921 vik_trw_layer_delete_track ( vtl, tr );
2922 vik_layer_emit_update ( VIK_LAYER(vtl) ); /* chase thru the hoops */
2926 case VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER:
2928 GList *iter = tr->trackpoints;
2929 while ((iter = iter->next)) {
2930 if (widgets->marker_tp == VIK_TRACKPOINT(iter->data))
2934 a_dialog_msg(VIK_GTK_WINDOW_FROM_LAYER(vtl), GTK_MESSAGE_ERROR,
2935 _("Failed spliting track. Track unchanged"), NULL);
2940 gchar *r_name = trw_layer_new_unique_sublayer_name(vtl,
2941 widgets->tr->is_route ? VIK_TRW_LAYER_SUBLAYER_ROUTE : VIK_TRW_LAYER_SUBLAYER_TRACK,
2943 iter->prev->next = NULL;
2945 VikTrack *tr_right = vik_track_new();
2947 vik_track_set_comment ( tr_right, tr->comment );
2948 tr_right->visible = tr->visible;
2949 tr_right->is_route = tr->is_route;
2950 tr_right->trackpoints = iter;
2952 if ( widgets->tr->is_route )
2953 vik_trw_layer_add_route(vtl, r_name, tr_right);
2955 vik_trw_layer_add_track(vtl, r_name, tr_right);
2956 vik_track_calculate_bounds ( tr );
2957 vik_track_calculate_bounds ( tr_right );
2961 vik_layer_emit_update ( VIK_LAYER(vtl) );
2965 fprintf(stderr, "DEBUG: unknown response\n");
2969 /* Keep same behaviour for now: destroy dialog if click on any button */
2971 vik_track_clear_property_dialog(tr);
2972 gtk_widget_destroy ( GTK_WIDGET(dialog) );
2977 * Force a redraw when checkbutton has been toggled to show/hide that information
2979 static void checkbutton_toggle_cb ( GtkToggleButton *togglebutton, PropWidgets *widgets, gpointer dummy )
2981 // Even though not resized, we'll pretend it is -
2982 // as this invalidates the saved images (since the image may have changed)
2983 draw_all_graphs ( widgets->dialog, widgets, TRUE );
2987 * Create the widgets for the given graph tab
2989 static GtkWidget *create_graph_page ( GtkWidget *graph,
2990 const gchar *markup,
2992 const gchar *markup2,
2994 const gchar *markup3,
2996 GtkWidget *checkbutton1,
2997 gboolean checkbutton1_default,
2998 GtkWidget *checkbutton2,
2999 gboolean checkbutton2_default )
3001 GtkWidget *hbox = gtk_hbox_new ( FALSE, 10 );
3002 GtkWidget *vbox = gtk_vbox_new ( FALSE, 10 );
3003 GtkWidget *label = gtk_label_new (NULL);
3004 GtkWidget *label2 = gtk_label_new (NULL);
3005 GtkWidget *label3 = gtk_label_new (NULL);
3006 gtk_box_pack_start (GTK_BOX(vbox), graph, FALSE, FALSE, 0);
3007 gtk_label_set_markup ( GTK_LABEL(label), markup );
3008 gtk_label_set_markup ( GTK_LABEL(label2), markup2 );
3009 gtk_label_set_markup ( GTK_LABEL(label3), markup3 );
3010 gtk_box_pack_start (GTK_BOX(hbox), label, FALSE, FALSE, 0);
3011 gtk_box_pack_start (GTK_BOX(hbox), value, FALSE, FALSE, 0);
3012 gtk_box_pack_start (GTK_BOX(hbox), label2, FALSE, FALSE, 0);
3013 gtk_box_pack_start (GTK_BOX(hbox), value2, FALSE, FALSE, 0);
3015 gtk_box_pack_start (GTK_BOX(hbox), label3, FALSE, FALSE, 0);
3016 gtk_box_pack_start (GTK_BOX(hbox), value3, FALSE, FALSE, 0);
3019 gtk_box_pack_end (GTK_BOX(hbox), checkbutton2, FALSE, FALSE, 0);
3020 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton2), checkbutton2_default);
3023 gtk_box_pack_end (GTK_BOX(hbox), checkbutton1, FALSE, FALSE, 0);
3024 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(checkbutton1), checkbutton1_default);
3026 gtk_box_pack_start (GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3031 static GtkWidget *create_table (int cnt, char *labels[], GtkWidget *contents[])
3036 table = GTK_TABLE(gtk_table_new (cnt, 2, FALSE));
3037 gtk_table_set_col_spacing (table, 0, 10);
3038 for (i=0; i<cnt; i++) {
3041 // Settings so the text positioning only moves around vertically when the dialog is resized
3042 // This also gives more room to see the track comment
3043 label = gtk_label_new(NULL);
3044 gtk_misc_set_alignment ( GTK_MISC(label), 1, 0.5 ); // Position text centrally in vertical plane
3045 gtk_label_set_markup ( GTK_LABEL(label), _(labels[i]) );
3046 gtk_table_attach ( table, label, 0, 1, i, i+1, GTK_FILL, GTK_SHRINK, 0, 0 );
3047 if (GTK_IS_MISC(contents[i])) {
3048 gtk_misc_set_alignment ( GTK_MISC(contents[i]), 0, 0.5 );
3050 if ( GTK_IS_COLOR_BUTTON(contents[i]) || GTK_IS_COMBO_BOX(contents[i]) )
3051 // Buttons compressed - otherwise look weird (to me) if vertically massive
3052 gtk_table_attach ( table, contents[i], 1, 2, i, i+1, GTK_FILL, GTK_SHRINK, 0, 5 );
3054 // Expand for comments + descriptions / labels
3055 gtk_table_attach_defaults ( table, contents[i], 1, 2, i, i+1 );
3058 return GTK_WIDGET (table);
3061 void vik_trw_layer_propwin_run ( GtkWindow *parent,
3066 gboolean start_on_stats )
3068 PropWidgets *widgets = prop_widgets_new();
3074 gint profile_size_value;
3075 // Ensure minimum values
3076 widgets->profile_width = 600;
3077 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_PROFILE_WIDTH, &profile_size_value ) )
3078 if ( profile_size_value > widgets->profile_width )
3079 widgets->profile_width = profile_size_value;
3081 widgets->profile_height = 300;
3082 if ( a_settings_get_integer ( VIK_SETTINGS_TRACK_PROFILE_HEIGHT, &profile_size_value ) )
3083 if ( profile_size_value > widgets->profile_height )
3084 widgets->profile_height = profile_size_value;
3086 gchar *title = g_strdup_printf(_("%s - Track Properties"), tr->name);
3087 GtkWidget *dialog = gtk_dialog_new_with_buttons (title,
3089 GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
3090 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
3091 _("Split at _Marker"), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER,
3092 _("Split _Segments"), VIK_TRW_LAYER_PROPWIN_SPLIT,
3093 _("_Reverse"), VIK_TRW_LAYER_PROPWIN_REVERSE,
3094 _("_Delete Dupl."), VIK_TRW_LAYER_PROPWIN_DEL_DUP,
3095 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
3097 widgets->dialog = dialog;
3098 g_signal_connect( G_OBJECT(dialog), "response", G_CALLBACK(propwin_response_cb), widgets);
3106 gdouble min_alt, max_alt;
3107 widgets->elev_box = vik_trw_layer_create_profile(GTK_WIDGET(parent), widgets, &min_alt, &max_alt);
3108 widgets->gradient_box = vik_trw_layer_create_gradient(GTK_WIDGET(parent), widgets);
3109 widgets->speed_box = vik_trw_layer_create_vtdiag(GTK_WIDGET(parent), widgets);
3110 widgets->dist_box = vik_trw_layer_create_dtdiag(GTK_WIDGET(parent), widgets);
3111 widgets->elev_time_box = vik_trw_layer_create_etdiag(GTK_WIDGET(parent), widgets);
3112 widgets->speed_dist_box = vik_trw_layer_create_sddiag(GTK_WIDGET(parent), widgets);
3113 GtkWidget *graphs = gtk_notebook_new();
3115 GtkWidget *content_prop[20];
3118 static gchar *label_texts[] = {
3119 N_("<b>Comment:</b>"),
3120 N_("<b>Description:</b>"),
3121 N_("<b>Color:</b>"),
3122 N_("<b>Draw Name:</b>"),
3123 N_("<b>Distance Labels:</b>"),
3125 static gchar *stats_texts[] = {
3126 N_("<b>Track Length:</b>"),
3127 N_("<b>Trackpoints:</b>"),
3128 N_("<b>Segments:</b>"),
3129 N_("<b>Duplicate Points:</b>"),
3130 N_("<b>Max Speed:</b>"),
3131 N_("<b>Avg. Speed:</b>"),
3132 N_("<b>Moving Avg. Speed:</b>"),
3133 N_("<b>Avg. Dist. Between TPs:</b>"),
3134 N_("<b>Elevation Range:</b>"),
3135 N_("<b>Total Elevation Gain/Loss:</b>"),
3136 N_("<b>Start:</b>"),
3138 N_("<b>Duration:</b>"),
3140 static gchar tmp_buf[50];
3144 widgets->w_comment = gtk_entry_new ();
3146 gtk_entry_set_text ( GTK_ENTRY(widgets->w_comment), tr->comment );
3147 g_signal_connect_swapped ( widgets->w_comment, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
3148 content_prop[cnt_prop++] = widgets->w_comment;
3150 widgets->w_description = gtk_entry_new ();
3151 if ( tr->description )
3152 gtk_entry_set_text ( GTK_ENTRY(widgets->w_description), tr->description );
3153 g_signal_connect_swapped ( widgets->w_description, "activate", G_CALLBACK(a_dialog_response_accept), GTK_DIALOG(dialog) );
3154 content_prop[cnt_prop++] = widgets->w_description;
3156 widgets->w_color = content_prop[cnt_prop++] = gtk_color_button_new_with_color ( &(tr->color) );
3158 static gchar *draw_name_labels[] = {
3163 N_("Start and End"),
3164 N_("Centre, Start and End"),
3168 widgets->w_namelabel = content_prop[cnt_prop++] = vik_combo_box_text_new ();
3169 gchar **pstr = draw_name_labels;
3171 vik_combo_box_text_append ( widgets->w_namelabel, *(pstr++) );
3172 gtk_combo_box_set_active ( GTK_COMBO_BOX(widgets->w_namelabel), tr->draw_name_mode );
3174 widgets->w_number_distlabels = content_prop[cnt_prop++] =
3175 gtk_spin_button_new ( GTK_ADJUSTMENT(gtk_adjustment_new(tr->max_number_dist_labels, 0, 100, 1, 1, 0)), 1, 0 );
3176 gtk_widget_set_tooltip_text ( GTK_WIDGET(widgets->w_number_distlabels), _("Maximum number of distance labels to be shown") );
3178 table = create_table (cnt_prop, label_texts, content_prop);
3180 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), GTK_WIDGET(table), gtk_label_new(_("Properties")));
3183 GtkWidget *content[20];
3186 vik_units_distance_t dist_units = a_vik_get_units_distance ();
3188 // NB This value not shown yet - but is used by internal calculations
3189 widgets->track_length_inc_gaps = vik_track_get_length_including_gaps(tr);
3191 tr_len = widgets->track_length = vik_track_get_length(tr);
3192 switch (dist_units) {
3193 case VIK_UNITS_DISTANCE_KILOMETRES:
3194 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km", tr_len/1000.0 );
3196 case VIK_UNITS_DISTANCE_MILES:
3197 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f miles", VIK_METERS_TO_MILES(tr_len) );
3199 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3200 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f NM", VIK_METERS_TO_NAUTICAL_MILES(tr_len) );
3203 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3205 widgets->w_track_length = content[cnt++] = gtk_label_new ( tmp_buf );
3207 tp_count = vik_track_get_tp_count(tr);
3208 g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", tp_count );
3209 widgets->w_tp_count = content[cnt++] = gtk_label_new ( tmp_buf );
3211 seg_count = vik_track_get_segment_count(tr) ;
3212 g_snprintf(tmp_buf, sizeof(tmp_buf), "%u", seg_count );
3213 widgets->w_segment_count = content[cnt++] = gtk_label_new ( tmp_buf );
3215 g_snprintf(tmp_buf, sizeof(tmp_buf), "%lu", vik_track_get_dup_point_count(tr) );
3216 widgets->w_duptp_count = content[cnt++] = gtk_label_new ( tmp_buf );
3218 vik_units_speed_t speed_units = a_vik_get_units_speed ();
3219 tmp_speed = vik_track_get_max_speed(tr);
3220 if ( tmp_speed == 0 )
3221 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
3223 switch (speed_units) {
3224 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
3225 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
3227 case VIK_UNITS_SPEED_MILES_PER_HOUR:
3228 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
3230 case VIK_UNITS_SPEED_METRES_PER_SECOND:
3231 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
3233 case VIK_UNITS_SPEED_KNOTS:
3234 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
3237 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
3238 g_critical("Houston, we've had a problem. speed=%d", speed_units);
3241 widgets->w_max_speed = content[cnt++] = gtk_label_new ( tmp_buf );
3243 tmp_speed = vik_track_get_average_speed(tr);
3244 if ( tmp_speed == 0 )
3245 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
3247 switch (speed_units) {
3248 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
3249 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
3251 case VIK_UNITS_SPEED_MILES_PER_HOUR:
3252 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
3254 case VIK_UNITS_SPEED_METRES_PER_SECOND:
3255 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
3257 case VIK_UNITS_SPEED_KNOTS:
3258 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
3261 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
3262 g_critical("Houston, we've had a problem. speed=%d", speed_units);
3265 widgets->w_avg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
3267 // Use 60sec as the default period to be considered stopped
3268 // this is the TrackWaypoint draw stops default value 'vtl->stop_length'
3269 // however this variable is not directly accessible - and I don't expect it's often changed from the default
3270 // so ATM just put in the number
3271 tmp_speed = vik_track_get_average_speed_moving(tr, 60);
3272 if ( tmp_speed == 0 )
3273 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
3275 switch (speed_units) {
3276 case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR:
3277 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f km/h", VIK_MPS_TO_KPH(tmp_speed));
3279 case VIK_UNITS_SPEED_MILES_PER_HOUR:
3280 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f mph", VIK_MPS_TO_MPH(tmp_speed));
3282 case VIK_UNITS_SPEED_METRES_PER_SECOND:
3283 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m/s", tmp_speed );
3285 case VIK_UNITS_SPEED_KNOTS:
3286 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f knots", VIK_MPS_TO_KNOTS(tmp_speed));
3289 g_snprintf (tmp_buf, sizeof(tmp_buf), "--" );
3290 g_critical("Houston, we've had a problem. speed=%d", speed_units);
3293 widgets->w_mvg_speed = content[cnt++] = gtk_label_new ( tmp_buf );
3295 switch (dist_units) {
3296 case VIK_UNITS_DISTANCE_KILOMETRES:
3297 // Even though kilometres, the average distance between points is going to be quite small so keep in metres
3298 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.2f m", (tp_count - seg_count) == 0 ? 0 : tr_len / ( tp_count - seg_count ) );
3300 case VIK_UNITS_DISTANCE_MILES:
3301 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.3f miles", (tp_count - seg_count) == 0 ? 0 : VIK_METERS_TO_MILES(tr_len / ( tp_count - seg_count )) );
3303 case VIK_UNITS_DISTANCE_NAUTICAL_MILES:
3304 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.3f NM", (tp_count - seg_count) == 0 ? 0 : VIK_METERS_TO_NAUTICAL_MILES(tr_len / ( tp_count - seg_count )) );
3307 g_critical("Houston, we've had a problem. distance=%d", dist_units);
3309 widgets->w_avg_dist = content[cnt++] = gtk_label_new ( tmp_buf );
3311 vik_units_height_t height_units = a_vik_get_units_height ();
3312 if ( min_alt == VIK_DEFAULT_ALTITUDE )
3313 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
3315 switch (height_units) {
3316 case VIK_UNITS_HEIGHT_METRES:
3317 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m - %.0f m", min_alt, max_alt );
3319 case VIK_UNITS_HEIGHT_FEET:
3320 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet - %.0f feet", VIK_METERS_TO_FEET(min_alt), VIK_METERS_TO_FEET(max_alt) );
3323 g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
3324 g_critical("Houston, we've had a problem. height=%d", height_units);
3327 widgets->w_elev_range = content[cnt++] = gtk_label_new ( tmp_buf );
3329 vik_track_get_total_elevation_gain(tr, &max_alt, &min_alt );
3330 if ( min_alt == VIK_DEFAULT_ALTITUDE )
3331 g_snprintf(tmp_buf, sizeof(tmp_buf), _("No Data"));
3333 switch (height_units) {
3334 case VIK_UNITS_HEIGHT_METRES:
3335 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f m / %.0f m", max_alt, min_alt );
3337 case VIK_UNITS_HEIGHT_FEET:
3338 g_snprintf(tmp_buf, sizeof(tmp_buf), "%.0f feet / %.0f feet", VIK_METERS_TO_FEET(max_alt), VIK_METERS_TO_FEET(min_alt) );
3341 g_snprintf(tmp_buf, sizeof(tmp_buf), "--" );
3342 g_critical("Houston, we've had a problem. height=%d", height_units);
3345 widgets->w_elev_gain = content[cnt++] = gtk_label_new ( tmp_buf );
3348 #define PACK(w) gtk_box_pack_start (GTK_BOX(right_vbox), w, FALSE, FALSE, 0);
3349 gtk_box_pack_start (GTK_BOX(right_vbox), e_cmt, FALSE, FALSE, 0);
3362 if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->timestamp )
3365 t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp;
3366 t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp;
3369 // Notional center of a track is simply an average of the bounding box extremities
3370 struct LatLon center = { (tr->bbox.north+tr->bbox.south)/2, (tr->bbox.east+tr->bbox.west)/2 };
3371 vik_coord_load_from_latlon ( &vc, vik_trw_layer_get_coord_mode(vtl), ¢er );
3373 widgets->tz = vu_get_tz_at_location ( &vc );
3376 msg = vu_get_time_string ( &t1, "%c", &vc, widgets->tz );
3377 widgets->w_time_start = content[cnt++] = gtk_label_new(msg);
3380 msg = vu_get_time_string ( &t2, "%c", &vc, widgets->tz );
3381 widgets->w_time_end = content[cnt++] = gtk_label_new(msg);
3384 g_snprintf(tmp_buf, sizeof(tmp_buf), _("%d minutes"), (int)(t2-t1)/60);
3385 widgets->w_time_dur = content[cnt++] = gtk_label_new(tmp_buf);
3387 widgets->w_time_start = content[cnt++] = gtk_label_new(_("No Data"));
3388 widgets->w_time_end = content[cnt++] = gtk_label_new(_("No Data"));
3389 widgets->w_time_dur = content[cnt++] = gtk_label_new(_("No Data"));
3392 table = create_table (cnt, stats_texts, content);
3394 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), GTK_WIDGET(table), gtk_label_new(_("Statistics")));
3396 if ( widgets->elev_box ) {
3397 GtkWidget *page = NULL;
3398 widgets->w_cur_dist = gtk_label_new(_("No Data"));
3399 widgets->w_cur_elevation = gtk_label_new(_("No Data"));
3400 widgets->w_show_dem = gtk_check_button_new_with_mnemonic(_("Show D_EM"));
3401 widgets->w_show_alt_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3402 page = create_graph_page (widgets->elev_box,
3403 _("<b>Track Distance:</b>"), widgets->w_cur_dist,
3404 _("<b>Track Height:</b>"), widgets->w_cur_elevation,
3406 widgets->w_show_dem, show_dem,
3407 widgets->w_show_alt_gps_speed, show_alt_gps_speed);
3408 g_signal_connect (widgets->w_show_dem, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3409 g_signal_connect (widgets->w_show_alt_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3410 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Elevation-distance")));
3413 if ( widgets->gradient_box ) {
3414 GtkWidget *page = NULL;
3415 widgets->w_cur_gradient_dist = gtk_label_new(_("No Data"));
3416 widgets->w_cur_gradient_gradient = gtk_label_new(_("No Data"));
3417 widgets->w_show_gradient_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3418 page = create_graph_page (widgets->gradient_box,
3419 _("<b>Track Distance:</b>"), widgets->w_cur_gradient_dist,
3420 _("<b>Track Gradient:</b>"), widgets->w_cur_gradient_gradient,
3422 widgets->w_show_gradient_gps_speed, show_gradient_gps_speed,
3424 g_signal_connect (widgets->w_show_gradient_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3425 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Gradient-distance")));
3428 if ( widgets->speed_box ) {
3429 GtkWidget *page = NULL;
3430 widgets->w_cur_time = gtk_label_new(_("No Data"));
3431 widgets->w_cur_speed = gtk_label_new(_("No Data"));
3432 widgets->w_cur_time_real = gtk_label_new(_("No Data"));
3433 widgets->w_show_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3434 page = create_graph_page (widgets->speed_box,
3435 _("<b>Track Time:</b>"), widgets->w_cur_time,
3436 _("<b>Track Speed:</b>"), widgets->w_cur_speed,
3437 _("<b>Time/Date:</b>"), widgets->w_cur_time_real,
3438 widgets->w_show_gps_speed, show_gps_speed,
3440 g_signal_connect (widgets->w_show_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3441 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Speed-time")));
3444 if ( widgets->dist_box ) {
3445 GtkWidget *page = NULL;
3446 widgets->w_cur_dist_time = gtk_label_new(_("No Data"));
3447 widgets->w_cur_dist_dist = gtk_label_new(_("No Data"));
3448 widgets->w_cur_dist_time_real = gtk_label_new(_("No Data"));
3449 widgets->w_show_dist_speed = gtk_check_button_new_with_mnemonic(_("Show S_peed"));
3450 page = create_graph_page (widgets->dist_box,
3451 _("<b>Track Distance:</b>"), widgets->w_cur_dist_dist,
3452 _("<b>Track Time:</b>"), widgets->w_cur_dist_time,
3453 _("<b>Time/Date:</b>"), widgets->w_cur_dist_time_real,
3454 widgets->w_show_dist_speed, show_dist_speed,
3456 g_signal_connect (widgets->w_show_dist_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3457 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Distance-time")));
3460 if ( widgets->elev_time_box ) {
3461 GtkWidget *page = NULL;
3462 widgets->w_cur_elev_time = gtk_label_new(_("No Data"));
3463 widgets->w_cur_elev_elev = gtk_label_new(_("No Data"));
3464 widgets->w_cur_elev_time_real = gtk_label_new(_("No Data"));
3465 widgets->w_show_elev_speed = gtk_check_button_new_with_mnemonic(_("Show S_peed"));
3466 widgets->w_show_elev_dem = gtk_check_button_new_with_mnemonic(_("Show D_EM"));
3467 page = create_graph_page (widgets->elev_time_box,
3468 _("<b>Track Time:</b>"), widgets->w_cur_elev_time,
3469 _("<b>Track Height:</b>"), widgets->w_cur_elev_elev,
3470 _("<b>Time/Date:</b>"), widgets->w_cur_elev_time_real,
3471 widgets->w_show_elev_dem, show_elev_dem,
3472 widgets->w_show_elev_speed, show_elev_speed);
3473 g_signal_connect (widgets->w_show_elev_dem, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3474 g_signal_connect (widgets->w_show_elev_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3475 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Elevation-time")));
3478 if ( widgets->speed_dist_box ) {
3479 GtkWidget *page = NULL;
3480 widgets->w_cur_speed_dist = gtk_label_new(_("No Data"));
3481 widgets->w_cur_speed_speed = gtk_label_new(_("No Data"));
3482 widgets->w_show_sd_gps_speed = gtk_check_button_new_with_mnemonic(_("Show _GPS Speed"));
3483 page = create_graph_page (widgets->speed_dist_box,
3484 _("<b>Track Distance:</b>"), widgets->w_cur_speed_dist,
3485 _("<b>Track Speed:</b>"), widgets->w_cur_speed_speed,
3487 widgets->w_show_sd_gps_speed, show_sd_gps_speed,
3489 g_signal_connect (widgets->w_show_sd_gps_speed, "toggled", G_CALLBACK (checkbutton_toggle_cb), widgets);
3490 gtk_notebook_append_page(GTK_NOTEBOOK(graphs), page, gtk_label_new(_("Speed-distance")));
3493 gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))), graphs, FALSE, FALSE, 0);
3495 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT_MARKER, FALSE);
3497 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_SPLIT, FALSE);
3498 if (vik_track_get_dup_point_count(tr) <= 0)
3499 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), VIK_TRW_LAYER_PROPWIN_DEL_DUP, FALSE);
3501 // On dialog realization configure_event causes the graphs to be initially drawn
3502 widgets->configure_dialog = TRUE;
3503 g_signal_connect ( G_OBJECT(dialog), "configure-event", G_CALLBACK (configure_event), widgets );
3505 g_signal_connect ( G_OBJECT(dialog), "destroy", G_CALLBACK (destroy_cb), widgets );
3507 vik_track_set_property_dialog(tr, dialog);
3508 gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT );
3509 gtk_widget_show_all ( dialog );
3511 // Gtk note: due to historical reasons, this must be done after widgets are shown
3512 if ( start_on_stats )
3513 gtk_notebook_set_current_page ( GTK_NOTEBOOK(graphs), 1 );
3518 * Update this property dialog
3519 * e.g. if the track has been renamed
3521 void vik_trw_layer_propwin_update ( VikTrack *trk )
3523 // If not displayed do nothing
3524 if ( !trk->property_dialog )
3527 // Update title with current name
3529 gchar *title = g_strdup_printf ( _("%s - Track Properties"), trk->name );
3530 gtk_window_set_title ( GTK_WINDOW(trk->property_dialog), title );