Skip to main content

Hurricane Ian Flood Replay

The Hurricane Ian Simulation replays a pre-computed GeoAI flood model of Hurricane Ian (2022) over Orlando, Florida. A 34 × 34 grid of cells is animated hour by hour, colored by composite risk score, alongside shelter status and a flood-aware evacuation route.

The entire replay dataset is fetched once and driven client-side, moving the timeline slider or adjusting risk weights requires no further server round-trips.

Enabling the Simulation

In the sidebar under Flood Analysis, click Hurricane Ian Simulation (sidebarFloodReplayBtn). The app:

  1. Opens the floating Hurricane Ian Simulation panel over the map
  2. Fetches /flood-workshop/replay (proxied to the JetStream2 backend)
  3. Builds one Cesium rectangle entity per grid cell plus shelter points and route polylines
  4. Flies the camera to the study area, offset eastward so the panel does not cover the grid

Panel Controls

ControlDescription
Timeline sliderScrub to any hour of the event; label shows the ISO timestamp and frame n / N
Play / StopAuto-advance through frames
Risk WeightsSliders for Flood, Population, and Lowland weights
Fix WeightsRescales the three adjustable weights so the total returns to 1.00
Stats gridCumulative rain, rain rate, wind gust, recommended shelter, Major+ cells, critical cells, population at risk, unsafe shelters
SheltersPer-shelter safe/unsafe status at the current hour
Risk Score legendGradient bar from 0 (low) to 100 (high)

Risk Model

Risk is recomputed in the browser whenever a weight slider moves, using normalized components that the API ships once:

risk[i] = clip(100 * ( w_flood * frame.flood_norm_t[i]
+ w_pop * grid[i].pop_norm_t
+ w_lf * grid[i].lf_t
+ w_lw * grid[i].landuse_weight_norm ), 0, 100)
WeightDefaultAdjustable
w_flood, flood depth (normalized per frame)0.60Yes
w_pop, population density0.20Yes
w_lf, lowland factor (terrain-derived)0.12Yes
w_lw, land use weight0.08No (fixed)

The four weights must sum to 1.00. If they do not, the total turns red, a warning appears, and a Fix Weights button proportionally rescales the adjustable three.

Risk tiers: Critical ≥ 50, High ≥ 35, Moderate ≥ 20.

Flood Classification

Cell flood classes use absolute depth cuts (metres):

ClassLabelDepth
0None/Minimal< 0.02 m
1Minor0.02 – 0.10 m
2Moderate0.10 – 0.25 m
3Major0.25 – 0.50 m
4Extreme> 0.50 m

Shelters carry a resilience factor of 0.5, they tolerate 50% more flooding than an ordinary cell before being marked unsafe.

Shelters and Routing

Four static shelters surround the Orlando study center (28.5383 °N, −81.3792 °W): North (capacity 450), East (600), South (500), West (350). Each ships with a pre-computed OSM road-network polyline, route distance in miles, travel time, and a GeoAI shelter score.

Every frame also contains:

  • selected_shelter, the recommended shelter for that hour
  • dyn_centroid, the population-weighted high-risk centroid
  • dyn_route_polyline, a flood-aware route from that centroid to the selected shelter

Shelter points turn from safe to unsafe coloring as flooding reaches them.

Data Source

Browser → GET /flood-workshop/replay (Express proxy, server/server.js)
→ GET https://ucf-urbangeolens-backend.cis251126.projects.jetstream-cloud.org
/flood-workshop/replay (FastAPI on JetStream2, port 8002)

Override the upstream host with the FLOOD_BACKEND_URL environment variable.

Response shape

KeyContents
metaCity, event, center lat/lon, grid_size, cell_deg, n_cells, n_frames, routing source, class labels, depth thresholds, risk tier thresholds, risk_weights_default, risk_formula
gridPer-cell static properties: lat, lon, elevation_m, landuse, landuse_label, population, pop_norm_t, lf_t, landuse_weight_norm
timelinePer-hour weather: time_iso, cumulative_rain_in, rain_in_hr, gust_mph, wind_factor, drainage
sheltersName, lat/lon, capacity, route_miles, travel_time_min, geoai_shelter_score, route_polyline
framesPer hour: flood_depths, flood_classes, flood_norm_t, risk_scores, flood_max_m, n_major_cells, n_critical_risk, pop_at_risk, selected_shelter, shelter_status, dyn_centroid, dyn_route_polyline

How the Model Is Built

The backend computes the full replay once at startup (Flood-Workshop/api_server.py), then serves it from memory:

  1. Weather, Open-Meteo historical data for the Ian window (falls back to a synthetic Ian-shaped curve)
  2. Terrain, USGS 3DEP 30 m DEM (falls back to synthetic terrain)
  3. Hydrology, SCS Curve Number runoff plus D8 flow accumulation and a Topographic Wetness Index
  4. Classification, a Random Forest flood classifier trained on the grid
  5. Routing, OSMnx road network shelter routing (falls back to straight-line haversine)

Startup takes several minutes. Until it finishes, GET / and GET /health report status: "starting" and /replay returns 503.

Troubleshooting

SymptomCause
Panel stuck on "Fetching simulation data..."Backend still warming up, or the JetStream2 VM is unreachable
503 Replay data is still loadingStartup pre-computation has not completed
500 Failed to fetch flood dataExpress could not reach FLOOD_BACKEND_URL
Weight total shown in redWeights do not sum to 1.00, click Fix Weights

Check the service on the VM with:

sudo systemctl status flood-workshop-api
journalctl -u flood-workshop-api -f

Implementation Notes

  • Frontend: client/public/js/app.js, initFloodReplay, createFloodReplayPanel, renderFloodReplayPanel, computeRiskScores, updateFloodFrame, buildFloodEntities, startFloodReplayPlay / stopFloodReplayPlay, initFloodHoverHandler, clearFloodReplayEntities
  • State: floodReplayData, floodReplayGridEntities, floodReplayShelterEntities, floodReplayRouteEntities, floodReplayFrameIndex, floodRiskWeights, floodCurrentRiskScores
  • Server proxy: GET /flood-workshop/replay in server/server.js
  • Distinct from the bounding-box Flood Level Analysis filter, which reads a static mock CSV and is unrelated to this simulation