-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
211 lines (170 loc) · 7.05 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
from os.path import join, dirname
import datetime
import pandas as pd
import numpy as np
from bokeh.io import curdoc
from bokeh.layouts import row, column
from bokeh.models import ColumnDataSource, DataRange1d, Select, Slider, Range1d
from bokeh.palettes import Blues4
from bokeh.plotting import figure
from datetime import datetime as dtdt
def profile(f):
def ret_f(*args, **kwargs):
t_start = dtdt.now()
ret_val = f(*args, **kwargs)
t_end = dtdt.now()
print t_end - t_start, f.__name__
return ret_val
return ret_f
def cb_profile(f):
def ret_f(attr, old, new):
t_start = dtdt.now()
ret_val = f(attr, old, new)
t_end = dtdt.now()
print t_end - t_start, f.__name__
return ret_val
return ret_f
@profile
def make_boat_tacks(df, boat_name):
df2 = df[boat_name].copy()
df2['CWD'] = df2['CourseWindDirection']
df2['WC_DIFF'] = (df2['CWD'] - df2['COG']) #Wind Course difference
#create a series named WC_DIFF180 that is the elementwise modulo 180 of WC_DIFF
df2['WC_DIFF180'] = df2['WC_DIFF'] % 180
df2['WC_DIFF360'] = df2['WC_DIFF'] % 360
df2['GYB_DIFF'] = df2['WC_DIFF'] - 180
df2['GYB_DIFF360'] = df2['GYB_DIFF'] % 360
# every timestamp where WC_DIFF180 is less than 1
tacks = df2[df2['WC_DIFF360'] <1].index.values
#print tacks
gybes = df2[df2['GYB_DIFF360'] <1].index.values
df3 = pd.DataFrame(
{
'Lat':df2.Lat, 'Lon':df2.Lon,
'SOG':df2.SOG, 'Heel':df2.Heel})
return df3, tacks, gybes
current_boat = 'JPN'
BOAT_NAMES = ['FRA', 'USA', 'JPN', 'SWE', 'GBR', 'NZL']
@profile
def make_plots(cds, zoom_cds, tack_im): #, full_race_cds):
"""cds is only used for the zoomed in portion of the map,
full_race_cds is used for p1 and p3
"""
boat_plot_opts = dict(tools="", plot_width=375, plot_height=375, min_border=0)
min_, max_ = tack_im.get_tstamps(0)
x_range = Range1d(min_, max_)
lat_min, lat_max, lon_min, lon_max = tack_im.get_lat_long_extents(0)
lat_range = Range1d(lat_min, lat_max)
lon_range = Range1d(lon_min, lon_max)
p1 = figure(title='Speed and Heel', x_range=x_range, **boat_plot_opts)
p2 = figure(title='Zoomed in COG', x_range=lon_range, y_range=lat_range,
**boat_plot_opts)
p3 = figure(title='Full Race course', **boat_plot_opts)
p1.line(x='time_col', y='SOG', source=cds, legend='Speed')
p1.line(x='time_col', y='Heel', source=cds, legend='Heel', color='green')
p2.line(x='zoomed_Lon', y='zoomed_Lat', source=zoom_cds, color='red')
p3.line(x='Lon', y='Lat', source=cds, color='blue', line_alpha=.1)
p3.line(x='zoomed_Lon', y='zoomed_Lat', source=zoom_cds, color='red')
row_fig = row(p1, p2, p3)
return x_range, lat_range, lon_range, row_fig
@profile
class IntervalManager(object):
def __init__(self, event_list, orig_df, window_size=150):
self.event_list = event_list
self.orig_df = orig_df
self.orig_index = orig_df.index
self.window_size = window_size
def __len__(self):
return len(self.event_list)
@profile
def get_tstamps(self, idx):
t_idx = self.orig_index.get_loc(self.event_list[idx])
start, end = t_idx - self.window_size, t_idx + self.window_size
start = np.max([start, 0])
orig_index_len = len(self.orig_index)
end = np.min( [orig_index_len - 1, end])
start_tstamp = self.orig_index[start]
end_tstamp = self.orig_index[end]
return start_tstamp, end_tstamp
def pad(self, min_, max_):
lat_padding = np.abs((max_ - min_) * 0.02)
min_ -= lat_padding
max_ += lat_padding
return min_, max_
@profile
def get_lat_long_extents(self, idx):
t_idx = self.orig_index.get_loc(self.event_list[idx])
start, end = t_idx - self.window_size, t_idx + self.window_size
start = np.max([start, 0])
orig_index_len = len(self.orig_index)
end = np.min( [orig_index_len - 1, end])
lat = self.orig_df.Lat[start:end]
lon = self.orig_df.Lon[start:end]
lat_min, lat_max = lat.min(), lat.max()
lat_min, lat_max = self.pad(lat_min, lat_max)
lon_min, lon_max = lon.min(), lon.max()
lon_min, lon_max = self.pad(lon_min, lon_max)
return (lat_min, lat_max, lon_min, lon_max)
@profile
def get_lat_lon_cds(self, idx):
t_idx = self.orig_index.get_loc(self.event_list[idx])
start, end = t_idx - self.window_size, t_idx + self.window_size
start = np.max([start, 0])
orig_index_len = len(self.orig_index)
end = np.min( [orig_index_len - 1, end])
lat = self.orig_df.Lat[start:end]
lon = self.orig_df.Lon[start:end]
df2 = pd.DataFrame({'zoomed_Lat':lat, 'zoomed_Lon':lon})
ll_cds = ColumnDataSource(data=df2)
return ll_cds
global_tack_im = None
@profile
def update_boat(boat_name):
global global_tack_im
df2, tacks, gybes = make_boat_tacks(full_df, boat_name)
df2['time_col'] = df2.index.values
full_cds = ColumnDataSource(data=df2)
global_tack_im = IntervalManager(tacks, df2)
zoom_cds = global_tack_im.get_lat_lon_cds(0)
return full_cds, zoom_cds, global_tack_im
@profile
def update_ranges(tack_num, tack_im, x_range, lat_range, lon_range, source):
start, end = tack_im.get_tstamps(tack_num)
x_range.start = start
x_range.end = end
lat_min, lat_max, lon_min, lon_max = tack_im.get_lat_long_extents(tack_num)
lat_range.start = lat_min
lat_range.end = lat_max
lon_range.start = lon_min
lon_range.end = lon_max
ll_cds = tack_im.get_lat_lon_cds(tack_num)
source.data.update(ll_cds.data)
boat_select = Select(value=current_boat, title='Boat', options=BOAT_NAMES)
tack_slider = Slider(start=1, end=10, value=1, step=1,
title="Tack Num")
@cb_profile
def update_plot(attrname, old, new):
global global_source, zoom_source
boat_name = boat_select.value
tack_num = tack_slider.value
src, zsrc, tack_im = update_boat(boat_name)
global_source.data.update(src.data)
zoom_source.data.update(zsrc.data)
tack_num = tack_slider.value
update_ranges(tack_num, global_tack_im, global_x_range,
global_lat_range, global_lon_range, zoom_source)
@cb_profile
def update_tack_slider(attrname, old, new):
global global_tack_im
tack_num = tack_slider.value
update_ranges(tack_num, global_tack_im, global_x_range,
global_lat_range, global_lon_range, zoom_source)
full_df = pd.read_hdf('race1.hd5')
global_source, zoom_source, global_tack_im = update_boat('USA')
global_x_range, global_lat_range, global_lon_range, plot = make_plots(global_source, zoom_source, global_tack_im)
update_ranges(3, global_tack_im, global_x_range, global_lat_range, global_lon_range, zoom_source)
boat_select.on_change('value', update_plot)
tack_slider.on_change('value', update_tack_slider)
controls = row(boat_select, tack_slider)
curdoc().add_root(column(controls, plot))
curdoc().title = "America's Cup tack analysis"