Parse Garmin FIT files and visualize your outdoor activities as interactive maps, health dashboards, and high-resolution printable posters.
- Interactive Map -- self-contained HTML map (Leaflet.js) with sport/person filters and a timeline slider
- Print Generator -- 300 DPI poster-quality PNGs in 4 visual themes, ready for print shops
- Health Dashboard -- Chart.js dashboard with heart rate, distance, elevation, and training load trends
# 1. Install dependencies
pip install -r requirements.txt
# 2. Place your .fit files in fit_files/ (subfolders per person work too)
mkdir -p fit_files
# fit_files/
# |-- alice/
# | |-- 2024-01-15_running.fit
# | +-- ...
# +-- bob/
# +-- ...
# 3. (Optional) Create a .env file for device-to-person mapping
cp .env.example .env
# Edit .env with your device serial numbers
# 4. Generate the interactive map
python garmin_map_builder.py
# 5. Generate a printable poster
python garmin_print_generator.py --theme dark --size A3 --city "MY CITY"pip install -r requirements.txtDependencies: fitparse, Pillow, requests, numpy, scipy
Export your activities from Garmin Connect:
- Go to Activities > select an activity > gear icon > Export Original
- Or use garmin-connect-export for bulk export
Place .fit files in the fit_files/ directory. Subdirectories per person are supported.
Create a .env file in the project root to map Garmin device serial numbers to names:
DEVICE_OWNERS=1234567890:Alice,9876543210:Bob
You can find your device serial in Garmin Connect or by inspecting the file_id message in any FIT file. If no .env is provided, activities are labeled "Unknown" unless organized into named subfolders under fit_files/.
Generates a self-contained HTML file with a dark-themed Leaflet map showing all GPS activities.
python garmin_map_builder.py # build from cache
python garmin_map_builder.py --force # re-parse all FIT filesFeatures:
- Filter by sport type and person
- Timeline slider with cumulative/monthly playback
- Click any route for details (distance, duration, elevation, heart rate, speed, calories)
- GPS track simplification for compact file size (~2 MB for 500+ activities)
Generates high-resolution PNG images suitable for printing at A3, A2, or A1 sizes.
# Basic usage
python garmin_print_generator.py
# Choose a theme
python garmin_print_generator.py --theme dark # map tiles + colored routes + glow
python garmin_print_generator.py --theme light # light map tiles + muted routes
python garmin_print_generator.py --theme routes_only # black background, glowing routes, no map
python garmin_print_generator.py --theme heatmap # black background, warm color intensity map
# Crop to a specific area
python garmin_print_generator.py --bbox 46.0 10.0 48.5 13.0
# Filter by person or sport
python garmin_print_generator.py --person Alice --sport Running Hiking
# Large poster
python garmin_print_generator.py --theme routes_only --size A1 --orientation landscape
# Quick preview (72 DPI, much faster)
python garmin_print_generator.py --preview
# Customize text overlay
python garmin_print_generator.py --city "BAVARIAN ALPS" --no-statsAll options:
| Flag | Description | Default |
|---|---|---|
--theme |
dark, light, routes_only, heatmap |
dark |
--size |
A3, A2, A1 |
A3 |
--orientation |
portrait, landscape |
portrait |
--dpi |
DPI override | 300 |
--bbox |
Crop: LAT_S LON_W LAT_N LON_E |
auto-fit |
--center / --zoom |
Center point + zoom level (8-18) | auto-fit |
--sport |
Filter by sport(s) | all |
--person |
Filter by person name | all |
--city |
City name in overlay | MY CITY |
--no-text |
Disable all text overlays | |
--no-legend |
Disable sport legend | |
--no-stats |
Disable stats line | |
--preview |
Fast 72 DPI preview | |
--force |
Re-parse all FIT files | |
--output |
Custom output path | auto-generated |
Output goes to output/ (gitignored).
Generates an HTML dashboard from all FIT files (including indoor/no-GPS activities).
python garmin_health_dashboard.pyCharts include: activity calendar, heart rate trends, weekly training volume, stress levels, daily steps, HR zone distribution, training load (TRIMP), seasonal sport mix, and recovery gap patterns.
Shared FIT parsing, sport classification, track simplification, and caching used by all other scripts. Not run directly.
One-time utility to sort a flat directory of FIT files into per-person subfolders based on device serial numbers or filename patterns.
- FIT parsing -- reads binary Garmin FIT files via
fitparse, extracts GPS tracks, session metadata (distance, HR, elevation, calories), and device serial numbers - Sport classification -- maps Garmin sport/sub_sport/name fields to display categories (Road Cycling, Trail Running, Hiking, etc.) with support for German sport names
- GPS cleanup -- filters GPS spikes, then applies Douglas-Peucker simplification for the interactive map (raw tracks are preserved for print quality)
- Caching -- parsed results are cached in
.garmin_cache.json(keyed by file path, mtime, and size) so repeat runs are instant - Tile fetching -- print generator downloads CartoDB map tiles and stitches them into a base image using Web Mercator projection
- Route rendering -- Pillow draws anti-aliased polylines (2x supersampling + LANCZOS downscale) with optional glow effect
- Heatmap -- numpy float32 accumulator with Gaussian blur, mapped to a warm color gradient
garmin-activity-toolkit/
|-- garmin_data.py # Shared: FIT parsing, caching, constants
|-- garmin_map_builder.py # Interactive Leaflet HTML map
|-- garmin_print_generator.py # 300 DPI printable poster PNGs
|-- garmin_health_dashboard.py # Chart.js health dashboard
|-- organize_fit_files.py # One-time: sort FIT files into subfolders
|-- requirements.txt
|-- .env.example # Template for device serial -> person mapping
|-- fit_files/ # Your FIT files go here (not tracked)
+-- output/ # Generated print PNGs (not tracked)
The print generator uses free CartoDB basemap tiles (dark_nolabels and light_nolabels variants with @2x retina resolution). No API key required. Tiles are fetched on demand with concurrent downloads and cached locally in .tile_cache/.
MIT