# Project export: StreetSense

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: TreeHacks 2026
- Tagline: An AI-powered platform that converts fragmented data into real-time safety intelligence, using multi-agent AI and smart route optimization to help navigate any city with local-level awareness.
- Devpost: https://devpost.com/software/saferoute-ye7zgr
- GitHub: https://github.com/as5322/StreetSense_TreeHacks
- Video: https://www.youtube.com/embed/aoe09-Td-Dw?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 1 GitHub contributor(s) — Aryan (2 commits)

## Devpost submission (written by the team)

### Inspiration

As an international student moving to London, I remember how overwhelming it felt to live in a new city and know almost nothing about it. Google searches helped. Social media helped. But every time I stepped outside, I was stitching together fragmented information from dozens of sources: news, Twitter, WhatsApp groups, Reddit threads, word of mouth. There was no single, real-time view of what was actually happening around me and that lack of context made me feel unsafe. In my first few weeks living alone, my bag was stolen. When I told local students, they weren’t shocked. They said, “Oh… that’s because you were in that area.” Another time, I followed Google Maps down a perfectly valid route. The local students I was with immediately stopped me: “No, not that street, take the next one.” They had something I didn’t: Local intuition. StreetSense was born from the idea of bringing all those scattered signals together into one system, giving newcomers the same awareness locals naturally develop over time.

### What it does

StreetSense gives you the context locals have, instantly. When you enter a new area, you lack the intuition that comes from living there for years. StreetSense recreates that intuition using a real-time, multi-agent intelligence system that aggregates live data, community reports, and spatial analysis. Real-Time, Context-Based Street Ratings I generate dynamic safety ratings at the street and landmark level by combining trusted ground truth sources (such as the UK Police Crime API) with live online data and social media signals. This provides granular, location-specific awareness instead of broad city-level statistics. Area Awareness with Safety Heatmaps I visualize risk through interactive heatmaps that show which areas require caution. Users can drill down into detailed breakdowns of prevalent issues — theft, poor lighting, harassment reports, and more — providing actionable insight rather than vague safety scores. Live Community Intelligence Users can post real-time updates to a shared live feed. These posts are analyzed and incorporated into area ratings, allowing the system to dynamically adjust based on what’s happening right now. Safety-Optimized Routing Our routing engine doesn’t just find the fastest path — it optimizes for both safety and efficiency. Routes dynamically minimize exposure to higher-risk zones while still getting users to their destination efficiently. Location-Specific Safety Hotspots StreetSense also highlights nearby “safe” locations — such as police stations, pubs, supermarkets, and other trusted venues — so that if you ever feel unsafe, you immediately know where help or refuge is closest.

### How we built it

StreetSense was built as a full-stack, real-time intelligence system combining modern frontend tooling, scalable backend infrastructure, and a multi-agent AI pipeline. Frontend Our frontend was built using React with Vite for fast development and optimized builds. For geospatial visualization and interaction, I used Mapbox GL, allowing us to render dynamic safety heatmaps, street-level risk overlays, and interactive routing in real time. To accelerate UI development, I used *v0 * for rapid frontend prototyping and design iteration. During development, I leveraged *Codex * directly inside VS Code as a live “vibe-coding” assistant for debugging, refactoring, and accelerating feature implementation. Backend & API Layer The backend was built using FastAPI (Python), enabling us to create a high-performance, asynchronous API layer capable of handling real-time data ingestion, analysis, and routing queries. Ground Truth Intelligence Layer To establish a reliable baseline safety model, I constructed a multi-source ground truth dataset by integrating: Twitter API (live social signals) Yelp API (location context signals - mocked for cost sake) UK Police Crime datasets (official ground truth data) These signals were enriched through a layered AI validation pipeline: Perplexity Sonar API – used for deeper contextual research, cross-referencing claims, and credibility investigation. Anthropic API – used for critical validation, structured classification, and semantic labeling of incidents. This layered approach allowed us to assign credibility scores and structured risk categories before incorporating signals into our scoring system. Real-Time Multi-Agent Architecture At the core of StreetSense is a custom multi-agent pipeline designed for continuous ingestion, verification, and scoring. I implemented: An Observer Controller to orchestrate data flow between agents A StageHead Agent (powered by OpenAI) responsible for initial scraping and structured extraction A configurable scraping module that adapts dynamically A validation layer powered by Perplexity Sonar API for credibility research and cross-verification A feedback loop where low-credibility outputs trigger adaptive configuration updates A final critical analysis and classification stage powered by Anthropic Each signal is assigned: A classification A credibility score A severity weight A location binding User-submitted feed posts pass through an additional semantic classification and critical scoring layer using Anthropic, ensuring community content meaningfully contributes to live area ratings. This architecture allows StreetSense to function as a self-correcting, continuously updating intelligence system.

