#! /usr/bin/env python # Copyright (C) 2009 by Akkana Peck. # You are free to use, share or modify this program under # the terms of the GPLv2 or, at your option, any later GPL. VERSION = 0.3 import sys, os, math, datetime, xml.dom.minidom try: import pylab have_pylab = True except ImportError: have_pylab = False print "pylab isn't installed; will print stats only, no plotting" #import matplotlib.pyplot as plt #from matplotlib.dates import YearLocator, MonthLocator, DateFormatter # Climb threshold: any short climb that doesn't gain more than # this number of feet won't be counted toward total climb. # GPS units (at least my Vista CX) have a lot of 1-2' # fluctuations that vastly overestimate the climb total; # just swinging the bag around, or unhooking it from your belt # to check your current position, can do that. CLIMB_THRESHOLD = 8 # How fast do we have to be moving to count toward the moving average speed? # This is in miles/hour. SPEED_THRESHOLD = .5 # The variables we're going to plot: times = [ ] eles = [ ] distances = [ ] # Some evil globals because python doesn't have static variables: lastlat = 0 lastlon = 0 lastele = -1 total_dist = 0 total_climb = 0 this_climb = 0 this_climb_start = 0 lasttime = None moving_time = datetime.timedelta(0) stopped_time = datetime.timedelta(0) def getVal(parent, name) : nodeList = parent.getElementsByTagName(name) if len(nodeList) < 1 : return "No " + name + " element" children = nodeList[0].childNodes if len(children) < 1 : return "No " + name + " children" node = children[0] if node.nodeType != node.TEXT_NODE: return name + " isn't a text node" return node.data def accumulate_climb(ele) : global lastele, total_climb, this_climb, this_climb_start if lastele >= 0 : # Not the first call if ele > lastele : # Climbed since last step if this_climb == 0 : this_climb_start = lastele this_climb = this_climb + ele - lastele else : if this_climb > CLIMB_THRESHOLD : total_climb = total_climb + this_climb this_climb = 0 elif ele <= this_climb_start : # We got a little hump but it isn't big enough to count; # probably an artifact like taking the GPS out of its case # or getting off the bike or something. So reset. this_climb = 0 lastele = ele def handleTrackPoint(point) : global lastlat, lastlon, lasttime, total_dist, moving_time, stopped_time time = datetime.datetime.strptime(getVal(point, "time"), '%Y-%m-%dT%H:%M:%SZ') lat = float(point.getAttribute("lat")) lon = float(point.getAttribute("lon")) ele = float(getVal(point, "ele")) ele = round(ele * 3.2808399, 2) # convert from meters to feet if lastlat != 0 and lastlon != 0 : dist = math.sqrt((lat - lastlat)**2 + (lon - lastlon)**2) * 69.046767 # 69.046767 converts nautical miles (arcminutes) to miles total_dist += dist delta_t = time - lasttime # This is a datetime.timedelta object speed = dist / delta_t.seconds * 60 * 60 # miles/hour if speed > SPEED_THRESHOLD : moving_time += delta_t #print "moving\t", else : stopped_time += delta_t #print "stopped\t", lasttime = time accumulate_climb(ele) lastlat = lat lastlon = lon #print total_dist, ele, "\t", time, lat, lon, "\t", total_climb #print total_dist, ele, "\t", time, total_climb distances.append(total_dist) eles.append(ele) def handleTrackFile(dom) : points = dom.getElementsByTagName("trkpt") for i in range (0, len(points), 1) : handleTrackPoint(points[i]) accumulate_climb(0) def main(args) : if len(args) < 2 : print "Ellie version", VERSION print "Usage:", args[0], file.gpx return 1 # # Parse the file: # try : dom = xml.dom.minidom.parse(args[1]) except IOError, e: print e #print dir(e) return e.errno handleTrackFile(dom) # # Plot the results: # if not have_pylab : print "%.1f miles. Total climb: %d'" % (total_dist, int(total_climb)) print "%d minutes moving, %d stopped" % (int(moving_time.seconds / 60), int(stopped_time.seconds / 60)) print "Average speed moving: %.1f mph" % (total_dist *60*60 / moving_time.seconds) return 0 pylab.plot(distances, eles) title_string = "Elevation profile (" + str(round(distances[-1], 1)) \ + " miles, " + str(int(total_climb)) + "' total climb)" pylab.title(title_string) pylab.xlabel("miles") # pylab.get_current_fig_manager().window.set_title(os.path.basename(args[0] + ": " + title_string)) pylab.ylabel("feet") pylab.grid(True) pylab.show() pass return 0 if __name__ == "__main__" : try: sys.exit(main(sys.argv)) except KeyboardInterrupt: print " Bye!" sys.exit(1)