A street coverage tracker that connects to your Strava account and visualizes which streets you've run (or walked/cycled) — like Pac-Man eating dots on a map.
Pac-Man Tracker imports your Strava activities, matches the GPS traces against OpenStreetMap street data, and builds a coverage map showing which streets you've traveled and which ones are still waiting. It also suggests routes through uncovered streets so you can efficiently fill in the gaps.
- Strava Integration — OAuth2 login, automatic activity sync, webhook-based real-time updates
- GPS-to-Street Matching — Buffers GPS traces by 15m and computes intersection ratios against street segments
- Coverage Dashboard — City → neighborhood → street drill-down with percentage progress
- Interactive Map — Color-coded street overlay (traveled vs untraveled) with activity layers
- Route Suggestions — Uses OSRM to generate walking routes through uncovered streets
- Progress Timeline — Daily snapshots with milestone detection (25%, 50%, 75%, 100%)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Frontend │──────▶│ Backend │──────▶│ Strava │
│ React SPA │ REST │ FastAPI │ OAuth │ API │
│ :5173 │◀──────│ :8000 │◀──────│ │
└──────────────┘ └──────┬───────┘ └──────────────┘
│
┌─────────┴─────────┐
│ │
┌─────▼─────┐ ┌───────▼──────┐
│ PostgreSQL │ │ OSRM │
│ + PostGIS │ │ :5000 │
│ :5432 │ │ (routing) │
└────────────┘ └──────────────┘
| Component | Tech Stack |
|---|---|
| Frontend | React 18, TypeScript, Vite, react-leaflet, Zustand |
| Backend | Python 3.12+, FastAPI, SQLAlchemy, GeoAlchemy2 |
| Database | PostgreSQL 16 + PostGIS 3.4 (via Docker Compose) |
| Routing | OSRM with foot profile (via Docker) |
| Geospatial | Shapely, GeoPandas, OSMnx, pyproj |
- Python 3.12+
- Node.js 18+
- Docker (for PostgreSQL + PostGIS database and OSRM routing server)
- A Strava API Application
git clone <repo-url> pacman-tracker
cd pacman-tracker
# Backend env
cp backend/.env.example backend/.env
# Edit backend/.env with your Strava credentials and a SECRET_KEY# Start PostgreSQL + PostGIS
docker compose up db -d
cd backend
python -m venv .venv
# Windows
.venv\Scripts\Activate.ps1
# macOS/Linux
source .venv/bin/activate
pip install -r requirements.txt
# Run database migrations
alembic upgrade head
# Start the API server
uvicorn app.main:app --reload --port 8000If you want to clear out the old local backend container, rebuild the image, and run it again with the existing env file and data volume, use:
.\infra\run-local-backend.ps1 -DetachUseful options:
.\infra\run-local-backend.ps1 -RemoveImage -PruneDangling -DetachWhen the backend runs in Docker and OSRM runs on the host machine, set OSRM_URL=http://host.docker.internal:5000 in backend/.env. If you run the backend directly on the host, http://localhost:5000 is still the right value.
cd frontend
npm install
npm run dev
# → http://localhost:5173OSRM (Open Source Routing Machine) is a routing engine that calculates walking directions between points on a map. The app uses it to generate route suggestions that prioritise untraveled streets — given a starting point and a target distance, OSRM solves a round-trip through selected waypoints and returns turn-by-turn geometry that gets drawn on the map.
OSRM runs as a separate service (not embedded in the backend). Locally it runs in Docker; in production it runs as an Azure Container Instance on a private VNet. It uses OpenStreetMap data for Washington state, pre-processed with a foot/walking profile.
# From project root — one-time data preparation (~30 min, downloads ~800 MB of OSM data)
docker compose --profile prepare up osrm-prepare
# Start the routing server on :5000
docker compose up osrmFor production deployment, see Section 6 of AZURE_DEPLOY.md.
pacman-tracker/
├── README.md ← You are here
├── docker-compose.yml ← OSRM routing server
├── infra/
│ ├── deploy-prod.ps1 ← Production deployment helper
│ └── run-local-backend.ps1 ← Local Docker cleanup + rebuild + run helper
├── backend/
│ ├── README.md ← Backend-specific docs
│ ├── .env.example ← Environment variable template
│ ├── pyproject.toml ← Project metadata + tool config
│ ├── requirements.txt ← Python dependencies
│ ├── app/
│ │ ├── main.py ← FastAPI app factory + lifespan
│ │ ├── config.py ← Pydantic settings (env vars)
│ │ ├── database.py ← SQLAlchemy engine + session management
│ │ ├── api/ ← Route handlers (8 routers)
│ │ ├── models/ ← SQLAlchemy ORM models (7 models)
│ │ ├── schemas/ ← Pydantic request/response schemas
│ │ ├── services/ ← Business logic layer (7 services)
│ │ └── scripts/ ← DB init + city data loading
│ └── tests/ ← pytest: contract, unit, integration
├── frontend/
│ ├── package.json
│ ├── src/
│ │ ├── App.tsx ← Router + layout
│ │ ├── api/ ← HTTP client
│ │ ├── components/ ← React components
│ │ ├── hooks/ ← Custom React hooks
│ │ ├── pages/ ← Page-level components
│ │ ├── store/ ← Zustand state management
│ │ ├── types/ ← TypeScript type definitions
│ │ └── utils/ ← Helper functions
│ └── tests/ ← Vitest component tests
└── specs/ ← Feature specifications
# Backend
cd backend
pytest # Run all tests
pytest --cov=app # With coverage report
pytest tests/unit/ # Unit tests only
# Frontend
cd frontend
npm test # Run Vitest in watch mode
npm run test:coverage # With coverageFor verifying fixes in a real browser without Strava OAuth, the repo ships a
local browser-based verification harness (Playwright + a dev auth bypass +
seeded demo data, all against an isolated pacman_verify database).
infra\verify-up.ps1 # warm stack: isolated DB + backend + frontend
cd frontend; npx playwright test tests/e2e # drive the browser
infra\verify-reset.ps1 # fast data-only reset between runsThe harness is local-only — the backend runs with DEV_AUTH_BYPASS=1
(logs a CRITICAL warning) and never touches dev/prod data. See
specs/008-browser-verification-harness/quickstart.md
for the full workflow.
The street data loader includes five launch cities:
| City | State | Projected CRS |
|---|---|---|
| Seattle | Washington | EPSG:2926 |
| Pittsburgh | Pennsylvania | EPSG:2272 |
| Chicago | Illinois | EPSG:3435 |
| New York | New York | EPSG:2263 |
| San Francisco | California | EPSG:2227 |
| Variable | Required | Default | Description |
|---|---|---|---|
STRAVA_CLIENT_ID |
Yes | — | From Strava API settings |
STRAVA_CLIENT_SECRET |
Yes | — | From Strava API settings |
STRAVA_REDIRECT_URI |
No | http://localhost:8000/api/v1/auth/strava/callback |
OAuth redirect URL |
STRAVA_WEBHOOK_VERIFY_TOKEN |
No | pacman-tracker-verify |
Webhook subscription token |
SECRET_KEY |
Yes | — | 32+ char string for Fernet encryption |
DATABASE_URL |
No | sqlite:///./data/pacman.db |
Database connection string |
OSRM_URL |
No | http://localhost:5000 |
OSRM routing server |
CORS_ORIGINS |
No | http://localhost:5173 |
Comma-separated allowed origins |
MIT