]> git.street.me.uk Git - andy/viking.git/blobdiff - src/viktrack.c
Add new graph type Elevation/Time in the track properties window.
[andy/viking.git] / src / viktrack.c
index 359dc4f8dcda424def8eab621f45c10c75bf883b..15c07895b7f31db2d5b90403ce25700f8d6244fe 100644 (file)
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
 
 #include <glib.h>
 #include <time.h>
-#include <stdio.h>
 #include <stdlib.h>
+#ifdef HAVE_STRING_H
 #include <string.h>
+#endif
+#ifdef HAVE_MATH_H
 #include <math.h>
+#endif
+
 #include "coords.h"
 #include "vikcoord.h"
 #include "viktrack.h"
@@ -83,7 +90,8 @@ void vik_track_free(VikTrack *tr)
   g_list_foreach ( tr->trackpoints, (GFunc) g_free, NULL );
   g_list_free( tr->trackpoints );
   if (tr->property_dialog)
-    gtk_widget_destroy ( GTK_WIDGET(tr->property_dialog) );
+    if ( GTK_IS_WIDGET(tr->property_dialog) )
+      gtk_widget_destroy ( GTK_WIDGET(tr->property_dialog) );
   g_free ( tr );
 }
 
@@ -108,9 +116,12 @@ VikTrack *vik_track_copy ( const VikTrack *tr )
 VikTrackpoint *vik_trackpoint_new()
 {
   VikTrackpoint *tp = g_malloc0(sizeof(VikTrackpoint));
-  tp->extended = FALSE;
   tp->speed = NAN;
   tp->course = NAN;
+  tp->altitude = VIK_DEFAULT_ALTITUDE;
+  tp->hdop = VIK_DEFAULT_DOP;
+  tp->vdop = VIK_DEFAULT_DOP;
+  tp->pdop = VIK_DEFAULT_DOP;
   return tp;
 }
 
@@ -369,8 +380,10 @@ gdouble *vik_track_make_elevation_map ( const VikTrack *tr, guint16 num_chunks )
   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)
+  if (chunk_length <= 0) {
+    g_free(pts);
     return NULL;
+  }
 
   current_dist = 0.0;
   current_area_under_curve = 0;
@@ -531,12 +544,10 @@ gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
      */
     if (t[0] + i*chunk_dur >= t[index]) {
       gdouble acc_t = 0, acc_s = 0;
-      numpts = 0;
       while (t[0] + i*chunk_dur >= t[index]) {
        acc_s += (s[index+1]-s[index]);
        acc_t += (t[index+1]-t[index]);
        index++;
-       numpts++;
       }
       v[i] = acc_s/acc_t;
     } 
@@ -552,6 +563,170 @@ gdouble *vik_track_make_speed_map ( const VikTrack *tr, guint16 num_chunks )
   return v;
 }
 
+/**
+ * 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;
+}
+
+/**
+ * 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;
+}
+
 /* by Alex Foobarian */
 VikTrackpoint *vik_track_get_closest_tp_by_percentage_dist ( VikTrack *tr, gdouble reldist, gdouble *meters_from_start )
 {
@@ -638,6 +813,84 @@ VikTrackpoint *vik_track_get_closest_tp_by_percentage_time ( VikTrack *tr, gdoub
   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;
+}
+
 gboolean vik_track_get_minmax_alt ( const VikTrack *tr, gdouble *min_alt, gdouble *max_alt )
 {
   *min_alt = 25000;