Predict where West London is risky — street by street, hour by hour — and see it live on a tactical command-center map.
StreetPulse is a full-stack safety platform. A Spring Boot backend fuses community-submitted incident reports with ~6,000 official Police UK crime records (12 months, rate-limit-aware ingestion with caching), runs every new report through an agentic triage pipeline (automatic severity escalation and duplicate merging), and serves it all through a REST API. A Python machine-learning service trains a gradient-boosted spatiotemporal risk model on the fused data — risk that genuinely shifts with the hour of day — and a tool-using AI agent answers safety questions by querying the live data before it speaks. The frontend is a dark, HUD-style command console: radar sweep, target-lock reticles on real incidents, and a scrolling intel feed.
- 🗺️ Live tactical map — Leaflet + CARTO dark tiles with an ML risk heat layer, ~500 police crime markers, community incident pins, a rotating radar sweep, and target-lock reticles that acquire real incidents one by one
- 🔮 ML risk forecasting — a scikit-learn
HistGradientBoostingRegressorlearns the spatial crime-intensity surface from ~6,000 events, plus a learned hour-of-day effect; scrub a 00:00–23:00 time slider and watch predicted risk shift across the day - 🤖 Tool-using AI agent — Gemini function-calling with four tools (
search_incidents,get_risk_score,get_police_crimes,get_stats); every answer is grounded in live data, with a data-driven fallback so it works with no API key at all - 🚨 Agentic report triage — reports describing danger ("knife", "followed", "assault"…) are auto-escalated to HIGH severity; near-identical reports (same category, ~150 m, 48 h) are merged into one confirmation instead of cluttering the map
- 👍 Community confirmations — one-tap "Confirm this report" validation with per-device double-confirm protection; most-confirmed reports surface in analytics
- 🔔 Live alerts — recent high-priority activity feeds the notification bell (unseen-count badge), the alerts page, and the scrolling intel ticker
- 📊 Crime intelligence dashboard — KPI cards with animated count-ups, 30-day incident trends, category breakdowns, monthly police-crime trends, area risk index — every card, chart, and row opens a detail popup
- 🔐 Accounts — registration and login with BCrypt-hashed passwords; sessions are browser-scoped so a fresh visit always starts anonymous
- 🛡️ Privacy-aware reporting — anonymous by default; the optional reporter email is write-only (never returned by any endpoint), stored under UK GDPR consent
| Layer | Tech | Role |
|---|---|---|
frontend/ |
React 18 · Vite · Leaflet · Chart.js | Command-center UI, live map, analytics, popups |
backend/ |
Spring Boot 3.2 · Java 17 | REST API, ingestion, agentic triage, alerts, auth, explainable risk baseline |
ml/ |
Python · FastAPI · scikit-learn | Learned spatiotemporal risk model (gradient boosting + diurnal effect) |
| database | H2 (file mode) | Incidents, users; self-healing schema maintenance on startup |
Police UK API ──► 12-month ingest (429-retry, cached) ──┐
Community reports ──► agentic triage (escalate, dedupe) ─┼──► H2 DB
│
Python ML service ◄──────────── trains on ───────┘
(risk surface + hour-of-day)
│ /predict · /grid
▼
React + Leaflet ◄── REST API ◄── Gemini agent (4 data tools)
The backend degrades gracefully at every layer: no Python service → an explainable kernel-density risk baseline (Gaussian spatial kernel × severity × recency, normalised to a data-derived reference) takes over; no Gemini key → the assistant answers from a data-driven analysis engine. The whole platform runs with zero paid APIs and zero API keys.
| Endpoint | Returns |
|---|---|
GET /api/incidents |
all community reports (filter by category, severity) |
POST /api/incidents |
submit a report (runs the triage pipeline) |
PATCH /api/incidents/{id}/upvote |
confirm a report |
GET /api/incidents/analytics |
totals, severity split, most-active area, last-24h count |
GET /api/police/crimes/recent |
~6,000 Police UK records, last 12 months |
GET /api/risk?lat&lng&hour |
0–100 risk score for a point + hour, with contributing factors |
GET /api/risk/grid?hour |
scored grid powering the heat layer |
POST /api/ai/agent |
tool-using agent answer, mode: agent | fallback |
GET /api/alerts |
recent high-priority activity |
POST /api/auth/register · /login |
accounts (BCrypt) |
ML service (optional, port 8000): GET /health · GET /predict · GET /grid · POST /retrain.
Requirements: Java 17, Maven 3.8+, Node 18+. Optional: Python 3.9+ for the ML service.
# Terminal 1 — backend (seeds sample data; police history ingests in background)
cd backend
mvn spring-boot:run
# Terminal 2 — ML service (optional: learned model instead of the baseline)
cd ml
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
uvicorn app:app --port 8000
# Terminal 3 — frontend
cd frontend
npm install
npm run devOpen http://localhost:5173. Everything works immediately with no keys. To enable the live AI agent, export a free GEMINI_API_KEY (https://aistudio.google.com/apikey) before starting the backend.
- Police UK Open Data — street-level crime records (Open Government Licence v3.0)
- Community reports — submitted in-app, triaged automatically
- OpenStreetMap / CARTO — dark basemap tiles
- Google Gemini (free tier, optional) — agent reasoning
- Anonymous reporting by default; optional email is write-only and never exposed by the API
- Coordinates stored at street-level precision; no tracking, no cookies
- Sign-in is session-scoped — a fresh visit always starts with no personal details