diff --git a/reference/changelog/index.html b/reference/changelog/index.html index c6b81340..55442e7b 100644 --- a/reference/changelog/index.html +++ b/reference/changelog/index.html @@ -873,6 +873,30 @@ + +
This is the pre-release series. Things haven't settled yet, so each minor version might introduce breaking changes.
activities.csv
from the Strava export.This is a software to view recorded outdoor activities and derive various insights from your data collection. All data is kept on your machine, hence it is suitable for people who have an affinity for data analysis and privacy.
It caters to serve a similar purpose as Strava with Statshunters statshunters does but while not requiring you to share your location data with a service provider.
One can use this program with a local collection of activity files (GPX, FIT, TCX, KML, CSV) or via the Strava API. The latter is appealing to people who want to keep their data with Strava primarily. In case that one wants to move, this might provide a noncommital way of testing out this project.
The main user interface is web-based, you can run this on your Linux, Mac or Windows laptop. If you want, it can also run on a home server or your personal cloud instance.
"},{"location":"#screenshot-tour","title":"Screenshot tour","text":"This is the view of a single activity:
You also get a beautiful interactive heatmap of all your activities:
Also there are plenty of summary statistics that lets you track how many rides, walks or hikes you have done:
If you're into explorer tiles or squadratinhos, this got you covered:
The configuration options are available within the interface such that you do not have to work with configuration files (like in earlier versions):
"},{"location":"#get-started","title":"Get started","text":"Install the software using one of the following options:
Get your activity data in place using one of the following options:
You can find the code on GitHub where you can also file issues. If you would like to use this yourself or contribute, feel free to reach out via the contact options from my website. I would especially appreciate improvements to the documentation. If you're familiar with Markdown and GitHub, you can also directly create a pull request. The code is licensed under the very permissive MIT license.
"},{"location":"acknowledgments/","title":"Acknowledgments","text":"This project builds on many amazing other projects and would not be possible without them.
"},{"location":"acknowledgments/#bootstrap-css","title":"Bootstrap CSS","text":"Writing CSS is not a trivial task. For many projects I have been using the Bootstrap CSS Framework which provides sensible default values, a 12-column grid system and a lot of components. Using this I didn't have to write any CSS myself and just attach a couple of classes to HTML elements.
"},{"location":"acknowledgments/#coloredlogs","title":"coloredlogs","text":"Log messages in multiple colors are neat. Using the coloredlogs package we can get these super easily.
"},{"location":"acknowledgments/#fitdecode","title":"fitdecode","text":"For reading FIT files I use the fitdecode library which completely handles all the parsing of this file format.
"},{"location":"acknowledgments/#flask","title":"Flask","text":"The webserver is implemented with Flask which provides a really easy way to get started. It also ships with a development webserver which is enough for this project at the moment.
"},{"location":"acknowledgments/#geojson","title":"GeoJSON","text":"Transferring geographic geometry data from the Python code to Leaflet is easiest with using the GeoJSON format. The official standard RFC is a bit hard to read, rather have a look at the Wikipedia article. And there is an online viewer that you can try out.
"},{"location":"acknowledgments/#github","title":"GitHub","text":"For a smooth open source project one needs a place to share the code and collect issues. GitHub provides all of this for free.
"},{"location":"acknowledgments/#gpxpy","title":"gpxpy","text":"For reading GPX files I use the gpxpy library. This allows me to read those files without having to fiddle with the underlying XML format.
"},{"location":"acknowledgments/#leaflet","title":"Leaflet","text":"The interactive maps on the website are powered by Leaflet, a very easy to use JavaScript library for embedding interactive Open Street Map maps. It can also display GeoJSON geometries natively, of which I also make heavy use.
"},{"location":"acknowledgments/#mkdocs","title":"MkDocs","text":"Writing documentation is more fun with a nice tool, therefore I use MkDocs together with Material for MkDocs. This powers this documentation.
"},{"location":"acknowledgments/#open-street-map","title":"Open Street Map","text":"All the maps displayed use tiles from the amazing Open Street Map. This map is created by volunteers, the server hosting is for free. Without these maps this project would be quite boring.
"},{"location":"acknowledgments/#pandas","title":"Pandas","text":"Working with thousands of activities, thousands of tiles and millions of points makes it necessary to have a good library for number crunching structured data. Pandas offers this and gives good performance and many features.
"},{"location":"acknowledgments/#parquet","title":"Parquet","text":"I need to store the intermediate data frames that I generate with Pandas. Storing as JSON has disadvantages because dates are not properly encoded. Also it is a text format and quite verbose. The Parquet format is super fast and memory efficient.
"},{"location":"acknowledgments/#poetry","title":"Poetry","text":"For managing all the Python package dependencies I use Poetry which makes it very easy to have all the Python project housekeeping with one tool.
"},{"location":"acknowledgments/#python","title":"Python","text":"Almost all of the code here is written in Python, a very nice and versatile programming language with a vast ecosystem of packages.
"},{"location":"acknowledgments/#requests","title":"Requests","text":"For doing HTTP requests I use the Requests library. It provides a really easy to use interface for GET and POST requests.
"},{"location":"acknowledgments/#scikit-learn","title":"Scikit-learn","text":"Finding out which cluster is the largest one can either be formed as a graph search problem or as a data science problem. Using the Scikit-learn library I can easily use the DBSCAN algorithm to find the clusters of explorer tiles.
"},{"location":"acknowledgments/#statshunters","title":"Statshunters","text":"The Statshunters page allows to import the activities from Strava and do analysis like explorer tiles, Eddington number and many other things. This has served as inspiration for this project.
"},{"location":"acknowledgments/#strava","title":"Strava","text":"Although I have recorded some of my bike rides, I only really started to record all of them when I started to use Strava. This is a nice platform to track all activities. They also offer a social network feature, which I don't really use. They provide some analyses of the data, but they lack some analyses which I have now implemented in this project.
"},{"location":"acknowledgments/#stravalib","title":"stravalib","text":"Strava has an API, and with stravalib there exists a nice Python wrapper. This makes it much easier to interface with Strava.
"},{"location":"acknowledgments/#strava-local-heatmap","title":"Strava local heatmap","text":"https://github.com/remisalmon/Strava-local-heatmap
"},{"location":"acknowledgments/#tcxreader","title":"tcxreader","text":"https://github.com/alenrajsp/tcxreader
"},{"location":"acknowledgments/#vega-altair","title":"Vega & Altair","text":"https://altair-viz.github.io/index.html https://vega.github.io/vega/
"},{"location":"acknowledgments/#velo-viewer","title":"Velo Viewer","text":""},{"location":"features/activity-view/","title":"Activity View","text":"When you have selected a particular activity, you can view various details about it. This is what the screen looks like, we will go through the different parts in the following.
"},{"location":"features/activity-view/#metadata","title":"Metadata","text":"You have a column with metadata about the activity. The activity kind, whether it is a commute and the equipment are currently only supported via the Strava API, but we can build something to infer that from directories as well.
The calories are broken in the Strava API wrapper library that I use, therefore they don't show even if they are there.
You can also see the ID which is an internal ID. When you use Strava API as a source, it will use the IDs that Strava gives. When you use files from a directory it will be computed from a hash of the path to the activity file.
"},{"location":"features/activity-view/#map-with-track","title":"Map with track","text":"The interactive map shows a line with the activity. The speed is color-coded and peaks at 35 km/h with a yellow color.
"},{"location":"features/activity-view/#distance-speed-and-altitude","title":"Distance, speed and altitude","text":"Then there are a couple of time series plots. One is the distance vs. time. You can see how much distance you covered when and also see plateaus when you went on a break.
From this we can also compute the speed, although that might be pretty noisy:
And more interesting is the distribution of the various speed zones. This gives you an understanding how much time you spent at which speed. The buckets are set in 5 km/h intervals, but we could also change that.
If the time series data has the altitude, which isn't always the case, you can see it in the plot there. Here we can see how I did a tour and continually rode downhill. Except at the end where I had to climb in order to get the explorer tile that I wanted.
"},{"location":"features/activity-view/#heart-rate","title":"Heart rate","text":"The heart rate isn't too helpful, I feel. Still I've created the plot from the given data.
More interesting regarding the heart rate are the zones which one has spent time during this activity.
The definition of the heart rate zones is not standardized. Usually there are five zones and they have the same names. What differs is how their ranges are computed and there is some chaos around that.
All definitions that I found take the maximum heart rate as the upper limit. One can measure this as part of a professional training or just use the 220 minus age prescription which at least for me matches close enough. What they differ on is how they use a lower bound. It seems that Polar or REI basically use 0 as the lower bound. My Garmin system also uses 0 as the lower bound. But as one can see in this blog, one can also use the resting heart rate as the lower bound.
Based on the maximum and resting heart rate we will then compute the heart rate zones using certain percentages of effort. We can compute the heart rate as the following:
rate = effort \u00d7 (maximum \u2013 minimum) + minimum
The zones then take the following efforts:
Zone Effort Training 1 50 to 60 % Warmup/Recovery 2 60 to 70 % Base Fitness 3 70 to 80 % Aerobic Endurance 4 80 to 90 % Anerobic Capacity 5 90 to 100 % Speed TrainingYou can decide how you want to do work with that. If you want to have the same definitions that say Garmin uses, you need to just enter your birth year and we can compute the rest. If you want to use a lower bound, you need to specify that.
"},{"location":"features/calendar/","title":"Calendar","text":"In order to access all the activities, there is a calendar view. It shows the years in rows and the months in columns. Each cell is a particular month and indicates the total distance traveled.
When you click on any of the months, you will see a calendar for a given month. Here is one of my months which doesn't show too personal data:
Clicking on an activity will lead you to the activity detail view.
"},{"location":"features/eddington/","title":"Eddington Number","text":"The astronomer Sir Arthur Eddington like to go on longer bike rides. Apparently he did a lot of rides and had 84 days where he rode at least 84 miles. The Eddington number for cycling was coined from this.
If you have an Eddington number E, it means that you have had E days with at least E kilometer distance. At the time of writing my number is 62, which means that I rode at least 62 km on 62 separate days. If I want to extend it to 63, I would need to have at least 63 km on 63 separate days. My bike rides that are just 62 km long will not count any more, making this challenge really hard.
In the following plot you can see in blue the number of days that exceed the given distance. You can see that I have a 998 days that exceed 1 km, that's pretty easy when one just records enough data over many years. But then there are only 259 days where I exceed 20 km as this is beyond the distance that I have when I only go for a walk or run some simple errands.
The red curve indicates how many rides one needs to get the Eddington number. As it is a semi-log plot, the straight line is curved like a log-curve.
You can see this cliff at around 80 km. That is the distance that I ride to work and back. I have many days with around 80 km, but longer rides are only on occasional bike trips. Therefore I think I will eventually make it to an Eddington number of 80, but beyond that will be super difficult.
This is a life-long challenge, so who knows what happens in the future.
"},{"location":"features/eddington/#length-unit","title":"Length unit","text":"The definition of the Eddington number depends on the length unit that one has. Eddington as a British person used the English mile as a base unit. Therefore his number of 84 is actually harder to achieve than a 84 based on kilometers because he needed to exceed 135 km (84 mi) on each ride.
Therefore using a different length unit as a base changes the meaning. The kilometer is easier, the mile (1609.344 m) is harder. One could also use nautical miles (1852 m). And while we are at arbitrary unit systems, we could also use furlongs (201.168 m).
"},{"location":"features/equipment/","title":"Equipment Overview","text":"When activities are tagged with the used equipment, one can tally the total distance traveled with each thing. This given as a table with the recently used equipment at the top:
And for each thing there is also a graph which shows how the distance accumulates over time:
Here one can see how with my old bike I only recorded the occasional bike trip and not nearly the 4000 km/a which I was doing. And then it got stolen in 2019, so I bought a new bike.
This metadata is downloaded via the Strava API, for the directory source it is not yet supported. I thought about using directories like Activities/{Activity Kind}/{Equipment Name}/{Activity Name}.gpx
. to indicate the kind and equipment. If you're interested in implementing this, let me know.
Maps accessible via the web browser are usually served as little image tiles. The Open Street Map uses the Web Mercator coordinate system to map from latitude and longitude to pixels on the map.
Each tile is 256\u00d7256 pixels in size. The zoom levels zoom in by a factor of two. Therefore all the tiles are organized in a quad tree. As you zoom in, each tile gets split into four tiles which can then show more detail. The following prescription maps from latitude and longitude (given in degrees) to tile indices:
def compute_tile(lat: float, lon: float, zoom: int = 14) -> tuple[int, int]:\n x = np.radians(lon)\n y = np.arcsinh(np.tan(np.radians(lat)))\n x = (1 + x / np.pi) / 2\n y = (1 - y / np.pi) / 2\n n = 2**zoom\n return int(x * n), int(y * n)\n
At zoom level 14 the tiles have a side length of roughly 1.5 km in Germany. These tiles are used as the basis for explorer tiles. The basic idea is that every tile where you have at least one point in an activity is considered an explored tile.
From your activities the program will extract all the tiles that you have visited. And then it does a few things with those. One main thing is that it will display these on an interactive map. When we zoom into one area where I've been on vacation in 2023, you can see the explored tiles there:
The filled tiles are explored, I have been there. The colored tiles are cluster tiles, that means that all their four neighbor tiles are also explored.
You can see here how I have explored a region and ensured that it is mostly contiguous.
There is another vacation from 2013 where I wasn't aware of the cluster tiles. I just did some bike trips and didn't look out for the tiles. There the tiles look like this:
You see all these gaps in there. Also there are three different clusters which are not connected. Each unique cluster is assigned a different color such that one can see where there are gaps between the cluster tiles. And filling the gaps is what the explorer tiles are about: This OCD (obsessive compulsive disorder) like craving to fill in the gaps.
Let's take a look at my main cluster of explorer tiles. Here I have explored much more than in the areas where I was on vacation.
You can see an additional feature, the blue square. This is the one largest square which can be fit into all explored tiles. In this picture it has size 21\u00b2. The idea of the square is to have a really tough challenge. Not only does one need to explore increasingly many tiles to expand the square by one unit, there must not be any gaps.
As you can see in this picture, there is a tile missing right at the top edge. I will never be able to get that because that is an off-limits area of the German air force at the airport. So I can expand my square to the south only.
You can click on each tile and get some information about that particular tile. You can see when you first explored that and with which activity. Also it shows the last activity there as well as the number of activities. If it is a local cluster, it will also show the cluster size.
There is also the option to color the tiles by first or last visit. Use one of the buttons above the map:
Then the map will show the first visit:
Or how recent your last visit is:
This uses Matplotlib's Plasma scale (see below) to color the age of a tile. Very new tiles will get a yellow color, a year old tiles a reddish color and tiles two years old or older a colder blue. This is the scale:
You can switch this with the buttons above the map.
"},{"location":"features/explorer-tiles/#squadratinhos","title":"Squadratinhos","text":"The explorer tiles at zoom level 14 are best suited for cycling and to discover the area around the city. There is a derived definition, the squadratinhos which are defined at zoom level 17 and therefore a factor 8 smaller in each direction. Each explorer tile is therefore divided into 256 squadratinhos.
These are better suited for walking and making sure that you really explored every little place in your neighborhood. Since they are so small, there are many properties which one cannot go onto, like industrial sites, airports or just a wide river.
For my home city it looks like this:
You can see how the squadratinhos are much smaller than the explorer tiles and how they lend themselves to more local exploring.
"},{"location":"features/explorer-tiles/#history","title":"History","text":"The map only shows the current state of your explorer tiles. In order to get a sense of how many new tiles you have discovered in the past, there are also plots that show you how you have extended the total number of squares, the size of your largest cluster and the size of your largest square over time:
"},{"location":"features/explorer-tiles/#missing-tile-files","title":"Missing tile files","text":"Looking at these maps you can see the gaps. And if you feel challenged to fill those, you might want to plan a \u201ctactical bike ride\u201d to explore those. Let us take another look at my tile history in Sint Annaland:
You can see those gaps in the clusters. To make it easier to explore tiles while on the go, we can export a file with the missing tiles. Pan and zoom the map to an area which you want to export. Below the map you will find two links:
Download missing tiles in visible area as GeoJSON or GPX.
This export is available as GeoJSON or GPX such that you can open it with other applications. For instance with GPX See on Linux it looks like this when opening the GeoJSON file:
You can then upload the GeoJSON file to Bikerouter and it will display there:
Then plan a route that goes through as many tiles as possible. Download the route as GPX and use an app like OsmAnd to ride along it.
"},{"location":"features/explorer-tiles/#missing-tiles-on-the-go","title":"Missing tiles on the go","text":"The above is nice to plan the route, perhaps you also want to take the missing tiles along to do spontaneous tile hunting.
A possibility should be Organic Maps which is a FOSS app that can display offline maps and also show GPX files.
Another method is to use Open Street Map uMap, either the one hosted in Germany or France. Then you can create a new personal map (consider limiting the access rights, default is public) and upload the GeoJSON file. Then you can use that map on the code to see your position and the missing tiles:
Yet another option is Offline Maps. That is able to display GeoJSON on Android, though one needs to buy the add-on for like 5 EUR.
On Android one can use the OsmAnd app to display tracks and also try to visualize the missing tiles. Unfortunately GeoJSON is not supported, therefore one has to play some tricks. The missing tiles are also exported as a GPX file with a track for each missing tile. This looks strange, but it is a bit helpful with OsmAnd. This is how the file looks like in GPXSee:
And on OsmAnd such files look like this:
Unfortunately OsmAnd becomes a very sluggish with such a huge track imported, so make sure to only export it from rather small regions.
"},{"location":"features/explorer-tiles/#square-planner","title":"Square planner","text":"From the explorer tile views you can open the square planner which allows you to see which tiles you need to explore in order to extend the square into a particular direction. The screen will open with the largest square that you have, then you can use the buttons to extend or move your square.
Using the buttons in the middle you can move the square, the buttons in the corners allow to extend or shrink the square.
When you have selected the square that you want to target, you can download the missing files in for that square as GeoJSON or GPX.
"},{"location":"features/heatmaps/","title":"Heatmaps","text":"A heatmap shows where you have been more often than other places. There is an interactive and zoomable heatmap that looks like this:
Here you can see where I mostly travel between Bonn and Cologne.
You have the option to download a rendered heatmap from the current viewport of the interactive map as in image with up to 4000\u00d74000 pixels, there is a link below the map.
"},{"location":"features/kind-rename/","title":"Kind rename","text":"Metadata and importing from several sources can be messy and in some cases Strava will export its acivity types/activity kinds under names like root='Ride' and not \"Ride\". That can lead to issues with tagging and the heatmap - in this case we have multiple, overlapping activity kinds for the same activity kinds.
To fix this, go to Admin -> Settings -> Kind rename
Existing name => New name
root='Ride'
to 'Ride'
put in root='Ride' => Ride
When you start the webserver, you will see the overview page. It looks like the following and shows the activities you've done in the past 30 days:
Then below that you see the latest 15 activities with their tracks on interactive maps.
Each card contains the name, activity type, distance and duration. The non-commute activities are highlighted with a blue border, the commutes just have a gray border.
Click on any of the names and you will see the details of that activity.
"},{"location":"features/share-picture/","title":"Share Picture","text":"On each activity page you will find a \"share picture\" like the following:
"},{"location":"features/share-picture/#privacy-zones","title":"Privacy zones","text":"You might want to remove points that are close to your home, work or relatives. For this you can define arbitrary polygons as \"privacy zones\".
To create such a polygon, go to GeoJSON.io. You will see a map similar to this one:
Select the polygon tool and click on the map to span the polygon.
Once you are done, press Enter to finish the polygon. In the left panel the GeoJSON output will appear:
For this case, we have this GeoJSON:
{\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\n \"type\": \"Feature\",\n \"properties\": {},\n \"geometry\": {\n \"coordinates\": [\n [\n [\n 6.87987514009842,\n 50.68272071401333\n ],\n [\n 6.878628929151887,\n 50.6819310903943\n ],\n [\n 6.8780142440226655,\n 50.68125883278765\n ],\n [\n 6.879563587362242,\n 50.68022375065988\n ],\n [\n 6.880599289703014,\n 50.68029311254671\n ],\n [\n 6.8814665851591315,\n 50.68102940933676\n ],\n [\n 6.881542368256589,\n 50.681723011688035\n ],\n [\n 6.8812729172415175,\n 50.682176515374465\n ],\n [\n 6.87987514009842,\n 50.68272071401333\n ]\n ]\n ],\n \"type\": \"Polygon\"\n }\n }\n ]\n}\n
Paste this in the appropriate settings menu.
You can name the zone to help you remember what it encompasses. You can add multiple zones.
Points that are within any of the privacy zones will not be shown in the share pictures. Except when all of the points are in the privacy zone, then all the points will be shown.
"},{"location":"features/upload/","title":"Uploading activities","text":"Some users don't want to restart the application each time they add new activities but run it on their home server. For this use case you can upload activities. Uploading files is a potential security issue and it is protected with a password. This feature is only enabled when you have set a password in the configuration file.
Put the following into your config.json
file:
{\n \"upload_password\": \"fb1c8d62-07a5-47bf-ada1-30aa66e41d8a\"\n}\n
Be sure to choose your own password and not use the one from this documentation.
Then you can go to the upload page and see this form:
Select a file that you want to upload and a target directory within the \u201cActivities\u201d directory. Finally, enter the password from the configuration file.
After you have uploaded the file, you will be redirected to the parsed activity.
"},{"location":"getting-started/advanced-metadata-extraction/","title":"Advanced Metadata extraction","text":"If you would like to set the metadata fields or change what part of the filename should be the activity name, you can use a custom directory structure with corresponding regular expressions.
An example directory structure:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n\u251c\u2500 Hike/\n\u2502 \u251c\u2500 Hiking Boots 2019/\n\u2502 \u2502 \u251c\u2500 2024-03-03-11-03-18 Some nice place with Alice and Bob.fit\n
"},{"location":"getting-started/advanced-metadata-extraction/#custom-regular-expressions","title":"Custom Regular expressions","text":"The program uses regular expressions to search for patterns in the relative path (in Activities) and extracts the relevant parts with named capture groups (?P<kind>)
, (?P<equipment>)
, (?P<name>)
.
You can use python to test your regular expressions. Read the python re documentation for help.
import re\nre.search(r'(?P<kind>[^/]+)/(?P<equipment>[^/]+)/(?P<name>[^/.]+)', '/Ride/Trekking Bike/2024-03-03-17-42-10 Home to Bakery.gpx').groupdict()\n
{'kind': 'Ride', 'equipment': 'Trekking Bike', 'name': '2024-03-03-17-42-10 Home to Bakery'}\n
You can add your custom regular expressions under the Admin
menu - Settings
- Metadata Extraction
in the WebUI. Settings are saved in your Playground
directory.
Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/(?P<name>[^/.]+)\n
Ride
Trekking Bike
2024-03-03-17-42-10 Home to Bakery
Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n\u2502 \u2502 \u251c\u2500 2024-03-04-16-52-26.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-21_10-28_Sun OsmAnd default track.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-22_07-55_Mon.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/[-\\d_ ]+(?P<name>[^/]+)(?:\\.\\w+)+$\n
Ride
Trekking Bike
Home to Bakery
;
; Sun OsmAnd default track
; Mon
Attention, name may be empty if it is not included in the file name. For OsmAnd default naming the weekday is included in the name.
"},{"location":"getting-started/advanced-metadata-extraction/#filename-after-first-space-as-name","title":"Filename after first space as Name","text":"Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-22_07-55_Mon.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-21_10-28_Sun OsmAnd default track.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/\\S+ ?(?P<name>[^/\\.]*)\n
Ride
Trekking Bike
Home to Bakery
;
; OsmAnd default track
Attention, name may be empty if it is not included in the file name (also for OsmAnd default naming).
"},{"location":"getting-started/advanced-metadata-extraction/#grouping-activity-files-under-a-common-name-for-example-all-your-commutes","title":"Grouping activity files under a common name, for example all your commutes","text":"Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 Commute/\n\u2502 \u2502 \u2502 \u251c\u2500 2024-03-04-07-06-12.gpx\n\u2502 \u2502 \u2502 \u251c\u2500 2024-03-04-15-42-32.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/(?P<name>[^/]+)/\n
Ride
Trekking Bike
Commute
(for all activities in Commute directory )Path:
Activities/\n\u251c\u2500 Run/\n\u2502 \u251c\u2500 2024-03-09-09-24-03 To the lake.gpx\n\u2502 \u251c\u2500 2024-03-10-09-44-37 To the top of the hill.gpx\n
(?P<kind>[^/]+)/[-\\d_ ]+(?P<name>[^/]+)(?:\\.\\w+)+$\n
Run
Unknown
To the lake
; To the top of the hill
If you you manually rename, move or delete your activity files, the program needs to reload to respect these changes. You can restart the program or visit Scan New Activities
in the admin menu of the WebUI.
Docker is a software that allows you to run Linux programs in a container. Docker Compose is a tool for defining multi-container Docker environments in a single YAML configuration file and deploy it with a single command.
Tailscale is a VPN solution based on the Wireguard protocol which lets you connect all devices within your virtual private network (tailnet). The Tailscale Docker container exposes the services only via a direct VPN connection, which avoids exposing ports to the open internet to connect to your geo-activity-playground instance on-the-go. It provides a domain with a valid Let's Encrypt certificate which is only accessible via the tailnet. The configuration is based on Docker Tailscale Guide.
This how-to will give you an example compose.yml
that can build the geo-activity-playground docker image from Github and start this project within a Docker container and connecting it via Tailscale.
More information on generating auth keys Navigate to https://login.tailscale.com/admin/settings/keys and generate an auth key.
Example Auth-Key configuration: - Description: docker - Reusable: yes - Expiration: 7 days - Ephemeral: No - Tags: tag:container
In order to use the tag, it must first be defined in your Access control policy in the admin console. Set the same tag as in the Auth-Key.
\"tagOwners\": {\n \"tag:container\": [\"autogroup:admin\"],\n},\n
When you apply a tag to a device for the first time and authenticate it, the tagged device's key expiry is disabled by default.
"},{"location":"getting-started/docker-compose-tailscale/#preparing-tailscale-configuration","title":"Preparing Tailscale configuration","text":"The geo-activity-playground service will be made available by using the Tailscale Serve functionality. It routes traffic from other devices on your Tailscale network (known as a tailnet) to a local service, in this case inside the container. It creates a reverse proxy to the geo-activity-playground container internal port 5000 (do not change it). TS_CERT_DOMAIN
is comprised of a subdomain (hostname set in the compose.yml
) and the tailnet root domain.
mkdir -p /docker/geo-activity-playground/{ts-state,ts-config}\ncd /docker/geo-activity-playground/ts-config\nnano geo-activity-playground.json\n
{\n \"TCP\": {\n \"443\": {\n \"HTTPS\": true\n }\n },\n \"Web\": {\n \"${TS_CERT_DOMAIN}:443\": {\n \"Handlers\": {\n \"/\": {\n \"Proxy\": \"http://127.0.0.1:5000\"\n }\n }\n }\n }\n}\n
"},{"location":"getting-started/docker-compose-tailscale/#compose-configuration-with-tailscale-network","title":"Compose configuration with Tailscale network","text":"With these steps the playground folder (which contains the activities) will be located in the docker project folder. The location can be changed in the compose.yml
.
mkdir -p /docker/geo-activity-playground/playground/Activities\ncd /docker/geo-activity-playground\nnano compose.yml\n
services:\n ts-geo-activity-playground:\n image: tailscale/tailscale:latest\n container_name: ts-geo-activity-playground\n hostname: geo-activity-playground # set your desired name, which will be the tailscale subdomain\n environment:\n - TS_AUTHKEY=tskey-auth-yyyyyyyyyyyyyyyyyyyyyyyyyyyy # paste your created Auth-Key\n - TS_EXTRA_ARGS=--advertise-tags=tag:container # set the same tag as in the Auth-Key\n - TS_SERVE_CONFIG=/config/geo-activity-playground.json\n - TS_STATE_DIR=/var/lib/tailscale\n volumes:\n - /docker/geo-activity-playground/ts-state:/var/lib/tailscale\n - /docker/geo-activity-playground/ts-config:/config\n - /dev/net/tun:/dev/net/tun\n cap_add:\n - net_admin\n - sys_module\n restart: unless-stopped\n geo-activity:\n build:\n context: https://github.com/martin-ueding/geo-activity-playground.git\n # this sets the build context to the DOCKERFILE located in the Github repository\n container_name: geo-activity-playground\n depends_on:\n - ts-geo-activity-playground # start container after the VPN network is active\n network_mode: service:ts-geo-activity-playground # link container network to tailscale container\n volumes:\n - /docker/geo-activity-playground/playground:/data # optional: change left side to your desired playground directory\n restart: unless-stopped\n
If you want to build the release version of geo-activity-playground from Github instead, you can adjust the build context and add the release tag. context: https://github.com/martin-ueding/geo-activity-playground.git#0.29.1
You need to set up your files according to one of the presented methods, like activity files or the Strava API. Consult the other pages in the sidebar for the details.
Once you have your playground directory, you can build the image and start the container.
docker compose build\ndocker compose up -d\n
This will start the webserver and expose it via your tailnet on https://[HOSTNAME].[YourTailnetName].ts.net/
, eg. https://geo-activity-playground.tail41a3.ts.net/
. In order to access your instance via that domain, you have to install and authenticate the Tailscale client app on your device you want to open it from.
If using the tagged release version of geo-activity-playground, update the tag to the latest one first.
docker compose down\ndocker compose build\ndocker compose up -d --force-recreate\n
"},{"location":"getting-started/docker-compose/","title":"Using Git Version via Docker Compose","text":"Docker is a software that allows you to run Linux programs in a container. Docker Compose is a tool for defining multi-container Docker environments in a single YAML configuration file and deploy it with a single command.
This how-to will give you an example compose.yml
that can build the geo-activity-playground docker image from Github and start this project within a Docker container.
With these steps the playground folder (which contains the activities) will be located in the docker project folder. The location can be changed in the compose.yml
.
mkdir -p /docker/geo-activity-playground/playground/Activities\ncd /docker/geo-activity-playground\nnano compose.yml\n
services:\n geo-activity:\n build:\n context: https://github.com/martin-ueding/geo-activity-playground.git\n # this sets the build context to the DOCKERFILE located in the Github repository\n container_name: geo-activity-playground\n volumes:\n - /docker/geo-activity-playground/playground:/data # optional: change left side to your desired playground directory\n ports:\n - 5000:5000 # optional: change the exposed port on the left side\n restart: unless-stopped\n
If you want to build the release version from Github instead, you can adjust the build context and add the release tag. context: https://github.com/martin-ueding/geo-activity-playground.git#0.29.1
You need to set up your files according to one of the presented methods, like activity files or the Strava API. Consult the other pages in the sidebar for the details.
Once you have your playground directory, you can build the image and start the container.
docker compose build\ndocker compose up -d\n
This will start the webserver on http://localhost:5000/ or at the port you chose to expose.
Note that port 5000 may not be available on macOS because of AirPlay, so you can map to another port.
"},{"location":"getting-started/docker-compose/#updating-the-image","title":"Updating the image","text":"If using the tagged release version, update the tag to the latest one first.
docker compose down\ndocker compose build\ndocker compose up -d --force-recreate\n
"},{"location":"getting-started/docker/","title":"Using Git Version via Docker","text":"Docker is a software that allows you to run Linux programs in a container. This how-to will show you how to build and start this project within a Docker container.
"},{"location":"getting-started/docker/#build-the-image","title":"Build the image","text":"First you need to build the Docker image. For this download the source code and build the image using the following commands:
git clone https://github.com/martin-ueding/geo-activity-playground.git\ncd geo-activity-playground\nsudo docker build -t geo-activity-playground .\n
Perhaps you do not need sudo
on your system.
You need to set up your files according to one of the presented methods, like activity files or the Strava API. Consult the other pages in the sidebar for the details.
Once you have your playground directory, you can launch the Docker image with the following. Be sure to replace path/to/playground
with your path.
sudo docker run -p 5000:5000 -v path/to/playground:/data -it geo-activity-playground\n
This will start the webserver on http://localhost:5000/.
Note that port 5000 may not be available on macOS because of AirPlay, so you can map to another port by replacing the port specifier from above with -p 8000:5000
. Then you can open http://localhost:8000/ in your browser.
As this project is still in development, you might want to have a peek into the development version. This is more advanced than using the stable versions, but not impossibly hard.
First you need to clone the git repository from GitHub using the following command:
git clone https://github.com/martin-ueding/geo-activity-playground.git\n
That will create a directory geo-activity-playground
in your current working directory.
Then change into that directory:
cd geo-activity-playground\n
Next we will use Poetry to install the dependencies of the project. First you need to make sure that you have Poetry available. On Ubuntu/Debian run sudo apt install python3-poetry
, on Fedora/RedHat run sudo dnf install poetry
to install it.
Then we can create the virtual environment:
poetry install\n
And next we can run the program:
poetry run geo-activity-playground --basedir path/to/your/playground --help\n
Replace the --help
with the subcommands described in the help message or the other parts described this documentation.
You will need the --basedir
option because you run the program from the source directory and not from your playground directory. If you install the stable version via PIP as described in the other page, you will not need this option.
Over time I will add more commits to the source control system. In order to update your clone to the latest version, execute the following:
git pull\n
This will download the missing changesets and apply them to your downloaded version. After that is done, you need to update your virtual environment with this:
poetry install\n
And then you can continue using it as before.
"},{"location":"getting-started/installing-stable-on-linux/","title":"Installing Stable On Linux","text":"In this how-to guide I will show you how you can install the latest stable version of this project on Linux.
"},{"location":"getting-started/installing-stable-on-linux/#pipx-method","title":"PIPX method","text":"The ideal way to install this project, is using pipx
. First ensure that you have it installed:
sudo apt install pipx
Fedora, RedHat sudo dnf install pipx
Arch, Manjaro sudo pacman -Syu python-pipx
Using PIPX, you can then install the latest version using this command:
pipx install geo-activity-playground\n
That should be it. You might need to ensure that the $PATH
is correct. For that see the section below.
If you don't want to use PIPX, you can also use regular PIP. First install PIP:
Distribution Command Ubuntu, Debiansudo apt install python3-pip
Fedora, RedHat sudo dnf install python3-pip
Arch, Manjaro sudo dnf install python-pip
Then install the package into your user directory.
pip install --user geo-activity-playground\n
That should be it. You might need to ensure that the $PATH
is correct. For that see the section below.
Next you can try to start the program by just entering the following into the terminal:
geo-activity-playground --help\n
If you get a help message, everything is fine. If you get an error about command not found, we need to adjust your PATH. Execute the following in your command line:
xdg-open ~/.profile\n
This brings up an editor with your shell profile. Add a line containing the following at the end of the file:
PATH=$PATH:$HOME/.local/bin\n
This adds the path to your shell environment. This becomes active after you log in again. In order to apply it also to your current shell session, execute export PATH=$PATH:$HOME/.local/bin
in the terminal window. Try the first command in this section again, you should see the help message now.
At some later point you likely want to upgrade to the latest version. For this use this command if you used PIPX:
pipx upgrade geo-activity-playground\n
If you used PIP, use this:
pip install --user --upgrade geo-activity-playground\n
"},{"location":"getting-started/installing-stable-on-windows/","title":"Installing Stable on Windows","text":"This how-to will show you the installation of the project on Windows. Here in the guide we use Windows 10 with the locale set to German, it should generalize to Windows 11 as well.
"},{"location":"getting-started/installing-stable-on-windows/#installing-python","title":"Installing Python","text":"First we need to install Python because that doesn't ship with Windows. Fortunately we can get it from the Microsoft Store. Open that via the start menu and you should see something like this:
Type \u201cPython\u201d into the search bar at the top. In the search results you likely see different Python versions like 3.11 and 3.10. The project is compatible with 3.10 to 3.12; I'd suggest to just go with 3.12. In case that you have already installed one of the other compatible versions, you can skip this step.
Here we select Python 3.11.
In the top right there is a blue button to install the software. Click that.
"},{"location":"getting-started/installing-stable-on-windows/#installing-the-project","title":"Installing the project","text":"After that has run through, you need to open the Power Shell via the start menu. It should open a command line window like this:
We can verify that Python is working by entering python --version
and pip --version
. It should give a sensible version message like this:
Then we can ues PIP to install the project. Type the following:
pip install -U geo-activity-playground\n
It should look like this:
Then press Enter and it will install it, looking like this:
That might take a while. After that has run through, it should give a success message:
Then we're done with this window, you can close it now.
"},{"location":"getting-started/installing-stable-on-windows/#putting-your-activity-files-in-place","title":"Putting your activity files in place","text":"Next you need to create a folder to put the files. I've placed mine just into the Documents folder. In there I've created a folder named exactly \u201cActivities\u201d.
Inside this folder there are my GPX/FIT/TCX/KML files as outlined in how-to guide on using activity files.
You need to have at least one activity file before you can start the program.
"},{"location":"getting-started/installing-stable-on-windows/#starting-the-webserver","title":"Starting the webserver","text":"Once you have your activity files in place, we need to add a start script. Right-click into the playground folder (next to the \u201cActivities folder\u201d) and in the context menu select \u201cCreate New\u201d and then \u201cText File\u201d. Name it start.bat
. Windows will ask you whether want to change the suffix (file extension) because it might get unusable. Yes, we want to do that. It should look like this:
Then right-click on that file and select \u201cEdit\u201d. A text editor will open up. Put the following content into this file:
python -m geo_activity_playground serve\npause\n
Then save and close it. I need you to create this file yourself and cannot offer a download because the Windows Defender will not allow you to execute such script files downloaded as a security precaution. If you create the file yourself, it will let you execute it.
Once you have the start.bat
there, you can double-click on it to execute it. A new terminal window should open and it should start to parse your activities.
After it has loaded everything, you can open http://127.0.0.1:5000/ in your browser. You should then see the landing page:
Also other functions like the heatmap work:
That's it, have fun!
"},{"location":"getting-started/moving-from-strava/","title":"Moving from Strava","text":"If you have been using Strava up to this point but want to use this project from now on, this is the correct guide. Here I will show how you can convert your data from Strava into the format of this project and keep adding new data without Strava in the future.
"},{"location":"getting-started/moving-from-strava/#download-your-archive-from-strava","title":"Download your archive from Strava","text":"Go to the Strava account download page and request a download of your data. This will take a while and you get a notification via e-mail when it is done.
Once it has run through, you will be able to download a ZIP file. Once extracted, it will have a structure like this:
.\n\u251c\u2500\u2500 activities [2217 entries exceeds filelimit, not opening dir]\n\u251c\u2500\u2500 activities.csv\n\u251c\u2500\u2500 applications.csv\n\u251c\u2500\u2500 bikes.csv\n\u251c\u2500\u2500 blocks.csv\n\u251c\u2500\u2500 categories_of_personal_information_we_collect.pdf\n\u251c\u2500\u2500 clubs\n\u251c\u2500\u2500 clubs.csv\n\u251c\u2500\u2500 comments.csv\n\u251c\u2500\u2500 community_content.json\n\u251c\u2500\u2500 community_personal_data.json\n\u251c\u2500\u2500 components.csv\n\u251c\u2500\u2500 connected_apps.csv\n\u251c\u2500\u2500 contacts.csv\n\u251c\u2500\u2500 email_preferences.csv\n\u251c\u2500\u2500 events.csv\n\u251c\u2500\u2500 favorites.csv\n\u251c\u2500\u2500 flags.csv\n\u251c\u2500\u2500 followers.csv\n\u251c\u2500\u2500 following.csv\n\u251c\u2500\u2500 general_preferences.csv\n\u251c\u2500\u2500 global_challenges.csv\n\u251c\u2500\u2500 goals.csv\n\u251c\u2500\u2500 group_challenges.csv\n\u251c\u2500\u2500 information_we_disclose_for_a_business_purpose.pdf\n\u251c\u2500\u2500 local_legend_segments.csv\n\u251c\u2500\u2500 logins.csv\n\u251c\u2500\u2500 media [252 entries exceeds filelimit, not opening dir]\n\u251c\u2500\u2500 media.csv\n\u251c\u2500\u2500 memberships.csv\n\u251c\u2500\u2500 messaging.json\n\u251c\u2500\u2500 metering.csv\n\u251c\u2500\u2500 mobile_device_identifiers.csv\n\u251c\u2500\u2500 orders.csv\n\u251c\u2500\u2500 partner_opt_outs.csv\n\u251c\u2500\u2500 posts.csv\n\u251c\u2500\u2500 privacy_zones.csv\n\u251c\u2500\u2500 profile.csv\n\u251c\u2500\u2500 profile.jpg\n\u251c\u2500\u2500 reactions.csv\n\u251c\u2500\u2500 routes\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 1.gpx\n\u251c\u2500\u2500 routes.csv\n\u251c\u2500\u2500 segments.csv\n\u251c\u2500\u2500 shoes.csv\n\u251c\u2500\u2500 social_settings.csv\n\u251c\u2500\u2500 starred_routes.csv\n\u251c\u2500\u2500 starred_segments.csv\n\u251c\u2500\u2500 support_tickets.csv\n\u2514\u2500\u2500 visibility_settings.csv\n
This directory contains a file activities.csv
with the metadata and also a directory activities
with the files that you have recorded.
Use the following command to create a directory from your Strava actitivies:
geo-activity-playground convert-strava-checkout ~/Downloads/export_123456/ ~/Documents/Outdoors/Playground\n
This should read through all the activities and create a directory structure with the pattern ~/Documents/Outdoors/Playground/Activities/{Kind}/{Equipment}/{Commute}/{Date} {Time} {Name}.{Suffix}
. For instance one file might be named Activities/Run/5212701.0/2019-07-09 09-59-25 Around the \u5317\u4eac\u5927\u5b66 campus.gpx.gz
.
The equipment might have nonsensical seeming names like 10370891.0
. The problem here is that Strava doesn't export the list of activities with that index. If your equipment doesn't have a nickname, it will just be such a number.
Now that the files from Strava are converted, consult the guide on using activity files to proceed from here.
"},{"location":"getting-started/moving-from-strava/#recording-more-activities","title":"Recording more activities","text":"Now that you don't record via Strava, you will need some other app to record your activities. There are Apps like OsmAnd or OpenTracks which provide such functionality. Export the files as GPX, FIT, TCX or KML files and put them into the directory structure. On the next start of the program, they will be picked up.
"},{"location":"getting-started/starting-the-webserver/","title":"Starting The Webserver","text":"Before you start here, you should have done these things:
Now we can start the webserver which provides most of the features. This is done with the serve
command. So depending on how you have installed it, the commands could look like these:
geo-activity-playground serve
if you are in the playground directory and have installed a stable version.geo-activity-playground --basedir ~/Dokumente/Karten/Playground serve
if your playground directory is somewhere else and you have installed a stable version.poetry run geo-activity-playground --basedir ~/Dokumente/Karten/Playground serve
if you have it from the git checkout and want to use local files in your directory as a data source.The webserver will start up and give you a bit of output like this:
2023-11-19 17:59:23 geo_activity_playground.importers.strava_api INFO Loading metadata file \u2026\n2023-11-19 17:59:23 stravalib.protocol.ApiV3 INFO GET 'https://www.strava.com/api/v3/athlete/activities' with params {'before': None, 'after': 1700392964, 'page': 1, 'per_page': 200}\n2023-11-19 17:59:23 geo_activity_playground.importers.strava_api INFO Checking for missing time series data \u2026\n * Serving Flask app 'geo_activity_playground.webui.app'\n * Debug mode: off\n2023-11-19 17:59:23 werkzeug INFO WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\n * Running on http://127.0.0.1:5000\n2023-11-19 17:59:23 werkzeug INFO Press CTRL+C to quit\n
The warning about the development server is fine. We are using this only to play around, not to power a web service for other users.
Open http://127.0.0.1:5000 to open the website in your browser. There might be some more messages about downloading and parsing data. The first startup will take quite some time. When it is done you will see something like this:
Click around and explore the various features.
"},{"location":"getting-started/starting-the-webserver/#setting-host-and-port","title":"Setting host and port","text":"In case you don't like the default value of 127.0.0.1:5000
, you can use the optional command line arguments --host
and --port
to specify your values.
Outdoor activities are usually recorded as .GPX
or .FIT
files. Some apps like OsmAnd , OpenTracks or Organic Maps, GPS handhelds, smartwatches or cycling computers give you these files.
Before starting the service you need to create a folder for your activities and put at least one activity file in there.
Create a Playground
folder on your storage somewhere and add a subfolder Activities
. There you can add your activity files. For example:
~/\n\u251c\u2500 Documents[or other location]/\n\u2502 \u251c\u2500 Playground/\n\u2502 \u2502 \u251c\u2500 Activities/\n\u2502 \u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n
The program will treat the files as read-only and does not modify them.
Once the service is running you can use the Uploader to add your files. You can manually rename, move or delete your activity files, but the program needs to reload to respect these changes. You can restart the program or visit Scan New Activities
in the admin menu of the WebUI.
Most activity file formats contain basic data like date
, time
and track points
. Each activity in geo-activity-playground also has the metadata fields kind
, equipment
and name
. They can be extracted from files that contain them.
If no metadata is found, kind
and equipment
default to Unknown
. The name
is then extracted from the file name (without the suffix). So for Activities/2024-03-03-17-42-10 Home to Bakery.gpx
the name
is 2024-03-03-17-42-10 Home to Bakery
.
Once you have your files put into the directory, you're all set and can proceed with the next steps.
You can extend the directory structure to categorize your activities, see Advanced Metadata Extraction.
"},{"location":"getting-started/using-strava-api/","title":"Using Strava API","text":"You might have all your data on the Strava service and would like to use this for additional analytics without moving your data. That is fine.
If you don't mind a bit of rate-limiting, you can just directly go ahead and start the webserver. It will offer to connect with Strava.
"},{"location":"getting-started/using-strava-api/#your-own-strava-app","title":"Your own Strava App","text":"In order to use the Strava API without sharing the rate-limiting with other users, you need to create your own app. If my explanation doesn't suit you, have a look at this how-to guide as well.
Navigate to the API settings page and create an app. It only needs to have read permissions.
After you are done with that, you can see your App here:
There is a \"client ID\" and a \"client secret\" that we are going to need for the next step. In general our app could be used by all sorts of people who can then access their data only. We want to access our own data, but we still need to authorize our app to use our data.
Open the webserver of this program and go the Strava API setup page. Enter your client ID and client secret, click on \"Connect to Strava\".
This will prompt an OAuth2 request where you have to grant permissions to your app. After that you will be redirected back to the app and it should be set up. At the moment you need to restart the webserver such that it can start to download the activities. Due to rate-limiting it can still take a while.
"},{"location":"getting-started/using-strava-api/#use-export-to-avoid-rate-limiting","title":"Use export to avoid rate limiting","text":"When you first start this program and use the Strava API as a data source, it will download the metadata for all your activities. Then it will start to download all the time series data for each activity. Strava has a rate limiting, so after the first 200 activities it will crash and you will have to wait for 15 minutes until you can try again and it will download the next batch.
Therefore it is recommended to use a Strava export in order to get started quicker. For this go to the Strava account download page and download all your data. You will get a ZIP file. Unpack the files into Playground/Strava Export
. These will be picked up there. Activities from Strava will only be downloaded after importing all these, and only the ones after the last one in the export will be downloaded. This way you can get started much quicker.
If you don't want to download new activities from Strava, use --skip-reload
to have the webserver start right away.
This is the log of high-level changes that I have done in the various versions.
"},{"location":"reference/changelog/#version-0","title":"Version 0","text":"This is the pre-release series. Things haven't settled yet, so each minor version might introduce breaking changes.
"},{"location":"reference/changelog/#version-034","title":"Version 0.34","text":"activities.csv
from the Strava export.--skip-strava
and replace it with --skip-reload
.Activities
directory._meta
.ignore_suffixes
which can be set to something like [\".kml\"]
to ignore certain file types.root=
prefix in activity kind when importing from Strava.stravalib
version.KeyError
when trying to use the heatmap or the explorer tile maps.stravalib
and with that also pydantic
. That fixes a bug with recursive_guard
.num_processes
option.strava-last-activity-date.json
which is needed such that the Strava API importer can pick up after all the activities that have been imported via the checkout.num_processes = 1
in the configuration.config.json
replaces the config.toml
and will automatically be generated.pandas >= 2.2.0
to make sure that it knows about include_groups
.Add an web interface to connect to Strava API using a shared application such that it becomes much simpler to set up.
GH-41: Compute moving time.
activities.parquet
exists. I've added a check.distance/km
.Breaking change: New way to extract metadata from paths and filenames. This uses regular expressions and is more versatile than the heuristic before. If you have used prefer_metadata_from_file
before, see the documentation on activity files for the new way.
GH-105: Ignore similar activities that have vanished.
GH-109: Better error message when trying to start up without any activity files.
Removed imagehash
from the dependencies.
unsupported operand type(s) for /: 'list' and 'int'
.activities.csv
.vegafusion[embed]
explicit in the dependencies.opener
.distance_km
and everything is represented as kilometer internally now.prefer_metadata_from_file
configuration option.--skip-strava
to the serve
command in order to start the webserver without reaching out to Strava first. This might be useful if the rate limit has been exceeded..DS_Store
files in the activity directory. GH-81.tcx.gz
files.Dockerfile
such that one can easily use this with Docker. GH-78if __name__ == \"__main__\"
clause such that one can use python -m geo_activity_playground
on Windows.Activity/{Kind}/{Equipment}/{Name}.{Format}
directory structure.geo-activity-playground
and not just geo-playground
. GH-11This is a software to view recorded outdoor activities and derive various insights from your data collection. All data is kept on your machine, hence it is suitable for people who have an affinity for data analysis and privacy.
It caters to serve a similar purpose as Strava with Statshunters statshunters does but while not requiring you to share your location data with a service provider.
One can use this program with a local collection of activity files (GPX, FIT, TCX, KML, CSV) or via the Strava API. The latter is appealing to people who want to keep their data with Strava primarily. In case that one wants to move, this might provide a noncommital way of testing out this project.
The main user interface is web-based, you can run this on your Linux, Mac or Windows laptop. If you want, it can also run on a home server or your personal cloud instance.
"},{"location":"#screenshot-tour","title":"Screenshot tour","text":"This is the view of a single activity:
You also get a beautiful interactive heatmap of all your activities:
Also there are plenty of summary statistics that lets you track how many rides, walks or hikes you have done:
If you're into explorer tiles or squadratinhos, this got you covered:
The configuration options are available within the interface such that you do not have to work with configuration files (like in earlier versions):
"},{"location":"#get-started","title":"Get started","text":"Install the software using one of the following options:
Get your activity data in place using one of the following options:
You can find the code on GitHub where you can also file issues. If you would like to use this yourself or contribute, feel free to reach out via the contact options from my website. I would especially appreciate improvements to the documentation. If you're familiar with Markdown and GitHub, you can also directly create a pull request. The code is licensed under the very permissive MIT license.
"},{"location":"acknowledgments/","title":"Acknowledgments","text":"This project builds on many amazing other projects and would not be possible without them.
"},{"location":"acknowledgments/#bootstrap-css","title":"Bootstrap CSS","text":"Writing CSS is not a trivial task. For many projects I have been using the Bootstrap CSS Framework which provides sensible default values, a 12-column grid system and a lot of components. Using this I didn't have to write any CSS myself and just attach a couple of classes to HTML elements.
"},{"location":"acknowledgments/#coloredlogs","title":"coloredlogs","text":"Log messages in multiple colors are neat. Using the coloredlogs package we can get these super easily.
"},{"location":"acknowledgments/#fitdecode","title":"fitdecode","text":"For reading FIT files I use the fitdecode library which completely handles all the parsing of this file format.
"},{"location":"acknowledgments/#flask","title":"Flask","text":"The webserver is implemented with Flask which provides a really easy way to get started. It also ships with a development webserver which is enough for this project at the moment.
"},{"location":"acknowledgments/#geojson","title":"GeoJSON","text":"Transferring geographic geometry data from the Python code to Leaflet is easiest with using the GeoJSON format. The official standard RFC is a bit hard to read, rather have a look at the Wikipedia article. And there is an online viewer that you can try out.
"},{"location":"acknowledgments/#github","title":"GitHub","text":"For a smooth open source project one needs a place to share the code and collect issues. GitHub provides all of this for free.
"},{"location":"acknowledgments/#gpxpy","title":"gpxpy","text":"For reading GPX files I use the gpxpy library. This allows me to read those files without having to fiddle with the underlying XML format.
"},{"location":"acknowledgments/#leaflet","title":"Leaflet","text":"The interactive maps on the website are powered by Leaflet, a very easy to use JavaScript library for embedding interactive Open Street Map maps. It can also display GeoJSON geometries natively, of which I also make heavy use.
"},{"location":"acknowledgments/#mkdocs","title":"MkDocs","text":"Writing documentation is more fun with a nice tool, therefore I use MkDocs together with Material for MkDocs. This powers this documentation.
"},{"location":"acknowledgments/#open-street-map","title":"Open Street Map","text":"All the maps displayed use tiles from the amazing Open Street Map. This map is created by volunteers, the server hosting is for free. Without these maps this project would be quite boring.
"},{"location":"acknowledgments/#pandas","title":"Pandas","text":"Working with thousands of activities, thousands of tiles and millions of points makes it necessary to have a good library for number crunching structured data. Pandas offers this and gives good performance and many features.
"},{"location":"acknowledgments/#parquet","title":"Parquet","text":"I need to store the intermediate data frames that I generate with Pandas. Storing as JSON has disadvantages because dates are not properly encoded. Also it is a text format and quite verbose. The Parquet format is super fast and memory efficient.
"},{"location":"acknowledgments/#poetry","title":"Poetry","text":"For managing all the Python package dependencies I use Poetry which makes it very easy to have all the Python project housekeeping with one tool.
"},{"location":"acknowledgments/#python","title":"Python","text":"Almost all of the code here is written in Python, a very nice and versatile programming language with a vast ecosystem of packages.
"},{"location":"acknowledgments/#requests","title":"Requests","text":"For doing HTTP requests I use the Requests library. It provides a really easy to use interface for GET and POST requests.
"},{"location":"acknowledgments/#scikit-learn","title":"Scikit-learn","text":"Finding out which cluster is the largest one can either be formed as a graph search problem or as a data science problem. Using the Scikit-learn library I can easily use the DBSCAN algorithm to find the clusters of explorer tiles.
"},{"location":"acknowledgments/#statshunters","title":"Statshunters","text":"The Statshunters page allows to import the activities from Strava and do analysis like explorer tiles, Eddington number and many other things. This has served as inspiration for this project.
"},{"location":"acknowledgments/#strava","title":"Strava","text":"Although I have recorded some of my bike rides, I only really started to record all of them when I started to use Strava. This is a nice platform to track all activities. They also offer a social network feature, which I don't really use. They provide some analyses of the data, but they lack some analyses which I have now implemented in this project.
"},{"location":"acknowledgments/#stravalib","title":"stravalib","text":"Strava has an API, and with stravalib there exists a nice Python wrapper. This makes it much easier to interface with Strava.
"},{"location":"acknowledgments/#strava-local-heatmap","title":"Strava local heatmap","text":"https://github.com/remisalmon/Strava-local-heatmap
"},{"location":"acknowledgments/#tcxreader","title":"tcxreader","text":"https://github.com/alenrajsp/tcxreader
"},{"location":"acknowledgments/#vega-altair","title":"Vega & Altair","text":"https://altair-viz.github.io/index.html https://vega.github.io/vega/
"},{"location":"acknowledgments/#velo-viewer","title":"Velo Viewer","text":""},{"location":"features/activity-view/","title":"Activity View","text":"When you have selected a particular activity, you can view various details about it. This is what the screen looks like, we will go through the different parts in the following.
"},{"location":"features/activity-view/#metadata","title":"Metadata","text":"You have a column with metadata about the activity. The activity kind, whether it is a commute and the equipment are currently only supported via the Strava API, but we can build something to infer that from directories as well.
The calories are broken in the Strava API wrapper library that I use, therefore they don't show even if they are there.
You can also see the ID which is an internal ID. When you use Strava API as a source, it will use the IDs that Strava gives. When you use files from a directory it will be computed from a hash of the path to the activity file.
"},{"location":"features/activity-view/#map-with-track","title":"Map with track","text":"The interactive map shows a line with the activity. The speed is color-coded and peaks at 35 km/h with a yellow color.
"},{"location":"features/activity-view/#distance-speed-and-altitude","title":"Distance, speed and altitude","text":"Then there are a couple of time series plots. One is the distance vs. time. You can see how much distance you covered when and also see plateaus when you went on a break.
From this we can also compute the speed, although that might be pretty noisy:
And more interesting is the distribution of the various speed zones. This gives you an understanding how much time you spent at which speed. The buckets are set in 5 km/h intervals, but we could also change that.
If the time series data has the altitude, which isn't always the case, you can see it in the plot there. Here we can see how I did a tour and continually rode downhill. Except at the end where I had to climb in order to get the explorer tile that I wanted.
"},{"location":"features/activity-view/#heart-rate","title":"Heart rate","text":"The heart rate isn't too helpful, I feel. Still I've created the plot from the given data.
More interesting regarding the heart rate are the zones which one has spent time during this activity.
The definition of the heart rate zones is not standardized. Usually there are five zones and they have the same names. What differs is how their ranges are computed and there is some chaos around that.
All definitions that I found take the maximum heart rate as the upper limit. One can measure this as part of a professional training or just use the 220 minus age prescription which at least for me matches close enough. What they differ on is how they use a lower bound. It seems that Polar or REI basically use 0 as the lower bound. My Garmin system also uses 0 as the lower bound. But as one can see in this blog, one can also use the resting heart rate as the lower bound.
Based on the maximum and resting heart rate we will then compute the heart rate zones using certain percentages of effort. We can compute the heart rate as the following:
rate = effort \u00d7 (maximum \u2013 minimum) + minimum
The zones then take the following efforts:
Zone Effort Training 1 50 to 60 % Warmup/Recovery 2 60 to 70 % Base Fitness 3 70 to 80 % Aerobic Endurance 4 80 to 90 % Anerobic Capacity 5 90 to 100 % Speed TrainingYou can decide how you want to do work with that. If you want to have the same definitions that say Garmin uses, you need to just enter your birth year and we can compute the rest. If you want to use a lower bound, you need to specify that.
"},{"location":"features/calendar/","title":"Calendar","text":"In order to access all the activities, there is a calendar view. It shows the years in rows and the months in columns. Each cell is a particular month and indicates the total distance traveled.
When you click on any of the months, you will see a calendar for a given month. Here is one of my months which doesn't show too personal data:
Clicking on an activity will lead you to the activity detail view.
"},{"location":"features/eddington/","title":"Eddington Number","text":"The astronomer Sir Arthur Eddington like to go on longer bike rides. Apparently he did a lot of rides and had 84 days where he rode at least 84 miles. The Eddington number for cycling was coined from this.
If you have an Eddington number E, it means that you have had E days with at least E kilometer distance. At the time of writing my number is 62, which means that I rode at least 62 km on 62 separate days. If I want to extend it to 63, I would need to have at least 63 km on 63 separate days. My bike rides that are just 62 km long will not count any more, making this challenge really hard.
In the following plot you can see in blue the number of days that exceed the given distance. You can see that I have a 998 days that exceed 1 km, that's pretty easy when one just records enough data over many years. But then there are only 259 days where I exceed 20 km as this is beyond the distance that I have when I only go for a walk or run some simple errands.
The red curve indicates how many rides one needs to get the Eddington number. As it is a semi-log plot, the straight line is curved like a log-curve.
You can see this cliff at around 80 km. That is the distance that I ride to work and back. I have many days with around 80 km, but longer rides are only on occasional bike trips. Therefore I think I will eventually make it to an Eddington number of 80, but beyond that will be super difficult.
This is a life-long challenge, so who knows what happens in the future.
"},{"location":"features/eddington/#length-unit","title":"Length unit","text":"The definition of the Eddington number depends on the length unit that one has. Eddington as a British person used the English mile as a base unit. Therefore his number of 84 is actually harder to achieve than a 84 based on kilometers because he needed to exceed 135 km (84 mi) on each ride.
Therefore using a different length unit as a base changes the meaning. The kilometer is easier, the mile (1609.344 m) is harder. One could also use nautical miles (1852 m). And while we are at arbitrary unit systems, we could also use furlongs (201.168 m).
"},{"location":"features/equipment/","title":"Equipment Overview","text":"When activities are tagged with the used equipment, one can tally the total distance traveled with each thing. This given as a table with the recently used equipment at the top:
And for each thing there is also a graph which shows how the distance accumulates over time:
Here one can see how with my old bike I only recorded the occasional bike trip and not nearly the 4000 km/a which I was doing. And then it got stolen in 2019, so I bought a new bike.
This metadata is downloaded via the Strava API, for the directory source it is not yet supported. I thought about using directories like Activities/{Activity Kind}/{Equipment Name}/{Activity Name}.gpx
. to indicate the kind and equipment. If you're interested in implementing this, let me know.
Maps accessible via the web browser are usually served as little image tiles. The Open Street Map uses the Web Mercator coordinate system to map from latitude and longitude to pixels on the map.
Each tile is 256\u00d7256 pixels in size. The zoom levels zoom in by a factor of two. Therefore all the tiles are organized in a quad tree. As you zoom in, each tile gets split into four tiles which can then show more detail. The following prescription maps from latitude and longitude (given in degrees) to tile indices:
def compute_tile(lat: float, lon: float, zoom: int = 14) -> tuple[int, int]:\n x = np.radians(lon)\n y = np.arcsinh(np.tan(np.radians(lat)))\n x = (1 + x / np.pi) / 2\n y = (1 - y / np.pi) / 2\n n = 2**zoom\n return int(x * n), int(y * n)\n
At zoom level 14 the tiles have a side length of roughly 1.5 km in Germany. These tiles are used as the basis for explorer tiles. The basic idea is that every tile where you have at least one point in an activity is considered an explored tile.
From your activities the program will extract all the tiles that you have visited. And then it does a few things with those. One main thing is that it will display these on an interactive map. When we zoom into one area where I've been on vacation in 2023, you can see the explored tiles there:
The filled tiles are explored, I have been there. The colored tiles are cluster tiles, that means that all their four neighbor tiles are also explored.
You can see here how I have explored a region and ensured that it is mostly contiguous.
There is another vacation from 2013 where I wasn't aware of the cluster tiles. I just did some bike trips and didn't look out for the tiles. There the tiles look like this:
You see all these gaps in there. Also there are three different clusters which are not connected. Each unique cluster is assigned a different color such that one can see where there are gaps between the cluster tiles. And filling the gaps is what the explorer tiles are about: This OCD (obsessive compulsive disorder) like craving to fill in the gaps.
Let's take a look at my main cluster of explorer tiles. Here I have explored much more than in the areas where I was on vacation.
You can see an additional feature, the blue square. This is the one largest square which can be fit into all explored tiles. In this picture it has size 21\u00b2. The idea of the square is to have a really tough challenge. Not only does one need to explore increasingly many tiles to expand the square by one unit, there must not be any gaps.
As you can see in this picture, there is a tile missing right at the top edge. I will never be able to get that because that is an off-limits area of the German air force at the airport. So I can expand my square to the south only.
You can click on each tile and get some information about that particular tile. You can see when you first explored that and with which activity. Also it shows the last activity there as well as the number of activities. If it is a local cluster, it will also show the cluster size.
There is also the option to color the tiles by first or last visit. Use one of the buttons above the map:
Then the map will show the first visit:
Or how recent your last visit is:
This uses Matplotlib's Plasma scale (see below) to color the age of a tile. Very new tiles will get a yellow color, a year old tiles a reddish color and tiles two years old or older a colder blue. This is the scale:
You can switch this with the buttons above the map.
"},{"location":"features/explorer-tiles/#squadratinhos","title":"Squadratinhos","text":"The explorer tiles at zoom level 14 are best suited for cycling and to discover the area around the city. There is a derived definition, the squadratinhos which are defined at zoom level 17 and therefore a factor 8 smaller in each direction. Each explorer tile is therefore divided into 256 squadratinhos.
These are better suited for walking and making sure that you really explored every little place in your neighborhood. Since they are so small, there are many properties which one cannot go onto, like industrial sites, airports or just a wide river.
For my home city it looks like this:
You can see how the squadratinhos are much smaller than the explorer tiles and how they lend themselves to more local exploring.
"},{"location":"features/explorer-tiles/#history","title":"History","text":"The map only shows the current state of your explorer tiles. In order to get a sense of how many new tiles you have discovered in the past, there are also plots that show you how you have extended the total number of squares, the size of your largest cluster and the size of your largest square over time:
"},{"location":"features/explorer-tiles/#missing-tile-files","title":"Missing tile files","text":"Looking at these maps you can see the gaps. And if you feel challenged to fill those, you might want to plan a \u201ctactical bike ride\u201d to explore those. Let us take another look at my tile history in Sint Annaland:
You can see those gaps in the clusters. To make it easier to explore tiles while on the go, we can export a file with the missing tiles. Pan and zoom the map to an area which you want to export. Below the map you will find two links:
Download missing tiles in visible area as GeoJSON or GPX.
This export is available as GeoJSON or GPX such that you can open it with other applications. For instance with GPX See on Linux it looks like this when opening the GeoJSON file:
You can then upload the GeoJSON file to Bikerouter and it will display there:
Then plan a route that goes through as many tiles as possible. Download the route as GPX and use an app like OsmAnd to ride along it.
"},{"location":"features/explorer-tiles/#missing-tiles-on-the-go","title":"Missing tiles on the go","text":"The above is nice to plan the route, perhaps you also want to take the missing tiles along to do spontaneous tile hunting.
A possibility should be Organic Maps which is a FOSS app that can display offline maps and also show GPX files.
Another method is to use Open Street Map uMap, either the one hosted in Germany or France. Then you can create a new personal map (consider limiting the access rights, default is public) and upload the GeoJSON file. Then you can use that map on the code to see your position and the missing tiles:
Yet another option is Offline Maps. That is able to display GeoJSON on Android, though one needs to buy the add-on for like 5 EUR.
On Android one can use the OsmAnd app to display tracks and also try to visualize the missing tiles. Unfortunately GeoJSON is not supported, therefore one has to play some tricks. The missing tiles are also exported as a GPX file with a track for each missing tile. This looks strange, but it is a bit helpful with OsmAnd. This is how the file looks like in GPXSee:
And on OsmAnd such files look like this:
Unfortunately OsmAnd becomes a very sluggish with such a huge track imported, so make sure to only export it from rather small regions.
"},{"location":"features/explorer-tiles/#square-planner","title":"Square planner","text":"From the explorer tile views you can open the square planner which allows you to see which tiles you need to explore in order to extend the square into a particular direction. The screen will open with the largest square that you have, then you can use the buttons to extend or move your square.
Using the buttons in the middle you can move the square, the buttons in the corners allow to extend or shrink the square.
When you have selected the square that you want to target, you can download the missing files in for that square as GeoJSON or GPX.
"},{"location":"features/heatmaps/","title":"Heatmaps","text":"A heatmap shows where you have been more often than other places. There is an interactive and zoomable heatmap that looks like this:
Here you can see where I mostly travel between Bonn and Cologne.
You have the option to download a rendered heatmap from the current viewport of the interactive map as in image with up to 4000\u00d74000 pixels, there is a link below the map.
"},{"location":"features/kind-rename/","title":"Kind rename","text":"Metadata and importing from several sources can be messy and in some cases Strava will export its acivity types/activity kinds under names like root='Ride' and not \"Ride\". That can lead to issues with tagging and the heatmap - in this case we have multiple, overlapping activity kinds for the same activity kinds.
To fix this, go to Admin -> Settings -> Kind rename
Existing name => New name
root='Ride'
to 'Ride'
put in root='Ride' => Ride
When you start the webserver, you will see the overview page. It looks like the following and shows the activities you've done in the past 30 days:
Then below that you see the latest 15 activities with their tracks on interactive maps.
Each card contains the name, activity type, distance and duration. The non-commute activities are highlighted with a blue border, the commutes just have a gray border.
Click on any of the names and you will see the details of that activity.
"},{"location":"features/share-picture/","title":"Share Picture","text":"On each activity page you will find a \"share picture\" like the following:
"},{"location":"features/share-picture/#privacy-zones","title":"Privacy zones","text":"You might want to remove points that are close to your home, work or relatives. For this you can define arbitrary polygons as \"privacy zones\".
To create such a polygon, go to GeoJSON.io. You will see a map similar to this one:
Select the polygon tool and click on the map to span the polygon.
Once you are done, press Enter to finish the polygon. In the left panel the GeoJSON output will appear:
For this case, we have this GeoJSON:
{\n \"type\": \"FeatureCollection\",\n \"features\": [\n {\n \"type\": \"Feature\",\n \"properties\": {},\n \"geometry\": {\n \"coordinates\": [\n [\n [\n 6.87987514009842,\n 50.68272071401333\n ],\n [\n 6.878628929151887,\n 50.6819310903943\n ],\n [\n 6.8780142440226655,\n 50.68125883278765\n ],\n [\n 6.879563587362242,\n 50.68022375065988\n ],\n [\n 6.880599289703014,\n 50.68029311254671\n ],\n [\n 6.8814665851591315,\n 50.68102940933676\n ],\n [\n 6.881542368256589,\n 50.681723011688035\n ],\n [\n 6.8812729172415175,\n 50.682176515374465\n ],\n [\n 6.87987514009842,\n 50.68272071401333\n ]\n ]\n ],\n \"type\": \"Polygon\"\n }\n }\n ]\n}\n
Paste this in the appropriate settings menu.
You can name the zone to help you remember what it encompasses. You can add multiple zones.
Points that are within any of the privacy zones will not be shown in the share pictures. Except when all of the points are in the privacy zone, then all the points will be shown.
"},{"location":"features/upload/","title":"Uploading activities","text":"Some users don't want to restart the application each time they add new activities but run it on their home server. For this use case you can upload activities. Uploading files is a potential security issue and it is protected with a password. This feature is only enabled when you have set a password in the configuration file.
Put the following into your config.json
file:
{\n \"upload_password\": \"fb1c8d62-07a5-47bf-ada1-30aa66e41d8a\"\n}\n
Be sure to choose your own password and not use the one from this documentation.
Then you can go to the upload page and see this form:
Select a file that you want to upload and a target directory within the \u201cActivities\u201d directory. Finally, enter the password from the configuration file.
After you have uploaded the file, you will be redirected to the parsed activity.
"},{"location":"getting-started/advanced-metadata-extraction/","title":"Advanced Metadata extraction","text":"If you would like to set the metadata fields or change what part of the filename should be the activity name, you can use a custom directory structure with corresponding regular expressions.
An example directory structure:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n\u251c\u2500 Hike/\n\u2502 \u251c\u2500 Hiking Boots 2019/\n\u2502 \u2502 \u251c\u2500 2024-03-03-11-03-18 Some nice place with Alice and Bob.fit\n
"},{"location":"getting-started/advanced-metadata-extraction/#custom-regular-expressions","title":"Custom Regular expressions","text":"The program uses regular expressions to search for patterns in the relative path (in Activities) and extracts the relevant parts with named capture groups (?P<kind>)
, (?P<equipment>)
, (?P<name>)
.
You can use python to test your regular expressions. Read the python re documentation for help.
import re\nre.search(r'(?P<kind>[^/]+)/(?P<equipment>[^/]+)/(?P<name>[^/.]+)', '/Ride/Trekking Bike/2024-03-03-17-42-10 Home to Bakery.gpx').groupdict()\n
{'kind': 'Ride', 'equipment': 'Trekking Bike', 'name': '2024-03-03-17-42-10 Home to Bakery'}\n
You can add your custom regular expressions under the Admin
menu - Settings
- Metadata Extraction
in the WebUI. Settings are saved in your Playground
directory.
Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/(?P<name>[^/.]+)\n
Ride
Trekking Bike
2024-03-03-17-42-10 Home to Bakery
Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n\u2502 \u2502 \u251c\u2500 2024-03-04-16-52-26.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-21_10-28_Sun OsmAnd default track.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-22_07-55_Mon.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/[-\\d_ ]+(?P<name>[^/]+)(?:\\.\\w+)+$\n
Ride
Trekking Bike
Home to Bakery
;
; Sun OsmAnd default track
; Mon
Attention, name may be empty if it is not included in the file name. For OsmAnd default naming the weekday is included in the name.
"},{"location":"getting-started/advanced-metadata-extraction/#filename-after-first-space-as-name","title":"Filename after first space as Name","text":"Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-22_07-55_Mon.gpx\n\u2502 \u2502 \u251c\u2500 2024-04-21_10-28_Sun OsmAnd default track.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/\\S+ ?(?P<name>[^/\\.]*)\n
Ride
Trekking Bike
Home to Bakery
;
; OsmAnd default track
Attention, name may be empty if it is not included in the file name (also for OsmAnd default naming).
"},{"location":"getting-started/advanced-metadata-extraction/#grouping-activity-files-under-a-common-name-for-example-all-your-commutes","title":"Grouping activity files under a common name, for example all your commutes","text":"Path:
Activities/\n\u251c\u2500 Ride/\n\u2502 \u251c\u2500 Trekking Bike/\n\u2502 \u2502 \u251c\u2500 Commute/\n\u2502 \u2502 \u2502 \u251c\u2500 2024-03-04-07-06-12.gpx\n\u2502 \u2502 \u2502 \u251c\u2500 2024-03-04-15-42-32.gpx\n
(?P<kind>[^/]+)/(?P<equipment>[^/]+)/(?P<name>[^/]+)/\n
Ride
Trekking Bike
Commute
(for all activities in Commute directory )Path:
Activities/\n\u251c\u2500 Run/\n\u2502 \u251c\u2500 2024-03-09-09-24-03 To the lake.gpx\n\u2502 \u251c\u2500 2024-03-10-09-44-37 To the top of the hill.gpx\n
(?P<kind>[^/]+)/[-\\d_ ]+(?P<name>[^/]+)(?:\\.\\w+)+$\n
Run
Unknown
To the lake
; To the top of the hill
If you you manually rename, move or delete your activity files, the program needs to reload to respect these changes. You can restart the program or visit Scan New Activities
in the admin menu of the WebUI.
Docker is a software that allows you to run Linux programs in a container. Docker Compose is a tool for defining multi-container Docker environments in a single YAML configuration file and deploy it with a single command.
Tailscale is a VPN solution based on the Wireguard protocol which lets you connect all devices within your virtual private network (tailnet). The Tailscale Docker container exposes the services only via a direct VPN connection, which avoids exposing ports to the open internet to connect to your geo-activity-playground instance on-the-go. It provides a domain with a valid Let's Encrypt certificate which is only accessible via the tailnet. The configuration is based on Docker Tailscale Guide.
This how-to will give you an example compose.yml
that can build the geo-activity-playground docker image from Github and start this project within a Docker container and connecting it via Tailscale.
More information on generating auth keys Navigate to https://login.tailscale.com/admin/settings/keys and generate an auth key.
Example Auth-Key configuration: - Description: docker - Reusable: yes - Expiration: 7 days - Ephemeral: No - Tags: tag:container
In order to use the tag, it must first be defined in your Access control policy in the admin console. Set the same tag as in the Auth-Key.
\"tagOwners\": {\n \"tag:container\": [\"autogroup:admin\"],\n},\n
When you apply a tag to a device for the first time and authenticate it, the tagged device's key expiry is disabled by default.
"},{"location":"getting-started/docker-compose-tailscale/#preparing-tailscale-configuration","title":"Preparing Tailscale configuration","text":"The geo-activity-playground service will be made available by using the Tailscale Serve functionality. It routes traffic from other devices on your Tailscale network (known as a tailnet) to a local service, in this case inside the container. It creates a reverse proxy to the geo-activity-playground container internal port 5000 (do not change it). TS_CERT_DOMAIN
is comprised of a subdomain (hostname set in the compose.yml
) and the tailnet root domain.
mkdir -p /docker/geo-activity-playground/{ts-state,ts-config}\ncd /docker/geo-activity-playground/ts-config\nnano geo-activity-playground.json\n
{\n \"TCP\": {\n \"443\": {\n \"HTTPS\": true\n }\n },\n \"Web\": {\n \"${TS_CERT_DOMAIN}:443\": {\n \"Handlers\": {\n \"/\": {\n \"Proxy\": \"http://127.0.0.1:5000\"\n }\n }\n }\n }\n}\n
"},{"location":"getting-started/docker-compose-tailscale/#compose-configuration-with-tailscale-network","title":"Compose configuration with Tailscale network","text":"With these steps the playground folder (which contains the activities) will be located in the docker project folder. The location can be changed in the compose.yml
.
mkdir -p /docker/geo-activity-playground/playground/Activities\ncd /docker/geo-activity-playground\nnano compose.yml\n
services:\n ts-geo-activity-playground:\n image: tailscale/tailscale:latest\n container_name: ts-geo-activity-playground\n hostname: geo-activity-playground # set your desired name, which will be the tailscale subdomain\n environment:\n - TS_AUTHKEY=tskey-auth-yyyyyyyyyyyyyyyyyyyyyyyyyyyy # paste your created Auth-Key\n - TS_EXTRA_ARGS=--advertise-tags=tag:container # set the same tag as in the Auth-Key\n - TS_SERVE_CONFIG=/config/geo-activity-playground.json\n - TS_STATE_DIR=/var/lib/tailscale\n volumes:\n - /docker/geo-activity-playground/ts-state:/var/lib/tailscale\n - /docker/geo-activity-playground/ts-config:/config\n - /dev/net/tun:/dev/net/tun\n cap_add:\n - net_admin\n - sys_module\n restart: unless-stopped\n geo-activity:\n build:\n context: https://github.com/martin-ueding/geo-activity-playground.git\n # this sets the build context to the DOCKERFILE located in the Github repository\n container_name: geo-activity-playground\n depends_on:\n - ts-geo-activity-playground # start container after the VPN network is active\n network_mode: service:ts-geo-activity-playground # link container network to tailscale container\n volumes:\n - /docker/geo-activity-playground/playground:/data # optional: change left side to your desired playground directory\n restart: unless-stopped\n
If you want to build the release version of geo-activity-playground from Github instead, you can adjust the build context and add the release tag. context: https://github.com/martin-ueding/geo-activity-playground.git#0.29.1
You need to set up your files according to one of the presented methods, like activity files or the Strava API. Consult the other pages in the sidebar for the details.
Once you have your playground directory, you can build the image and start the container.
docker compose build\ndocker compose up -d\n
This will start the webserver and expose it via your tailnet on https://[HOSTNAME].[YourTailnetName].ts.net/
, eg. https://geo-activity-playground.tail41a3.ts.net/
. In order to access your instance via that domain, you have to install and authenticate the Tailscale client app on your device you want to open it from.
If using the tagged release version of geo-activity-playground, update the tag to the latest one first.
docker compose down\ndocker compose build\ndocker compose up -d --force-recreate\n
"},{"location":"getting-started/docker-compose/","title":"Using Git Version via Docker Compose","text":"Docker is a software that allows you to run Linux programs in a container. Docker Compose is a tool for defining multi-container Docker environments in a single YAML configuration file and deploy it with a single command.
This how-to will give you an example compose.yml
that can build the geo-activity-playground docker image from Github and start this project within a Docker container.
With these steps the playground folder (which contains the activities) will be located in the docker project folder. The location can be changed in the compose.yml
.
mkdir -p /docker/geo-activity-playground/playground/Activities\ncd /docker/geo-activity-playground\nnano compose.yml\n
services:\n geo-activity:\n build:\n context: https://github.com/martin-ueding/geo-activity-playground.git\n # this sets the build context to the DOCKERFILE located in the Github repository\n container_name: geo-activity-playground\n volumes:\n - /docker/geo-activity-playground/playground:/data # optional: change left side to your desired playground directory\n ports:\n - 5000:5000 # optional: change the exposed port on the left side\n restart: unless-stopped\n
If you want to build the release version from Github instead, you can adjust the build context and add the release tag. context: https://github.com/martin-ueding/geo-activity-playground.git#0.29.1
You need to set up your files according to one of the presented methods, like activity files or the Strava API. Consult the other pages in the sidebar for the details.
Once you have your playground directory, you can build the image and start the container.
docker compose build\ndocker compose up -d\n
This will start the webserver on http://localhost:5000/ or at the port you chose to expose.
Note that port 5000 may not be available on macOS because of AirPlay, so you can map to another port.
"},{"location":"getting-started/docker-compose/#updating-the-image","title":"Updating the image","text":"If using the tagged release version, update the tag to the latest one first.
docker compose down\ndocker compose build\ndocker compose up -d --force-recreate\n
"},{"location":"getting-started/docker/","title":"Using Git Version via Docker","text":"Docker is a software that allows you to run Linux programs in a container. This how-to will show you how to build and start this project within a Docker container.
"},{"location":"getting-started/docker/#build-the-image","title":"Build the image","text":"First you need to build the Docker image. For this download the source code and build the image using the following commands:
git clone https://github.com/martin-ueding/geo-activity-playground.git\ncd geo-activity-playground\nsudo docker build -t geo-activity-playground .\n
Perhaps you do not need sudo
on your system.
You need to set up your files according to one of the presented methods, like activity files or the Strava API. Consult the other pages in the sidebar for the details.
Once you have your playground directory, you can launch the Docker image with the following. Be sure to replace path/to/playground
with your path.
sudo docker run -p 5000:5000 -v path/to/playground:/data -it geo-activity-playground\n
This will start the webserver on http://localhost:5000/.
Note that port 5000 may not be available on macOS because of AirPlay, so you can map to another port by replacing the port specifier from above with -p 8000:5000
. Then you can open http://localhost:8000/ in your browser.
As this project is still in development, you might want to have a peek into the development version. This is more advanced than using the stable versions, but not impossibly hard.
First you need to clone the git repository from GitHub using the following command:
git clone https://github.com/martin-ueding/geo-activity-playground.git\n
That will create a directory geo-activity-playground
in your current working directory.
Then change into that directory:
cd geo-activity-playground\n
Next we will use Poetry to install the dependencies of the project. First you need to make sure that you have Poetry available. On Ubuntu/Debian run sudo apt install python3-poetry
, on Fedora/RedHat run sudo dnf install poetry
to install it.
Then we can create the virtual environment:
poetry install\n
And next we can run the program:
poetry run geo-activity-playground --basedir path/to/your/playground --help\n
Replace the --help
with the subcommands described in the help message or the other parts described this documentation.
You will need the --basedir
option because you run the program from the source directory and not from your playground directory. If you install the stable version via PIP as described in the other page, you will not need this option.
Over time I will add more commits to the source control system. In order to update your clone to the latest version, execute the following:
git pull\n
This will download the missing changesets and apply them to your downloaded version. After that is done, you need to update your virtual environment with this:
poetry install\n
And then you can continue using it as before.
"},{"location":"getting-started/installing-stable-on-linux/","title":"Installing Stable On Linux","text":"In this how-to guide I will show you how you can install the latest stable version of this project on Linux.
"},{"location":"getting-started/installing-stable-on-linux/#pipx-method","title":"PIPX method","text":"The ideal way to install this project, is using pipx
. First ensure that you have it installed:
sudo apt install pipx
Fedora, RedHat sudo dnf install pipx
Arch, Manjaro sudo pacman -Syu python-pipx
Using PIPX, you can then install the latest version using this command:
pipx install geo-activity-playground\n
That should be it. You might need to ensure that the $PATH
is correct. For that see the section below.
If you don't want to use PIPX, you can also use regular PIP. First install PIP:
Distribution Command Ubuntu, Debiansudo apt install python3-pip
Fedora, RedHat sudo dnf install python3-pip
Arch, Manjaro sudo dnf install python-pip
Then install the package into your user directory.
pip install --user geo-activity-playground\n
That should be it. You might need to ensure that the $PATH
is correct. For that see the section below.
Next you can try to start the program by just entering the following into the terminal:
geo-activity-playground --help\n
If you get a help message, everything is fine. If you get an error about command not found, we need to adjust your PATH. Execute the following in your command line:
xdg-open ~/.profile\n
This brings up an editor with your shell profile. Add a line containing the following at the end of the file:
PATH=$PATH:$HOME/.local/bin\n
This adds the path to your shell environment. This becomes active after you log in again. In order to apply it also to your current shell session, execute export PATH=$PATH:$HOME/.local/bin
in the terminal window. Try the first command in this section again, you should see the help message now.
At some later point you likely want to upgrade to the latest version. For this use this command if you used PIPX:
pipx upgrade geo-activity-playground\n
If you used PIP, use this:
pip install --user --upgrade geo-activity-playground\n
"},{"location":"getting-started/installing-stable-on-windows/","title":"Installing Stable on Windows","text":"This how-to will show you the installation of the project on Windows. Here in the guide we use Windows 10 with the locale set to German, it should generalize to Windows 11 as well.
"},{"location":"getting-started/installing-stable-on-windows/#installing-python","title":"Installing Python","text":"First we need to install Python because that doesn't ship with Windows. Fortunately we can get it from the Microsoft Store. Open that via the start menu and you should see something like this:
Type \u201cPython\u201d into the search bar at the top. In the search results you likely see different Python versions like 3.11 and 3.10. The project is compatible with 3.10 to 3.12; I'd suggest to just go with 3.12. In case that you have already installed one of the other compatible versions, you can skip this step.
Here we select Python 3.11.
In the top right there is a blue button to install the software. Click that.
"},{"location":"getting-started/installing-stable-on-windows/#installing-the-project","title":"Installing the project","text":"After that has run through, you need to open the Power Shell via the start menu. It should open a command line window like this:
We can verify that Python is working by entering python --version
and pip --version
. It should give a sensible version message like this:
Then we can ues PIP to install the project. Type the following:
pip install -U geo-activity-playground\n
It should look like this:
Then press Enter and it will install it, looking like this:
That might take a while. After that has run through, it should give a success message:
Then we're done with this window, you can close it now.
"},{"location":"getting-started/installing-stable-on-windows/#putting-your-activity-files-in-place","title":"Putting your activity files in place","text":"Next you need to create a folder to put the files. I've placed mine just into the Documents folder. In there I've created a folder named exactly \u201cActivities\u201d.
Inside this folder there are my GPX/FIT/TCX/KML files as outlined in how-to guide on using activity files.
You need to have at least one activity file before you can start the program.
"},{"location":"getting-started/installing-stable-on-windows/#starting-the-webserver","title":"Starting the webserver","text":"Once you have your activity files in place, we need to add a start script. Right-click into the playground folder (next to the \u201cActivities folder\u201d) and in the context menu select \u201cCreate New\u201d and then \u201cText File\u201d. Name it start.bat
. Windows will ask you whether want to change the suffix (file extension) because it might get unusable. Yes, we want to do that. It should look like this:
Then right-click on that file and select \u201cEdit\u201d. A text editor will open up. Put the following content into this file:
python -m geo_activity_playground serve\npause\n
Then save and close it. I need you to create this file yourself and cannot offer a download because the Windows Defender will not allow you to execute such script files downloaded as a security precaution. If you create the file yourself, it will let you execute it.
Once you have the start.bat
there, you can double-click on it to execute it. A new terminal window should open and it should start to parse your activities.
After it has loaded everything, you can open http://127.0.0.1:5000/ in your browser. You should then see the landing page:
Also other functions like the heatmap work:
That's it, have fun!
"},{"location":"getting-started/moving-from-strava/","title":"Moving from Strava","text":"If you have been using Strava up to this point but want to use this project from now on, this is the correct guide. Here I will show how you can convert your data from Strava into the format of this project and keep adding new data without Strava in the future.
"},{"location":"getting-started/moving-from-strava/#download-your-archive-from-strava","title":"Download your archive from Strava","text":"Go to the Strava account download page and request a download of your data. This will take a while and you get a notification via e-mail when it is done.
Once it has run through, you will be able to download a ZIP file. Once extracted, it will have a structure like this:
.\n\u251c\u2500\u2500 activities [2217 entries exceeds filelimit, not opening dir]\n\u251c\u2500\u2500 activities.csv\n\u251c\u2500\u2500 applications.csv\n\u251c\u2500\u2500 bikes.csv\n\u251c\u2500\u2500 blocks.csv\n\u251c\u2500\u2500 categories_of_personal_information_we_collect.pdf\n\u251c\u2500\u2500 clubs\n\u251c\u2500\u2500 clubs.csv\n\u251c\u2500\u2500 comments.csv\n\u251c\u2500\u2500 community_content.json\n\u251c\u2500\u2500 community_personal_data.json\n\u251c\u2500\u2500 components.csv\n\u251c\u2500\u2500 connected_apps.csv\n\u251c\u2500\u2500 contacts.csv\n\u251c\u2500\u2500 email_preferences.csv\n\u251c\u2500\u2500 events.csv\n\u251c\u2500\u2500 favorites.csv\n\u251c\u2500\u2500 flags.csv\n\u251c\u2500\u2500 followers.csv\n\u251c\u2500\u2500 following.csv\n\u251c\u2500\u2500 general_preferences.csv\n\u251c\u2500\u2500 global_challenges.csv\n\u251c\u2500\u2500 goals.csv\n\u251c\u2500\u2500 group_challenges.csv\n\u251c\u2500\u2500 information_we_disclose_for_a_business_purpose.pdf\n\u251c\u2500\u2500 local_legend_segments.csv\n\u251c\u2500\u2500 logins.csv\n\u251c\u2500\u2500 media [252 entries exceeds filelimit, not opening dir]\n\u251c\u2500\u2500 media.csv\n\u251c\u2500\u2500 memberships.csv\n\u251c\u2500\u2500 messaging.json\n\u251c\u2500\u2500 metering.csv\n\u251c\u2500\u2500 mobile_device_identifiers.csv\n\u251c\u2500\u2500 orders.csv\n\u251c\u2500\u2500 partner_opt_outs.csv\n\u251c\u2500\u2500 posts.csv\n\u251c\u2500\u2500 privacy_zones.csv\n\u251c\u2500\u2500 profile.csv\n\u251c\u2500\u2500 profile.jpg\n\u251c\u2500\u2500 reactions.csv\n\u251c\u2500\u2500 routes\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 1.gpx\n\u251c\u2500\u2500 routes.csv\n\u251c\u2500\u2500 segments.csv\n\u251c\u2500\u2500 shoes.csv\n\u251c\u2500\u2500 social_settings.csv\n\u251c\u2500\u2500 starred_routes.csv\n\u251c\u2500\u2500 starred_segments.csv\n\u251c\u2500\u2500 support_tickets.csv\n\u2514\u2500\u2500 visibility_settings.csv\n
This directory contains a file activities.csv
with the metadata and also a directory activities
with the files that you have recorded.
Use the following command to create a directory from your Strava actitivies:
geo-activity-playground convert-strava-checkout ~/Downloads/export_123456/ ~/Documents/Outdoors/Playground\n
This should read through all the activities and create a directory structure with the pattern ~/Documents/Outdoors/Playground/Activities/{Kind}/{Equipment}/{Commute}/{Date} {Time} {Name}.{Suffix}
. For instance one file might be named Activities/Run/5212701.0/2019-07-09 09-59-25 Around the \u5317\u4eac\u5927\u5b66 campus.gpx.gz
.
The equipment might have nonsensical seeming names like 10370891.0
. The problem here is that Strava doesn't export the list of activities with that index. If your equipment doesn't have a nickname, it will just be such a number.
Now that the files from Strava are converted, consult the guide on using activity files to proceed from here.
"},{"location":"getting-started/moving-from-strava/#recording-more-activities","title":"Recording more activities","text":"Now that you don't record via Strava, you will need some other app to record your activities. There are Apps like OsmAnd or OpenTracks which provide such functionality. Export the files as GPX, FIT, TCX or KML files and put them into the directory structure. On the next start of the program, they will be picked up.
"},{"location":"getting-started/starting-the-webserver/","title":"Starting The Webserver","text":"Before you start here, you should have done these things:
Now we can start the webserver which provides most of the features. This is done with the serve
command. So depending on how you have installed it, the commands could look like these:
geo-activity-playground serve
if you are in the playground directory and have installed a stable version.geo-activity-playground --basedir ~/Dokumente/Karten/Playground serve
if your playground directory is somewhere else and you have installed a stable version.poetry run geo-activity-playground --basedir ~/Dokumente/Karten/Playground serve
if you have it from the git checkout and want to use local files in your directory as a data source.The webserver will start up and give you a bit of output like this:
2023-11-19 17:59:23 geo_activity_playground.importers.strava_api INFO Loading metadata file \u2026\n2023-11-19 17:59:23 stravalib.protocol.ApiV3 INFO GET 'https://www.strava.com/api/v3/athlete/activities' with params {'before': None, 'after': 1700392964, 'page': 1, 'per_page': 200}\n2023-11-19 17:59:23 geo_activity_playground.importers.strava_api INFO Checking for missing time series data \u2026\n * Serving Flask app 'geo_activity_playground.webui.app'\n * Debug mode: off\n2023-11-19 17:59:23 werkzeug INFO WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.\n * Running on http://127.0.0.1:5000\n2023-11-19 17:59:23 werkzeug INFO Press CTRL+C to quit\n
The warning about the development server is fine. We are using this only to play around, not to power a web service for other users.
Open http://127.0.0.1:5000 to open the website in your browser. There might be some more messages about downloading and parsing data. The first startup will take quite some time. When it is done you will see something like this:
Click around and explore the various features.
"},{"location":"getting-started/starting-the-webserver/#setting-host-and-port","title":"Setting host and port","text":"In case you don't like the default value of 127.0.0.1:5000
, you can use the optional command line arguments --host
and --port
to specify your values.
Outdoor activities are usually recorded as .GPX
or .FIT
files. Some apps like OsmAnd , OpenTracks or Organic Maps, GPS handhelds, smartwatches or cycling computers give you these files.
Before starting the service you need to create a folder for your activities and put at least one activity file in there.
Create a Playground
folder on your storage somewhere and add a subfolder Activities
. There you can add your activity files. For example:
~/\n\u251c\u2500 Documents[or other location]/\n\u2502 \u251c\u2500 Playground/\n\u2502 \u2502 \u251c\u2500 Activities/\n\u2502 \u2502 \u2502 \u251c\u2500 2024-03-03-17-42-10 Home to Bakery.gpx\n
The program will treat the files as read-only and does not modify them.
Once the service is running you can use the Uploader to add your files. You can manually rename, move or delete your activity files, but the program needs to reload to respect these changes. You can restart the program or visit Scan New Activities
in the admin menu of the WebUI.
Most activity file formats contain basic data like date
, time
and track points
. Each activity in geo-activity-playground also has the metadata fields kind
, equipment
and name
. They can be extracted from files that contain them.
If no metadata is found, kind
and equipment
default to Unknown
. The name
is then extracted from the file name (without the suffix). So for Activities/2024-03-03-17-42-10 Home to Bakery.gpx
the name
is 2024-03-03-17-42-10 Home to Bakery
.
Once you have your files put into the directory, you're all set and can proceed with the next steps.
You can extend the directory structure to categorize your activities, see Advanced Metadata Extraction.
"},{"location":"getting-started/using-strava-api/","title":"Using Strava API","text":"You might have all your data on the Strava service and would like to use this for additional analytics without moving your data. That is fine.
If you don't mind a bit of rate-limiting, you can just directly go ahead and start the webserver. It will offer to connect with Strava.
"},{"location":"getting-started/using-strava-api/#your-own-strava-app","title":"Your own Strava App","text":"In order to use the Strava API without sharing the rate-limiting with other users, you need to create your own app. If my explanation doesn't suit you, have a look at this how-to guide as well.
Navigate to the API settings page and create an app. It only needs to have read permissions.
After you are done with that, you can see your App here:
There is a \"client ID\" and a \"client secret\" that we are going to need for the next step. In general our app could be used by all sorts of people who can then access their data only. We want to access our own data, but we still need to authorize our app to use our data.
Open the webserver of this program and go the Strava API setup page. Enter your client ID and client secret, click on \"Connect to Strava\".
This will prompt an OAuth2 request where you have to grant permissions to your app. After that you will be redirected back to the app and it should be set up. At the moment you need to restart the webserver such that it can start to download the activities. Due to rate-limiting it can still take a while.
"},{"location":"getting-started/using-strava-api/#use-export-to-avoid-rate-limiting","title":"Use export to avoid rate limiting","text":"When you first start this program and use the Strava API as a data source, it will download the metadata for all your activities. Then it will start to download all the time series data for each activity. Strava has a rate limiting, so after the first 200 activities it will crash and you will have to wait for 15 minutes until you can try again and it will download the next batch.
Therefore it is recommended to use a Strava export in order to get started quicker. For this go to the Strava account download page and download all your data. You will get a ZIP file. Unpack the files into Playground/Strava Export
. These will be picked up there. Activities from Strava will only be downloaded after importing all these, and only the ones after the last one in the export will be downloaded. This way you can get started much quicker.
If you don't want to download new activities from Strava, use --skip-reload
to have the webserver start right away.
This is the log of high-level changes that I have done in the various versions.
"},{"location":"reference/changelog/#version-0","title":"Version 0","text":"This is the pre-release series. Things haven't settled yet, so each minor version might introduce breaking changes.
"},{"location":"reference/changelog/#version-034","title":"Version 0.34","text":""},{"location":"reference/changelog/#version-0341","title":"Version 0.34.1","text":"activities.csv
from the Strava export.--skip-strava
and replace it with --skip-reload
.Activities
directory._meta
.ignore_suffixes
which can be set to something like [\".kml\"]
to ignore certain file types.root=
prefix in activity kind when importing from Strava.stravalib
version.KeyError
when trying to use the heatmap or the explorer tile maps.stravalib
and with that also pydantic
. That fixes a bug with recursive_guard
.num_processes
option.strava-last-activity-date.json
which is needed such that the Strava API importer can pick up after all the activities that have been imported via the checkout.num_processes = 1
in the configuration.config.json
replaces the config.toml
and will automatically be generated.pandas >= 2.2.0
to make sure that it knows about include_groups
.Add an web interface to connect to Strava API using a shared application such that it becomes much simpler to set up.
GH-41: Compute moving time.
activities.parquet
exists. I've added a check.distance/km
.Breaking change: New way to extract metadata from paths and filenames. This uses regular expressions and is more versatile than the heuristic before. If you have used prefer_metadata_from_file
before, see the documentation on activity files for the new way.
GH-105: Ignore similar activities that have vanished.
GH-109: Better error message when trying to start up without any activity files.
Removed imagehash
from the dependencies.
unsupported operand type(s) for /: 'list' and 'int'
.activities.csv
.vegafusion[embed]
explicit in the dependencies.opener
.distance_km
and everything is represented as kilometer internally now.prefer_metadata_from_file
configuration option.--skip-strava
to the serve
command in order to start the webserver without reaching out to Strava first. This might be useful if the rate limit has been exceeded..DS_Store
files in the activity directory. GH-81.tcx.gz
files.Dockerfile
such that one can easily use this with Docker. GH-78if __name__ == \"__main__\"
clause such that one can use python -m geo_activity_playground
on Windows.Activity/{Kind}/{Equipment}/{Name}.{Format}
directory structure.geo-activity-playground
and not just geo-playground
. GH-11