# running-heatmap: personal Strava heatmaps from a data export, no API

> A Jupyter notebook that turns the zip file Strava lets you download into a single interactive HTML map with six layers. It is built for one runner's own history, not for a public map service.

**moresamwilson/running-heatmap** — Generate heatmaps from your Strava export - frequency, pace, heart rate and gradient.

- Repository: https://github.com/moresamwilson/running-heatmap
- Stars: 581 · Forks: 60
- Language: Jupyter Notebook
- License: MIT
- Published: 2026-09-20 · Updated: 2026-09-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/moresamwilson-running-heatmap

## What running-heatmap does that Strava's own heatmap does not

Strava's public heatmap aggregates everyone's activity and is built for route discovery. running-heatmap does the opposite: it reads one person's export and draws only that person's paths. The README describes the output as a single HTML file with six switchable layers, covering frequency on a linear scale, frequency on a log scale, average pace, average heart rate, absolute gradient and gradient direction. There is no API call and no account linking. The input is the zip Strava produces from its account download request, which the README says to unzip and place next to heatmap.ipynb.

The intended user is a runner who wants to see their own training geography without paying for a third-party service or handing a token to one. The layers are the interesting part. Frequency answers where you actually go. Pace and heart rate answer how hard those places are for you. Gradient answers which direction each climb runs. None of that is available from the public Strava heatmap, because the public map has no access to your heart rate or your pace.

## How the notebook turns .fit.gz files into six raster layers

The pipeline is a notebook, so the data flow is visible in the cells rather than hidden in a package. Activities are read from ACTIVITIES_DIR, filtered by ACTIVITY_TYPES and by DATE_FROM and DATE_TO, then parsed with fitparse. The README notes that parsing .fit.gz files is slow, so GPS data is cached after the first run, and changing the date range or config will not re-parse files already loaded. That caching behaviour is the reason a second run with a different date window is fast.

The projection choice is the most deliberate detail in the README. The raster grid is built in Web Mercator (EPSG:3857) so it aligns to the map tile basemap with no reprojection step. Anything measured in real-world metres, meaning the clip radius around home and the rise-over-run calculation for gradient, is computed in a local UTM projection instead, because Web Mercator distorts distances at higher latitudes. The README is explicit that the visual output is unaffected and the point is measurement accuracy. This is a correct trade-off, and it is also the kind of detail that a wrapper library would have hidden from you.

## Installing running-heatmap and drawing your first map

The repository has four top-level entries: LICENSE, README.md, heatmap.ipynb and requirements.txt. There is no package on PyPI and no CLI, so setup means cloning the repository and installing the pinned dependencies from requirements.txt. The README gives a single command for that.

```bash
pip install -r requirements.txt
```

That installs numpy, pandas, fitparse, folium, pyproj, scipy, Pillow and matplotlib at the versions pinned in the file. The project is a notebook, so the next step is requesting your data from Strava through Settings, My Account, Download or Delete Your Account, Download Request, then unzipping the export and placing the folder next to heatmap.ipynb.

Before running the cells you edit the config cell. The README shows the four keys and the values it expects.

```python
ACTIVITIES_DIR = "your_export_folder"   # name of the unzipped folder
ACTIVITY_TYPES = ["Run"]                # Run, Ride, Hike, Walk, ...
DATE_FROM      = "2024-01-01"           # or None for no lower limit
DATE_TO        = "2024-12-31"           # or None for today
```

Run all cells and the map is written to outputs/heatmap.html. Open that file in a browser and you get the layer switcher. If the map comes back empty or clipped to the wrong area, the home detection described below is the first thing to check.

## Home detection is a heuristic and it can pick the wrong place

The notebook does not ask where you live. It takes the most common activity start point in the date range and treats that as home, then includes only activities within RADIUS_KM of that point. The README states the failure case plainly: if you started more runs from somewhere else, work or a club, than from home in that period, that location wins. The override is HOME_LAT and HOME_LON.

This matters more than it first appears, because the filter is silent. A date range that happens to contain a run streak from a holiday flat, or a season of club sessions, can shift the detected centre and quietly drop the runs you wanted to see. The README does not document any warning when the radius excludes activities, so the only signal is a map that looks thinner than expected. If your training is spread across two regular start points, set HOME_LAT and HOME_LON explicitly rather than trusting the vote.

## What the frequency and gradient layers actually measure

Two of the six layers do not mean what their names suggest, and the README says so. The frequency layers count GPS samples per pixel, not distinct activities, because GPS records at roughly 1 Hz. A slow run deposits more points along the same path than a fast one. So the orange layer is closer to time spent on each road than to how many times you ran it. The README calls this arguably more useful, and for training purposes it probably is, but anyone expecting a pass counter will misread the map. The log scale version exists because a few favourite routes dominate the linear scale and wash out everything else.

Pace and heart rate are all-time averages within the selected date range. Each pixel is the mean across every activity that crossed it, so a route you used to run slowly and now run fast shows somewhere in the middle. The README's advice is to narrow the date range for a specific period. The gradient layers inherit the noise of GPS altitude, which the README puts at roughly plus or minus 10 to 20 metres vertically against 3 to 5 metres horizontally. On hilly terrain that is usable. On flat routes the signal-to-noise ratio is poor and the layer will look like static. If your running is mostly flat, two of the six layers are close to decoration.

## How it compares with paying for a hosted personal heatmap

The obvious alternative is a hosted service that connects to your Strava account and renders a personal heatmap for you, with no local install and a shareable link. The difference in approach is where the data lives and what you can compute. A hosted tool holds an API token, pulls activities on its own schedule, and shows you the layers its authors chose. running-heatmap holds nothing: you download the export yourself, the notebook reads local .fit.gz files, and the output is an HTML file on your disk. That also means you can open the notebook and change the aggregation, which you cannot do with a hosted map.

The cost is real. You install eight Python packages, you wait for the first parse, and every new run requires a fresh Strava export and a re-run. There is no scheduling, no automatic refresh and no sharing story beyond sending someone the HTML file. For a one-off look at a year of training, the notebook wins on cost and control. For a map you want updated weekly without touching a terminal, a hosted service is the better fit.

## Licence, pinned dependencies and the upgrade cost

The repository is MIT licensed, so you can copy, modify and redistribute the notebook, including in commercial work, provided the copyright notice and permission notice travel with it. That is the usual permissive arrangement and it is the whole of the licence implication here; the notebook ships no data and no service.

Upgrades are the part to think about. requirements.txt pins exact versions of numpy, pandas, fitparse, folium, pyproj, scipy, Pillow and matplotlib, including numpy 2.4.4 and pandas 3.0.2. Those pins are what makes the notebook reproducible, and they are also what will make it awkward in a year, because pandas major versions have historically moved APIs that notebook code depends on. There is no test suite in the repository and no CI, so a dependency bump is verified by running the cells and looking at the map. The last push to the repository was on 2026-05-08. The README also points to a video as the origin of the code, which suggests the notebook is a shared artifact rather than a maintained library with a release process.

## Conclusion

Adopt it if you already have a Strava export and want a personal heatmap you own as one HTML file, and you are willing to edit a config cell in heatmap.ipynb and re-run it when you want a different date range. Do not adopt it if you want a hosted map you can share with a link, if your runs are mostly on flat ground and you care about gradient, or if you do not want to install numpy, pandas, fitparse, folium, pyproj, scipy, Pillow and matplotlib. Before trusting any layer, check the auto-detected home point against where you actually start most runs, because a club or work start can win the vote and silently clip your data.

## FAQ

### What is a running heat map?

In this project it is a single HTML file with six layers drawn over a map basemap, showing where your runs went and, per pixel, how often, how fast, at what heart rate and at what gradient you crossed it. The layers can be switched on and off in the browser.

### Is Strava heatmap free?

The README does not discuss Strava's own heatmap pricing. What it does say is that running-heatmap needs no API and works from the zip file Strava lets you download from Settings, My Account, Download or Delete Your Account, Download Request.

### Is running-heatmap a free heatmap tool?

Yes. It is MIT licensed, installs from requirements.txt, and produces outputs/heatmap.html locally with no account, token or hosted service involved.

### Can ChatGPT create heatmaps?

The README does not mention ChatGPT or any language model. running-heatmap creates its heatmaps by parsing local .fit.gz files from a Strava export with fitparse and rendering them with folium.

## Sources

- [Issues](https://github.com/moresamwilson/running-heatmap/issues)
- [License: MIT](https://github.com/moresamwilson/running-heatmap/blob/main/LICENSE)
- [moresamwilson/running-heatmap on GitHub](https://github.com/moresamwilson/running-heatmap)
- [README](https://github.com/moresamwilson/running-heatmap/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/moresamwilson-running-heatmap