### Challenges we ran into

Managing LLM token limits at scale With multiple AI agents handling scraping, validation, and classification, staying within token limits while preserving meaningful context required careful prompt design and data structuring. Building a complex real-time system solo StreetSense combines frontend mapping, backend APIs, multi-agent AI orchestration, probabilistic modeling, and graph routing. Keeping the architecture modular and maintainable was critical. Tuning the Gaussian risk model Balancing spatial risk propagation was non-trivial. Too much spread distorted neighborhoods; too little lost contextual influence. Iterative parameter tuning was required to achieve realistic heatmaps. Designing a debuggable multi-agent pipeline LLM systems can be opaque. I engineered clear stage separation, observer-controlled flow, and structured logging to make debugging deterministic and manageable.

### Accomplishments we're proud of

It works I successfully built a fully functioning, real-time multi-agent system where all components: heatmaps, live feed, credibility scoring, and safety-optimized routing, integrate seamlessly. Accurate, context-aware scoring Through layered validation and spatial modeling, the system produces safety insights that align closely with real-world intuition and lived experience.

### What we learned

How to design a feedback-driven multi-agent system Building StreetSense taught me how to architect AI systems where agents validate, critique, and refine each other’s outputs and how important observability and modularity are when working with LLMs. New tools unlock speed I explored powerful tools like Stagehand, modern AI APIs, and rapid frontend prototyping workflows learning how to combine them into a cohesive system. The problem is real Through research and conversations, I gained a much deeper understanding of how vulnerable people feel when entering unfamiliar environments and how fragmented safety information currently is.

### What's next

After testing with friends and fellow students, the feedback was consistent: this solves a real problem especially in the UK where no good free solution to this problem exists. The system is at a stage where, with scaling and infrastructure hardening, it could evolve into a production-ready product.

## README (from the GitHub repository)

## Inspiration

As an international student moving to London, I remember how overwhelming it felt to live in a new city and know almost nothing about it.

Google searches helped. Social media helped. But every time I stepped outside, I was stitching together fragmented information from dozens of sources: news, Twitter, WhatsApp groups, Reddit threads, word of mouth.

There was no single, real-time view of what was actually happening around me and that lack of context made me feel unsafe.

In my first few weeks living alone, my bag was stolen. When I told local students, they weren’t shocked. They said, “Oh… that’s because you were in that area.”

Another time, I followed Google Maps down a perfectly valid route. The local students I was with immediately stopped me: “No, not that street, take the next one.”

They had something I didn’t: Local intuition.

StreetSense was born from the idea of bringing all those scattered signals together into one system, giving newcomers the same awareness locals naturally develop over time.

## What it does

StreetSense gives you the context locals have, instantly.

When you enter a new area, you lack the intuition that comes from living there for years. StreetSense recreates that intuition using a real-time, multi-agent intelligence system that aggregates live data, community reports, and spatial analysis.

**Real-Time, Context-Based Street Ratings**
I generate dynamic safety ratings at the street and landmark level by combining trusted ground truth sources (such as the UK Police Crime API) with live online data and social media signals. This provides granular, location-specific awareness instead of broad city-level statistics.

**Area Awareness with Safety Heatmaps**
I visualize risk through interactive heatmaps that show which areas require caution. Users can drill down into detailed breakdowns of prevalent issues — theft, poor lighting, harassment reports, and more — providing actionable insight rather than vague safety scores.

**Live Community Intelligence**
Users can post real-time updates to a shared live feed. These posts are analyzed and incorporated into area ratings, allowing the system to dynamically adjust based on what’s happening right now.

**Safety-Optimized Routing**
Our routing engine doesn’t just find the fastest path — it optimizes for both safety and efficiency. Routes dynamically minimize exposure to higher-risk zones while still getting users to their destination efficiently.

**Location-Specific Safety Hotspots**
StreetSense also highlights nearby “safe” locations — such as police stations, pubs, supermarkets, and other trusted venues — so that if you ever feel unsafe, you immediately know where help or refuge is closest.

## How we built it

StreetSense was built as a full-stack, real-time intelligence system combining modern frontend tooling, scalable backend infrastructure, and a multi-agent AI pipeline.

**Frontend**
Our frontend was built using **React** with  **Vite ** for fast development and optimized builds. For geospatial visualization and interaction, I used **Mapbox GL**, allowing us to render dynamic safety heatmaps, street-level risk overlays, and interactive routing in real time. To accelerate UI development, I used **v0 ** for rapid frontend prototyping and design iteration. During development, I leveraged **Codex ** directly inside VS Code as a live “vibe-coding” assistant for debugging, refactoring, and accelerating feature implementation.

**Backend & API Layer**
The backend was built using **FastAPI (Python)**, enabling us to create a high-performance, asynchronous API layer capable of handling real-time data ingestion, analysis, and routing queries.

**Ground Truth Intelligence Layer**

To establish a reliable baseline safety model, I constructed a multi-source ground truth dataset by integrating:
- Twitter API (live social signals)
- Yelp API (location context signals - mocked for cost sake)
- UK Police Crime datasets (official ground truth data)

These signals were enriched through a layered AI validation pipeline:
**Perplexity Sonar API** – used for deeper contextual research, cross-referencing claims, and credibility investigation.
**Anthropic API** – used for critical validation, structured classification, and semantic labeling of incidents.

This layered approach allowed us to assign credibility scores and structured risk categories before incorporating signals into our scoring system.

**Real-Time Multi-Agent Architecture**
At the core of StreetSense is a custom multi-agent pipeline designed for continuous ingestion, verification, and scoring.

I implemented:
- An Observer Controller to orchestrate data flow between agents
- A **StageHead Agent** (powered by **OpenAI**) responsible for initial scraping and structured extraction
- A configurable scraping module that adapts dynamically
- A validation layer powered by **Perplexity Sonar API** for credibility research and cross-verification
- A feedback loop where low-credibility outputs trigger adaptive configuration updates
- A final critical analysis and classification stage powered by **Anthropic**

Each signal is assigned:
- A classification
- A credibility score
- A severity weight
- A location binding

User-submitted feed posts pass through an additional **semantic classification** and critical scoring layer using **Anthropic**, ensuring community content meaningfully contributes to live area ratings.

This architecture allows StreetSense to function as a self-correcting, continuously updating intelligence system.

## Challenges we ran into
**Managing LLM token limits at scale**
With multiple AI agents handling scraping, validation, and classification, staying within token limits while preserving meaningful context required careful prompt design and data structuring.

**Building a complex real-time system solo**
StreetSense combines frontend mapping, backend APIs, multi-agent AI orchestration, probabilistic modeling, and graph routing. Keeping the architecture modular and maintainable was critical.

**Tuning the Gaussian risk model**
Balancing spatial risk propagation was non-trivial. Too much spread distorted neighborhoods; too little lost contextual influence. Iterative parameter tuning was required to achieve realistic heatmaps.

**Designing a debuggable multi-agent pipeline**
LLM systems can be opaque. I engineered clear stage separation, observer-controlled flow, and structured logging to make debugging deterministic and manageable.

## Accomplishments that we're proud of
**It works**
I successfully built a fully functioning, real-time multi-agent system where all components: heatmaps, live feed, credibility scoring, and safety-optimized routing, integrate seamlessly.

**Accurate, context-aware scoring**
Through layered validation and spatial modeling, the system produces safety insights that align closely with real-world intuition and lived experience.

## What we learned
**How to design a feedback-driven multi-agent system**
Building StreetSense taught me how to architect AI systems where agents validate, critique, and refine each other’s outputs and how important observability and modularity are when working with LLMs.

**New tools unlock speed**
I explored powerful tools like Stagehand, modern AI APIs, and rapid frontend prototyping workflows learning how to combine them into a cohesive system.

**The problem is real**
Through research and conversations, I gained a much deeper understanding of how vulnerable people feel when entering unfamiliar environments and how fragmented safety information currently is.

## What's next for StreetSense
After testing with friends and fellow students, the feedback was consistent: this solves a real problem especially in the UK where no good free solution to this problem exists. The system is at a stage where, with scaling and infrastructure hardening, it could evolve into a production-ready product. 


## Detected evidence (automated analysis)

