This project tracks whether a freight train is blocking SE 12th Ave and SE Division St in Portland, OR. It combines three TripCheck camera stills, optional AI review, TripCheck incident data, and TriMet delay drift, then writes the current status to Supabase and renders it in a React UI.
The traffic map uses two sources:
- TripCheck incidents are plotted on the local Leaflet map for the side streets around the crossing.
- Quick-links to Waze and Google Maps (with traffic layer) are shown alongside the map for live street-level congestion.
How it works
- The backend fetches TripCheck still images for cameras 214, 211, and 212.
- Each image is compared with its per-camera baseline in backend/baselines using SSIM and, where configured, edge detection.
- Optional AI review (Groq + Llama 4 Scout with ratelimit fallback to Gemini Flash) can refine the result when
GROQ_API_KEYis configured. - If no train is visually confirmed, the backend also checks TripCheck incidents and TriMet delay drift to produce a three-state
intersection_status:blockingpossibly_blockednot_blocked
- The frontend calls
/_/backendas the source of truth on page load and on polling; the backend returns either a cached Supabase result or a fresh detection run depending on cooldown, and the browser falls back to the persisted Supabase row only if the backend is unavailable.
- Docker Desktop for the local backend server used by
make up - Bun or npm for the frontend
- Supabase for persisted status storage
- Optional: TripCheck API key and TriMet app ID for richer traffic signals
Copy .env.example to .env and fill in the values you need:
cp .env.example .env| Variable | Used by | Notes |
|---|---|---|
SUPABASE_URL |
backend | Required to persist detection results |
SUPABASE_KEY |
backend | Service-role key |
VITE_SUPABASE_URL |
frontend | Public browser value |
VITE_SUPABASE_ANON_KEY |
frontend | Public browser value |
TRIPCHECK_API_KEY |
backend | Preferred TripCheck incidents key |
TRIPCHECK_PRIMARY_KEY |
backend | Legacy alias, still accepted |
TRIMET_APP_ID |
backend | Enables TriMet delay drift signal |
GROQ_API_KEY |
backend | Optional AI review (Llama 4 Scout via Groq) |
GEMINI_API_KEY |
backend | Fallback if groq is rate limited |
If you omit the traffic-related keys, the app still works, but the traffic map will fall back to Waze-only traffic and the amber proxy signals will be reduced.
| Command | Purpose |
|---|---|
make install |
Install frontend dependencies |
make up |
Start frontend and the local backend server together |
make up-frontend |
Start only Vite |
make up-backend |
Start only the Docker backend server |
make venv |
Create backend/.venv and install Python deps |
make detect |
Run one local detection pass |
make clean |
Stop Docker and remove local dependency folders |
make install
cp .env.example .env
make upThis starts:
- Vite on
http://localhost:5173 - A local backend HTTP server in Docker on
http://localhost:8000 - A Vite proxy from
/_/backendto that backend server
When you run make up, backend logs are streamed directly in the terminal. You will see request logs, TripCheck/TriMet checks, per-camera scores, and the final intersection_status for each detection run.
make up-backendor:
docker compose up --buildThis runs the same local HTTP backend server used by make up.
make venv
export $(grep -v '^#' .env | xargs)
make detectTo run the local HTTP backend without Docker:
backend/.venv/bin/python backend/detect.py --serve 8000backend/.venv/bin/python backend/test_detect.py --image train12clinton.png --camera 214
backend/.venv/bin/python backend/test_detect.py \
--sample 214=train12clinton.png \
--sample 211=train8division.png
backend/.venv/bin/python backend/detect.py --dry-runbackend/test_detect.py writes cropped debug images next to the sample you test so you can confirm the exact comparison region.
If you have a folder of labeled screenshots, you can grid-search better thresholds from real blocked/clear examples instead of hand-tuning them.
Expected dataset layout:
dataset/
214/
blocked/
clear/
211/
blocked/
clear/
Then run:
backend/.venv/bin/python backend/train_thresholds.py --dataset ./datasetAdd --camera 214 to tune a single camera, or --json to export machine-readable output.
.github/workflows/monitor.yml still runs backend/detect.py every 5 minutes and also allows manual runs. If you use the workflow, set these repository secrets:
SUPABASE_URLSUPABASE_KEYGROQ_API_KEYif you want AI review in CI