Skip to content

Jared-Krajewski/12thAndDivision

Repository files navigation

12th & Division Train Monitor

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

  1. The backend fetches TripCheck still images for cameras 214, 211, and 212.
  2. Each image is compared with its per-camera baseline in backend/baselines using SSIM and, where configured, edge detection.
  3. Optional AI review (Groq + Llama 4 Scout with ratelimit fallback to Gemini Flash) can refine the result when GROQ_API_KEY is configured.
  4. If no train is visually confirmed, the backend also checks TripCheck incidents and TriMet delay drift to produce a three-state intersection_status:
    • blocking
    • possibly_blocked
    • not_blocked
  5. The frontend calls /_/backend as 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.

Prerequisites

  • 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

Environment Variables

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.

Makefile

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

Full stack

make install
cp .env.example .env
make up

This starts:

  • Vite on http://localhost:5173
  • A local backend HTTP server in Docker on http://localhost:8000
  • A Vite proxy from /_/backend to 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.

Backend only in Docker

make up-backend

or:

docker compose up --build

This runs the same local HTTP backend server used by make up.

Backend only without Docker

make venv
export $(grep -v '^#' .env | xargs)
make detect

To run the local HTTP backend without Docker:

backend/.venv/bin/python backend/detect.py --serve 8000

Detection validation with saved screenshots

backend/.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-run

backend/test_detect.py writes cropped debug images next to the sample you test so you can confirm the exact comparison region.

Threshold tuning with labeled screenshots

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 ./dataset

Add --camera 214 to tune a single camera, or --json to export machine-readable output.

GitHub Actions

.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_URL
  • SUPABASE_KEY
  • GROQ_API_KEY if you want AI review in CI

About

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors