Strava api to export GPX file of activity
Asked Answered
I

3

8

I am well able to download my GPX file of an activity when logged in to Strava and calling this link: https://www.strava.com/activities/5656012590/export_original I get the original gpx. It looks as I need it.

Is there a v3 api way? I would like to access it with the swagger generated code, a la

new ActivitiesApi(getApiClientWithToken(token)).getLoggedInAthleteActivities(System.currentTimeSeconds().toInteger(), 0, 1, 30)

(Groovy code, this works for getting activities)

The only thing I found is https://www.strava.com/api/v3/routes/{id}/export_gpx. But from what I see in the Api response of activities, there is no route attached to it. In activities I can see an 'externalId' which is set to something like '123456123.gpx' and I can see the polylines from the map. But converting polylines sounds like too much effort now and I guess it misses some points. Accessing the externalID, I have no idea.

In the end I don't really care how to get the GPX. If it is a cURL call with passing the token via post and then downloading it, would be fine, as well as getting it with the Java API from swagger. I would prefer the latter option though.

Inexpert answered 20/7, 2021 at 15:39 Comment(1)
well. In the end there seems to be no way. What I did is getting the activity stream and creating my own GPX out of it. It might have a lower resolution than the original file, but for my use case this is ok.Inexpert
I
2

Closest I could get is using the streams API as described in other posts.

        StreamSet streamSet = streamsApi.getActivityStreams(activityId as long, ['latlng', 'altitude', 'time'], true)

        def altitudes = streamSet.getAltitude().getData()
        def latLong = streamSet.getLatlng().getData()
        def times = streamSet.getTime().getData()

        ArrayList<StreamData> data = []
        for (int i = 0; i < times.size(); i++) {
            StreamData streamData = new StreamData()
            streamData.id = activityId as long
            streamData.time = times[i]
            streamData.startTime = startTime
            streamData.altitude = altitudes[i]
            streamData.latitude = latLong[i][0]
            streamData.longitude = latLong[i][1]
            data << streamData
        }

and then building my own GPX file from it:

    def stringWriter = new StringWriter()
    def gpxBuilder = new MarkupBuilder(stringWriter)

    gpxBuilder.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
    gpxBuilder.gpx() {
        metadata() {
            time(startTime)
        }
        trk() {
            name(NEW_ACTIVITY_NAME)
            type(convertedType)
            trkseg() {
                for (def item : streamData) {
                    trkpt(lat: item.latitude, lon: item.longitude) {
                        ele(item.altitude)
                        time(UtcTimeString)
                    }
                }
            }
        }
    }

    stringWriter.toString()

Uploading this to Strava works well.

Inexpert answered 27/7, 2021 at 13:1 Comment(0)
L
8

Just adding this here in case it is useful. The Strava API also has the ability to download data streams (see here API Documentation). I believe this gives slightly more info such as private activities and GPX points within privacy zones etc. assuming the API scope is set to read_all.

I was using the API with Python but I assume it works basically identically in Java. Also note I am just doing this as a hobby so there is likely a more efficient way to do the following...

Example on creating gpx file available here

The code below should download data streams from the Strava API and then generate a GPX file from these data streams.

import requests as r
import pandas as pd
import json
import gpxpy.gpx
from datetime import datetime, timedelta

#This info is in activity summary downloaded from API
id = 12345678 #activity ID
start_time = '2022-01-01T12:04:10Z' #activity start_time_local

#get access token for API
with open('config/strava_tokens.json') as f:
    strava_tokens = json.load(f)
access_token = strava_tokens['access_token']

# Make API call
url = f"https://www.strava.com/api/v3/activities/{id}/streams"
header = {'Authorization': 'Bearer ' + access_token}
latlong = r.get(url, headers=header, params={'keys':['latlng']}).json()[0]['data']
time_list = r.get(url, headers=header, params={'keys':['time']}).json()[1]['data']
altitude = r.get(url, headers=header, params={'keys':['altitude']}).json()[1]['data']

# Create dataframe to store data 'neatly'
data = pd.DataFrame([*latlong], columns=['lat','long'])
data['altitude'] = altitude
start = datetime.strptime(start_time, "%Y-%m-%dT%H:%M:%SZ")
data['time'] = [(start+timedelta(seconds=t)) for t in time_list]

gpx = gpxpy.gpx.GPX()
# Create first track in our GPX:
gpx_track = gpxpy.gpx.GPXTrack()
gpx.tracks.append(gpx_track)
# Create first segment in our GPX track:
gpx_segment = gpxpy.gpx.GPXTrackSegment()
gpx_track.segments.append(gpx_segment)
# Create points:
for idx in data.index:
    gpx_segment.points.append(gpxpy.gpx.GPXTrackPoint(
                data.loc[idx, 'lat'],
                data.loc[idx, 'long'],
                elevation=data.loc[idx, 'altitude'],
                time=data.loc[idx, 'time']
    ))
# Write data to gpx file
with open('output.gpx', 'w') as f:
    f.write(gpx.to_xml())

I've just added a python code to GitHub that should hopefully download all a users GPX files from Strava.

Lise answered 11/1, 2022 at 10:39 Comment(0)
I
2

Closest I could get is using the streams API as described in other posts.

        StreamSet streamSet = streamsApi.getActivityStreams(activityId as long, ['latlng', 'altitude', 'time'], true)

        def altitudes = streamSet.getAltitude().getData()
        def latLong = streamSet.getLatlng().getData()
        def times = streamSet.getTime().getData()

        ArrayList<StreamData> data = []
        for (int i = 0; i < times.size(); i++) {
            StreamData streamData = new StreamData()
            streamData.id = activityId as long
            streamData.time = times[i]
            streamData.startTime = startTime
            streamData.altitude = altitudes[i]
            streamData.latitude = latLong[i][0]
            streamData.longitude = latLong[i][1]
            data << streamData
        }

and then building my own GPX file from it:

    def stringWriter = new StringWriter()
    def gpxBuilder = new MarkupBuilder(stringWriter)

    gpxBuilder.mkp.xmlDeclaration(version: "1.0", encoding: "utf-8")
    gpxBuilder.gpx() {
        metadata() {
            time(startTime)
        }
        trk() {
            name(NEW_ACTIVITY_NAME)
            type(convertedType)
            trkseg() {
                for (def item : streamData) {
                    trkpt(lat: item.latitude, lon: item.longitude) {
                        ele(item.altitude)
                        time(UtcTimeString)
                    }
                }
            }
        }
    }

    stringWriter.toString()

Uploading this to Strava works well.

Inexpert answered 27/7, 2021 at 13:1 Comment(0)
G
1

Similar to the other answers about data streams, if you want to include additional data fields like heart rate, I have made a python package strava2gpx that will let you do all of that with the Strava API. It grabs the data using the data streams from the Strava API and then compiles the activity with all of its data streams into a gpx file.

Project Github | PyPI

pip install strava2gpx

from strava2gpx import strava2gpx
import asyncio

async def main():
    client_id = '123456'
    refresh_token = 'adfh750a7s5df8a00dh7asdf98a9s8df6s9asdf8'
    client_secret = 'ahgdyt5672i3y8d345hgd2345c23hjgd1234yd23'

# create an instance of strava2gpx
s2g = strava2gpx(client_id, client_secret, refresh_token)

# connect to 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())
Greenfinch answered 18/7 at 23:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.