static void gpx_write_track ( const gchar *name, VikTrack *t, GpxWritingContext *context ) { FILE *f = context->file; gchar *tmp; gboolean first_tp_is_newsegment = FALSE; /* must temporarily make it not so, but we want to restore state. not that it matters. */ tmp = entitize ( name ); fprintf ( f, "<trk%s>\n <name>%s</name>\n", t->visible ? "" : " hidden=\"hidden\"", tmp ); g_free ( tmp ); if ( t->comment ) { tmp = entitize ( t->comment ); fprintf ( f, " <desc>%s</desc>\n", tmp ); g_free ( tmp ); } fprintf ( f, " <trkseg>\n" ); if ( t->trackpoints && t->trackpoints->data ) { first_tp_is_newsegment = VIK_TRACKPOINT(t->trackpoints->data)->newsegment; VIK_TRACKPOINT(t->trackpoints->data)->newsegment = FALSE; /* so we won't write </trkseg><trkseg> already */ g_list_foreach ( t->trackpoints, (GFunc) gpx_write_trackpoint, context ); VIK_TRACKPOINT(t->trackpoints->data)->newsegment = first_tp_is_newsegment; /* restore state */ } fprintf ( f, "</trkseg>\n</trk>\n" ); }
/* * Apply DEM data (if available) - to only the last trackpoint */ void vik_track_apply_dem_data_last_trackpoint ( VikTrack *tr ) { gint16 elev; if ( tr->trackpoints ) { /* As in vik_track_apply_dem_data above - use 'best' interpolation method */ elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->coord), VIK_DEM_INTERPOL_BEST ); if ( elev != VIK_DEM_INVALID_ELEVATION ) VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->altitude = elev; } }
static void gpx_write_track ( VikTrack *t, GpxWritingContext *context ) { // Don't write invisible tracks when specified if (context->options && !context->options->hidden && !t->visible) return; FILE *f = context->file; gchar *tmp; gboolean first_tp_is_newsegment = FALSE; /* must temporarily make it not so, but we want to restore state. not that it matters. */ // Sanity clause if ( t->name ) tmp = entitize ( t->name ); else tmp = g_strdup ("track"); // NB 'hidden' is not part of any GPX standard - this appears to be a made up Viking 'extension' // luckily most other GPX processing software ignores things they don't understand fprintf ( f, "<%s%s>\n <name>%s</name>\n", t->is_route ? "rte" : "trk", t->visible ? "" : " hidden=\"hidden\"", tmp ); g_free ( tmp ); if ( t->comment ) { tmp = entitize ( t->comment ); fprintf ( f, " <cmt>%s</cmt>\n", tmp ); g_free ( tmp ); } if ( t->description ) { tmp = entitize ( t->description ); fprintf ( f, " <desc>%s</desc>\n", tmp ); g_free ( tmp ); } /* No such thing as a rteseg! */ if ( !t->is_route ) fprintf ( f, " <trkseg>\n" ); if ( t->trackpoints && t->trackpoints->data ) { first_tp_is_newsegment = VIK_TRACKPOINT(t->trackpoints->data)->newsegment; VIK_TRACKPOINT(t->trackpoints->data)->newsegment = FALSE; /* so we won't write </trkseg><trkseg> already */ g_list_foreach ( t->trackpoints, (GFunc) gpx_write_trackpoint, context ); VIK_TRACKPOINT(t->trackpoints->data)->newsegment = first_tp_is_newsegment; /* restore state */ } /* NB apparently no such thing as a rteseg! */ if (!t->is_route) fprintf ( f, " </trkseg>\n"); fprintf ( f, "</%s>\n", t->is_route ? "rte" : "trk" ); }
/** * Based on a simple average speed, but with a twist - to give a moving average. * . GPSs often report a moving average in their statistics output * . bicycle speedos often don't factor in time when stopped - hence reporting a moving average for speed * * Often GPS track will record every second but not when stationary * This method doesn't use samples that differ over the specified time limit - effectively skipping that time chunk from the total time * * Suggest to use 60 seconds as the stop length (as the default used in the TrackWaypoint draw stops factor) */ gdouble vik_track_get_average_speed_moving (const VikTrack *tr, int stop_length_seconds) { gdouble len = 0.0; guint32 time = 0; if ( tr->trackpoints ) { GList *iter = tr->trackpoints->next; while (iter) { if ( VIK_TRACKPOINT(iter->data)->has_timestamp && VIK_TRACKPOINT(iter->prev->data)->has_timestamp && (! VIK_TRACKPOINT(iter->data)->newsegment) ) { if ( ( VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp ) < stop_length_seconds ) { len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) ); time += ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp); } } iter = iter->next; } } return (time == 0) ? 0 : ABS(len/time); }
gulong vik_track_get_dup_point_count ( const VikTrack *tr ) { gulong num = 0; GList *iter = tr->trackpoints; while ( iter ) { if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->next->data)->coord) ) ) num++; iter = iter->next; } return num; }
VikTrackpoint* vik_track_get_tp_by_max_speed ( const VikTrack *tr ) { gdouble maxspeed = 0.0, speed = 0.0; if ( !tr->trackpoints ) return NULL; GList *iter = tr->trackpoints; VikTrackpoint *max_speed_tp = NULL; while (iter) { if (iter->prev) { if ( VIK_TRACKPOINT(iter->data)->has_timestamp && VIK_TRACKPOINT(iter->prev->data)->has_timestamp && (! VIK_TRACKPOINT(iter->data)->newsegment) ) { speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) ) / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp); if ( speed > maxspeed ) { maxspeed = speed; max_speed_tp = VIK_TRACKPOINT(iter->data); } } } iter = iter->next; } if (!max_speed_tp) return NULL; return max_speed_tp; }
void vik_track_remove_dup_points ( VikTrack *tr ) { GList *iter = tr->trackpoints; while ( iter ) { if ( iter->next && vik_coord_equals ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->next->data)->coord) ) ) { g_free ( iter->next->data ); tr->trackpoints = g_list_delete_link ( tr->trackpoints, iter->next ); } else iter = iter->next; } }
gdouble vik_track_get_length_including_gaps(const VikTrack *tr) { gdouble len = 0.0; if ( tr->trackpoints ) { GList *iter = tr->trackpoints->next; while (iter) { len += vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) ); iter = iter->next; } } return len; }
void vik_track_apply_dem_data ( VikTrack *tr ) { GList *tp_iter; gint16 elev; tp_iter = tr->trackpoints; while ( tp_iter ) { /* TODO: of the 4 possible choices we have for choosing an elevation * (trackpoint in between samples), choose the one with the least elevation change * as the last */ elev = a_dems_get_elev_by_coord ( &(VIK_TRACKPOINT(tp_iter->data)->coord), VIK_DEM_INTERPOL_BEST ); if ( elev != VIK_DEM_INVALID_ELEVATION ) VIK_TRACKPOINT(tp_iter->data)->altitude = elev; tp_iter = tp_iter->next; } }
/* by Alex Foobarian */ VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist, gdouble *meters_from_start ) { gdouble dist = vik_track_get_length_including_gaps(tr) * reldist; gdouble current_dist = 0.0; gdouble current_inc = 0.0; if ( tr->trackpoints ) { GList *iter = tr->trackpoints->next; GList *last_iter = NULL; gdouble last_dist = 0.0; while (iter) { current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) ); last_dist = current_dist; current_dist += current_inc; if ( current_dist >= dist ) break; last_iter = iter; iter = iter->next; } if (!iter) { /* passing the end the track */ if (last_iter) { if (meters_from_start) *meters_from_start = last_dist; return(VIK_TRACKPOINT(last_iter->data)); } else return NULL; } /* we've gone past the dist already, was prev trackpoint closer? */ /* should do a vik_coord_average_weighted() thingy. */ if ( iter->prev && abs(current_dist-current_inc-dist) < abs(current_dist-dist) ) { if (meters_from_start) *meters_from_start = last_dist; iter = iter->prev; } else if (meters_from_start) *meters_from_start = current_dist; return VIK_TRACKPOINT(iter->data); } return NULL; }
void vik_track_convert ( VikTrack *tr, VikCoordMode dest_mode ) { GList *iter = tr->trackpoints; while (iter) { vik_coord_convert ( &(VIK_TRACKPOINT(iter->data)->coord), dest_mode ); iter = iter->next; } }
void vik_track_get_total_elevation_gain(const VikTrack *tr, gdouble *up, gdouble *down) { gdouble diff; *up = *down = 0; if ( tr->trackpoints && VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE ) { GList *iter = tr->trackpoints->next; while (iter) { diff = VIK_TRACKPOINT(iter->data)->altitude - VIK_TRACKPOINT(iter->prev->data)->altitude; if ( diff > 0 ) *up += diff; else *down -= diff; iter = iter->next; } } else *up = *down = VIK_DEFAULT_ALTITUDE; }
gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt ) { *min_alt = 25000; *max_alt = -5000; if ( tr && tr->trackpoints && tr->trackpoints->data && (VIK_TRACKPOINT(tr->trackpoints->data)->altitude != VIK_DEFAULT_ALTITUDE) ) { GList *iter = tr->trackpoints->next; gdouble tmp_alt; while (iter) { tmp_alt = VIK_TRACKPOINT(iter->data)->altitude; if ( tmp_alt > *max_alt ) *max_alt = tmp_alt; if ( tmp_alt < *min_alt ) *min_alt = tmp_alt; iter = iter->next; } return TRUE; } return FALSE; }
void vik_track_reverse ( VikTrack *tr ) { GList *iter; tr->trackpoints = g_list_reverse(tr->trackpoints); /* fix 'newsegment' */ iter = g_list_last ( tr->trackpoints ); while ( iter ) { if ( ! iter->next ) /* last segment, was first, cancel newsegment. */ VIK_TRACKPOINT(iter->data)->newsegment = FALSE; if ( ! iter->prev ) /* first segment by convention has newsegment flag. */ VIK_TRACKPOINT(iter->data)->newsegment = TRUE; else if ( VIK_TRACKPOINT(iter->data)->newsegment && iter->next ) { VIK_TRACKPOINT(iter->next->data)->newsegment = TRUE; VIK_TRACKPOINT(iter->data)->newsegment = FALSE; } iter = iter->prev; } }
VikTrackpoint* vik_track_get_tp_by_min_alt ( const VikTrack *tr ) { gdouble minalt = 25000.0; if ( !tr->trackpoints ) return NULL; GList *iter = tr->trackpoints; VikTrackpoint *min_alt_tp = NULL; while (iter) { if ( VIK_TRACKPOINT(iter->data)->altitude < minalt ) { minalt = VIK_TRACKPOINT(iter->data)->altitude; min_alt_tp = VIK_TRACKPOINT(iter->data); } iter = iter->next; } if (!min_alt_tp) return NULL; return min_alt_tp; }
guint vik_track_get_segment_count(const VikTrack *tr) { guint num = 1; GList *iter = tr->trackpoints; if ( !iter ) return 0; while ( (iter = iter->next) ) { if ( VIK_TRACKPOINT(iter->data)->newsegment ) num++; } return num; }
VikTrackpoint *vik_track_get_closest_tp_by_percentage_time ( VikTrack *tr, gdouble reltime, time_t *seconds_from_start ) { time_t t_pos, t_start, t_end, t_total; t_start = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp; t_end = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp; t_total = t_end - t_start; t_pos = t_start + t_total * reltime; if ( !tr->trackpoints ) return NULL; GList *iter = tr->trackpoints; while (iter) { if (VIK_TRACKPOINT(iter->data)->timestamp == t_pos) break; if (VIK_TRACKPOINT(iter->data)->timestamp > t_pos) { if (iter->prev == NULL) /* first trackpoint */ break; time_t t_before = t_pos - VIK_TRACKPOINT(iter->prev)->timestamp; time_t t_after = VIK_TRACKPOINT(iter->data)->timestamp - t_pos; if (t_before <= t_after) iter = iter->prev; break; } else if ((iter->next == NULL) && (t_pos < (VIK_TRACKPOINT(iter->data)->timestamp + 3))) /* last trackpoint: accommodate for round-off */ break; iter = iter->next; } if (!iter) return NULL; if (seconds_from_start) *seconds_from_start = VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(tr->trackpoints->data)->timestamp; return VIK_TRACKPOINT(iter->data); }
gdouble vik_track_get_max_speed(const VikTrack *tr) { gdouble maxspeed = 0.0, speed = 0.0; if ( tr->trackpoints ) { GList *iter = tr->trackpoints->next; while (iter) { if ( VIK_TRACKPOINT(iter->data)->has_timestamp && VIK_TRACKPOINT(iter->prev->data)->has_timestamp && (! VIK_TRACKPOINT(iter->data)->newsegment) ) { speed = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->prev->data)->coord) ) / ABS(VIK_TRACKPOINT(iter->data)->timestamp - VIK_TRACKPOINT(iter->prev->data)->timestamp); if ( speed > maxspeed ) maxspeed = speed; } iter = iter->next; } } return maxspeed; }
/** * Correlate the image against the specified track */ static void trw_layer_geotag_track ( const gchar *name, VikTrack *track, geotag_options_t *options ) { // If already found match then don't need to check this track if ( options->found_match ) return; VikTrackpoint *trkpt; VikTrackpoint *trkpt_next; GList *mytrkpt = track->trackpoints; for ( mytrkpt = mytrkpt; mytrkpt; mytrkpt = mytrkpt->next ) { // Do something for this trackpoint... trkpt = VIK_TRACKPOINT(mytrkpt->data); // is it exactly this point? if ( options->PhotoTime == trkpt->timestamp ) { options->coord = trkpt->coord; options->altitude = trkpt->altitude; options->found_match = TRUE; break; } // Now need two trackpoints, hence check next is available if ( !mytrkpt->next ) break; trkpt_next = VIK_TRACKPOINT(mytrkpt->next->data); // TODO need to use 'has_timestamp' property if ( trkpt->timestamp == trkpt_next->timestamp ) continue; if ( trkpt->timestamp > trkpt_next->timestamp ) continue; // When interpolating between segments, no need for any special segment handling if ( !options->ov.interpolate_segments ) // Don't check between segments if ( trkpt_next->newsegment ) // Simply move on to consider next point continue; // Too far if ( trkpt->timestamp > options->PhotoTime ) break; // Is is between this and the next point? if ( (options->PhotoTime > trkpt->timestamp) && (options->PhotoTime < trkpt_next->timestamp) ) { options->found_match = TRUE; // Interpolate /* Calculate the "scale": a decimal giving the relative distance * in time between the two points. Ie, a number between 0 and 1 - * 0 is the first point, 1 is the next point, and 0.5 would be * half way. */ gdouble scale = (gdouble)trkpt_next->timestamp - (gdouble)trkpt->timestamp; scale = ((gdouble)options->PhotoTime - (gdouble)trkpt->timestamp) / scale; struct LatLon ll_result, ll1, ll2; vik_coord_to_latlon ( &(trkpt->coord), &ll1 ); vik_coord_to_latlon ( &(trkpt_next->coord), &ll2 ); ll_result.lat = ll1.lat + ((ll2.lat - ll1.lat) * scale); // NB This won't cope with going over the 180 degrees longitude boundary ll_result.lon = ll1.lon + ((ll2.lon - ll1.lon) * scale); // set coord vik_coord_load_from_latlon ( &(options->coord), VIK_COORD_LATLON, &ll_result ); // Interpolate elevation options->altitude = trkpt->altitude + ((trkpt_next->altitude - trkpt->altitude) * scale); break; } } }
/** * vik_trw_layer_tpwin_set_tp: * @tpwin: The Trackpoint Edit Window * @tpl: The #Glist of trackpoints pointing at the current trackpoint * @track_name: The name of the track in which the trackpoint belongs * @is_route: Is the track of the trackpoint actually a route? * * Sets the Trackpoint Edit Window to the values of the current trackpoint given in @tpl. * */ void vik_trw_layer_tpwin_set_tp ( VikTrwLayerTpwin *tpwin, GList *tpl, const gchar *track_name, gboolean is_route ) { static char tmp_str[64]; static struct LatLon ll; VikTrackpoint *tp = VIK_TRACKPOINT(tpl->data); if ( tp->name ) gtk_entry_set_text ( GTK_ENTRY(tpwin->trkpt_name), tp->name ); else gtk_editable_delete_text ( GTK_EDITABLE(tpwin->trkpt_name), 0, -1 ); gtk_widget_set_sensitive ( tpwin->trkpt_name, TRUE ); /* Only can insert if not at the end (otherwise use extend track) */ gtk_widget_set_sensitive ( tpwin->button_insert, (gboolean) GPOINTER_TO_INT (tpl->next) ); gtk_widget_set_sensitive ( tpwin->button_delete, TRUE ); /* We can only split up a track if it's not an endpoint. Makes sense to me. */ gtk_widget_set_sensitive ( tpwin->button_split, tpl->next && tpl->prev ); gtk_widget_set_sensitive ( tpwin->button_forward, (gboolean) GPOINTER_TO_INT (tpl->next) ); gtk_widget_set_sensitive ( tpwin->button_back, (gboolean) GPOINTER_TO_INT (tpl->prev) ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lat), TRUE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->lon), TRUE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->alt), TRUE ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->ts), tp->has_timestamp ); gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->time), tp->has_timestamp ); // Enable adding timestamps - but not on routepoints if ( !tp->has_timestamp && !is_route ) { gtk_widget_set_sensitive ( GTK_WIDGET(tpwin->time), TRUE ); GtkWidget *img = gtk_image_new_from_stock ( GTK_STOCK_ADD, GTK_ICON_SIZE_MENU ); gtk_button_set_image ( GTK_BUTTON(tpwin->time), img ); } else vik_trw_layer_tpwin_set_track_name ( tpwin, track_name ); tpwin->sync_to_tp_block = TRUE; /* don't update while setting data. */ vik_coord_to_latlon ( &(tp->coord), &ll ); gtk_spin_button_set_value ( tpwin->lat, ll.lat ); gtk_spin_button_set_value ( tpwin->lon, ll.lon ); vik_units_height_t height_units = a_vik_get_units_height (); switch (height_units) { case VIK_UNITS_HEIGHT_METRES: gtk_spin_button_set_value ( tpwin->alt, tp->altitude ); break; case VIK_UNITS_HEIGHT_FEET: gtk_spin_button_set_value ( tpwin->alt, VIK_METERS_TO_FEET(tp->altitude) ); break; default: gtk_spin_button_set_value ( tpwin->alt, tp->altitude ); g_critical("Houston, we've had a problem. height=%d", height_units); } tpwin_update_times ( tpwin, tp ); tpwin->sync_to_tp_block = FALSE; // don't update while setting data. vik_units_speed_t speed_units = a_vik_get_units_speed (); vik_units_distance_t dist_units = a_vik_get_units_distance (); if ( tpwin->cur_tp ) { switch (dist_units) { case VIK_UNITS_DISTANCE_KILOMETRES: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f m", vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord))); break; case VIK_UNITS_DISTANCE_MILES: case VIK_UNITS_DISTANCE_NAUTICAL_MILES: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f yards", vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord))*1.0936133); break; default: g_critical("Houston, we've had a problem. distance=%d", dist_units); } gtk_label_set_text ( tpwin->diff_dist, tmp_str ); if ( tp->has_timestamp && tpwin->cur_tp->has_timestamp ) { g_snprintf ( tmp_str, sizeof(tmp_str), "%ld s", tp->timestamp - tpwin->cur_tp->timestamp); gtk_label_set_text ( tpwin->diff_time, tmp_str ); if ( tp->timestamp == tpwin->cur_tp->timestamp ) gtk_label_set_text ( tpwin->diff_speed, "--" ); else { switch (speed_units) { case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f km/h", VIK_MPS_TO_KPH(vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / (ABS(tp->timestamp - tpwin->cur_tp->timestamp))) ); break; case VIK_UNITS_SPEED_MILES_PER_HOUR: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f mph", VIK_MPS_TO_MPH(vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / (ABS(tp->timestamp - tpwin->cur_tp->timestamp))) ); break; case VIK_UNITS_SPEED_METRES_PER_SECOND: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f m/s", vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / ABS(tp->timestamp - tpwin->cur_tp->timestamp) ); break; case VIK_UNITS_SPEED_KNOTS: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f knots", VIK_MPS_TO_KNOTS(vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / (ABS(tp->timestamp - tpwin->cur_tp->timestamp))) ); break; case VIK_UNITS_SPEED_SECONDS_PER_KM: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f s/km", VIK_MPS_TO_PACE_SPK(vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / (ABS(tp->timestamp - tpwin->cur_tp->timestamp))) ); break; case VIK_UNITS_SPEED_MINUTES_PER_KM: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f min/km", VIK_MPS_TO_PACE_MPK(vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / (ABS(tp->timestamp - tpwin->cur_tp->timestamp))) ); break; case VIK_UNITS_SPEED_SECONDS_PER_MILE: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f sec/mi", VIK_MPS_TO_PACE_SPM(vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / (ABS(tp->timestamp - tpwin->cur_tp->timestamp))) ); break; case VIK_UNITS_SPEED_MINUTES_PER_MILE: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f min/mi", VIK_MPS_TO_PACE_MPM(vik_coord_diff(&(tp->coord), &(tpwin->cur_tp->coord)) / (ABS(tp->timestamp - tpwin->cur_tp->timestamp))) ); break; default: g_snprintf ( tmp_str, sizeof(tmp_str), "--" ); g_critical("Houston, we've had a problem. speed=%d", speed_units); } gtk_label_set_text ( tpwin->diff_speed, tmp_str ); } } else { gtk_label_set_text ( tpwin->diff_time, NULL ); gtk_label_set_text ( tpwin->diff_speed, NULL ); } } if ( isnan(tp->course) ) g_snprintf ( tmp_str, sizeof(tmp_str), "--" ); else g_snprintf ( tmp_str, sizeof(tmp_str), "%05.1f\302\260", tp->course ); gtk_label_set_text ( tpwin->course, tmp_str ); if ( isnan(tp->speed) ) g_snprintf ( tmp_str, sizeof(tmp_str), "--" ); else { switch (speed_units) { case VIK_UNITS_SPEED_MILES_PER_HOUR: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f mph", VIK_MPS_TO_MPH(tp->speed) ); break; case VIK_UNITS_SPEED_METRES_PER_SECOND: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f m/s", tp->speed ); break; case VIK_UNITS_SPEED_KNOTS: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f knots", VIK_MPS_TO_KNOTS(tp->speed) ); break; case VIK_UNITS_SPEED_SECONDS_PER_KM: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f s/km", VIK_MPS_TO_PACE_SPK(tp->speed) ); break; case VIK_UNITS_SPEED_MINUTES_PER_KM: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f min/km", VIK_MPS_TO_PACE_MPK(tp->speed) ); break; case VIK_UNITS_SPEED_SECONDS_PER_MILE: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f s/mi", VIK_MPS_TO_PACE_SPM(tp->speed) ); break; case VIK_UNITS_SPEED_MINUTES_PER_MILE: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f min/mi", VIK_MPS_TO_PACE_MPM(tp->speed) ); break; default: // VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: g_snprintf ( tmp_str, sizeof(tmp_str), "%.2f km/h", VIK_MPS_TO_KPH(tp->speed) ); break; } } gtk_label_set_text ( tpwin->speed, tmp_str ); switch (dist_units) { case VIK_UNITS_DISTANCE_KILOMETRES: g_snprintf ( tmp_str, sizeof(tmp_str), "%.5f m", tp->hdop ); gtk_label_set_text ( tpwin->hdop, tmp_str ); g_snprintf ( tmp_str, sizeof(tmp_str), "%.5f m", tp->pdop ); gtk_label_set_text ( tpwin->pdop, tmp_str ); break; case VIK_UNITS_DISTANCE_NAUTICAL_MILES: case VIK_UNITS_DISTANCE_MILES: g_snprintf ( tmp_str, sizeof(tmp_str), "%.5f yards", tp->hdop*1.0936133 ); gtk_label_set_text ( tpwin->hdop, tmp_str ); g_snprintf ( tmp_str, sizeof(tmp_str), "%.5f yards", tp->pdop*1.0936133 ); gtk_label_set_text ( tpwin->pdop, tmp_str ); break; default: g_critical("Houston, we've had a problem. distance=%d", dist_units); } switch (height_units) { case VIK_UNITS_HEIGHT_METRES: g_snprintf ( tmp_str, sizeof(tmp_str), "%.5f m", tp->vdop ); break; case VIK_UNITS_HEIGHT_FEET: g_snprintf ( tmp_str, sizeof(tmp_str), "%.5f feet", VIK_METERS_TO_FEET(tp->vdop) ); break; default: g_snprintf ( tmp_str, sizeof(tmp_str), "--" ); g_critical("Houston, we've had a problem. height=%d", height_units); } gtk_label_set_text ( tpwin->vdop, tmp_str ); g_snprintf ( tmp_str, sizeof(tmp_str), "%d / %d", tp->nsats, tp->fix_mode ); gtk_label_set_text ( tpwin->sat, tmp_str ); tpwin->cur_tp = tp; }
/** * Make a speed/distance map */ gdouble *vik_track_make_speed_dist_map ( const VikTrack *tr, guint16 num_chunks ) { gdouble *v, *s, *t; time_t t1, t2; gint i, pt_count, numpts, index; GList *iter; gdouble duration, total_length, chunk_length; if ( ! tr->trackpoints ) return NULL; t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp; t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp; duration = t2 - t1; if ( !t1 || !t2 || !duration ) return NULL; if (duration < 0) { g_warning("negative duration: unsorted trackpoint timestamps?"); return NULL; } total_length = vik_track_get_length_including_gaps ( tr ); chunk_length = total_length / num_chunks; pt_count = vik_track_get_tp_count(tr); if (chunk_length <= 0) { return NULL; } v = g_malloc ( sizeof(gdouble) * num_chunks ); s = g_malloc ( sizeof(double) * pt_count ); t = g_malloc ( sizeof(double) * pt_count ); // No special handling of segments ATM... iter = tr->trackpoints->next; numpts = 0; s[0] = 0; t[0] = VIK_TRACKPOINT(iter->prev->data)->timestamp; numpts++; while (iter) { s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) ); t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp; numpts++; iter = iter->next; } // Iterate through a portion of the track to get an average speed for that part // This will essentially interpolate between segments, which I think is right given the usage of 'get_length_including_gaps' index = 0; /* index of the current trackpoint. */ for (i = 0; i < num_chunks; i++) { // Similar to the make_speed_map, but instead of using a time chunk, use a distance chunk if (s[0] + i*chunk_length >= s[index]) { gdouble acc_t = 0, acc_s = 0; while (s[0] + i*chunk_length >= s[index]) { acc_s += (s[index+1]-s[index]); acc_t += (t[index+1]-t[index]); index++; } v[i] = acc_s/acc_t; } else if (i) { v[i] = v[i-1]; } else { v[i] = 0; } } g_free(s); g_free(t); return v; }
/** * This uses the 'time' based method to make the graph, (which is a simpler compared to the elevation/distance) * This results in a slightly blocky graph when it does not have many trackpoints: <60 * NB Somehow the elevation/distance applies some kind of smoothing algorithm, * but I don't think any one understands it any more (I certainly don't ATM) */ gdouble *vik_track_make_elevation_time_map ( const VikTrack *tr, guint16 num_chunks ) { time_t t1, t2; gdouble duration, chunk_dur; GList *iter = tr->trackpoints; if (!iter || !iter->next) /* zero- or one-point track */ return NULL; /* test if there's anything worth calculating */ gboolean okay = FALSE; while ( iter ) { if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) { okay = TRUE; break; } iter = iter->next; } if ( ! okay ) return NULL; t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp; t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp; duration = t2 - t1; if ( !t1 || !t2 || !duration ) return NULL; if (duration < 0) { g_warning("negative duration: unsorted trackpoint timestamps?"); return NULL; } gint pt_count = vik_track_get_tp_count(tr); // Reset iterator back to the beginning iter = tr->trackpoints; gdouble *pts = g_malloc ( sizeof(gdouble) * num_chunks ); // The return altitude values gdouble *s = g_malloc(sizeof(double) * pt_count); // calculation altitudes gdouble *t = g_malloc(sizeof(double) * pt_count); // calculation times chunk_dur = duration / num_chunks; s[0] = VIK_TRACKPOINT(iter->data)->altitude; t[0] = VIK_TRACKPOINT(iter->data)->timestamp; iter = tr->trackpoints->next; gint numpts = 1; while (iter) { s[numpts] = VIK_TRACKPOINT(iter->data)->altitude; t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp; numpts++; iter = iter->next; } /* In the following computation, we iterate through periods of time of duration chunk_dur. * The first period begins at the beginning of the track. The last period ends at the end of the track. */ gint index = 0; /* index of the current trackpoint. */ gint i; for (i = 0; i < num_chunks; i++) { /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur. * find the first trackpoint outside the current interval, averaging the heights between intermediate trackpoints. */ if (t[0] + i*chunk_dur >= t[index]) { gdouble acc_s = s[index]; // initialise to first point while (t[0] + i*chunk_dur >= t[index]) { acc_s += (s[index+1]-s[index]); index++; } pts[i] = acc_s; } else if (i) { pts[i] = pts[i-1]; } else { pts[i] = 0; } } g_free(s); g_free(t); return pts; }
/** * Make a distance/time map, heavily based on the vik_track_make_speed_map method */ gdouble *vik_track_make_distance_map ( const VikTrack *tr, guint16 num_chunks ) { gdouble *v, *s, *t; gdouble duration, chunk_dur; time_t t1, t2; int i, pt_count, numpts, index; GList *iter; if ( ! tr->trackpoints ) return NULL; t1 = VIK_TRACKPOINT(tr->trackpoints->data)->timestamp; t2 = VIK_TRACKPOINT(g_list_last(tr->trackpoints)->data)->timestamp; duration = t2 - t1; if ( !t1 || !t2 || !duration ) return NULL; if (duration < 0) { g_warning("negative duration: unsorted trackpoint timestamps?"); return NULL; } pt_count = vik_track_get_tp_count(tr); v = g_malloc ( sizeof(gdouble) * num_chunks ); chunk_dur = duration / num_chunks; s = g_malloc(sizeof(double) * pt_count); t = g_malloc(sizeof(double) * pt_count); iter = tr->trackpoints->next; numpts = 0; s[0] = 0; t[0] = VIK_TRACKPOINT(iter->prev->data)->timestamp; numpts++; while (iter) { s[numpts] = s[numpts-1] + vik_coord_diff ( &(VIK_TRACKPOINT(iter->prev->data)->coord), &(VIK_TRACKPOINT(iter->data)->coord) ); t[numpts] = VIK_TRACKPOINT(iter->data)->timestamp; numpts++; iter = iter->next; } /* In the following computation, we iterate through periods of time of duration chunk_dur. * The first period begins at the beginning of the track. The last period ends at the end of the track. */ index = 0; /* index of the current trackpoint. */ for (i = 0; i < num_chunks; i++) { /* we are now covering the interval from t[0] + i*chunk_dur to t[0] + (i+1)*chunk_dur. * find the first trackpoint outside the current interval, averaging the distance between intermediate trackpoints. */ if (t[0] + i*chunk_dur >= t[index]) { gdouble acc_s = 0; // No need for acc_t while (t[0] + i*chunk_dur >= t[index]) { acc_s += (s[index+1]-s[index]); index++; } // The only bit that's really different from the speed map - just keep an accululative record distance v[i] = i ? v[i-1]+acc_s : acc_s; } else if (i) { v[i] = v[i-1]; } else { v[i] = 0; } } g_free(s); g_free(t); return v; }
/* I understood this when I wrote it ... maybe ... Basically it eats up the * proper amounts of length on the track and averages elevation over that. */ gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks ) { gdouble *pts; gdouble total_length, chunk_length, current_dist, current_area_under_curve, current_seg_length, dist_along_seg = 0.0; gdouble altitude1, altitude2; guint16 current_chunk; gboolean ignore_it = FALSE; GList *iter = tr->trackpoints; if (!iter || !iter->next) /* zero- or one-point track */ return NULL; { /* test if there's anything worth calculating */ gboolean okay = FALSE; while ( iter ) { if ( VIK_TRACKPOINT(iter->data)->altitude != VIK_DEFAULT_ALTITUDE ) { okay = TRUE; break; } iter = iter->next; } if ( ! okay ) return NULL; } iter = tr->trackpoints; g_assert ( num_chunks < 16000 ); pts = g_malloc ( sizeof(gdouble) * num_chunks ); total_length = vik_track_get_length_including_gaps ( tr ); chunk_length = total_length / num_chunks; /* Zero chunk_length (eg, track of 2 tp with the same loc) will cause crash */ if (chunk_length <= 0) { g_free(pts); return NULL; } current_dist = 0.0; current_area_under_curve = 0; current_chunk = 0; current_seg_length = 0; current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->next->data)->coord) ); altitude1 = VIK_TRACKPOINT(iter->data)->altitude; altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude; dist_along_seg = 0; while ( current_chunk < num_chunks ) { /* go along current seg */ if ( current_seg_length && (current_seg_length - dist_along_seg) > chunk_length ) { dist_along_seg += chunk_length; /* / * pt2 * * /x altitude = alt_at_pt_1 + alt_at_pt_2 / 2 = altitude1 + slope * dist_value_of_pt_inbetween_pt1_and_pt2 * /xx avg altitude = area under curve / chunk len *pt1 *xxx avg altitude = altitude1 + (altitude2-altitude1)/(current_seg_length)*(dist_along_seg + (chunk_len/2)) * / xxx * / xxx **/ if ( ignore_it ) // Seemly can't determine average for this section - so use last known good value (much better than just sticking in zero) pts[current_chunk] = altitude1; else pts[current_chunk] = altitude1 + (altitude2-altitude1)*((dist_along_seg - (chunk_length/2))/current_seg_length); current_chunk++; } else { /* finish current seg */ if ( current_seg_length ) { gdouble altitude_at_dist_along_seg = altitude1 + (altitude2-altitude1)/(current_seg_length)*dist_along_seg; current_dist = current_seg_length - dist_along_seg; current_area_under_curve = current_dist*(altitude_at_dist_along_seg + altitude2)*0.5; } else { current_dist = current_area_under_curve = 0; } /* should only happen if first current_seg_length == 0 */ /* get intervening segs */ iter = iter->next; while ( iter && iter->next ) { current_seg_length = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord), &(VIK_TRACKPOINT(iter->next->data)->coord) ); altitude1 = VIK_TRACKPOINT(iter->data)->altitude; altitude2 = VIK_TRACKPOINT(iter->next->data)->altitude; ignore_it = VIK_TRACKPOINT(iter->next->data)->newsegment; if ( chunk_length - current_dist >= current_seg_length ) { current_dist += current_seg_length; current_area_under_curve += current_seg_length * (altitude1+altitude2) * 0.5; iter = iter->next; } else { break; } } /* final seg */ dist_along_seg = chunk_length - current_dist; if ( ignore_it || ( iter && !iter->next ) ) { pts[current_chunk] = current_area_under_curve / current_dist; if (!iter->next) { int i; for (i = current_chunk + 1; i < num_chunks; i++) pts[i] = pts[current_chunk]; break; } } else { current_area_under_curve += dist_along_seg * (altitude1 + (altitude2 - altitude1)*dist_along_seg/current_seg_length); pts[current_chunk] = current_area_under_curve / chunk_length; } current_dist = 0; current_chunk++; } } return pts; }
/** * @val_analyse_track: * @trk: The track to be analyse * * Function to collect statistics, using the internal track functions */ static void val_analyse_track ( VikTrack *trk ) { //val_reset ( TS_TRACK ); gdouble min_alt; gdouble max_alt; gdouble up; gdouble down; gdouble length = 0.0; gdouble length_gaps = 0.0; gdouble max_speed = 0.0; gulong trackpoints = 0; guint segments = 0; tracks_stats[TS_TRACKS].count++; trackpoints = vik_track_get_tp_count (trk); segments = vik_track_get_segment_count (trk); length = vik_track_get_length (trk); length_gaps = vik_track_get_length_including_gaps (trk); max_speed = vik_track_get_max_speed (trk); if ( !trk->is_route ) { // Eddington number will be in the current Units distance preference gdouble e_len; switch (a_vik_get_units_distance ()) { case VIK_UNITS_DISTANCE_MILES: e_len = VIK_METERS_TO_MILES(length); break; case VIK_UNITS_DISTANCE_NAUTICAL_MILES: e_len = VIK_METERS_TO_NAUTICAL_MILES(length); break; //VIK_UNITS_DISTANCE_KILOMETRES default: e_len = length/1000.0; break; } gdouble *gd = g_malloc ( sizeof(gdouble) ); *gd = e_len; tracks_stats[TS_TRACKS].e_list = g_list_prepend ( tracks_stats[TS_TRACKS].e_list, gd ); } int ii; for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) { tracks_stats[ii].trackpoints += trackpoints; tracks_stats[ii].segments += segments; tracks_stats[ii].length += length; tracks_stats[ii].length_gaps += length_gaps; if ( max_speed > tracks_stats[ii].max_speed ) tracks_stats[ii].max_speed = max_speed; } if ( vik_track_get_minmax_alt (trk, &min_alt, &max_alt) ) { for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) { if ( min_alt < tracks_stats[ii].min_alt ) tracks_stats[ii].min_alt = min_alt; if ( max_alt > tracks_stats[ii].max_alt ) tracks_stats[ii].max_alt = max_alt; } } vik_track_get_total_elevation_gain (trk, &up, &down ); for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) { tracks_stats[ii].elev_gain += up; tracks_stats[ii].elev_loss += down; } if ( trk->trackpoints && VIK_TRACKPOINT(trk->trackpoints->data)->timestamp ) { time_t t1, t2; t1 = VIK_TRACKPOINT(g_list_first(trk->trackpoints)->data)->timestamp; t2 = VIK_TRACKPOINT(g_list_last(trk->trackpoints)->data)->timestamp; // Assume never actually have a track with a time of 0 (1st Jan 1970) for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) { if ( tracks_stats[ii].start_time == 0) tracks_stats[ii].start_time = t1; if ( tracks_stats[ii].end_time == 0) tracks_stats[ii].end_time = t2; } // Initialize to the first value for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) { if (t1 < tracks_stats[ii].start_time) tracks_stats[ii].start_time = t1; if (t2 > tracks_stats[ii].end_time) tracks_stats[ii].end_time = t2; } for (ii = 0; ii < G_N_ELEMENTS(tracks_stats); ii++) { tracks_stats[ii].duration = tracks_stats[ii].duration + (int)(t2-t1); } } }