Category: Hiking/Exploration

  • How to get GPX Files from the Strava API – strava2gpx (Python)

    How to get GPX Files from the Strava API – strava2gpx (Python)

    The current Strava API does not allow for users to request activities in the form of a gpx file. The strava2gpx package for Python uses the Strava API to pull data streams which it then compiles into GPX files, effectively allowing users to get GPX files from the Strava API. This tutorial will cover usage of the strava2gpx package in Python.

    Before beginning, it is important to note that gpx files deal with gps data, so Strava activities that do not have gps data, currently will not work with the strava2gpx package. (eg. Yoga, Weightlifting, Pool Swimming, etc.)

    Why not use strava.com/activities/id/export_gpx?

    Will this get heartrate data?

    Installing strava2gpx

    The strava2gpx Python package is available through PyPi and can be downloaded easily by running the following command in your development environment:

    pip install strava2gpx
    strava2gpx installation in terminal
    strava2gpx installation in terminal

    If you already have your credentials for Strava API access you can skip this section. Go to https://www.strava.com/settings/api and if you have not already, go through the steps to create an app there. Eventually, you should get to a screen that looks like this:

    Strava Api Application Page

    The information that we are interested in is the Client ID, Client Secret, and the Refresh Token. Copy those down in a safe spot and don’t share them.

    Next go to the following url and replace YOUR_CLIENT_ID with the Client ID that we just copied down.

    https://www.strava.com/oauth/authorize?client_id=[YOUR_CLIENT_ID]&response_type=code&redirect_uri=http://localhost/exchange_token&approval_prompt=force&scope=activity:read_all
    

    That will open a page that looks like this:

    We are interested in the code part of the url that it redirected to, which in this example is the part that says “9ds8fg340vss0f98sd3fdefwfs.” Copy that down as well. That will be YOUR_AUTH_CODE that we use later.

    Now run the following curl command in the terminal, being sure to replace the YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, and YOUR_AUTHORIZATION_CODE with the values we have previously retrieved:

    curl -X POST https://www.strava.com/oauth/token \
      -d client_id=YOUR_CLIENT_ID \
      -d client_secret=YOUR_CLIENT_SECRET \
      -d code=YOUR_AUTHORIZATION_CODE \
      -d grant_type=authorization_code

    That should return a json that has your athlete information and a refresh_token and an access_token.

    {
      "access_token": "your_access_token",
      "refresh_token": "your_refresh_token",
      ...
    }
    

    Copy down this new refresh_token for the next section.

    Getting the GPX File in Python

    Copy the code below into your code editor and replace client_id, client_secret, and refresh_token with their respective values you copied in the previous section from the Strava API. The client_id and client_secret come from the Strava API page and the refresh_token comes from the various web requests we made.

    from strava2gpx import strava2gpx
    import asyncio
    
    async def main():
        '''
        put in your Strava Api client_id, refresh_token, and client_secret
        '''
        client_id = '123456'
        refresh_token = 'adfh750a7s5df8a00dh7asdf98a9s8df6s9asdf8'
        client_secret = 'ahgdyt5672i3y8d345hgd2345c23hjgd1234yd23'
    
        # create an instance of strava2gpx
        s2g = strava2gpx(client_id, client_secret, refresh_token)
    
        # connect to the Strava API
        await s2g.connect()
    
        # write activity to output.gpx by activity id
        await s2g.write_to_gpx(11893637629, "output")
    
    if __name__ == '__main__':
        asyncio.run(main())

    On the line

    # write activity to output.gpx by activity id
        await s2g.write_to_gpx(11893637629, "output")

    you can replace the number (11893637629) with one of your own Strava activity IDs which can be found in the url when looking at them in Strava. (Ex: strava.com/activities/11893637629) This will pull the data from that activity and then compile it into a file called output.gpx. You can change the name of the file by editing the second argument where it currently reads “output”.

    Save the python file as strava2gpx.py and run it in the terminal with the following command

    python3 strava2gpx.py

    When the program has run to completion, you will have an “output.gpx” file in the same directory. This file can be uploaded to other services like Garmin Connect and similar and contains all data like heartrate, power, etc.

    The package provides an additional function called get_activities_list() which will get a list of all the user’s data and store it in a list of lists where each element is in the following format:

    [name, id, start_date, type]
    ex:
    ['Fun Run', 11893637629, '2024-07-15T11:50:49Z', 'Run']

    This gives room for bulk processing of activities using custom actions like using date-time to choose what activities are processed or naming them by id and whatnot. Below is an example of calling the function.

    from strava2gpx import strava2gpx
    import asyncio
    
    async def main():
        '''
        put in your Strava Api client_id, refresh_token, and client_secret
        '''
        client_id = '123456'
        refresh_token = 'adfh750a7s5df8a00dh7asdf98a9s8df6s9asdf8'
        client_secret = 'ahgdyt5672i3y8d345hgd2345c23hjgd1234yd23'
    
        # create an instance of strava2gpx
        s2g = strava2gpx(client_id, client_secret, refresh_token)
    
        # connect to the Strava API
        await s2g.connect()
    
        # get a list of all user's Strava activities
      activities_list = await s2g.get_activities_list()  
      print(activities_list[0:5])
    
    if __name__ == '__main__':
        asyncio.run(main())

    The above code would generate the following output:

    [
        ['Legs may be sore', 11910466229, '2024-07-17T11:57:21Z', 'Ride'],
        ['Tall Grass, Hidden Dirt', 11906994862, '2024-07-17T00:10:57Z', 'Ride'],
        ['A little thunder there, a little MTB here', 11898361818, '2024-07-16T01:16:13Z', 'Ride'],
        ['Morning Run', 11893637629, '2024-07-15T11:50:49Z', 'Run'],
        ['Afternoon Yoga', 11880523323, '2024-07-13T19:09:04Z', 'Yoga']
    ]
  • Holmes Cabin – Kaysville, UT

    Holmes Cabin – Kaysville, UT

    Holme’s Cabin is a lesser known cabin located about halfway up the mountains East of Kaysville. It is accessible starting from the Kaysville East Wilderness Park trailhead and involves climbing 2800 feet of elevation in about three miles making it a very steep hike or run. It is on the ridge South of Adam’s Canyon and North of its namesake Holmes Creek.

    Starting the Journey

    The closest place to park is at East Mountain Wilderness Park. From there, you can travel up the dirt road until you get to the turn off for the Bonneville Shoreline Trail. Keep north on the trail, going past the bridge until you get to this point pictured below and also marked here on google maps.

    The fork in the path towards Holmes' cabin

    At the blue marker pointing left for BST, you will want to take a right to start going up the mountain. The next section of trail is crossed by a lot of other smaller foot paths through the sage brush. A good rule of thumb for keeping on the right path is that you should always be heading up the mountain. There are no downhills and the path does not cut across the mountain to make the way less steep.

    The trek to the cabin is not a lot of mileage, but the steep trail conditions can make it take many hours. If done in the winter, expect considerable snow.

    James in snow up to his knees

    In snowy conditions it is particularly difficult to find the trail since it is not typically well traveled. In this last winter I had to consult maps on my watch very frequently to keep on the right track towards the end of the run.

    The Cabin

    Of the Davis County cabins, Holmes cabin is a sturdy contender. It is not as robust as its neighbor Adam’s cabin, but it is in far better condition than Fernwood cabin which at this point I am guessing has collapsed. Holmes cabin has quite a lot of provisions, though don’t expect free food. The cans of food that were there earlier this year expired in 2014. There are enough supplies to start a fire though if needed. Additionally, there are also sleeping bags hanging from the rafters.

    Sleeping bags hanging in Holmes' cabin
    Some Sleeping Bags

    Years ago there was a blog post about some winter backpackers who stayed the night at Holmes cabin. That blog seems to have since gone offline, but to my remembrance they appeared to have had an enjoyable time staying the night. It is important to note, that like the other cabins, you do have to worry about rodent droppings. Squirrels and mice are frequent inhabitants of these cabins and they leave stuff everywhere. The first time I visited Holmes cabin, I opened the door and squirrels went scurrying out from everywhere much to the delight of my dog.

    Racer resting at Holmes Cabin
    Racer resting at Holmes Cabin

    Holmes Cabin Pictures