forked from ryanbaumann/StravaSegExp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Strava API Exploration 160327.py
228 lines (155 loc) · 8.11 KB
/
Strava API Exploration 160327.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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# coding: utf-8
# In[22]:
#Goal - Select all segments in a given lat/long bounds using the Strava API
#Problem - API only reuturns top 10 segments in bound
import stravalib
import pandas as pd
import numpy as np
import os
from sqlalchemy import create_engine
from polyline.codec import PolylineCodec
from datetime import datetime
import json
# In[2]:
engine = create_engine('postgresql+psycopg2://admin:password@localhost:5432/webdev6',
convert_unicode=True)
# In[3]:
client = stravalib.client.Client(access_token=os.environ['STRAVA_APICODE'])
athlete = client.get_athlete()
print 'athlete name %s, athlete id %s.' %(athlete.firstname, athlete.id)
# In[16]:
def get_segs_from_api(client, extents, act_type):
"""Get segments for a client in extents [40.681, -89.636, 40.775, -89.504]
with act_type riding or running.
Returns a dataframe of the segments with all details of the segments
"""
segment_explorer = client.explore_segments(extents,
activity_type=act_type)
return segment_explorer
def seg_to_df(segment_explorer, act_type):
dflist = []
for seg in segment_explorer:
print 'seg id %s, seg name %s, seg dist %s' % (seg.id, seg.name, seg.distance)
if act_type=='riding':
acttype='ride'
else:
acttype='run'
seg_detail = seg.segment
newrow = {'seg_id' : int(seg.id),
'name' : str(seg.name),
'act_type' : str(acttype),
'elev_low' : 0, #float(seg_detail.elevation_low),
'elev_high' : 0, #float(seg_detail.elevation_high),
'start_lat' : float(seg.start_latlng[0]),
'start_long' : float(seg.start_latlng[1]),
'end_lat' : float(seg.end_latlng[0]),
'end_long' : float(seg.end_latlng[1]),
'date_created' : datetime.utcnow(), #seg_detail.created_at.replace(tzinfo=None),
'effort_cnt' : 0, #int(seg_detail.effort_count),
'ath_cnt' : 0, #int(seg_detail.athlete_count),
'cat' : int(seg.climb_category),
'elev_gain' : float(seg.elev_difference),
'distance' : float(seg.distance),
'seg_points' : str(seg.points),
'seg_points_decode' : PolylineCodec().decode(seg.points)
}
dflist.append(newrow)
seg_df = pd.DataFrame(dflist)
return seg_df
# In[17]:
segment_explorer = get_segs_from_api(client, [40.8, -89.7, 40.9, -89.6], 'riding')
seg_df = seg_to_df(segment_explorer, 'riding')
# In[6]:
def create_points(lat_series, long_series):
# Creates a string from a lat/long column to map to a Geography Point
# datatype in PostGIS
point_col = 'Point(' + str(long_series) + ' ' + str(lat_series) + ')'
return point_col
seg_df['start_point'] = map(create_points, seg_df['start_lat'], seg_df['start_long'])
seg_df['end_point'] = map(create_points, seg_df['end_lat'], seg_df['end_long'])
# In[7]:
def get_acts_in_db(engine, table_name):
# Return a list of already cached segments in the database
already_dl_seg_id_list = []
try:
args = 'SELECT seg_id from "%s"' % (table_name)
df = pd.read_sql(args, engine)
already_dl_seg_id_list = df['seg_id']
except:
print "no activities in database! downloading all segments in range..."
return already_dl_seg_id_list
def clean_cached_segs(dl_lst, new_seg_df):
# Remove segments already in database from the dataframe
new_seg_df['rows_to_drop'] = new_seg_df['seg_id'].isin(dl_lst)
new_seg_df.drop(new_seg_df[new_seg_df.rows_to_drop==True].index, inplace=True)
return new_seg_df
# In[8]:
dl_lst = get_acts_in_db(engine, 'Segment')
seg_df = clean_cached_segs(dl_lst, seg_df)
# In[18]:
seg_df.head(2)
# In[51]:
#Converts a dataframe to a geojson Point output
def df_to_geojson_point(df, properties, lat='latitude', lon='longitude'):
geojson = {'type':'FeatureCollection', 'features':[]}
for _, row in df.iterrows():
feature = {'type':'Feature',
'properties':{},
'geometry':{'type':'Point',
'coordinates':[]}}
feature['geometry']['coordinates'] = [row[lon],row[lat]]
for prop in properties:
feature['properties'][prop] = row[prop]
geojson['features'].append(feature)
return geojson
#Converts a dataframe to a geojson LineString output
def df_to_geojson_line(df, properties, coords):
geojson = {'type':'FeatureCollection', 'features':[]}
for _, row in df.iterrows():
feature = {'type':'Feature',
'properties':{},
'geometry':{'type':'LineString',
'coordinates': ''}}
feature['geometry']['coordinates'] = row[coords]
for prop in properties:
feature['properties'][prop] = row[prop]
geojson['features'].append(feature)
return geojson
# In[52]:
geojson = df_to_geojson_line(seg_df, ['name', 'act_type', 'distance', 'elev_gain'], 'seg_points_decode')
# In[53]:
output_filename = 'dataset.js'
with open(output_filename, 'wb') as output_file:
output_file.write('var dataset = ')
json.dump(geojson, output_file, indent=2)
# In[10]:
seg_df.set_index('seg_id', inplace=True)
seg_df.drop(['start_lat','start_long','end_lat','end_long', 'rows_to_drop'], axis=1, inplace=True)
seg_df.to_sql('Segment', engine, if_exists='append', index=True, index_label='seg_id')
# In[34]:
from IPython.display import Javascript
# In[40]:
from IPython.display import Javascript
#Create a javascript variable with our geojson data to visualize in the browser
#The data object 'vizObj' will be a global varialbe in our window that
#We can pass to another javascript function call
Javascript("""window.vizObj={};""".format(geojson))
# In[42]:
get_ipython().run_cell_magic(u'javascript', u'', u'//Testing that the window.vizObj variable is accessable\nconsole.log(window.vizObj);')
# In[43]:
#Now let's make some HTML to style our intended mapbox output
from IPython.display import HTML
HTML("""
<style> #map {
position: relative;
width: auto;
height: 650px;
overflow:visible;
}
</style>
""")
# In[44]:
get_ipython().run_cell_magic(u'javascript', u'', u"//Load required javascript libraries\nrequire.config({\n paths: {\n mapboxgl: 'https://api.tiles.mapbox.com/mapbox-gl-js/v0.16.0/mapbox-gl',\n bootstrap: 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min'\n }\n});")
# In[56]:
get_ipython().run_cell_magic(u'javascript', u'', u'IPython.OutputArea.auto_scroll_threshold = 9999;\nrequire([\'mapboxgl\', \'bootstrap\'], function(mapboxgl, bootstrap){\n mapboxgl.accessToken = \'pk.eyJ1IjoicnNiYXVtYW5uIiwiYSI6IjdiOWEzZGIyMGNkOGY3NWQ4ZTBhN2Y5ZGU2Mzg2NDY2In0.jycgv7qwF8MMIWt4cT0RaQ\';\n var map = new mapboxgl.Map({\n container: \'map\', // container id\n style: \'mapbox://styles/mapbox/dark-v8\', //stylesheet location\n center: [-89.948470, 40.783860], // starting position\n zoom: 10 // starting zoom \n });\n \n \n function addSegLayer(mapid) {\n // Mapbox GL JS Api - import segment\n var segment_src = new mapboxgl.GeoJSONSource({\n data: window.vizObj,\n maxzoom: 18,\n buffer: 1,\n tolerance: 1\n });\n try {\n mapid.addSource(\'segment\', segment_src);\n mapid.addLayer({\n id: \'segment\',\n type: \'line\',\n source: \'segment\',\n paint: {\n "line-opacity": 1,\n "line-width": 5,\n "line-color": \'red\',\n }\n });\n } catch (err) {\n console.log(err);\n }\n };\n \n map.once(\'style.load\', function(e) {\n addSegLayer(map);\n map.addControl(new mapboxgl.Navigation({\n position: \'top-left\'\n }));\n });\n \n});\nelement.append("<div id=\'map\'></div>");')
# In[ ]: