/* 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; }
/** * 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; }
/** * @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); } } }
/* 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; }
/** * vu_trackpoint_formatted_message: * @format_code: String describing the message to generate * @trkpt: The trackpoint for which the message is generated about * @trkpt_prev: A trackpoint (presumed previous) for interpolating values with the other trackpoint (such as speed) * @trk: The track in which the trackpoints reside * @climb: Vertical speed (Out of band (i.e. not in a trackpoint) value for display currently only for GPSD usage) * * TODO: One day replace this cryptic format code with some kind of tokenizer parsing * thus would make it more user friendly and maybe even GUI controlable. * However for now at least there is some semblance of user control */ gchar* vu_trackpoint_formatted_message ( gchar *format_code, VikTrackpoint *trkpt, VikTrackpoint *trkpt_prev, VikTrack *trk, gdouble climb ) { if ( !trkpt ) return NULL; gint len = 0; if ( format_code ) len = strlen ( format_code ); if ( len > FMT_MAX_NUMBER_CODES ) len = FMT_MAX_NUMBER_CODES; gchar* values[FMT_MAX_NUMBER_CODES]; int i; for ( i = 0; i < FMT_MAX_NUMBER_CODES; i++ ) { values[i] = '\0'; } gchar *speed_units_str = NULL; vik_units_speed_t speed_units = a_vik_get_units_speed (); switch (speed_units) { case VIK_UNITS_SPEED_MILES_PER_HOUR: speed_units_str = g_strdup ( _("mph") ); break; case VIK_UNITS_SPEED_METRES_PER_SECOND: speed_units_str = g_strdup ( _("m/s") ); break; case VIK_UNITS_SPEED_KNOTS: speed_units_str = g_strdup ( _("knots") ); break; default: // VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: speed_units_str = g_strdup ( _("km/h") ); break; } gchar *separator = g_strdup ( " | " ); for ( i = 0; i < len; i++ ) { switch ( g_ascii_toupper ( format_code[i] ) ) { case 'G': values[i] = g_strdup ( _("GPSD") ); break; // GPS Preamble case 'K': values[i] = g_strdup ( _("Trkpt") ); break; // Trkpt Preamble case 'S': { gdouble speed = 0.0; gchar *speedtype = NULL; if ( isnan(trkpt->speed) && trkpt_prev ) { if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) { if ( trkpt->timestamp != trkpt_prev->timestamp ) { // Work out from previous trackpoint location and time difference speed = vik_coord_diff(&(trkpt->coord), &(trkpt_prev->coord)) / ABS(trkpt->timestamp - trkpt_prev->timestamp); speedtype = g_strdup ( "*" ); // Interpolated } else speedtype = g_strdup ( "**" ); } else speedtype = g_strdup ( "**" ); } else { speed = trkpt->speed; speedtype = g_strdup ( "" ); } switch (speed_units) { case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: speed = VIK_MPS_TO_KPH(speed); break; case VIK_UNITS_SPEED_MILES_PER_HOUR: speed = VIK_MPS_TO_MPH(speed); break; case VIK_UNITS_SPEED_KNOTS: speed = VIK_MPS_TO_KNOTS(speed); break; default: // VIK_UNITS_SPEED_METRES_PER_SECOND: // Already in m/s so nothing to do break; } values[i] = g_strdup_printf ( _("%sSpeed%s %.1f%s"), separator, speedtype, speed, speed_units_str ); g_free ( speedtype ); break; } case 'B': { gdouble speed = 0.0; gchar *speedtype = NULL; if ( isnan(climb) && trkpt_prev ) { if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) { if ( trkpt->timestamp != trkpt_prev->timestamp ) { // Work out from previous trackpoint altitudes and time difference // 'speed' can be negative if going downhill speed = (trkpt->altitude - trkpt_prev->altitude) / ABS(trkpt->timestamp - trkpt_prev->timestamp); speedtype = g_strdup ( "*" ); // Interpolated } else speedtype = g_strdup ( "**" ); // Unavailable } else speedtype = g_strdup ( "**" ); } else { speed = climb; speedtype = g_strdup ( "" ); } switch (speed_units) { case VIK_UNITS_SPEED_KILOMETRES_PER_HOUR: speed = VIK_MPS_TO_KPH(speed); break; case VIK_UNITS_SPEED_MILES_PER_HOUR: speed = VIK_MPS_TO_MPH(speed); break; case VIK_UNITS_SPEED_KNOTS: speed = VIK_MPS_TO_KNOTS(speed); break; default: // VIK_UNITS_SPEED_METRES_PER_SECOND: // Already in m/s so nothing to do break; } // Go for 2dp as expect low values for vertical speeds values[i] = g_strdup_printf ( _("%sClimb%s %.2f%s"), separator, speedtype, speed, speed_units_str ); g_free ( speedtype ); break; } case 'A': { vik_units_height_t height_units = a_vik_get_units_height (); switch (height_units) { case VIK_UNITS_HEIGHT_FEET: values[i] = g_strdup_printf ( _("%sAlt %dfeet"), separator, (int)round(VIK_METERS_TO_FEET(trkpt->altitude)) ); break; default: //VIK_UNITS_HEIGHT_METRES: values[i] = g_strdup_printf ( _("%sAlt %dm"), separator, (int)round(trkpt->altitude) ); break; } break; } case 'C': { gint heading = isnan(trkpt->course) ? 0 : (gint)round(trkpt->course); values[i] = g_strdup_printf ( _("%sCourse %03d\302\260" ), separator, heading ); break; } case 'P': { if ( trkpt_prev ) { gint diff = (gint) round ( vik_coord_diff ( &(trkpt->coord), &(trkpt_prev->coord) ) ); gchar *dist_units_str = NULL; vik_units_distance_t dist_units = a_vik_get_units_distance (); // expect the difference between track points to be small hence use metres or yards switch (dist_units) { case VIK_UNITS_DISTANCE_MILES: case VIK_UNITS_DISTANCE_NAUTICAL_MILES: dist_units_str = g_strdup ( _("yards") ); break; default: // VIK_UNITS_DISTANCE_KILOMETRES: dist_units_str = g_strdup ( _("m") ); break; } values[i] = g_strdup_printf ( _("%sDistance diff %d%s"), separator, diff, dist_units_str ); g_free ( dist_units_str ); } break; } case 'T': { gchar *msg; if ( trkpt->has_timestamp ) { // Compact date time format msg = vu_get_time_string ( &(trkpt->timestamp), "%x %X", &(trkpt->coord), NULL ); } else msg = g_strdup ("--"); values[i] = g_strdup_printf ( _("%sTime %s"), separator, msg ); g_free ( msg ); break; } case 'M': { if ( trkpt_prev ) { if ( trkpt->has_timestamp && trkpt_prev->has_timestamp ) { time_t t_diff = trkpt->timestamp - trkpt_prev->timestamp; values[i] = g_strdup_printf ( _("%sTime diff %lds"), separator, t_diff ); } } break; } case 'X': values[i] = g_strdup_printf ( _("%sNo. of Sats %d"), separator, trkpt->nsats ); break; case 'F': { if ( trk ) { // Distance to the end 'Finish' (along the track) gdouble distd = vik_track_get_length_to_trackpoint (trk, trkpt); gdouble diste = vik_track_get_length_including_gaps ( trk ); gdouble dist = diste - distd; gchar *dist_units_str = NULL; vik_units_distance_t dist_units = a_vik_get_units_distance (); switch (dist_units) { case VIK_UNITS_DISTANCE_MILES: dist_units_str = g_strdup ( _("miles") ); dist = VIK_METERS_TO_MILES(dist); break; case VIK_UNITS_DISTANCE_NAUTICAL_MILES: dist_units_str = g_strdup ( _("NM") ); dist = VIK_METERS_TO_NAUTICAL_MILES(dist); break; default: // VIK_UNITS_DISTANCE_KILOMETRES: dist_units_str = g_strdup ( _("km") ); dist = dist / 1000.0; break; } values[i] = g_strdup_printf ( _("%sTo End %.2f%s"), separator, dist, dist_units_str ); g_free ( dist_units_str ); } break; } case 'D': { if ( trk ) { // Distance from start (along the track) gdouble distd = vik_track_get_length_to_trackpoint (trk, trkpt); gchar *dist_units_str = NULL; vik_units_distance_t dist_units = a_vik_get_units_distance (); switch (dist_units) { case VIK_UNITS_DISTANCE_MILES: dist_units_str = g_strdup ( _("miles") ); distd = VIK_METERS_TO_MILES(distd); break; case VIK_UNITS_DISTANCE_NAUTICAL_MILES: dist_units_str = g_strdup ( _("NM") ); distd = VIK_METERS_TO_NAUTICAL_MILES(distd); break; default: // VIK_UNITS_DISTANCE_KILOMETRES: dist_units_str = g_strdup ( _("km") ); distd = distd / 1000.0; break; } values[i] = g_strdup_printf ( _("%sDistance along %.2f%s"), separator, distd, dist_units_str ); g_free ( dist_units_str ); } break; } case 'L': { // Location (Lat/Long) gchar *lat = NULL, *lon = NULL; struct LatLon ll; vik_coord_to_latlon (&(trkpt->coord), &ll); a_coords_latlon_to_string ( &ll, &lat, &lon ); values[i] = g_strdup_printf ( "%s%s %s", separator, lat, lon ); g_free ( lat ); g_free ( lon ); break; } case 'N': // Name of track values[i] = g_strdup_printf ( _("%sTrack: %s"), separator, trk->name ); break; case 'E': // Name of trackpoint if available if ( trkpt->name ) values[i] = g_strdup_printf ( "%s%s", separator, trkpt->name ); else values[i] = g_strdup ( "" ); break; default: break; } } g_free ( separator ); g_free ( speed_units_str ); gchar *msg = g_strconcat ( values[0], values[1], values[2], values[3], values[4], values[5], values[6], values[7], values[8], NULL ); for ( i = 0; i < FMT_MAX_NUMBER_CODES; i++ ) { if ( values[i] != '\0' ) g_free ( values[i] ); } return msg; }