Metadata-Version: 2.5
Name: strava-globe
Version: 0.3.0
Summary: Your entire Strava history as glowing tracks on a realistic 3D Earth — local, private, no API keys
Project-URL: Repository, https://github.com/lukaszkolodziejczyk/strava-globe
Project-URL: Issues, https://github.com/lukaszkolodziejczyk/strava-globe/issues
Author: Lukasz Kolodziejczyk
License-Expression: BSD-3-Clause
License-File: LICENSE
Keywords: cesium,fit,globe,gps,gpx,map,running,strava,visualization
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.11
Requires-Dist: fitfast>=0.1.0
Requires-Dist: numpy
Requires-Dist: pillow
Requires-Dist: platformdirs
Requires-Dist: reverse-geocoder
Requires-Dist: simplification
Description-Content-Type: text/markdown

# Strava Globe

Your entire Strava history as glowing tracks on a realistic 3D Earth.

![Every activity on one globe](https://raw.githubusercontent.com/lukaszkolodziejczyk/strava-globe/main/docs/hero.jpg)

Point it at the export zip Strava gives you, and you get a private, local globe
(CesiumJS + satellite imagery) you can spin, zoom from orbit down to street
level, and fly around:

- **Every GPS track** drawn on the globe, colored by activity type, with
  hover tooltips (name · date · distance · place) — click a track to fly to it.
- **▶ Tour mode**: your activities are clustered into places, named via offline
  reverse geocoding, ordered by first visit — and the camera flies through your
  travel history stop by stop, with a card per place and a closing summary.
- **Your photos, where you took them**: pictures attached to activities show up
  on the activity card and on tour stops — click one for a full-size lightbox.
  App-generated stat cards (training-plan summaries and the like) are detected
  and skipped, so only real photographs appear.
- **Filters** by activity type and year, satellite or street basemap, and an
  idle globe spin until you grab it.
- **No accounts, no API keys, no upload.** Your tracks never leave your machine.

![Flight tour flying between places](https://raw.githubusercontent.com/lukaszkolodziejczyk/strava-globe/main/docs/tour.gif)

*Tour mode mid-journey: Rawai → Ao Nang, each stop card carrying the photos
taken there — click one for full size.*

| From orbit to city… | …to street level |
|---|---|
| ![A city's worth of tracks](https://raw.githubusercontent.com/lukaszkolodziejczyk/strava-globe/main/docs/city.jpg) | ![Runs along a coastline](https://raw.githubusercontent.com/lukaszkolodziejczyk/strava-globe/main/docs/street.jpg) |

## Quickstart

1. Request your archive on strava.com: *Settings → My Account → Download or
   Delete Your Account → Request Your Archive* (the zip arrives by email).
2. Run (with [uv](https://docs.astral.sh/uv/), no install needed):

   ```bash
   uvx strava-globe ~/Downloads/export_12345678.zip
   ```

   or classically:

   ```bash
   pip install strava-globe
   strava-globe ~/Downloads/export_12345678.zip
   ```

The globe builds and opens at http://localhost:8933. Next time, plain
`uvx strava-globe` reuses the last build (the generated data lives in your
user data directory; `--port`, `--no-browser`, and `--data-dir` are available).

## How it works

The builder (`strava_globe/tracks.py`) reads `activities.csv` from the
export — zip or extracted folder — then for every activity file:

- decodes FIT with [fitfast](https://github.com/lukaszkolodziejczyk/fitfast)
  and GPX with a small XML parser (indoor activities without GPS are skipped),
- splits tracks at GPS dropouts (>500 m between fixes),
- simplifies each segment with Ramer-Douglas-Peucker (~5 m tolerance,
  [`simplification`](https://pypi.org/project/simplification/)),
- tags the activity with its nearest city and country — offline GeoNames
  lookup via [`reverse-geocoder`](https://pypi.org/project/reverse_geocoder/),
- thumbnails any photos attached to the activity (EXIF-rotated, two sizes,
  Pillow) into your local data directory — dropping app screenshots, detected
  when an image is both locally flat (synthetic gradients, share of near-zero
  variance 8×8 blocks) and extremely compressible (bytes per pixel); real
  photographs carry sensor noise everywhere and never trip both,
- and writes one compact `activities.json` (~1.7 MB for 725 tracks /
  1.9 M raw GPS points).

The web app (`strava_globe/web/`) is dependency-free: CesiumJS 1.131 from the
jsdelivr CDN, imagery streamed from Esri World Imagery (satellite) or
OpenStreetMap (streets), served by a stdlib static server with caching
disabled.

## Controls

| | |
|---|---|
| drag / scroll | spin / zoom the globe |
| hover a track | tooltip with name, date, distance, place |
| click a track | fly to that activity |
| `T` or **▶ Tour** | replay your travels place by place |
| space · ⏮ ⏭ / arrows · Esc | pause · previous/next stop · end tour |
| ⌂ Home | back to the full globe |

## Privacy

Your GPS data stays local: the generated `activities.json` lives in your user
data directory (never in this repo or the package), and the app talks only to
the CDN and the map tile servers (which necessarily see which map areas you
view, as with any online map).

## Notes

- Rendering quirk: `msaaSamples = 1` and `dynamicAtmosphereLighting = false`
  are deliberate — MSAA and sun-driven atmosphere shading render a black globe
  on software-GL stacks (e.g. headless/embedded Chromium).
- Tile imagery: Esri World Imagery is used under its
  [terms](https://www.esri.com/en-us/legal/terms/full-master-agreement) for
  personal/non-commercial viewing; OpenStreetMap tiles per the
  [OSM tile usage policy](https://operations.osmfoundation.org/policies/tiles/).
- Not affiliated with or endorsed by Strava, Garmin, Esri, or OpenStreetMap.
  "Strava" is used descriptively: the input is the data export Strava provides
  to its users.

## Development

```bash
git clone https://github.com/lukaszkolodziejczyk/strava-globe
cd strava-globe
uv run strava-globe path/to/export.zip
```

Releases: push a `v*` tag — GitHub Actions builds and publishes to PyPI via
trusted publishing (`.github/workflows/release.yml`).

## License

[BSD 3-Clause](LICENSE). Place names derived from
[GeoNames](https://www.geonames.org/) data (CC-BY 4.0) via `reverse-geocoder`.
