In [279]:
%pylab inline
# make plot bigger
pylab.rcParams['figure.figsize'] = (15.0, 12.0)


Populating the interactive namespace from numpy and matplotlib
WARNING: pylab import has clobbered these variables: ['f', 'datetime']
`%matplotlib` prevents importing * from pylab and numpy

In [280]:
import glob
import os
import platform
import json
import math
from pprint import pprint
from lxml import objectify
from shapely.geometry import Point, LineString
from copy import deepcopy 
from datetime import datetime, timedelta
from operator import itemgetter

In [281]:
# detect Dropbox path
platform_name = platform.system()
if platform_name == 'Windows':
    # Windows > XP
    dropbox_info_path = "%s\Dropbox\info.json" % os.getenv('APPDATA')
else:
    # Linux/Mac
    dropbox_info_path = "%s/.dropbox/info.json" % os.getenv('HOME')
with open(dropbox_info_path, "r") as f:    
    dropbox_path = json.load(f)['personal']['path']      

print dropbox_path


/Users/tiratatp/Dropbox

In [282]:
def distance(lat1, lat2, lon1, lon2, el1, el2):    
    R = 6371 # Radius of the earth

    latDistance = math.radians(lat2 - lat1)
    lonDistance = math.radians(lon2 - lon1)
    a = math.sin(latDistance / 2) * math.sin(latDistance / 2) \
            + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) \
            * math.sin(lonDistance / 2) * math.sin(lonDistance / 2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
    distance = R * c * 1000 # convert to meters

    height = el1 - el2
    distance = math.pow(distance, 2) + math.pow(height, 2);
    return math.sqrt(distance)

def distance_trackpoint(t1, t2):
    return distance(t1.Position['LatitudeDegrees'], t2.Position['LatitudeDegrees'], \
                    t1.Position['LongitudeDegrees'], t2.Position['LongitudeDegrees'], \
                    t1.AltitudeMeters, t2.AltitudeMeters)

def point_from_trackpoint(t):
    return Point(t.Position['LatitudeDegrees'], t.Position['LongitudeDegrees'], t.AltitudeMeters)

def time_trackpoint(t1, t2):
    time1 = datetime.strptime(str(t1.Time), "%Y-%m-%dT%H:%M:%S.%fZ")
    time2 = datetime.strptime(str(t2.Time), "%Y-%m-%dT%H:%M:%S.%fZ")
    return (time2 - time1).total_seconds()
        
def split_trackpoints(t1, t2, distance):
    new_length_ratio = float(distance) / distance_trackpoint(t1, t2)
    
    # split distant
    line = LineString([point_from_trackpoint(t1), point_from_trackpoint(t2)])
    mid = deepcopy(t1)
    minpoint = line.interpolate(new_length_ratio, normalized=True)
    mid.Position['LatitudeDegrees'] = minpoint.x
    mid.Position['LongitudeDegrees'] = minpoint.y
    mid.AltitudeMeters = minpoint.z
    
    # split time
    time1 = datetime.strptime(str(t1.Time), "%Y-%m-%dT%H:%M:%S.%fZ")
    midtime = time1 + timedelta(seconds=time_trackpoint(t1, t2) * new_length_ratio)
    mid.Time = midtime.strftime("%Y-%m-%dT%H:%M:%S.%fZ")
    return (t1, mid, t2)

In [283]:
all_tcx = glob.glob(os.path.join(dropbox_path, "Apps", "tapiriik", "*.tcx"))
activities = []
if len(all_tcx) == 0:
    raise IOError("Cannot find tapiriik folder! Visit https://tapiriik.com to set it up.")
for f in all_tcx:
    activity_root = objectify.parse(f).getroot().Activities.Activity    
    activity = {
        'laps': [],
        'type': activity_root.attrib['Sport'].lower(),        
        'start': activity_root.Id,
        'start_datetime': datetime.strptime(str(activity_root.Id), "%Y-%m-%dT%H:%M:%S.%fZ"),
    }   
    for lap in activity_root.Lap:
        current_lap = {
            'segments': [], # 1 km segments
            'maximum_speed': None,
            'calories': lap.Calories,
            'distance_meters': lap.DistanceMeters,
            'total_time_seconds': lap.TotalTimeSeconds,            
        }
        if hasattr(lap, 'MaximumSpeed'):
            current_lap['maximum_speed'] = lap.MaximumSpeed        
        current_segment = {
            'trackpoints': [],
            'distance': 0.0,
            'time': 0.0
        }
        previous_trackpoint = None
        for trackpoint in lap.Track.Trackpoint:
            if not hasattr(trackpoint, 'AltitudeMeters'):
                continue
            if previous_trackpoint is None:
                previous_trackpoint = trackpoint
                continue   
            new_distance = distance_trackpoint(previous_trackpoint, trackpoint)
            if current_segment['distance'] + new_distance > 1000: 
                while current_segment['distance'] + new_distance > 1000:
                    need_distance = 1000 - current_segment['distance']      
                    (previous_trackpoint, mid, trackpoint) = split_trackpoints(previous_trackpoint, trackpoint, need_distance)

                    #current_segment['trackpoints'].append(mid)
                    current_segment['distance'] += distance_trackpoint(previous_trackpoint, mid)
                    current_segment['time'] += time_trackpoint(previous_trackpoint, mid)
                    current_segment['speed'] = current_segment['distance'] / current_segment['time']
                
                    current_lap['segments'].append(current_segment)
                    current_segment = {
                        'trackpoints': [],
                        'distance': 0.0,
                        'time': 0.0
                    }
                    
                    new_distance = distance_trackpoint(mid, trackpoint)
                    previous_trackpoint = mid            
            else:
                #current_segment['trackpoints'].append(trackpoint)
                current_segment['distance'] += new_distance
                current_segment['time'] += time_trackpoint(previous_trackpoint, trackpoint)                                
            previous_trackpoint = trackpoint
        current_lap['average_speed'] = np.mean([s['speed'] for s in current_lap['segments']])
        activity['laps'].append(current_lap)
    activity['maximum_speed'] = max([lap['maximum_speed'] for lap in activity['laps']])
    activity['calories'] = sum([lap['calories'] for lap in activity['laps']])
    activity['distance_meters'] = sum([lap['distance_meters'] for lap in activity['laps']])
    activity['total_time_seconds'] = sum([lap['total_time_seconds'] for lap in activity['laps']])
    activity['average_speed'] = np.mean([lap['average_speed'] for lap in activity['laps']])
    activities.append(activity)

In [284]:
#pprint(activities, depth=2)

In [286]:
x = []
y1 = []
y2 = []

for a in activities:
    if a['type'] != 'biking':
        continue
    if a['maximum_speed'] is not None:
        x.append(a['start_datetime'])
        y1.append(a['maximum_speed'] * 3.6)
        y2.append(a['average_speed'] * 3.6)

fig = plt.figure()
ax1 = fig.add_subplot(311)
ax1.plot(x,y1, label='Maximum')
ax1.plot(x,y2, label='Average')
ax1.set_ylabel('Speed (km/h)')    
plt.legend(loc=0)

x = []
y = []
for a in activities:
    if a['type'] != 'biking':
        continue
    fastest = None
    for l in a['laps']:
        for s in l['segments']:
            if fastest is None:
                fastest = s['time']
            elif fastest > s['time']:
                fastest = s['time']
    if fastest is not None:
        x.append(a['start_datetime'])
        #y.append(timedelta(seconds=fastest))
        y.append(fastest)
            
ax2 = fig.add_subplot(312)
# uncomment this to set ymin to 0
#ax2.set_ylim(bottom=0)
ax2.plot(x,y)
ax2.set_ylabel('Best 1 km Segment (s)')   

x = []
y = []
for a in activities:
    if a['type'] != 'biking':
        continue
    if a['distance_meters'] is not None:
        x.append(a['start_datetime'])
        y.append(a['distance_meters'] / 1000.0)
ax3 = fig.add_subplot(313)
ax3.plot(x,y)
ax3.set_ylabel('Distance (m)')    
plt.legend(loc=0)

plt.show()



In [ ]: