# Urban World Model Visualiser

Interactive web pages for the South Kensington core008 urban world model: the three physical fields in
`physics_assert/` (wind, temperature, pollutant concentration; 4 m grid, 768 × 704 cells, north up), the
3-D city model, and the traffic / UAV replay from `demo_rev02/`.

## Start locally

```bash
cd UrbanWorldModelVisualizer
python3 serve.py          # opens http://localhost:8787/web/ (next free port if busy)
python3 serve.py 9000     # custom port
```

Only the system Python 3 is needed, no numpy. `serve.py` is a static server with HTTP Range support; the
pages read individual frames straight from the `.npy` files (float16 decoded in the browser), so nothing
is preprocessed or copied.

## 2-D viewer (`web/`)

- **Wind**: layers 8–12 m and 40–44 m, 100 steps × 25 s; speed or u / v / w; animated streamline particles or arrows.
- **Temperature**: 2-D label model (61 frames), 3-D controlled solver reference (8 frames), U-Net one-step / recursive (5 frames);
  temperature, change from the first frame, or U-Net − solver error.
- **Pollution**: 12–16 m concentration, 100 steps, log / linear colour scale, source-region overlay.
- Common: adjustable colour map and range, building mask overlay, scroll zoom / drag pan / double-click reset, hover readout
  (cell index, domain and region coordinates, components, roof height, land cover), play / step, time series of the fluid-cell
  mean and maximum (computed in the background for every frame; click a curve to seek).
- URL parameters, e.g. `?field=pollution&t=99`, `?field=wind&layer=40&q=u&overlay=arrows`,
  `?field=temperature&source=onestep&q=error&t=4`.

## 3-D view (`web/3d/`)

`http://localhost:8787/web/3d/` (linked from the 2-D page). Loads `south_kensington_core008_web.glb` (254 MB, three.js +
meshopt decoding; three.js is served from `demo_rev02/vendor`, the isotherm line modules from `web/vendor/jsm/lines`, so no
network access is needed). The page runs one loop with two parts:

1. **Campus tour** (traffic and UAVs from `demo_rev02/`): campus overview (an orbit descending towards the Imperial College
   buildings) → the busiest signalised junction on the campus ring roads (only junctions within 165 m of the campus buildings,
   i.e. Kensington Gore / Exhibition Road and Queen's Gate) → following a car passing the campus → a wide UAV view (high orbit
   over the campus showing the nearby hubs and rooftop stations; UAV models enlarged 4× with screen-space dots and the flight
   corridors drawn). Cars are the SUMO replay, UAVs the NVMF schedule replay, signals read the recorded states. The repository
   only ships the first 300 s of the traffic replay, so the replay loops inside that window; drop the full-hour files into
   `demo_rev02/data/traffic/replay/` to get the whole hour.
2. **Fields overhead**: climb to the overhead view, hide the ground, roads, trees and the traffic layers, then play the wind
   speed field, the temperature field and the pollution field one after another (12 steps/s by default), and return to the
   campus tour.

Layers: wind speed as a heat map at 10 m (building cells cut out, optional streamline particles); temperature as a ground heat
map (transparent outside the study area and inside buildings) or as isotherms (every 0.1 °C, broken at walls); pollution as a
translucent concentration layer at 14 m (log opacity). The camera buttons jump to any shot or to the fields; "Auto loop" off
holds the current shot; dragging the view interrupts a flight. The overlay mode lets you combine layers manually.
URL parameters: `?pose=overhead&step=80`, `?pose=campus&shot=junction&t=120&hold=1`.

Alignment between the grid and the model (top of `web/3d/main.js`): model X = domain x − 2116, model Z = −(domain y − 2124),
Y up; i.e. the manifest's region coordinates are the model's (X, −Z). Verified against the building extent in the README of the
data. SUMO → world: X = x − 2912.594719173, Z = 1704.705026026 − y (from the demo_rev02 README).

## Deployment on the workstation (ESE-YL222)

The project is synced to `~/workspace/UrbanWorldModelVisualizer` on the workstation. Start / restart:

```bash
ssh ESE-YL222
cd ~/workspace/UrbanWorldModelVisualizer && ./start_server.sh      # 0.0.0.0:8787, log in server.log, pid in server.pid
```

- **Public URL (anyone)**: https://ese-yl222.tail8083c2.ts.net/web/3d/ , published with Tailscale Funnel (the college network
  blocks the domain and port used by Cloudflare Tunnel). Check / stop / republish: `tailscale funnel status`,
  `tailscale funnel --https=443 off`, `tailscale funnel --bg 8787`.
- Inside the Tailscale network: http://100.84.141.116:8787/web/3d/ . On campus / VPN, and from outside over IPv6:
  http://ese-yl222.ese.ic.ac.uk:8787/web/3d/ (IPv4 inbound is firewalled).
- Sync local changes: `rsync -az --exclude .DS_Store ~/Desktop/UrbanWorldModelVisualizer/ ESE-YL222:workspace/UrbanWorldModelVisualizer/`.
- Remote viewers get roughly 20 Mbps through the Funnel; the fields section streams raw frames and will stutter remotely
  until the frames are pre-rendered into small textures (not done yet).

## Files

- `serve.py` — local server with Range support; `start_server.sh` — detached start for the workstation
- `web/index.html`, `web/style.css`, `web/app.js` — 2-D page (no dependencies, plain JS + Canvas)
- `web/npy.js` — shared `.npy` Range reader module
- `web/3d/` — 3-D page (three.js); `replay.js` wraps the demo_rev02 traffic / UAV modules
- `web/vendor/jsm/lines/` — three.js fat-line modules
- `south_kensington_core008_web.glb` — 3-D model
- `physics_assert/` — field data and notes (see `README_source_run.md` and `manifest.json` inside)
- `demo_rev02/` — traffic / UAV replay package (unchanged)
