Create artistic visualisations with your exercise data (Python version)

Overview

strava_py

Create artistic visualisations with your exercise data (Python version).

This is a port of the R strava package to Python.

Examples

Facets

A plot of activities as small multiples. The concept behind this plot was originally inspired by Sisu.

facets

Map

A map of activities viewed in plan.

map

How to use

Bulk export from Strava

The process for downloading data is described on the Strava website here: [https://support.strava.com/hc/en-us/articles/216918437-Exporting-your-Data-and-Bulk-Export#Bulk], but in essence, do the following:

  1. Log in to Strava
  2. Select "Settings" from the main drop-down menu at top right of the screen
  3. Select "My Account" from the navigation menu to the left of the screen.
  4. Under the "Download or Delete Your Account" heading, click the "Get Started" button.
  5. Under the "Download Request", heading, click the "Request Your Archive" button. Don't click anything else on that page, i.e. particularly not the "Request Account Deletion" button.
  6. Wait for an email to be sent
  7. Click the link in email to download zipped folder containing activities
  8. Unzip files

Process the data

The main function for importing and processing activity files expects a path to a directory of unzipped GPX and / or FIT files. If required, the fit2gpx package provides useful tools for pre-processing bulk files exported from Strava, e.g. unzipping activity files (see Use Case 3: Strava Bulk Export Tools).

df = process_data(<path to folder with GPX and / or FIT files>)

Plot activities as small multiples

plot_facets(df, output_file = 'plot.png')

Plot activity map

plot_map(df, lon_min=None, lon_max= None, lat_min=None, lat_max=None,
             alpha=0.3, linewidth=0.3, output_file="map.png")
Comments
  • Getting:

    Getting: "gpxpy.gpx.GPXException: latitude is mandatory in None (got None)"

    Hey,

    I tried using the CLI tool but I'm getting this error: gpxpy.gpx.GPXException: latitude is mandatory in None (got None)

    I tried setting --lon_max, --lat_max to 180,90 and --lat_min, --lon_min to 0,0 but still getting the same error.

    Is this a problem with a specific .gpx file? And if so how can I find that one because the error doesn't show which file.

    Thanks

    opened by Alpha249 7
  • Starting cli.py gives error: ModuleNotFoundError: No module named 'stravavis'

    Starting cli.py gives error: ModuleNotFoundError: No module named 'stravavis'

    Thanks for the app, it looks quite nice. But as I'm a bit new to Python, I'm having trouble to get it started.

    I cloned the repo and tried from its root directory: [email protected] strava_py % python3 src/stravavis/cli.py /Users/jmizv/Downloads/gpx

    But I just got that error message:

    Traceback (most recent call last):
      File "/Users/jmizv/IdeaProjects/strava_py/src/stravavis/cli.py", line 91, in <module>
        main()
      File "/Users/jmizv/IdeaProjects/strava_py/src/stravavis/cli.py", line 41, in main
        from stravavis.plot_calendar import plot_calendar
    ModuleNotFoundError: No module named 'stravavis'
    [email protected] strava_py % 
    

    I'm probably missing something obvious but I don't see it. Could you, @marcusvolz, please give an advice?

    opened by jmizv 7
  • Add CLI as strava_py

    Add CLI as strava_py

    This PR adds a CLI for creating visualisations, and puts it into an installable package, ready for distributions via https://pypi.org so it can be installed using pip. (I can help with how to do that later!)

    First, I moved the strava_py directory into a src directory. This is commonly used for Python projects. The main benefits is to make sure when you're testing, you're testing against something that has been installed, and not something that happens to have the same directory name in your current dir. Much more:

    • https://packaging.python.org/en/latest/tutorials/packaging-projects/#a-simple-project
    • https://blog.ionelmc.ro/2014/05/25/python-packaging/
    • https://hynek.me/articles/testing-packaging/

    The way to install from source:

    pip install .
    

    Or if you're developing, -e means an editable install, so you can make changes to your local source tree and they're reflected in what's run:

    pip install -e .
    

    Then I went for strava_py as the CLI name (this can be changed):

    $ strava_py --help
    usage: strava_py [-h] [-o OUTPUT_FILE] path
    
    positional arguments:
      path                  Input path to folder with GPX and / or FIT files
    
    options:
      -h, --help            show this help message and exit
      -o OUTPUT_FILE, --output_file OUTPUT_FILE
                            Output PNG file (default: plot.png)
    

    Example run:

    $ strava_py /tmp/my_strava_activities
    Processing data...
    Processing: 6628958226.gpx
    Processing: 6626094662.gpx
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 142); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 162); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 203); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 249); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 285); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 326); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 351); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 397); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 443); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 487); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 531); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 575); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 619); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 663); adding dummy dev data...
      warnings.warn(msg)
    /Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 707); adding dummy dev data...
      warnings.warn(msg)
    Processing: 6678512291.fit
    Processing: 6619621876.gpx
    Processing: 6604948302.gpx
    Processing: 6860888905.fit
    Processing: 6678648994.fit
    Processing: 6676505895.fit
    Processing: 6810573487.fit
    Processing: 6623947053.gpx
    Processing: 6630963143.gpx
    Plotting facets...
    Saved to plot.png
    

    I'll also add a bunch of inline comments to explain what some things do, please feel free to ask more about any of this!

    opened by hugovk 3
  • Elevations

    Elevations

    @hugovk Thanks for reviewing these and for the recent PRs. This one just adds the elevations small multiples plot.

    I think I might have missed the suggested commits in your previous PRs, could you let me know if everything went through ok? Thanks!

    opened by marcusvolz 2
  • ValueError: All objects passed were None

    ValueError: All objects passed were None

    Hi, I have been trying to apply this package on my data pull from Strava but in the first step of processing data I'm getting this error of "ValueError: All objects passed were None" attaching a screenshot for reference. I tried to give address of either the activities or routes folder which has gpx files but still the error remains constant Screenshot 2022-12-25 at 16 03 22

    opened by shu3hamiitkgp 1
  • Fix squashed maps

    Fix squashed maps

    This is similar to https://github.com/marcusvolz/strava/issues/5.

    Here's the same GPX using main:

    strava-map

    Like https://stackoverflow.com/a/14457180/724176, rather than using latitude and longitude values, this uses a Mercator projection calculation to come up with x and y values instead.

    Dummy, unit map width and height values are used, as we don't know and don't need to know the final size as matplotlib will take care of that.

    And with this PR:

    strava-map

    Measuring performance:

    With main, on 2,946 GPX files (but commenting out some bits of cli.py to only run "Processing data..." and "Plotting map..."), it takes 11m45s on a Mac M1 16GB.

    With the PR it takes a comparable 11m43s, which is really good because the transform is done by Pandas so presumably in C.

    opened by hugovk 1
  • UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>

    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to

    When I execute stravavis against this single attached file (5670595833.gpx.txt, you need to remove the txt extension) it fails with the below error. I tend to add emojis to my strava activity titles so this is in my opinion the problem. Not sure if you could exclude this easily? The files are certainly not encoded with Cp1252.

    C:\Users\...>stravavis C:\Users\...
    Processing data...
    Processing ----------------------------------------   0% -:--:--
    multiprocessing.pool.RemoteTraceback:
    """
    Traceback (most recent call last):
      File "C:\Program Files\Python310\lib\multiprocessing\pool.py", line 125, in worker
        result = (True, func(*args, **kwds))
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 13, in process_file
        return process_gpx(fpath)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 21, in process_gpx
        activity = gpxpy.parse(open(gpxfile))
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\__init__.py", line 37, in parse
        parser = mod_parser.GPXParser(xml_or_file)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\parser.py", line 70, in __init__
        self.init(xml_or_file)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\parser.py", line 82, in init
        text = xml_or_file.read() if hasattr(xml_or_file, 'read') else xml_or_file # type: ignore
      File "C:\Program Files\Python310\lib\encodings\cp1252.py", line 23, in decode
        return codecs.charmap_decode(input,self.errors,decoding_table)[0]
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>
    """
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "C:\Users\...\AppData\Roaming\Python\Python310\Scripts\stravavis.exe\__main__.py", line 7, in <module>
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\cli.py", line 51, in main
        df = process_data(args.path)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 108, in process_data
        processed = list(it)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\rich\progress.py", line 168, in track
        yield from progress.track(
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\rich\progress.py", line 1211, in track
        for value in sequence:
      File "C:\Program Files\Python310\lib\multiprocessing\pool.py", line 870, in next
        raise value
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>
    

    5670595833.gpx.txt

    bug 
    opened by jmizv 1
  • Add --bbox CLI param as a shortcut

    Add --bbox CLI param as a shortcut

    It's a bit tedious to enter four params and values on the command line when creating a map:

    stravavis activities --lon_min 23.516665 --lat_min 60.000467 --lon_max 26.390877 --lat_max 61.360291

    This PR adds a --bbox shortcut for those, that takes a bounding box of comma-separated values of lon_min, lat_min, lon_max, lat_max:

    stravavis activities --bbox 23.516665,60.000467,26.390877,61.360291

    It's quite easy to find bounding boxes online, for example: http://bboxfinder.com/#60.000467,23.516665,61.360291,26.390877


    I also noticed that these lat/lon values are not actually passed from the CLI to the plotting function, and neither are alpha or linewidth, so let's fix that too :)


    Finally, bump some GitHub Actions versions for the CI.

    enhancement 
    opened by hugovk 1
  • Updated README: Add installation instructions and link directly to images

    Updated README: Add installation instructions and link directly to images

    Images are broken on PyPI:

    image

    Instead of linking to things like https://github.com/marcusvolz/strava_py/blob/main/plots/facets001.png, which is an HTML page showing the image, and which GitHub can apparently handle redirects to for the README, link directly to the image file, like https://raw.githubusercontent.com/marcusvolz/strava_py/main/plots/facets001.png

    This also adds installation instructions to the README.

    opened by hugovk 1
  • More progress bars for slow loops, and refactor

    More progress bars for slow loops, and refactor

    Also in Python we generally prefer to iterate over objects in a list (or tuple, or other so-called "iterable") rather than using an index of range(len(thing)).

    So for example, instead of:

    n = len(activities)
    for i in range(n):
        print(activities[i])
    

    This is more direct and readable:

    for activity in activities:
        print(activity)
    

    If we do need an index as well, enumerate is often used:

    for i, activity in enumerate(activities):
        print(i, activity)
    
    opened by hugovk 1
  • Use multiprocessing to speed up processing of input files

    Use multiprocessing to speed up processing of input files

    Use multiprocessing to speed up processing of input files, using the number of processes which matches the CPU count.

    On my old dual-core Mac, the processing step speeds up:

    • 601 files (all of 2021): 2m7s -> 1m20s
    • 2,629 files (my full Strava archive): 10m29s -> 5m9s

    Will be a larger gain for machines with more CPUs.

    multiprocessing needs a single worker function, this is the new process_file which decides which of process_gpx and process_fit to call. They also need to be top-level functions to work with multiprocessing.

    I also formatted the file using the popular Black autoformatter:

    python -m pip install -U black
    black . 
    

    I also fixed a bug when using an input path like ~/dir, it needs to expand ~ before checking it's a dir (and appending *).

    opened by hugovk 1
Releases(v0.1.0)
  • v0.1.0(Dec 30, 2022)

    What's Changed

    • Add --bbox CLI param as a shortcut by @hugovk in https://github.com/marcusvolz/strava_py/pull/18
    • Add support for Python 3.11 by @hugovk in https://github.com/marcusvolz/strava_py/pull/26
    • Catch exception to skip invalid file by @hugovk in https://github.com/marcusvolz/strava_py/pull/27
    • Fix encoding on Windows by @hugovk in https://github.com/marcusvolz/strava_py/pull/20
    • Fix squashed maps by @hugovk in https://github.com/marcusvolz/strava_py/pull/21
    • Fix README typo by @hugovk in https://github.com/marcusvolz/strava_py/pull/24

    Full Changelog: https://github.com/marcusvolz/strava_py/compare/v0.0.2...v0.1.0

    Source code(tar.gz)
    Source code(zip)
  • v0.0.2(Sep 1, 2022)

    What's Changed

    • Updated README: Add installation instructions and link directly to images by @hugovk in https://github.com/marcusvolz/strava_py/pull/17

    Full Changelog: https://github.com/marcusvolz/strava_py/compare/v0.0.1...v0.0.2

    Source code(tar.gz)
    Source code(zip)
  • v0.0.1(Sep 1, 2022)

    What's Changed

    • Add CLI as strava_py by @hugovk in https://github.com/marcusvolz/strava_py/pull/1
    • added plot_map by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/2
    • Rename package and CLI program to stravavis by @hugovk in https://github.com/marcusvolz/strava_py/pull/3
    • Allow input path specification and show progress bar by @hugovk in https://github.com/marcusvolz/strava_py/pull/4
    • Elevations by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/5
    • Test run on GitHub Actions by @hugovk in https://github.com/marcusvolz/strava_py/pull/6
    • Landscape by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/8
    • Use multiprocessing to speed up processing of input files by @hugovk in https://github.com/marcusvolz/strava_py/pull/9
    • Calendar by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/10
    • Fix local variable 'activities' referenced before assignment by @hugovk in https://github.com/marcusvolz/strava_py/pull/11
    • Fix ValueError with --activities_path by @hugovk in https://github.com/marcusvolz/strava_py/pull/12
    • Dumbbell by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/13
    • Allow --activities_path to point to file or directory by @hugovk in https://github.com/marcusvolz/strava_py/pull/14
    • More progress bars for slow loops, and refactor by @hugovk in https://github.com/marcusvolz/strava_py/pull/15

    New Contributors

    • @hugovk made their first contribution in https://github.com/marcusvolz/strava_py/pull/1
    • @marcusvolz made their first contribution in https://github.com/marcusvolz/strava_py/pull/2

    Full Changelog: https://github.com/marcusvolz/strava_py/commits/v0.0.1

    Source code(tar.gz)
    Source code(zip)
Owner
Marcus Volz
Data visualisation | mathematical visualisation | 3D visualisation
Marcus Volz
A simple Monte Carlo simulation using Python and matplotlib library

Monte Carlo python simulation Install linux dependencies sudo apt update sudo apt install build-essential \ software-properties-commo

Samuel Terra 2 Dec 13, 2021
Flexitext is a Python library that makes it easier to draw text with multiple styles in Matplotlib

Flexitext is a Python library that makes it easier to draw text with multiple styles in Matplotlib

Tomás Capretto 93 Dec 28, 2022
A set of three functions, useful in geographical calculations of different sorts

GreatCircle A set of three functions, useful in geographical calculations of different sorts. Available for PHP, Python, Javascript and Ruby. Live dem

72 Sep 30, 2022
NorthPitch is a python soccer plotting library that sits on top of Matplotlib

NorthPitch is a python soccer plotting library that sits on top of Matplotlib.

Devin Pleuler 30 Feb 22, 2022
kyle's vision of how datadog's python client should look

kyle's datadog python vision/proposal not for production use See examples/comprehensive.py for a mostly working example of the proposed API. 📈 🐶 ❤️

Kyle Verhoog 2 Nov 21, 2021
a python function to plot a geopandas dataframe

Pretty GeoDataFrame A minimum python function (~60 lines) to draw pretty geodataframe. Based on matplotlib, shapely, descartes. Installation just use

haoming 27 Dec 05, 2022
Generate the report for OCULTest.

Sample report generated in this function Usage example from utils.gen_report import generate_report if __name__ == '__main__': # def generate_rep

Philip Guo 1 Mar 10, 2022
Sky attention heatmap of submissions to astrometry.net

astroheat Installation Requires Python 3.6+, Tested with Python 3.9.5 Install library dependencies pip install -r requirements.txt The program require

4 Jun 20, 2022
The implementation of the paper "HIST: A Graph-based Framework for Stock Trend Forecasting via Mining Concept-Oriented Shared Information".

The HIST framework for stock trend forecasting The implementation of the paper "HIST: A Graph-based Framework for Stock Trend Forecasting via Mining C

Wentao Xu 111 Jan 03, 2023
A blender import/export system for Defold

defold-blender-export A Blender export system for the Defold game engine. Setup Notes There are no exhaustive documents for this tool yet. Its just no

David Lannan 27 Dec 30, 2022
I'm doing Genuary, an aritifiacilly generated month to build code that make beautiful things

Genuary 2022 I'm doing Genuary, an aritifiacilly generated month to build code that make beautiful things. Every day there is a new prompt for making

Joaquín Feltes 1 Jan 10, 2022
An automatic prover for tautologies in Metamath

completeness An automatic prover for tautologies in Metamath This program implements the constructive proof of the Completeness Theorem for propositio

Scott Fenton 2 Dec 15, 2021
Python & Julia port of codes in excellent R books

X4DS This repo is a collection of Python & Julia port of codes in the following excellent R books: An Introduction to Statistical Learning (ISLR) Stat

Gitony 5 Jun 21, 2022
Info for The Great DataTas plot-a-thon

The Great DataTas plot-a-thon Datatas is organising a Data Visualisation competition: The Great DataTas plot-a-thon We will be using Tidy Tuesday data

2 Nov 21, 2021
A high-level plotting API for pandas, dask, xarray, and networkx built on HoloViews

hvPlot A high-level plotting API for the PyData ecosystem built on HoloViews. Build Status Coverage Latest dev release Latest release Docs What is it?

HoloViz 697 Jan 06, 2023
A toolkit to generate MR sequence diagrams

mrsd: a toolkit to generate MR sequence diagrams mrsd is a Python toolkit to generate MR sequence diagrams, as shown below for the basic FLASH sequenc

Julien Lamy 3 Dec 25, 2021
Lightweight data validation and adaptation Python library.

Valideer Lightweight data validation and adaptation library for Python. At a Glance: Supports both validation (check if a value is valid) and adaptati

Podio 258 Nov 22, 2022
Advanced hot reloading for Python

The missing element of Python - Advanced Hot Reloading Details Reloadium adds hot reloading also called "edit and continue" functionality to any Pytho

Reloadware 1.9k Jan 04, 2023
The interactive graphing library for Python (includes Plotly Express) :sparkles:

plotly.py Latest Release User forum PyPI Downloads License Data Science Workspaces Our recommended IDE for Plotly’s Python graphing library is Dash En

Plotly 12.7k Jan 05, 2023