+ 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(tr->trackpoints->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;
+}
+
+/**
+ * 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 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(tr->trackpoints->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;
+}
+
+/**
+ * vik_track_get_tp_by_dist:
+ * @trk: The Track on which to find a Trackpoint
+ * @meters_from_start: The distance along a track that the trackpoint returned is near
+ * @get_next_point: Since there is a choice of trackpoints, this determines which one to return
+ * @tp_metres_from_start: For the returned Trackpoint, returns the distance along the track
+ *
+ * TODO: Consider changing the boolean get_next_point into an enum with these options PREVIOUS, NEXT, NEAREST
+ *
+ * Returns: The #VikTrackpoint fitting the criteria or NULL
+ */
+VikTrackpoint *vik_track_get_tp_by_dist ( VikTrack *trk, gdouble meters_from_start, gboolean get_next_point, gdouble *tp_metres_from_start )
+{
+ gdouble current_dist = 0.0;
+ gdouble current_inc = 0.0;
+ if ( tp_metres_from_start )
+ *tp_metres_from_start = 0.0;
+
+ if ( trk->trackpoints ) {
+ GList *iter = g_list_next ( g_list_first ( trk->trackpoints ) );
+ while (iter) {
+ current_inc = vik_coord_diff ( &(VIK_TRACKPOINT(iter->data)->coord),
+ &(VIK_TRACKPOINT(iter->prev->data)->coord) );
+ current_dist += current_inc;
+ if ( current_dist >= meters_from_start )
+ break;
+ iter = g_list_next ( iter );
+ }
+ // passed the end of the track
+ if ( !iter )
+ return NULL;
+
+ if ( tp_metres_from_start )
+ *tp_metres_from_start = current_dist;
+
+ // we've gone past the distance already, is the previous trackpoint wanted?
+ if ( !get_next_point ) {
+ if ( iter->prev ) {
+ if ( tp_metres_from_start )
+ *tp_metres_from_start = current_dist-current_inc;
+ return VIK_TRACKPOINT(iter->prev->data);
+ }
+ }
+ return VIK_TRACKPOINT(iter->data);
+ }
+
+ return NULL;
+}
+
+/* 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;
+}
+
+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->data)->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);
+}
+
+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;
+}
+
+VikTrackpoint* vik_track_get_tp_by_max_alt ( const VikTrack *tr )
+{
+ gdouble maxalt = -5000.0;
+ if ( !tr->trackpoints )
+ return NULL;
+
+ GList *iter = tr->trackpoints;
+ VikTrackpoint *max_alt_tp = NULL;
+
+ while (iter) {
+ if ( VIK_TRACKPOINT(iter->data)->altitude > maxalt ) {
+ maxalt = VIK_TRACKPOINT(iter->data)->altitude;
+ max_alt_tp = VIK_TRACKPOINT(iter->data);
+ }
+ iter = iter->next;
+ }
+
+ if (!max_alt_tp)
+ return NULL;
+
+ return max_alt_tp;
+}
+
+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;
+}
+
+VikTrackpoint *vik_track_get_tp_first( const VikTrack *tr )
+{
+ if ( !tr->trackpoints )
+ return NULL;
+
+ return (VikTrackpoint*)g_list_first(tr->trackpoints)->data;
+}
+
+VikTrackpoint *vik_track_get_tp_last ( const VikTrack *tr )
+{
+ if ( !tr->trackpoints )
+ return NULL;
+
+ return (VikTrackpoint*)g_list_last(tr->trackpoints)->data;
+}
+
+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_marshall ( VikTrack *tr, guint8 **data, guint *datalen)