Indexed codebase: 49 recognized source files, 149 KB.
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- TypeScript (language) — detected in the code
- Anthropic (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (56 of 56)

```
.gitignore
backend/__init__.py
backend/data_gen.py
backend/db/__init__.py
backend/db/database.py
backend/db/db_writer.py
backend/live_pipeline/__init__.py
backend/live_pipeline/criticality_agent.py
backend/live_pipeline/locations.py
backend/live_pipeline/observer_agent.py
backend/live_pipeline/observer.py
backend/live_pipeline/pipeline_models.py
backend/live_pipeline/pipeline.py
backend/live_pipeline/query_planner.py
backend/live_pipeline/scraper_agent.py
backend/live_pipeline/validator_agent.py
backend/main.py
backend/routes/__init__.py
backend/routes/heatmap.py
backend/routes/location_summary.py
backend/routes/routing.py
backend/routes/social.py
backend/run.bat
backend/static_analysis_pipeline/__init__.py
backend/static_analysis_pipeline/criticality_analysis_agent.py
backend/static_analysis_pipeline/data_generators/twitter_gen.py
backend/static_analysis_pipeline/data_generators/yelp_gen.py
backend/static_analysis_pipeline/data_source_twitter.py
backend/static_analysis_pipeline/data_source_uk_gov.py
backend/static_analysis_pipeline/data_source_yelp.py
backend/utils.py
frontend/.gitignore
frontend/eslint.config.js
frontend/index.html
frontend/package.json
frontend/README.md
frontend/src/App.css
frontend/src/App.tsx
frontend/src/components/HeatmapToggle.tsx
frontend/src/components/LocationPage.tsx
frontend/src/components/MapHeader.tsx
frontend/src/components/maplegend.tsx
frontend/src/components/MapView.tsx
frontend/src/components/RoutePanel.tsx
frontend/src/components/SummeryPanel.tsx
frontend/src/components/TopPills.tsx
frontend/src/index.css
frontend/src/lib/locationFeed.ts
frontend/src/main.tsx
frontend/src/pages/LocationFeedPage.tsx
frontend/src/ui/socialStyles.ts
frontend/tsconfig.app.json
frontend/tsconfig.json
frontend/tsconfig.node.json
frontend/vite.config.ts
README.md
```

### Dependencies

- frontend/package.json: @eslint/js@^9.39.1, @types/geojson@^7946.0.16, @types/mapbox-gl@^3.4.1, @types/node@^24.10.1, @types/react@^19.2.7, @types/react-dom@^19.2.3, @vitejs/plugin-react@^5.1.1, eslint@^9.39.1, eslint-plugin-react-hooks@^7.0.1, eslint-plugin-react-refresh@^0.4.24, globals@^16.5.0, lucide-react@^0.564.0, mapbox-gl@^3.18.1, react@^19.2.0, react-dom@^19.2.0, react-router-dom@^7.13.0, typescript@~5.9.3, typescript-eslint@^8.48.0, vite@^7.3.1

### Recent commits (newest first)

- Add comprehensive README for StreetSense project
- intial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### frontend/package.json

```
{
  "name": "frontend",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc -b && vite build",
    "lint": "eslint .",
    "preview": "vite preview"
  },
  "dependencies": {
    "lucide-react": "^0.564.0",
    "mapbox-gl": "^3.18.1",
    "react": "^19.2.0",
    "react-dom": "^19.2.0",
    "react-router-dom": "^7.13.0"
  },
  "devDependencies": {
    "@eslint/js": "^9.39.1",
    "@types/geojson": "^7946.0.16",
    "@types/mapbox-gl": "^3.4.1",
    "@types/node": "^24.10.1",
    "@types/react": "^19.2.7",
    "@types/react-dom": "^19.2.3",
    "@vitejs/plugin-react": "^5.1.1",
    "eslint": "^9.39.1",
    "eslint-plugin-react-hooks": "^7.0.1",
    "eslint-plugin-react-refresh": "^0.4.24",
    "globals": "^16.5.0",
    "typescript": "~5.9.3",
    "typescript-eslint": "^8.48.0",
    "vite": "^7.3.1"
  }
}

```

### backend/main.py

```python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from routes import routing, social, heatmap, location_summary
from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

app.include_router(routing.router)
app.include_router(social.router)
app.include_router(heatmap.router)
app.include_router(location_summary.router)

```

### frontend/src/main.tsx

```typescript
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <App />
  </StrictMode>,
)

```

### frontend/src/App.tsx

```typescript
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useState } from "react";
import MapView from "./components/MapView";
import RoutePanel from "./components/RoutePanel";
import SummaryPanel from "./components/SummeryPanel";
import LocationFeedPage from "./pages/LocationFeedPage";
import MapLegend from "./components/maplegend";

function App() {
  const [route, setRoute] = useState<[number, number][] | null>(null);

  type SelectedLocation = {
    name: string;
    lat: number;
    lng: number;
    kind: "road" | "poi" | "pin" | "safe_poi";
  };

  const [location, setLocation] = useState<SelectedLocation | null>(null);

  return (
    <BrowserRouter>
      <Routes>
        <Route
          path="/"
          element={
            <>
              <RoutePanel onRouteFetched={setRoute} />

              {location && (
                <SummaryPanel
                  name={location.name}
                  lat={location.lat}
                  lng={location.lng}
                />
              )}

              <MapLegend />


              <MapView
                route={route}
                onLocationSelect={(loc) => setLocation(loc)}
              />
            </>

            
          }
        />

        <Route path="/location/:id" element={<LocationFeedPage />} />

      </Routes>
    </BrowserRouter>
  );
}

export default App;

```

### frontend/vite.config.ts

```typescript
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
})

```

### frontend/index.html

```html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>frontend</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### frontend/eslint.config.js

```javascript
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
      reactHooks.configs.flat.recommended,
      reactRefresh.configs.vite,
    ],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
  },
])

```

### backend/data_gen.py

```python
"""
seed_truth.py

Generates a smooth, spatially-consistent "truth" vector over a bounding box
using random anchors + Gaussian distance weighting, then upserts into DB.

Run:
  python seed_truth.py

Tweak:
  - BOUNDS for your city
  - GRID_STEP_M for density
  - N_ANCHORS and SIGMA_M for smoothness
"""

from __future__ import annotations

import json
import math
import random
from dataclasses import dataclass
from typing import Dict, List, Tuple, Optional
from db.db_writer import DBWriter

# -------------------------
# Config (London-ish bounds)
# -------------------------
BOUNDS = {
    "min_lat": 51.20,
    "max_lat": 51.75,
    "min_lng": -0.65,
    "max_lng": 0.45,
}

db = DBWriter()


CATEGORIES = [
    "crime",
    "public_safety",
    "transport",
    "infrastructure",
    "policy",
    "protest",
    "weather",
    "other",
]

SEED = 1337

# Grid resolution (smaller = more points = more DB rows)
GRID_STEP_M = 250  # 250m grid is already a lot

# Number of random anchors (more = more texture)
N_ANCHORS = 60

# Smoothness radius (bigger = smoother neighborhoods)
SIGMA_M = 900  # try 700..1500

# Overall "riskiness" baseline
BASE_RISK = 0.15
RISK_VARIANCE = 0.65  # how strong anchors can get

# If you want a few *very* strong dangerous pockets
N_SUPER_HOTSPOTS = 6


# -------------------------
# Geo helpers
# -------------------------
def haversine_m(lat1: float, lng1: float, lat2: float, lng2: float) -> float:
    R = 6371000.0
    phi1 = math.radians(lat1)
    phi2 = math.radians(lat2)
    dphi = math.radians(lat2 - lat1)
    dlambda = math.radians(lng2 - lng1)
    a = math.sin(dphi / 2) ** 2 + math.cos(phi1) * math.cos(phi2) * math.sin(dlambda / 2) ** 2
    return 2 * R * math.atan2(math.sqrt(a), math.sqrt(1 - a))


def meters_to_deg_lat(m: float) -> float:
    return m / 111_320.0


def meters_to_deg_lng(m: float, at_lat: float) -> float:
    return m / (111_320.0 * math.cos(math.radians(at_lat)))


def clamp01(x: float) -> float:
    return 0.0 if x < 0 else 1.0 if x > 1 else x


# -------------------------
# Smooth field model
# -------------------------
@dataclass
class Anchor:
    lat: float
    lng: float
    vec: Dict[str, float]  # category -> 0..1
    strength: float        # 0..1 overall scaling


def random_category_vec(rng: random.Random, strength: float) -> Dict[str, float]:
    """
    Create a category vector with some structure:
    - pick 1-2 dominant categories
    - spread some residual to others
    """
    vec = {c: 0.0 for c in CATEGORIES}

    # Choose dominant categories
    dom = rng.sample(CATEGORIES, k=2 if rng.random() < 0.55 else 1)
    for c in dom:
        vec[c] = clamp01(BASE_RISK + strength * (0.6 + 0.4 * rng.random()))

    # Add mild correlated noise to others
    for c in CATEGORIES:
        if c in dom:
            continue
        vec[c] = clamp01(BASE_RISK * (0.3 + 0.7 * rng.random()) + strength * (0.05 * rng.random()))

    return vec


def gaussian_weight(d_m: float, sigma_m: float) -> float:
    # exp(-d^2 / (2 sigma^2))
    return math.exp(-(d_m * d_m) / (2.0 * sigma_m * sigma_m))


def blend_truth(lat: float, lng: float, anchors: List[Anchor], sigma_m: float) -> Dict[str, float]:
    # Weighted average of anchor vectors
    wsum = 0.0
    out = {c: 0.0 for c in CATEGORIES}

    for a in anchors:
        d = haversine_m(lat, lng, a.lat, a.lng)
        w = gaussian_weight(d, sigma_m) * (0.35 + 0.65 * a.strength)
        if w < 1e-6:
            continue
        wsum += w
        for c in CATEGORIES:
            out[c] += w * a.vec[c]

    if wsum <= 1e-9:
        return out

    for c in CATEGORIES:
        out[c] = clamp01(out[c] / wsum)

    return out


# -------------------------
# DB hook (EDIT THIS)
# -------------------------
import sqlite3
from pathlib import Path
import time

def upsert_truth(lat: float, lng: float, truth: Dict[str, float]) -> None:
    # IMPORTANT: your DB uses column name "long" not "lng"
    conn = sqlite3.connect(db.path)
    cur = conn.cursor()

    # Ensure row exists
    cur.execute("INSERT OR IGNORE INTO truth (lat, long) VALUES (?, ?)", (lat, lng))

    # Update all category columns
    cols = ", ".join([f"{c} = ?" for c in CATEGORIES] + ["updated_at = CURRENT_TIMESTAMP"])
    values = [float(truth[c]) for c in CATEGORIES] + [lat, lng]

    cur.execute(
        f"""
        UPDATE truth
        SET {cols}
        WHERE lat = ? AND long = ?
        """,
        values,
    )

    conn.commit()
    conn.close()



# -------------------------
# Main seeding logic
# -------------------------
def generate_anchors(rng: random.Random) -> List[Anchor]:
    anchors: List[Anchor] = []
    for i in range(N_ANCHORS):
        lat = rng.uniform(BOUNDS["min_lat"], BOUNDS["max_lat"])
        lng = rng.uniform(BOUNDS["min_lng"], BOUNDS["max_lng"])

        strength = clamp01(rng.random() * RISK_VARIANCE)
        vec = random_category_vec(rng, strength)

        anchors.append(Anchor(lat=lat, lng=lng, vec=vec, strength=strength))

    # Add a few strong hotspots
    for _ in range(N_SUPER_HOTSPOTS):
        lat = rng.uniform(BOUNDS["min_lat"], BOUNDS["max_lat"])
        lng = rng.uniform(BOUNDS["min_lng"], BOUNDS["max_lng"])
        strength = clamp01(0.85 + 0.15 * rng.random())
        vec = random_category_vec(rng, strength)
        anchors.append(Anchor(lat=lat, lng=lng, vec=vec, strength=strength))

    return anchors


def grid_points() -> List[Tuple[float, float]]:
    pts: List[Tuple[float, float]] = []
    lat = BOUNDS["min_lat"]
    while lat <= BOUNDS["max_lat"]:
        dlat = meters_to_deg_lat(GRID_STEP_M)
        dlng = meters_to_deg_lng(GRID_STEP_M, at_lat=lat)

        lng = BOUNDS["min_lng"]
        while lng <= BOUNDS["max_lng"]:
            pts.append((lat, lng))
            lng += dlng

        lat += dlat

    return pts


def main():
    rng = random.Random(SEED)

    anchors = generate_anchors(rng)
    pts = grid_points()

    print(f"Seeding {len(pts)} grid points with {len(anchors)} anchors...")
 
[truncated — 847 more characters]
```

### frontend/src/App.css

```css
#root {
  max-width: 1280px;
  margin: 0 auto;
  padding: 2rem;
  text-align: center;
}

.logo {
  height: 6em;
  padding: 1.5em;
  will-change: filter;
  transition: filter 300ms;
}
.logo:hover {
  filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
  filter: drop-shadow(0 0 2em #61dafbaa);
}

@keyframes logo-spin {
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
}

@media (prefers-reduced-motion: no-preference) {
  a:nth-of-type(2) .logo {
    animation: logo-spin infinite 20s linear;
  }
}

.card {
  padding: 2em;
}

.read-the-docs {
  color: #888;
}

```

### backend/live_pipeline/locations.py

```python
from dataclasses import dataclass
from typing import List


@dataclass
class MonitoredLocation:
    name: str
    city: str
    lat: float
    long: float
    keywords: List[str] | None = None


MONITORED_LOCATIONS: List[MonitoredLocation] = [
    MonitoredLocation(
        name="Oxford Street",
        city="London",
        lat=51.5154,
        long=-0.1410,
    ),
    MonitoredLocation(
        name="Camden Town",
        city="London",
        lat=51.5390,
        long=-0.1426,
    ),
    MonitoredLocation(
        name="Stratford",
        city="London",
        lat=51.5413,
        long=-0.0030,
    ),
    MonitoredLocation(
        name="King's Cross",
        city="London",
        lat=51.5308,
        long=-0.1238,
    ),
]

```

[32 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]