# PlanX Urban Resilience — Academic Reference Manual

Covering Urban Resilience v2.1.1 · 43 Algorithms · 10 Tool Groups
Yusuf Eminoğlu · github.com/YusufEminoglu/planx_urban_resilience

## How to Use This Manual

This manual documents every Processing algorithm in the PlanX Urban Resilience suite. It is written for spatial planners, disaster-risk researchers, graduate students and QGIS practitioners who need both the scientific rationale and the operational guidance to run each tool correctly.

Every algorithm entry follows an identical eight-section template:

1. Overview — what the tool produces and its planning purpose.
2. Theoretical Background — academic lineage, assumptions, and limits.
3. Mathematical Formulation — equations, algorithms, and complexity.
4. Input Data Requirements — layer types, fields, and preprocessing.
5. Parameters — every parameter, its type, default, and operational guidance.
6. Output Description — every output field, its type, units, and typical range.
7. Symbolic Representation — recommended QGIS renderers, colour ramps, and classification.
8. Interpretation Guide — thresholds, benchmark bands, diagnostic readings, and planning actions.

Each entry closes with Academic References. All equations are rendered with MathJax and numbered. The manual uses these typographic conventions: `monospace` for parameter names, field names, and Processing IDs; bold for key concepts on first introduction; italic for journal and book titles.

CRS warning. All spatial operations assume the input layers share a common projected coordinate system (e.g. a local UTM zone). Results from layers in an unprojected geographic CRS (EPSG:4326) are geometrically meaningless — distances, areas, and buffers will be in decimal degrees, not metres.

## Introduction to PlanX Urban Resilience

PlanX Urban Resilience is a 43-algorithm QGIS Processing suite for city-scale resilience screening. It spans the full disaster-risk reduction cycle: hazard exposure (seismic, heat, flood, air quality, drought), vulnerability (social, infrastructure, accessibility), response (emergency access, evacuation, shelter siting), and synthesis (multi-hazard composite, equity-adjusted priority, cost-benefit, lifecycle economics).

The suite is offline-first: every algorithm runs on local vector and raster data without cloud dependencies, API calls, or external solvers. This design serves planning studios in low-connectivity environments, classroom reproducibility, and audit-grade deterministic workflows. All Monte Carlo modules expose a seed parameter so results are exactly replicable.

### Architecture

The suite is organised as a QGIS `ProcessingProvider` (`planx_urban_resilience`) with ten tool groups. Each algorithm is a self-contained Python class following QGIS's `QgsProcessingAlgorithm` contract. Shared functionality — clamping, classification helpers, weight parsing, reproducible random numbers — lives in `processing/_helpers.py`. Network algorithms share a graph builder in `processing/accessibility/_network_graph.py`. The companion `dashboard.py` QGIS panel provides a scrollable, searchable module catalogue with one-click launch shortcuts.

### The Ten Tool Groups

# | Group | Tools | Domain
--- | --- | --- | ---
0 | Demo & QA | 1 | Synthetic data generation
1 | Seismic: Debris & Logistics | 1 | Earthquake engineering, debris modelling
2 | Heat: Comfort & Exposure | 1 | Urban microclimate, heat-island screening
3 | Flood: Pluvial Susceptibility | 1 | Hydrology, DEM-based terrain analysis
4 | Social: Vulnerability & Equity | 1 | Social vulnerability indices, demographics
5 | Emergency: Accessibility & Networks | 7 | Graph theory, Dijkstra, evacuation, shelter siting
6 | Air: Exposure Screening | 1 | Proximity-based air quality screening
7 | Drought: Green Infrastructure Stress | 1 | Landscape ecology, patch analysis
8 | Synthesis: Multi-Hazard & Priority | 16 | Composite indices, equity, climate projection
9 | Spatial Statistics | 4 | Getis-Ord Gi*, LISA, trend hot-spots, IDW
10 | Economics | 3 | Cost-benefit, lifecycle NPV, Pareto optimisation
R | Reporting, Visualisation & QA | 10 | HTML/Markdown/PDF/GeoJSON export, symbology

### Design Principles

- Offline-first. No cloud APIs, no external solvers, no internet dependency. Every algorithm runs on local data alone.
- Deterministic. All stochastic modules expose a seed parameter. Same seed + same inputs = identical outputs, every run.
- Screening, not compliance. These are planning-support indices for prioritisation and classroom analysis. They are not substitutes for site-specific engineering studies, hydrodynamic models, or structural assessments.
- Transparent weights. Every composite algorithm exposes user-controlled weights with sensible defaults. Defaults are documented and justified in the relevant Theoretical Background section.
- Pure Python. The suite carries no compiled dependencies. Statistical modules (Gi*, LISA, correlation, IDW) run in pure Python for auditability and zero-install deployment.

## 0. Demo & QA

The Demo group provides a single synthetic-data generator. It produces a self-contained artificial study area with buildings, roads, impervious surfaces, green infrastructure, water bodies, shelters, social-unit polygons, and sensitive receptors — all with plausible geometries and essential attribute fields. Use it for classroom demonstrations, smoke-testing the full suite, or prototyping a workflow before acquiring real data.

### Synthetic Resilience Demo Dataset

Processing ID: `planx_urban_resilience:synthetic_resilience_demo_dataset`

#### 1. Overview

Generates a complete artificial resilience-study dataset in a single run. The output consists of nine layers — buildings (with `floor_count` and `construction_year` fields), roads (with capacity and class), impervious surfaces, green patches, water bodies, shelter points, social-unit polygons (with population, elderly, children, disability, and low-income fields), and sensitive-receptor points. A `SCALE` parameter controls spatial extent. Every output is in a projected CRS suitable for metric analysis.

#### 2. Theoretical Background

Academic lineage. Synthetic datasets are a standard tool in spatial-algorithm validation (Fisher & Tate, 2006), used wherever the goal is to test that a *pipeline* runs correctly rather than to test against ground truth — the GIS analogue of a software unit-test fixture. The generator uses a fixed geometric layout — a central spine road with perpendicular branches, clustered buildings along each branch, and concentric land-use zones — to produce a dataset where every one of the other 42 modules in the suite can find its required inputs (fields, geometry types, CRS) in one run. The geometry is deterministic (no random component; contrast this with the Monte Carlo seed used in the Seismic module), so two runs with the same `SCALE` value produce byte-identical layers — a property test suites and classroom demonstrations both depend on.

Key assumptions.

- The layout is a fixed procedural template, not a statistical or agent-based city generator — every `SCALE` value scales the same relative geometry uniformly. It cannot substitute for empirical urban-form diversity (irregular blocks, mixed densities, real street topology).
- Attribute distributions (floor counts, population, construction years) are hand-picked plausible values, not sampled from any real census, cadastre, or building-stock survey.
- The output CRS is fixed to EPSG:3857 (Web Mercator) — a projected CRS suitable for metric operations, but one that distorts area and true north away from the equator; fine here because only relative geometry matters, not appropriate for area-accurate production analysis.

When to use vs. when NOT to use. Use it the first time you install the suite, to confirm all 43 tools accept a consistent set of layers without error; for CI/regression smoke-testing after a code change; for classroom teaching, where every student must see identical results from identical steps. Do NOT use it as a stand-in for a real case study, for any published or decision-supporting analysis, or to test edge cases the fixed template does not contain (disconnected road networks, multipart geometries, missing-CRS layers, extreme aspect ratios) — those need hand-built or real-world fixtures instead.

#### 3. Mathematical Formulation

The layout follows a simple cellular template. A spine road runs east–west at y = 0; N branch roads run north–south at regular x-intervals. Building footprints are placed along each branch with fixed offsets. Green patches and water bodies are placed in the inter-branch zones. Shelter points are placed at the midpoint of each branch. Social-unit polygons are regular grid cells covering the entire extent.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
This algorithm requires no input layers — it generates all data from the SCALE parameter.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`SCALE` | Integer | 1000 | Spatial extent in metres. Larger values produce more buildings and longer roads.
`STUDY_AREA` | Output sink | — | Study-area boundary polygon.
`BUILDINGS` | Output sink | — | Building footprints with `floor_count`, `construction_year`, `land_use`.
`ROADS` | Output sink | — | Road network with `capacity_ph`, `class`, `direction`.
`IMPERVIOUS` | Output sink | — | Impervious surface polygons.
`GREEN` | Output sink | — | Green infrastructure / park patches.
`WATER` | Output sink | — | Water bodies.
`SHELTERS` | Output sink | — | Shelter / safe-assembly points with `capacity`.
`SOCIAL_UNITS` | Output sink | — | Neighbourhood polygons with population, elderly, children, disability, low-income fields.
`SENSITIVE` | Output sink | — | Sensitive-receptor points (schools, hospitals).

#### 6. Output Description

Nine output layers, each added to the QGIS project. Field schemas are designed to match the input requirements of the hazard, vulnerability, emergency, and synthesis modules. The building layer includes `floor_count` (integer, 1–8), `construction_year` (integer, 1960–2020), and `land_use` (string). Roads include `capacity_ph` (persons/hour), `class` (arterial/collector/local), and `direction` (both/ft/bt). Social units include `population`, `elderly`, `children`, `disability`, and `low_income` — all numeric counts.

#### 7. Symbolic Representation

Buildings: single-symbol fill, 20% opacity, dark grey. Roads: categorised by `class` — arterial = 1.0 mm dark grey, collector = 0.6 mm, local = 0.3 mm light grey. Green: solid #4dac26 fill at 40% opacity. Water: solid #0571b0 fill. Shelters: green triangle markers, size 4 mm. Sensitive receptors: red circle markers, size 3 mm. Social units: graduated by `population`, OrRd ramp, 5 natural-breaks classes.

#### 8. Interpretation Guide

The synthetic dataset is a workflow validation tool, not a real-city substitute. After generating it, run each hazard module in sequence, join scores to the social-unit polygons via Join Hazard Scores to Planning Units, and feed the result into the Multi-Hazard Composite and Adaptation Priority Synthesis tools. Confirm that all 43 algorithms accept the outputs without errors before switching to real data. The deterministic layout means every student in a classroom sees the same starting dataset.

#### Academic References

Fisher, P.F. & Tate, N.J. (2006). "Causes and consequences of error in digital elevation models." Progress in Physical Geography, 30(4), 467–489.

## 1. Seismic: Debris & Logistics

The Seismic group models post-earthquake building collapse, debris generation, and evacuation-network degradation. The single algorithm uses a Monte Carlo framework driven by a user-supplied moment magnitude scenario and building-age fragility functions. It produces six output layers spanning static network topology through dynamic debris envelopes to post-disaster clearance corridors.

### Post-Earthquake Collapse, Debris & Evacuation Network (Monte Carlo)

Processing ID: `planx_urban_resilience:seismic_debris_monte_carlo`

#### 1. Overview

Simulates a single earthquake scenario on a city block. For each building, computes a collapse probability from construction year and moment magnitude (Mw), then performs a Monte Carlo draw to decide whether it collapses. Collapsed buildings generate a debris envelope (horizontal spread proportional to building height) that is subtracted from the road network. The output is a six-layer cascade: net logistics network topology, morphological encroachment areas, per-building vulnerability points, dissolved debris envelope, cumulative road-blockage areas, and the surviving post-disaster clearance corridors.

#### 2. Theoretical Background

Academic lineage. Post-earthquake debris and network-blockage modelling emerged as its own research thread after the 1995 Kobe and 1999 Kocaeli earthquakes, when responders found that debris — not just collapsed buildings — was what actually cut off access to survivors: narrow historic streets choked with rubble were impassable for days even where the buildings themselves had been cleared. FEMA's HAZUS-MH (2003) formalised the resulting fragility-curve methodology into a national-scale, GIS-deployable standard, stratifying US building stock by construction era and material into empirical collapse-probability bands. Goretti & Sarli (2006) then closed the gap between "building collapses" and "road is blocked" by calibrating horizontal debris-spread ratios directly from Italian post-earthquake field surveys — the coefficient k in this tool's debris-radius formula descends directly from their work. FEMA P-58 (2018) later generalised the fragility/consequence chain into a full performance-based seismic-assessment methodology, from which the void/solid volume ratio guidance here is drawn. This tool is a screening-scale synthesis of that lineage: HAZUS-style stratified fragility plus Goretti-and-Sarli-style debris geometry, run as a fast Monte Carlo loop instead of a full building-by-building engineering assessment.

Key assumptions.

- Fragility is four discrete construction-era bins, not a continuous lognormal fragility curve as a function of spectral acceleration — the actual HAZUS methodology it is derived from uses continuous curves; this is a screening-scale, four-bin proxy.
- One uniform magnitude scenario applies to the entire study area — no distance attenuation from an epicentre, no site-specific soil amplification (Vs30), no near-fault directivity or basin effects.
- Each building's collapse draw is an independent Bernoulli trial — real earthquake damage is spatially correlated through shared soil conditions, liquefaction zones and construction cohorts, which this model cannot represent.
- Debris geometry is a fixed-radius buffer (`H` × k) around the building footprint — a single "spread ratio" standing in for collapse mechanism (pancake vs. lean-to vs. overturning), which in reality produce very different real debris footprints and directions.

When to use vs. when NOT to use. Use it for rapid multi-scenario screening (compare Mw 6.5 against Mw 7.5 to see the non-linear jump in road loss), for teaching the debris→network→evacuation causal chain, and for first-pass identification of which corridors a resilience plan should widen, brace, or duplicate. Do NOT use it for engineering-grade retrofit prioritisation of individual buildings (commission a real HAZUS-MH run or a site-specific structural assessment instead), for liquefaction- or landslide-triggered damage (out of scope of this fragility model), or for post-event real-time damage assessment — this is a pre-event planning tool, not a rapid-response one.

#### 3. Mathematical Formulation

$$P_{\text{collapse}} = \min\left(\text{base}_p \times \exp\left(0.8 (M_w - 7.0)\right),\; 1.0\right) \tag{1}$$ $$\text{debris\_radius} = H \times k \quad \text{(only if collapsed)} \tag{2}$$ $$\text{debris\_volume} = A_{\text{footprint}} \times H \times v_r \quad \text{(only if collapsed)} \tag{3}$$

where \(H\) = floor count × floor height, \(k\) = debris spread coefficient, \(v_r\) = void/solid volume ratio, and \(A_{\text{footprint}}\) = building polygon area. Debris geometry is a square-end-cap buffer of radius debris_radius around the footprint. Road blockage = intersection of debris envelope with the net road network. Clearance corridors = net road network minus the union of all road-blockage geometries.

Performance note. The algorithm unionises all building and parcel geometries with `QgsGeometry.unaryUnion`. For study areas with >5 000 buildings, expect 10–30 seconds of processing time. The main loop is O(buildings × parcels) in the worst case; the spatial index on parcels keeps typical performance near O(buildings × log(parcels)).

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Building Footprints | Vector (Polygon) | Yes | Must have numeric `floor_count` and `construction_year` fields. Missing values default to 1 floor and year 2000.
Cadastral Parcels | Vector (Polygon) | Yes | Used for morphological encroachment (setback) analysis.
Study Area / Network Boundary | Vector (Polygon) | Yes | Defines the road/void fabric. Buildings are subtracted from this to produce the net road network.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT_BUILDINGS` | Vector (Polygon) | — | Building footprint layer.
`FIELD_FLOORS` | Field (Numeric) | — | Number of storeys per building.
`FIELD_YEAR` | Field (Numeric) | — | Construction year; drives the fragility lookup.
`MAGNITUDE` | Double | 7.0 | Moment magnitude (Mw). Range 4.0–9.0. Has exponential effect on fragility.
`INPUT_PARCELS` | Vector (Polygon) | — | Cadastral parcel layer.
`INPUT_STUDY_AREA` | Vector (Polygon) | — | Study-area boundary defining the road/void fabric.
`FLOOR_HEIGHT` | Double | 3.0 | Average inter-storey height (m).
`DEBRIS_FACTOR` | Double | 0.40 | Debris spread coefficient k. Typically 0.30–0.50. Higher for masonry, lower for steel.
`SOLID_VOLUME_RATIO` | Double | 0.30 | Void/solid ratio. RC: 0.25–0.35, masonry: 0.35–0.45, steel: 0.10–0.20 (FEMA guidance).
`SEED` | Integer | 42 | Monte Carlo random seed. Same seed + same inputs = identical collapse draw. Change to sample a different realisation.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
[01_Static] Net Logistics Network | (geometry only) | Road/void fabric after subtracting buildings. MultiPolygon.
[02_Static] Morphological Conflict | (inherited from parcels) | Parcel areas encroached by buildings. Negative setback = encroachment.
[03_Dynamic] Structural Damage Index | `height_m`, `setback_dist_m`, `collapse_prob`, `collapsed` (0/1), `debris_radius_m`, `debris_vol_m3`, `blocked_area_m2` | Per-building point layer. `collapsed` = 1 if Monte Carlo draw collapsed this building.
[04_Dynamic] Macro-Debris Envelope | (geometry only) | Dissolved union of all debris buffers. Feed into Seismic Cascade.
[05_Dynamic] Critical Blockage Areas | (geometry only) | Intersection of debris envelope with the road network — the area actually blocked.
[06_Dynamic] Evacuation Corridors | (geometry only) | Remaining open road segments. Feed into Network Emergency Accessibility for post-quake routing.

#### 7. Symbolic Representation

Vulnerability points: graduated by `collapse_prob`, Viridis ramp, 5 natural-breaks classes, marker size 2.0–6.0 mm. Debris envelope: semi-transparent red fill (30% opacity), no stroke. Blockage areas: solid dark red fill at 50% opacity. Clearance corridors: bright green lines, 0.8 mm width — the surviving routes. Net road network: light grey fill with 0.3 mm dark grey stroke.

#### 8. Interpretation Guide

Collapse probability: sort descending to identify retrofit priorities. Values above 0.80 indicate pre-code construction under >M7.0. Total blocked area (printed to the log) is the headline metric — compare across Mw 6.5 vs 7.5 to see the non-linear jump in road loss. Clearance corridors: map these with Network Emergency Accessibility to answer "after the quake, which shelters can still be reached?".

Workflow integration. Feed [04_Dynamic] into Seismic Cascade for a complete debris→network→accessibility-loss chain. Feed [03_Dynamic] into Population-Weighted Exposure to translate collapse probabilities into human-impact estimates.

#### Academic References

FEMA. (2003). HAZUS-MH MR4 Technical Manual. Federal Emergency Management Agency, Washington, DC.

Goretti, A. & Sarli, V. (2006). "Road network and damaged buildings in urban areas: short and long-term interaction." Bulletin of Earthquake Engineering, 4(2), 159–175. DOI: 10.1007/s10518-006-9004-3

FEMA. (2018). Seismic Performance Assessment of Buildings, Volume 1 — Methodology (FEMA P-58-1), Second Edition. Washington, DC.

## 2. Heat: Comfort & Exposure

The Heat group provides an offline-first urban heat comfort risk screening model. It combines impervious surface share, building density, green/cooling deficit (distance to parks and water), and optional vulnerable-asset exposure into a 0–100 heat risk score on a user-defined grid. It is a planning-support index, not a microclimate simulation.

### Urban Heat Comfort Risk (screening)

Processing ID: `planx_urban_resilience:urban_heat_comfort_risk`

#### 1. Overview

Scores every cell of a user-defined grid for heat risk using four additive components: impervious surface share (roads, hardscape), building density, green deficit / cooling distance (distance to nearest park or water body), and vulnerable asset exposure (schools, hospitals, elderly housing). Each component is normalised 0–100 and weighted by user-controlled coefficients. The output includes a full heat-risk grid, a priority-cooling-zone layer (cells scoring ≥ 55), and an exposed-assets layer.

#### 2. Theoretical Background

Academic lineage. Oke's (1982) energy-balance account of the urban heat island — showing that UHI intensity is driven by the surface radiation budget (reduced sky-view factor, increased anthropogenic heat, low-albedo/impervious materials storing daytime heat and releasing it at night) — established the physical mechanism this tool screens for by proxy. Because instrumented or remote-sensed temperature is rarely available to a planning studio, subsequent work built land-cover surrogates for heat exposure: Stewart & Oke's (2012) Local Climate Zone (LCZ) taxonomy classifies urban form by exactly the variables this tool scores directly — impervious fraction, building density, sky-view-obstructing geometry — and Voogt & Oke's (2003) remote-sensing review confirmed imperviousness and vegetation deficit as the two dominant, most measurable drivers of surface UHI, which is why they carry the two largest default weights here. Bowler et al.'s (2010) systematic review of park-cooling studies supplied the empirical 200–400 m range for how far a green space's cooling effect reaches, which sets this tool's default `COOLING_DISTANCE` of 400 m at the upper end of that observed range (a conservative, generous-credit choice for cooling access).

Key assumptions.

- The index is a static land-cover composite, not a measured or simulated temperature — it has no time-of-day, season, or synoptic-weather dimension, whereas real UHI intensity is strongest at night under calm, clear conditions and can be negligible on a windy or overcast day (Oke, 1982).
- Every green or water feature counts equally toward the cooling-distance term regardless of its size, canopy density, or evapotranspiration rate — a 50 m² pocket park scores identically to a 5-hectare park at the same distance, though their real cooling magnitude differs substantially.
- Weights are uniform across the whole study area — the model has no mechanism for regional climate, prevailing wind exposure, or building-material albedo to modulate the composite.

When to use vs. when NOT to use. Use it for city-wide, first-pass cooling-priority screening; for comparing the relative heat burden of different neighbourhoods; for siting candidate interventions (street trees, reflective pavement, splash pads, pocket parks) where budget is limited and every hectare needs a defensible rank. Do NOT use it to predict actual air temperature, surface temperature, or a heat-index value — that requires a real microclimate simulation (ENVI-met, Urban Weather Generator) or satellite land-surface-temperature retrieval; do not use it for diurnal/nocturnal UHI-intensity research, or for health-outcome attribution studies without pairing the output against real observed temperature or mortality/morbidity data.

#### 3. Mathematical Formulation

$$\text{heat\_score} = \frac{w_i \cdot I + w_b \cdot B + w_g \cdot \frac{D_g + C}{2} + w_v \cdot V}{\max(w_i + w_b + w_g + w_v, 0.0001)} \tag{1}$$

where \(I\) = impervious share × 100, \(B\) = building share × 100, \(D_g\) = (1 − green_share) × 100, \(C\) = clamp(dist_to_cooling / cooling_distance × 100), \(V\) = clamp(vulnerable_count × 20), and all weights are user-supplied.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Study area | Vector (Polygon) | Yes | Defines the grid extent.
Buildings | Vector (Polygon) | Yes | Building footprints.
Impervious surfaces | Vector (Polygon) | No | Roads, hardscape. If omitted, impervious share is 0.
Green areas / tree canopies | Vector (Polygon/Point) | No | Parks, street trees, green roofs.
Water / cooling areas | Vector (Polygon/Line) | No | Lakes, rivers, fountains — treated as cooling features.
Vulnerable facilities | Vector (Point/Polygon) | No | Schools, hospitals, care homes.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`STUDY_AREA` | Vector (Polygon) | — | Study-area boundary.
`BUILDINGS` | Vector (Polygon) | — | Building footprints.
`IMPERVIOUS` | Vector (Polygon) | (optional) | Roads / hardscape.
`GREEN` | Vector | (optional) | Green areas / tree canopies.
`WATER` | Vector | (optional) | Water / cooling areas.
`VULNERABLE` | Vector | (optional) | Vulnerable facilities or population points.
`CELL_SIZE` | Double | 100.0 | Grid cell size in map units. Smaller cells = finer resolution but O(n²) runtime.
`COOLING_DISTANCE` | Double | 400.0 | Maximum walking distance to a cooling/green area (m).
`IMPERVIOUS_WEIGHT` | Double | 0.30 | Weight of impervious surface share in the composite.
`GREEN_DEFICIT_WEIGHT` | Double | 0.30 | Weight of green deficit / cooling distance in the composite.
`BUILDING_WEIGHT` | Double | 0.25 | Weight of building density in the composite.
`VULNERABILITY_WEIGHT` | Double | 0.15 | Weight of vulnerable-asset exposure in the composite.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Heat risk grid | `cell_id`, `imperv`, `build_dens`, `green_share`, `cool_dist`, `vuln_count`, `heat_score`, `risk_class` | Full grid with all component scores and the weighted composite.
Priority cooling zones | (same schema) | Subset of grid cells where heat_score ≥ 55. The intervention priority map.
Exposed buildings / assets | `asset_type`, `heat_score`, `risk_class` | Buildings and vulnerable assets falling in high-heat cells.

#### 7. Symbolic Representation

Heat risk grid: graduated by `heat_score`, YlOrRd ramp (5-class quantile), 0.3 mm border in light grey. Priority cooling zones: same ramp, 0.5 mm darker border, 60% opacity — these are the intervention targets. Exposed assets: categorised by `risk_class` — Very High = red markers, High = orange, Moderate = yellow, Low = not shown.

#### 8. Interpretation Guide

heat_score ≥ 75 (Very High): dense building clusters with almost no green space and distant from water — classic UHI core. heat_score 55–74 (High): residential districts with moderate tree cover but high impervious share. heat_score 35–54 (Moderate): suburban fringe with mix of green and built cover. heat_score < 35 (Low): park-adjacent or water-adjacent cells. Priority cooling zones (≥55) should be cross-referenced with Population-Weighted Exposure to identify cells where high heat risk coincides with high population density. Feed priority zones into Cost-Benefit Analyzer to rank green-infrastructure interventions.

#### Academic References

Oke, T.R. (1982). "The energetic basis of the urban heat island." Quarterly Journal of the Royal Meteorological Society, 108(455), 1–24.

Stewart, I.D. & Oke, T.R. (2012). "Local Climate Zones for urban temperature studies." Bulletin of the American Meteorological Society, 93(12), 1879–1900. DOI: 10.1175/BAMS-D-11-00019.1

Bowler, D.E. et al. (2010). "Urban greening to cool towns and cities: a systematic review." Landscape and Urban Planning, 97(3), 147–155.

## 3. Flood: Pluvial Susceptibility

The Flood group provides an offline-first pluvial (rainfall-driven) flood susceptibility screening model. It uses a DEM to compute relative low-elevation, flat-slope proxy, and drainage proximity scores on a user-defined grid. The model identifies low-lying, poorly-drained urban cells and marks exposed buildings and road segments. It is a planning-support index, not a hydrodynamic model.

### Pluvial Flood Susceptibility (screening)

Processing ID: `planx_urban_resilience:pluvial_flood_susceptibility`

#### 1. Overview

Scores every grid cell for pluvial flood susceptibility using three additive components: relative low elevation (how low a cell sits within the study area's elevation range), flat / low-slope proxy (local relief within a user-defined neighbourhood radius), and drainage proximity (distance to the nearest stream or water body). Buildings and road segments intersecting high-score cells are flagged as exposed.

#### 2. Theoretical Background

Academic lineage. Beven & Kirkby's (1979) TOPMODEL introduced the founding idea that a site's propensity to saturate and pond is predictable from topography alone — low, flat, well-drained-toward positions accumulate water; high, steep, well-drained-away positions shed it. Their original Topographic Wetness Index needs a full catchment-contributing-area computation, which is unavailable in a typical planning-studio workflow without a hydrologically-conditioned DEM and flow-routing toolchain; this tool substitutes two lightweight proxies that a planner can compute from a raw DEM in seconds — relative elevation within the study envelope stands in for contributing area, and local relief within a fixed sampling radius stands in for slope. This "cheap multi-criteria proxy instead of full hydrology" strategy is exactly the family of methods surveyed and validated by Tehrany, Pradhan & Jebur (2014), who combined elevation-derived layers in a GIS overlay/machine-learning framework for rapid municipal-scale flood-susceptibility mapping — this tool is a transparent, weighted-linear instance of that same family, trading their ensemble-learning accuracy for interpretability and zero training-data requirements. The drainage-proximity term follows the same distance-to-stream logic used in FEMA's HAZUS-MH flood methodology.

Key assumptions.

- This is a topographic proxy only — there is no rainfall intensity-duration-frequency (IDF) curve, no infiltration or imperviousness-adjusted runoff coefficient, and no storm-drain/pipe capacity in the model.
- Local relief is sampled at just four cardinal points at radius `NEIGHBORHOOD` — a coarse slope proxy, not a true multi-directional flow-accumulation algorithm (contrast the sibling PlanX plugin's D8 `flow_accumulation` tool, which routes flow across the full grid).
- The elevation range used to normalise "relative lowness" comes from a coarse pre-scan at `max(2×CELL_SIZE, NEIGHBORHOOD)` spacing, not every DEM cell — a very small depression that falls entirely inside one sampling gap can be missed.
- Without a supplied drainage layer, the drainage-proximity term defaults to a neutral 50 — the composite silently loses discriminating power on that component rather than failing loudly.

When to use vs. when NOT to use. Use it for rapid topographic screening before committing budget to a full hydrodynamic study; for prioritising which sub-catchments most deserve detailed modelling; for teaching the elevation–slope–drainage logic behind surface-water risk. Do NOT use it for regulatory floodplain delineation, insurance rate-mapping, or design-storm return-period analysis — commission a true 2D hydrodynamic model (HEC-RAS, TUFLOW, InfoWorks ICM) for those; also avoid it on sites whose flood risk is dominated by piped storm-drain capacity rather than surface topography, since the model has no representation of underground infrastructure. The engine is in `processing/flood/pluvial_flood_susceptibility.py`; DEM sampling is clamped to the raster extent with a half-pixel inset, preventing out-of-range reads at study-area boundaries.

#### 3. Mathematical Formulation

$$\text{flood\_score} = \frac{w_e \cdot E + w_s \cdot S + w_d \cdot D}{\max(w_e + w_s + w_d, 0.0001)} \tag{1}$$ $$E = 100\left(1 - \frac{elev - elev_{\min}}{elev_{\max} - elev_{\min}}\right) \quad \text{(relative lowness)} \tag{2}$$ $$S = 100\left(1 - \frac{|relief|}{\max(r, 1.0)}\right) \quad \text{(flatness proxy)} \tag{3}$$ $$D = 100\left(1 - \frac{dist_{\text{drain}}}{\max(4r, 1.0)}\right) \quad \text{(drainage proximity)} \tag{4}$$

where \(r\) is the neighbourhood radius and local relief is max − min of four cardinal samples at distance \(r\). If no drainage layer is supplied, \(D\) defaults to 50.0 (neutral).

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
DEM | Raster | Yes | Digital Elevation Model. Must be in a projected CRS with metric units.
Study area | Vector (Polygon) | Yes | Defines the grid extent and the elevation envelope.
Buildings | Vector (Polygon) | No | Exposed buildings are flagged when they intersect high-score cells.
Roads | Vector (Line) | No | Exposed road segments intersecting high-score cells.
Drainage / streams | Vector (Line/Polygon) | No | Stream network or water-body layer. If omitted, drainage proximity defaults to neutral (50).

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`DEM` | Raster | — | Digital Elevation Model.
`STUDY_AREA` | Vector (Polygon) | — | Study-area boundary.
`BUILDINGS` | Vector (Polygon) | (optional) | Building footprints.
`ROADS` | Vector (Line) | (optional) | Road centre-lines.
`DRAINAGE` | Vector (Line/Polygon) | (optional) | Drainage / stream / water network.
`CELL_SIZE` | Double | 100.0 | Grid cell size in map units.
`NEIGHBORHOOD` | Double | 150.0 | DEM neighbourhood radius for local-relief calculation.
`ELEVATION_WEIGHT` | Double | 0.45 | Weight of relative low-elevation component.
`SLOPE_WEIGHT` | Double | 0.30 | Weight of flat / low-slope proxy.
`DRAINAGE_WEIGHT` | Double | 0.25 | Weight of drainage proximity.
`EXPOSURE_THRESHOLD` | Double | 55.0 | Score above which buildings/roads are flagged as exposed.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Flood susceptibility grid | `cell_id`, `elev`, `rel_low`, `slope_proxy`, `drain_dist`, `flood_score`, `risk_class` | Full grid; drain_dist = −1 when no drainage layer supplied.
Exposed buildings | `asset_type`, `flood_score`, `risk_class` | Buildings intersecting high-score cells (≥ threshold). Point geometry.
Exposed road segments | `asset_type`, `flood_score`, `risk_class` | Road segments intersecting high-score cells. MultiLineString geometry.

#### 7. Symbolic Representation

Flood susceptibility grid: graduated by `flood_score`, Blues ramp (5-class natural breaks), 0.3 mm border. Exposed buildings: red markers, size scaled by `flood_score`. Exposed roads: categorised — red for high exposure (≥75), orange for moderate. Overlay with a hillshade of the DEM at 50% transparency for terrain context.

#### 8. Interpretation Guide

flood_score ≥ 75: cells in the lowest 25% of the elevation range, with minimal local relief and near a stream — classic floodplain floor. flood_score 55–74: transitional slopes near drainage corridors. flood_score < 35: ridge-top or hillslope cells far from water. Exposed buildings: cross-reference with Social Vulnerability Index to identify high-flood-exposure + high-social-vulnerability neighbourhoods. Feed exposed-road outputs into Network Criticality to see whether key evacuation routes lie in flood-prone cells.

Not a hydrodynamic model. This tool does not simulate rainfall intensity, infiltration, or surface-water flow routing. It ranks cells by topographic susceptibility alone. Use it for spatial prioritisation; do not use it to set flood-insurance zones or regulatory floodplain boundaries.

#### Academic References

Beven, K.J. & Kirkby, M.J. (1979). "A physically based, variable contributing area model of basin hydrology." Hydrological Sciences Journal, 24(1), 43–69.

Tehrany, M.S., Pradhan, B. & Jebur, M.N. (2014). "Flood susceptibility mapping using a novel ensemble weights-of-evidence and support vector machine models in GIS." Journal of Hydrology, 512, 332–343.

## 4. Social: Vulnerability & Equity

The Social group provides a weighted social vulnerability index (SVI) following the Cutter et al. (2003) framework. It normalises neighbourhood- or census-polygon indicators (elderly, children, disability, low-income, population density) to 0–100 and combines them with user-controlled weights. Missing data fields are flagged but do not block the computation.

### Social Vulnerability Index (screening)

Processing ID: `planx_urban_resilience:social_vulnerability_index`

#### 1. Overview

Computes a normalised 0–100 Social Vulnerability Index per polygon from up to five demographic indicators. All indicators are treated as risk-increasing (higher count or rate = higher vulnerability). Each indicator is min-max normalised across the study area, then combined via weighted arithmetic mean. The output includes the composite score, a class label, population density, and a data-quality flag listing any missing fields.

#### 2. Theoretical Background

Academic lineage. Cutter, Boruff & Shirley's (2003) Social Vulnerability Index (SoVI) was the paper that moved hazards research decisively from "vulnerability is whoever is exposed to the hazard" toward "vulnerability is a property of people and places, independent of any one hazard" — built from principal-components analysis over ~250 US-county socioeconomic and demographic variables. Cutter & Finch's (2008) follow-up tracked how county-level social vulnerability changed over four decades and reported which of the underlying dimensions carried the most explanatory weight; this tool's default weights (elderly 0.25, low-income 0.25, disability 0.20, children 0.15, density 0.15) are set to track that reported relative importance, though this implementation replaces SoVI's factor-analytic machinery with a transparent weighted linear sum a planner can audit field-by-field. The five default indicators map onto the dimensions Cutter's original work identified as most predictive of adverse outcomes: age extremes (elderly, children), socioeconomic status (low-income), special-needs populations (disability), and population pressure (density).

Key assumptions.

- Min-max normalisation is relative to the supplied layer's own value range, not a fixed regional or national benchmark — a "high" score in one study area could read as merely "moderate" against a wider region, and re-running the tool on a different extent changes every score, even for unchanged features.
- Linear min-max normalisation replaces Cutter's original principal-components/factor-analytic approach — it assumes each raw indicator is roughly monotonic with vulnerability and does not correct for correlation between indicators the way factor analysis does (e.g. low-income and disability often co-vary; a PCA-based index would down-weight that redundancy automatically, this one will not).
- All five indicators are asserted risk-increasing. The model has no way to represent a protective factor (e.g. strong social cohesion, high home-ownership) without the user first inverting that field's sign upstream in a Field Calculator expression.

When to use vs. when NOT to use. Use it for relative prioritisation within one study area or city; for combining with a hazard score to compute equity-adjusted priority; for classroom teaching of composite-index construction with fully transparent, inspectable weights. Do NOT use it for cross-city or cross-region comparison without re-basing both to a common reference range (min-max is extent-relative by design); do not present it as a substitute for the full, peer-reviewed SoVI or CDC/ATSDR Social Vulnerability Index computation in an academic study; and do not use it where causal inference about which factor drives vulnerability is required — the factor-analytic method exists precisely to answer that question, and this tool does not attempt to.

The engine is in `processing/social/social_vulnerability_index.py`.

#### 3. Mathematical Formulation

$$\text{svi\_score} = \frac{\sum_{k} w_k \cdot n_k}{\sum_{k} w_k} \quad \text{for fields with } w_k > 0 \text{ and field present} \tag{1}$$ $$n_k = 100 \cdot \frac{x_{i,k} - \min(x_k)}{\max(x_k) - \min(x_k)} \tag{2}$$

where \(k\) ∈ {elderly, children, disability, low_income, density} and \(w_k\) is the user-supplied weight. Indicators with zero weight or missing fields are excluded from the summation. Population density = population / area_ha.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Neighbourhood / census areas | Vector (Polygon) | Yes | Must have at least one of the five indicator fields. Use a metric CRS when density is used.
Population field | Field (Numeric) | No | Used for population density. If omitted, density weight is zeroed.
Elderly count/rate | Field (Numeric) | No | Count or rate of residents 65+.
Children count/rate | Field (Numeric) | No | Count or rate of residents < 18.
Disability / care need | Field (Numeric) | No | Count or rate of residents with disabilities.
Low-income / deprivation | Field (Numeric) | No | Count or rate below poverty line or in lowest income quintile.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`AREAS` | Vector (Polygon) | — | Neighbourhood / census areas.
`POPULATION_FIELD` | Field (Numeric) | (optional) | Population field for density computation.
`ELDERLY_FIELD` | Field (Numeric) | (optional) | Elderly count/rate field.
`CHILDREN_FIELD` | Field (Numeric) | (optional) | Children count/rate field.
`DISABILITY_FIELD` | Field (Numeric) | (optional) | Disability / care-need field.
`LOW_INCOME_FIELD` | Field (Numeric) | (optional) | Low-income / deprivation field.
`WEIGHT_ELDERLY` | Double | 0.25 | Weight of elderly indicator.
`WEIGHT_CHILDREN` | Double | 0.15 | Weight of children indicator.
`WEIGHT_DISABILITY` | Double | 0.20 | Weight of disability indicator.
`WEIGHT_LOW_INCOME` | Double | 0.25 | Weight of low-income indicator.
`WEIGHT_DENSITY` | Double | 0.15 | Weight of population density.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`svi_score` | Double | Weighted composite 0–100. Higher = more vulnerable.
`svi_class` | String | Very High (≥75) / High (55–74) / Moderate (35–54) / Low (<35).
`pop_density` | Double | Population / hectare. 0 if no population field supplied.
`data_flags` | String | Semicolon-separated list of missing fields, or "ok" if all active-weight fields are present.

#### 7. Symbolic Representation

Graduated by `svi_score`, YlOrBr ramp, 5-class quantile classification, 0.3 mm border. Overlay with the hazard-score layer being analysed (heat, flood, etc.) to visually identify high-hazard + high-vulnerability coincidences. Alternatively, feed both into Bivariate Choropleth Symbology for a formal 3×3 matrix renderer.

#### 8. Interpretation Guide

svi_score ≥ 75: the most vulnerable neighbourhoods — multiple overlapping indicators. These are the equity-priority targets. svi_score 55–74: elevated vulnerability, typically driven by one dominant factor. data_flags: if any indicator with non-zero weight is missing, the flag reminds you the score is based on incomplete information. Feed SVI into Equity-Adjusted Adaptation Priority to raise the priority of high-hazard + high-vulnerability units. Feed into Risk-Recovery Priority Matrix alongside a recovery-capacity score to find the gap.

#### Academic References

Cutter, S.L., Boruff, B.J. & Shirley, W.L. (2003). "Social vulnerability to environmental hazards." Social Science Quarterly, 84(2), 242–261. DOI: 10.1111/1540-6237.8402002

Cutter, S.L. & Finch, C. (2008). "Temporal and spatial changes in social vulnerability to natural hazards." Proceedings of the National Academy of Sciences, 105(7), 2301–2306.

## 5. Emergency: Accessibility, Evacuation & Networks

The Emergency group is the largest operational cluster in the suite — seven algorithms spanning Euclidean shelter coverage, network-based accessibility routing, edge-betweenness criticality, capacity-constrained evacuation simulation with optional timestep queuing, seismic cascade analysis, and optimal shelter siting via greedy maximal-coverage location. Together they model the full emergency-response spatial chain: who can reach a shelter → which roads are bottlenecks → how long does evacuation take → what happens to the network after the quake → where should new shelters go?

### Emergency Accessibility and Shelter Coverage

Processing ID: `planx_urban_resilience:emergency_accessibility_shelter_coverage`

#### 1. Overview

Creates a Euclidean grid-based access-deficit map from shelter or safe-assembly points. Each cell's score is the distance to the nearest shelter, normalised between a target service distance and a critical distance, with an optional barrier-overlap penalty. Underserved assets (buildings or population points beyond the service distance) are flagged in a separate layer.

#### 2. Theoretical Background

Academic lineage. Spatial-accessibility measurement traces to Hansen's (1959) gravity-based accessibility concept — access as a function of both opportunity size and separation cost. Radke & Mu (2000) introduced the two-step floating catchment area (2SFCA) method to operationalise this for service planning: step one sums demand within a catchment of each supply point, step two sums supply-to-demand ratios reachable from each demand point. Luo & Wang (2003) popularised and refined 2SFCA for healthcare access, establishing it as the standard method in the field. This tool implements the simplest member of that family: a single-step distance-decay score to the nearest shelter, without 2SFCA's catchment-overlap correction (which prevents double-counting supply shared by nearby demand points) or its supply/demand ratio (which would require a shelter capacity field, absent here by design — see Evacuation Time Simulation for the capacity-aware sibling tool). The barrier penalty follows the impedance-surface tradition in accessibility modelling: cells overlapped by barrier polygons (waterways, highways without crossings) receive a proportional score increase, standing in for the routing detour a real barrier would force.

Key assumptions.

- Straight-line distance ignores real street topology — a river or highway without a crossing can make two geometrically close points functionally far apart; the barrier penalty is an additive correction, not a true reroute, so it can only push the score toward "worse," never actually recompute the detour.
- Only the nearest shelter counts. The model does not know whether that shelter has any capacity left — two population points equidistant from the same shelter get an identical score whether the shelter can hold ten people or ten thousand.
- The linear ramp between SERVICE_DISTANCE and CRITICAL_DISTANCE is a modelling choice, not an empirically fitted decay curve — real accessibility often decays faster near the origin (Bowler et al.-style meta-analyses in other domains typically find non-linear decay).

When to use vs. when NOT to use. Use it for a first-pass screening pass with no road-network data on hand, or to test sensitivity to the SERVICE_DISTANCE/CRITICAL_DISTANCE choice quickly across a whole study area. Do NOT use it anywhere real routing barriers matter (rivers, highways, gated developments, one-way street systems) — switch to Network Emergency Accessibility, which routes on the actual road graph; do not use it for capacity-aware siting or assignment decisions — Evacuation Time Simulation and Optimal Shelter Siting both account for shelter capacity, this tool does not.

The engine is in `processing/accessibility/emergency_accessibility.py`.

#### 3. Mathematical Formulation

$$\text{access\_score} = \text{clamp}\left(100\cdot\frac{d - d_{\text{service}}}{\max(d_{\text{critical}} - d_{\text{service}}, 1.0)} + \frac{A_{\text{barrier}}}{A_{\text{cell}}}\cdot w_{\text{barrier}}\right) \tag{1}$$

where \(d\) = Euclidean distance to nearest shelter, clamped to \(d_{\text{critical}}\) if no shelter found.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Study area | Vector (Polygon) | Yes | Grid extent.
Shelters / safe assembly | Vector (Point/Polygon) | Yes | Shelter locations.
Population assets | Vector (Point/Polygon) | No | Buildings/facilities to check for underserved status.
Barriers | Vector (Polygon) | No | Barrier polygons that increase access difficulty.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`STUDY_AREA` | Vector (Polygon) | — | Study area boundary.
`SHELTERS` | Vector | — | Shelters / safe assembly areas.
`POPULATION_ASSETS` | Vector | (optional) | Population assets to evaluate.
`BARRIERS` | Vector (Polygon) | (optional) | Barrier / blocked areas.
`CELL_SIZE` | Double | 100.0 | Grid cell size.
`SERVICE_DISTANCE` | Double | 500.0 | Target service (walking) distance.
`CRITICAL_DISTANCE` | Double | 1200.0 | Distance at which access_score saturates at 100.
`BARRIER_WEIGHT` | Double | 25.0 | Score penalty per 100% barrier overlap.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Accessibility grid | `cell_id`, `dist_shelter`, `barrier_pct`, `access_score`, `access_class` | Grid with access deficit. access_class: Critical / Underserved / Watch / Covered.
Underserved assets | `asset_type`, `dist_shelter`, `access_score`, `access_class` | Assets beyond service distance or in high-score cells.

#### 7. Symbolic Representation

Access grid: graduated by `access_score`, RdYlGn ramp (reversed: green = low deficit, red = high deficit), 5-class natural breaks. Underserved assets: red markers, size 3 mm. Overlay shelters as green triangles at size 5 mm.

#### 8. Interpretation Guide

access_class = Critical: >75% of the way from service to critical distance — these cells lack any nearby shelter. Underserved: 55–74%. Watch: 35–54%, within reach but not comfortable. Covered: <35%. Cross-reference underserved assets with Social Vulnerability Index to find vulnerable populations with poor shelter access.

#### Academic References

Hansen, W.G. (1959). "How accessibility shapes land use." Journal of the American Institute of Planners, 25(2), 73–76.

Radke, J. & Mu, L. (2000). "Spatial decompositions, modeling and mapping service regions to predict access to social programs." Geographic Information Sciences, 6(2), 105–112.

Luo, W. & Wang, F. (2003). "Measures of spatial accessibility to health care in a GIS environment." Environment and Planning B, 30(6), 865–884.

### Network Emergency Accessibility

Processing ID: `planx_urban_resilience:network_emergency_accessibility`

#### 1. Overview

Computes road-network distance and travel time from population origins to the nearest shelter using multi-source Dijkstra on a graph built from line-segment vertices. Each origin receives `net_dist_m`, `time_min`, an access class, and a QA flag (`large_snap_distance` if the origin's snap to the graph exceeds 5× the tolerance). Origins classified as Underserved, Critical, or Unreachable are written to a separate underserved layer.

#### 2. Theoretical Background

Academic lineage. Dijkstra's (1959) shortest-path algorithm is the computational foundation underneath essentially every network-accessibility GIS tool, including commercial routing engines. The graph itself is built by snapping road vertices at a user-defined tolerance, following the node-snapping tradition described by Cardinal et al. (2011) for turning arbitrary line geometry into a routable graph without a dedicated topology-cleaning step. Running Dijkstra "backwards" — a single traversal seeded from every destination node simultaneously (multi-source Dijkstra) rather than one traversal per origin — is a standard optimisation in accessibility software whenever destinations are far fewer than origins (here: a handful of shelters against potentially thousands of buildings), cutting the runtime from O(origins × edges log vertices) to a single O(edges log vertices) pass.

Key assumptions.

- Travel speed is one constant (SPEED_KMH) for every segment in the graph — a highway and a footpath are traversed at the identical assumed speed, so time_min differences reflect distance alone, never road-class or surface differences.
- Graph nodes come from raw vertex snapping at SNAP_TOLERANCE, not true topological noding. A road layer with near-miss endpoints (a common digitising artefact) silently produces a disconnected subgraph rather than an error — the `qa_flag = "not_connected"` class exists specifically to surface that, not to fix it; inspect and re-node the source layer if it appears frequently.
- The default walking speed (4.5 km/h) assumes unobstructed, healthy adult pedestrian movement — no stairs, steep slopes, or mobility constraints are represented.

When to use vs. when NOT to use. Use it whenever routing must respect real street topology — this is the primary, more realistic sibling to the Euclidean Emergency Accessibility and Shelter Coverage tool. Before running, make sure the road layer is well-noded (QGIS's "Split with Lines" or a v.clean-equivalent workflow); a high fraction of `qa_flag = "large_snap_distance"` results is a symptom of a poorly noded network, not a modelling limitation. Do NOT use it where multiple travel modes with different speeds matter within the same run — SPEED_KMH is a single global constant, so mixed pedestrian/vehicular scenarios need two separate runs; also do not use it for capacity- or congestion-aware analysis — see Evacuation Time Simulation for that.

The engine uses the shared graph builder in `processing/accessibility/_network_graph.py`.

#### 3. Mathematical Formulation

$$\text{time\_min} = \frac{\text{net\_dist\_m}}{1000 \cdot \text{speed\_kmh}} \times 60 \tag{1}$$ $$\text{access\_cls} = f(\text{time\_min}, \text{target}, \text{critical}) \tag{2}$$

where \(f\) assigns Covered (≤ target), Watch (≤ mid), Underserved (≤ critical), Critical (> critical), or Unreachable (net_dist = −1).

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Road network | Vector (Line) | Yes | Must be connected and well-noded. Disconnected segments produce unreachable origins.
Origins | Vector (Point/Polygon) | Yes | Buildings / facilities / population points.
Destinations | Vector (Point/Polygon) | Yes | Shelters / safe assembly areas.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`ROADS` | Vector (Line) | — | Road network.
`ORIGINS` | Vector | — | Origin features.
`DESTINATIONS` | Vector | — | Destination features.
`SPEED_KMH` | Double | 4.5 | Assumed walking speed (km/h).
`TARGET_MINUTES` | Double | 10.0 | Target access time (≤ this = Covered).
`CRITICAL_MINUTES` | Double | 25.0 | Critical access time (≥ this = Critical).
`SNAP_TOLERANCE` | Double | 1.0 | Node-snapping tolerance in map units.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Accessibility origins | `net_dist_m`, `snap_dist`, `time_min`, `access_cls`, `qa_flag` | All origins. net_dist_m = −1 and qa_flag = "not_connected" if unreachable.
Underserved origins | (same schema) | Subset in Underserved / Critical / Unreachable classes.

#### 7. Symbolic Representation

Graduated by `time_min`, RdYlGn ramp (reversed: green = fast access, red = slow/critical), 5-class quantile. Underserved origins: red markers. Graph node count and edge count are printed to the Processing log for diagnostic use.

#### 8. Interpretation Guide

qa_flag = large_snap_distance: the origin is far from any road vertex — check the road layer for gaps or increase snap tolerance. not_connected: the road subgraph containing this origin has no path to any destination — the analysis reveals a genuine network-disconnection problem. Feed underserved outputs into Optimal Shelter Siting to find candidate sites that close the access gap.

#### Academic References

Dijkstra, E.W. (1959). "A note on two problems in connexion with graphs." Numerische Mathematik, 1, 269–271.

Cardinal, J. et al. (2011). "A unified framework for rich routing problems." Computers & Operations Research, 38(5), 831–843.

### Network Criticality (edge betweenness proxy)

Processing ID: `planx_urban_resilience:network_criticality`

#### 1. Overview

For every origin → nearest-destination shortest path on the road network, counts how many times each edge is used. The resulting per-edge count is normalised 0–100 as `criticality_score`. High-score edges are bottlenecks: closing one forces the most reroutes. This is an offline proxy for edge betweenness centrality — computationally O(O × V log V) against the full O(VE) betweenness.

#### 2. Theoretical Background

Academic lineage. Betweenness centrality (Freeman, 1977) measures how much shortest-path traffic flows through a node or edge, summed over every origin-destination pair in the graph — a purely topological measure, independent of any real demand. Brandes' (2001) algorithm made the exact computation tractable at O(VE), but even that remains too slow for interactive use on a large road network with a QGIS Processing dialog open and waiting (Girvan & Newman, 2002, used betweenness this way for community detection, not routing). This tool deliberately narrows the scope from "all shortest paths between all node pairs" to "the shortest path from each real origin to its single nearest real destination" — an origin-destination (OD) usage-count proxy that trades betweenness's generality for realism (it reflects an actual emergency-access flow pattern) and speed (O(origins × edges log vertices) instead of O(edges × nodes)). This narrower, demand-weighted framing is closer in spirit to traffic-assignment-based criticality measures used in transportation engineering than to graph-theoretic betweenness proper.

Key assumptions.

- `usage_count` is a raw traffic-frequency proxy, not a removal-impact measure. A high-usage edge with several parallel alternatives costs little to lose (traffic simply reroutes); a low-usage edge can still be a true single point of failure if it is the only connector for a small pocket of demand — usage-count alone cannot tell these two cases apart.
- Normalisation is to the single busiest edge within this run — `criticality_score` values are not comparable across two runs with different origin/destination sets.
- Each origin routes to its single nearest destination only; if a shelter fills up or closes, the "next nearest" routing this tool assumes never happens on its own — combine with Evacuation Time Simulation for capacity-aware reassignment.

When to use vs. when NOT to use. Use it for rapid triage of which corridors carry the most demand-weighted traffic under normal, all-shelters-available conditions, and pair it with Evacuation Time Simulation's per-edge load output to check whether the busiest corridors also carry the highest evacuation congestion. Do NOT use it to identify single points of failure or redundancy gaps — a usage-count proxy cannot distinguish "busy but redundant" from "busy and irreplaceable"; that question needs an extra-cost-on-removal analysis (test each candidate edge by removing it and re-routing, then measuring the detour this forces), which is out of scope for this tool.

The engine is in `processing/accessibility/network_criticality.py`.

#### 3. Mathematical Formulation

$$\text{usage\_count}(e) = |\{o \in O : e \in \text{path}(o, \text{nearest\_dest}(o))\}| \tag{1}$$ $$\text{criticality\_score}(e) = 100 \cdot \frac{\text{usage\_count}(e)}{\max_{e'} \text{usage\_count}(e')} \tag{2}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Road network | Vector (Line) | Yes | Connected line layer.
Origins | Vector (Point/Polygon) | Yes | Demand-side features.
Destinations | Vector (Point/Polygon) | Yes | Supply-side features (shelters, hospitals).

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`ROADS` | Vector (Line) | — | Road network.
`ORIGINS` | Vector | — | Origins (centroids used for polygons).
`DESTINATIONS` | Vector | — | Destinations.
`SNAP_TOLERANCE` | Double | 2.0 | Node-snapping tolerance.
`ROAD_DIRECTION_FIELD` | Field (String) | (optional) | Direction field: both / ft (forward only) / bt (backward only).

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`edge_id` | Integer | Sequential edge identifier.
`length_m` | Double | Edge segment length in metres.
`usage_count` | Integer | Number of origin→destination paths using this edge.
`criticality_score` | Double | 0–100 normalised usage count. 100 = the single most-used edge.

#### 7. Symbolic Representation

Graduated by `criticality_score`, OrRd ramp, 5-class natural breaks, line width 0.3–1.5 mm (scaled by score). The top 5% of edges (score ≥ 90) are the critical bottlenecks — render these at 1.5 mm in dark red.

#### 8. Interpretation Guide

Edges with criticality_score ≥ 90 are the system's single points of failure — close one and a large share of origins lose their shortest path. score 50–89: important secondary corridors. Feed the output into the Seismic Cascade debris layer: do blocked segments coincide with high-criticality edges? That is the cascade scenario to prioritise. Pair with Evacuation Time Simulation to verify that the bottleneck edges also carry the highest evacuation load.

#### Academic References

Freeman, L.C. (1977). "A set of measures of centrality based on betweenness." Sociometry, 40(1), 35–41.

Girvan, M. & Newman, M.E.J. (2002). "Community structure in social and biological networks." Proceedings of the National Academy of Sciences, 99(12), 7821–7826.

Brandes, U. (2001). "A faster algorithm for betweenness centrality." Journal of Mathematical Sociology, 25(2), 163–177.

### Evacuation Time Simulation

Processing ID: `planx_urban_resilience:evacuation_time_simulation`

#### 1. Overview

Greedy capacity-constrained shelter assignment on a road network. Origins with population are assigned to shelters along shortest paths, respecting shelter capacity. An optional timestep queue defers departures when road segments exceed per-interval flow capacity. Outputs include per-origin travel time, delay, and evacuation class; per-shelter utilisation; and per-edge trip count, population load, congestion percentage, and bottleneck score. The Processing log reports P50 and P95 evacuation times.

#### 2. Theoretical Background

Academic lineage. Evacuation-transportation modelling has historically split into two traditions (Murray-Tuite & Wolshon, 2013, review both): microscopic traffic simulation, which tracks individual vehicle behaviour and is computationally expensive at city scale, and macroscopic flow models, which treat evacuating traffic as a continuous flow obeying conservation laws. Daganzo's (1994) Cell Transmission Model established the macroscopic tradition this tool belongs to: discretise time into steps and cap how much flow can cross a link per step, without simulating individual vehicle trajectories. Yperman's (2007) Link Transmission Model refined that idea for large networks; this tool's `TIMESTEP_MINUTES` queue mechanism is a simplified, planning-scale instance of the same discretised-flow-with-capacity-cap logic. The underlying shelter-assignment step is a greedy nearest-capacity heuristic in the facility-location tradition of Church & ReVelle (1974); Southworth's (1991) state-of-the-art review situates this combination of routing plus capacity-constrained assignment as the standard practical approach for regional evacuation-time estimation before committing to a full microsimulation.

Key assumptions.

- Assignment is greedy, not globally optimal, and order-dependent: the `ASSIGN_ORDER` parameter (farthest-first vs. largest-population-first) changes which shelters fill and which origins go unassigned, sometimes substantially, because a greedy algorithm locks in early decisions that later origins must live with.
- An unassigned population does not vanish — it is marked `qa_flag = "no_capacity"` and needs either more shelter capacity or more sites (feed the result into Optimal Shelter Siting).
- Road capacity is a fixed persons/hour ceiling with no speed-density degradation as flow approaches that ceiling — real traffic flow slows non-linearly near jam density (the fundamental diagram of traffic flow), which this model's hard cap does not represent.
- The model assumes perfect compliance: everyone assigned departs and drives directly to their assigned shelter. It has no representation of shadow evacuation (people leaving who were not told to) or evacuation refusal, both well-documented real-world evacuation behaviours (Murray-Tuite & Wolshon, 2013).

When to use vs. when NOT to use. Use it for macro-scale evacuation screening: comparing shelter-capacity adequacy against a population, identifying which corridors become congestion-prone under load, and testing sensitivity to the assignment-order choice. Do NOT use it for micro-level traffic-engineering decisions such as signal timing or lane configuration — that needs a true microsimulation (VISSIM, SUMO); do not treat its evacuation-time outputs as behaviourally realistic predictions without adjusting for compliance, shadow evacuation, and departure-time spread, all of which shift real evacuation curves later and wider than a perfect-compliance model predicts.

Road capacity comes from either a numeric field or a road-class lookup (arterial 1800 pph, collector 1200 pph, local 600 pph). The engine is in `processing/accessibility/evacuation_simulation.py`.

#### 3. Mathematical Formulation

$$\text{congestion\_pct}(e) = \min\left(100, 100 \cdot \frac{\sum_{o} \text{pop}_o \cdot [e \in \text{path}(o)]}{\text{capacity\_ph}(e)}\right) \tag{1}$$ $$\text{evac\_min}(o) = t \cdot \Delta + \frac{\text{dist}_{\text{path}(o, s)}}{1000 \cdot \text{speed\_kmh}} \times 60 \tag{2}$$

where \(t\) is the timestep index, \(\Delta\) is the timestep interval in minutes, and \(s\) is the assigned shelter. When timestep = 0 (instant mode), \(t = 0\) and assignment is purely greedy by distance.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Road network | Vector (Line) | Yes | Connected line layer. Optional capacity and direction fields.
Origins | Vector (Point/Polygon) | Yes | Must have a population field.
Shelters | Vector (Point/Polygon) | Yes | Must have a capacity field.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`ROADS` | Vector (Line) | — | Road network.
`ORIGINS` | Vector | — | Origins with population.
`POP_FIELD` | Field (Numeric) | — | Population per origin.
`SHELTERS` | Vector | — | Shelters.
`CAPACITY_FIELD` | Field (Numeric) | — | Shelter capacity (persons).
`SPEED_KMH` | Double | 30.0 | Travel speed (km/h). Use 4.5 for walking, 30 for vehicular.
`TARGET_MINUTES` | Double | 15.0 | Target evacuation time.
`CRITICAL_MINUTES` | Double | 45.0 | Critical evacuation time.
`ROAD_CAPACITY_FIELD` | Field (Numeric) | (optional) | Persons/hour per road segment.
`ROAD_CLASS_FIELD` | Field (String) | (optional) | arterial / collector / local → default capacities.
`ROAD_DIRECTION_FIELD` | Field (String) | (optional) | both / ft / bt.
`DEFAULT_LANE_CAPACITY` | Double | 1200.0 | Default road capacity pph.
`TIMESTEP_MINUTES` | Double | 0.0 | Timestep interval. 0 = instant greedy assignment (no queuing).
`MAX_TIMESTEPS` | Integer | 48 | Maximum queue timesteps before stalling.
`SNAP_TOLERANCE` | Double | 2.0 | Node-snapping tolerance.
`ASSIGN_ORDER` | Enum | Farthest first | Assignment order: farthest-first or largest-population-first.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Evacuation origins | `evac_shelter`, `travel_min`, `evac_delay_min`, `evac_min`, `evac_timestep`, `evac_pop`, `evac_class`, `qa_flag` | Per-origin assignment. OnTime / Watch / Late / Unassigned.
Shelter utilization | `cap_total`, `cap_used`, `cap_pct`, `assign_count` | Per-shelter fill statistics.
Evacuation edge load | `edge_id`, `length_m`, `trip_count`, `load_pop`, `road_capacity_ph`, `congestion_pct`, `bottleneck_score` | Per-edge load and congestion.

#### 7. Symbolic Representation

Origins: categorised by `evac_class` — OnTime = green, Watch = yellow, Late = orange, Unassigned = red. Edges: graduated by `congestion_pct`, YlOrRd ramp, line width 0.3–2.0 mm. Shelters: graduated by `cap_pct`, Blues ramp — full shelters are dark blue.

#### 8. Interpretation Guide

Unassigned origins: either the origin cannot snap to the graph, or all reachable shelters are full — increase shelter capacity or add sites. congestion_pct > 100: the segment's assigned load exceeds its hourly capacity — a bottleneck. P95 evacuation time (in log): the time by which 95% of the assigned population has reached a shelter. Compare instant mode vs timestep-queue mode to see the congestion delay. Feed congested edges into Cost-Benefit Analyzer to rank road-widening interventions.

#### Academic References

Church, R.L. & ReVelle, C.S. (1974). "The maximal covering location problem." Papers of the Regional Science Association, 32, 101–118.

Daganzo, C.F. (1994). "The cell transmission model: a dynamic representation of highway traffic consistent with the hydrodynamic theory." Transportation Research Part B, 28(4), 269–287. DOI: 10.1016/0191-2615(94)90002-7

Yperman, I. (2007). The Link Transmission Model for Dynamic Network Loading. PhD thesis, KU Leuven.

Southworth, F. (1991). "Regional evacuation modeling: a state-of-the-art review." Oak Ridge National Laboratory, ORNL/TM-11740.

Murray-Tuite, P. & Wolshon, B. (2013). "Evacuation transportation modeling: an overview of research, development, and practice." Transportation Research Part C, 27, 25–45. DOI: 10.1016/j.trc.2012.11.005

### Seismic Cascade: Debris → Network Degradation

Processing ID: `planx_urban_resilience:seismic_cascade_accessibility`

#### 1. Overview

Pipes the seismic debris polygon output into the road network, drops blocked edges, then re-runs multi-source Dijkstra on the degraded network and compares pre- vs post-hazard accessibility per origin. Outputs include pre- and post-hazard distances, delta, reachability flags, and a status label (Newly cut off / Always cut off / Worse / Same / Recovered). This is the suite's headline cross-domain cascading-hazard analysis.

#### 2. Theoretical Background

Academic lineage. Cascading-hazard modelling — linking a primary event (earthquake) to secondary consequences (network degradation, accessibility loss) rather than treating each as an isolated analysis — gained prominence after events like the 2011 Tōhoku earthquake made clear that infrastructure interdependencies (a damaged bridge cutting a hospital off from its service area) can matter as much as direct physical damage. NIST's (2015) Community Resilience Planning Guide formalised this into a repeatable methodology for US municipalities: perturb a baseline network model with physical damage, then measure the resulting service gap. This tool is a direct implementation of that perturb-and-measure pattern, deliberately chained to the Seismic module's own debris output — the two tools are designed to be run back-to-back, one producing exactly the debris polygons the other consumes.

Key assumptions.

- Debris blockage is binary — a road segment is either fully passable or fully blocked. There is no partial-capacity representation for a segment merely narrowed by debris on one side, which a full HAZUS-style consequence model would represent as reduced capacity rather than closure.
- There is no repair or clearance timeline. The "post" graph represents the worst moment immediately after the event, not the gradual reopening that follows as clearance crews work over hours and days.
- Origins and destinations are snapped to the pre- and post-graphs independently, so an origin near a blocked segment can register as "Always cut off" even where a short detour exists just outside the snap tolerance — always spot-check large deltas on the map, not the attribute table alone.
- Only debris-driven blockage is modelled. A road segment with genuine pavement or bridge damage from shaking, but no adjacent building debris, is invisible to this tool — it never asks whether the road surface itself survived the earthquake.

When to use vs. when NOT to use. Use it immediately after Seismic Debris Monte Carlo to answer "who loses shelter access, and by how much, under this scenario" — and run it across several Mw values (as the Interpretation Guide below recommends) to find the magnitude at which the network fails catastrophically rather than gradually. Do NOT use it as a substitute for structural assessment of road infrastructure itself (bridges, retaining walls, pavement) — its blockage model comes entirely from adjacent-building debris, not from any seismic hazard applied to the road asset; do not use it without first running the Seismic module on the same buildings/parcels/study-area inputs, since its debris-polygon input has no independent meaning outside that pairing.

The engine builds two graphs — pre (all edges) and post (edges not intersecting debris polygons) — and runs multi-source Dijkstra from all destinations on each. The engine is in `processing/accessibility/seismic_cascade.py`.

#### 3. Mathematical Formulation

$$\text{cas\_delta\_dist}(o) = \text{dist}_{\text{post}}(o) - \text{dist}_{\text{pre}}(o) \tag{1}$$ $$\text{cas\_status}(o) = \begin{cases} \text{Newly cut off} & \text{pre reachable, post not} \\ \text{Always cut off} & \text{neither reachable} \\ \text{Worse} & \text{post > 1.01 × pre} \\ \text{Same} & \text{both reachable, post ≤ 1.01 × pre} \\ \text{Recovered} & \text{pre not, post reachable (rare)} \end{cases} \tag{2}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Road network | Vector (Line) | Yes | Connected line layer.
Debris / impact polygons | Vector (Polygon) | Yes | Typically [04_Dynamic] from Monte Carlo Debris.
Origins | Vector (Point/Polygon) | Yes | Population/buildings to evaluate.
Destinations | Vector (Point/Polygon) | Yes | Shelters / safe areas.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`ROADS` | Vector (Line) | — | Road network.
`DEBRIS` | Vector (Polygon) | — | Debris polygons.
`ORIGINS` | Vector | — | Origins.
`DESTINATIONS` | Vector | — | Destinations.
`SNAP_TOLERANCE` | Double | 2.0 | Node-snapping tolerance.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`cas_pre_dist` | Double | Pre-hazard network distance to nearest destination (m). NULL if unreachable.
`cas_post_dist` | Double | Post-hazard network distance (m).
`cas_delta_dist` | Double | post − pre. NULL if either unreachable.
`cas_pre_reachable` | Integer | 1 = reachable pre-hazard.
`cas_post_reachable` | Integer | 1 = reachable post-hazard.
`cas_status` | String | Newly cut off / Always cut off / Worse / Same / Recovered.

#### 7. Symbolic Representation

Categorised by `cas_status`: Newly cut off = dark red, Always cut off = grey, Worse = orange, Same = green. Pipe into a QGIS layout with the pre and post maps side-by-side for a before/after atlas page.

#### 8. Interpretation Guide

Newly cut off: origins that were reachable pre-quake but are now isolated — the intervention priority class. Worse: still reachable but via a significantly longer detour (≥1% distance increase). Median pre- and post-hazard distances are printed to the log — the gap between them is the aggregate accessibility loss from the scenario. Run this algorithm for multiple Mw values (6.5, 7.0, 7.5) and compare the "Newly cut off" counts to identify the magnitude threshold where the network fails catastrophically.

#### Academic References

NIST. (2015). Community Resilience Planning Guide for Buildings and Infrastructure Systems. NIST Special Publication 1190.

### Optimal Shelter Siting (greedy maximal coverage)

Processing ID: `planx_urban_resilience:optimal_shelter_siting`

#### 1. Overview

Solves the Maximal Covering Location Problem (MCLP): from a set of candidate shelter sites, pick K so the population covered within a maximum walking distance is maximised. Uses greedy selection — at each step pick the candidate that adds the most uncovered demand. This is a 0.63-of-optimal approximation in the worst case (Cornuéjols et al., 1977) but is the de-facto practical choice. An optional existing-shelter layer pre-covers some demand so the K new picks fill the gaps.

#### 2. Theoretical Background

Academic lineage. Facility-location covering theory has two foundational formulations. Toregas, Swain, ReVelle & Bergman (1971) posed the Location Set Covering Problem (LSCP): find the minimum number of facilities needed to cover all demand within a service standard — a question that is frequently infeasible under a real municipal budget. Church & ReVelle (1974) reformulated the question into one that is always answerable: given a fixed number of facilities K, which K locations maximise the demand covered? This is the Maximal Covering Location Problem (MCLP) this tool solves directly. Exact MCLP is an integer program; Cornuéjols, Fisher & Nemhauser (1977) proved that for this class of submodular maximisation problems (coverage functions have diminishing marginal returns — visible directly in this tool's own per-step log, where each additional site typically adds less than the last), a simple greedy heuristic is guaranteed to reach at least (1 − 1/e) ≈ 0.63 of the true optimum, and in practice performs far better: Daskin (2013) reports greedy routinely achieving >95% of optimal coverage on real spatial facility-location instances.

Key assumptions.

- Coverage is a hard binary cutoff at MAX_DISTANCE — a demand unit at 499 m counts fully, one at 501 m counts zero. This is unlike the neighbouring Emergency Accessibility and Shelter Coverage tool's smooth distance-decay score, and can make results sensitive to the exact MAX_DISTANCE choice near that boundary.
- Distance is Euclidean, not network-based — a straight line across a river counts as "covered" even where no bridge exists. Pre-routing demand through Network Emergency Accessibility and using the resulting network distance as a coverage-eligibility field is recommended wherever real routing barriers are present.
- The greedy algorithm is a fast, near-optimal heuristic, not an exact solver — for small candidate/demand sets an exact integer-programming MCLP solve could find a marginally better K-site combination, but greedy's simplicity, speed, and near-optimality make it the standard practical choice in the facility-location literature (Daskin, 2013).

When to use vs. when NOT to use. Use it for capital-budget-constrained "where do we add K new shelters" decisions, especially paired with an EXISTING layer to find the true remaining coverage gap rather than re-covering demand existing shelters already serve. Do NOT use it for facility relocation decisions — MCLP only ever adds sites, it never proposes moving or removing an existing one; a different formulation (a relocation or p-median variant) is needed for that. Do not use it where distance alone cannot stand in for real network access without first routing demand through the network-based sibling tool, per the Euclidean-distance caveat above.

The engine uses Euclidean distance for speed. The engine is in `processing/accessibility/optimal_shelter_siting.py`.

#### 3. Mathematical Formulation

$$\text{candidate}^* = \arg\max_{c \in C \setminus S} \sum_{d \in D : \text{dist}(c,d) \le r_{\max}, \text{not covered}(d)} \text{pop}(d) \tag{1}$$

where \(C\) = candidate set, \(S\) = already-selected set, \(D\) = demand units, \(r_{\max}\) = coverage radius. Repeat K times.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Candidate sites | Vector (Point/Polygon) | Yes | Potential new shelter locations.
Demand units | Vector (Point/Polygon) | Yes | Must have a population field.
Existing shelters | Vector (Point/Polygon) | No | Pre-covers demand before greedy selection.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`CANDIDATES` | Vector | — | Candidate shelter sites.
`DEMAND` | Vector | — | Demand units with population.
`POP_FIELD` | Field (Numeric) | — | Population field.
`MAX_DISTANCE` | Double | 500.0 | Maximum coverage distance (map units).
`NUM_SITES` | Integer | 5 | Number of sites to select (K).
`EXISTING` | Vector | (optional) | Existing shelter layer.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`oss_selected` | Integer | 1 if selected, else 0.
`oss_rank` | Integer | 1..K selection order. NULL if not selected.
`oss_pop_added` | Double | Additional population covered when this site was selected.
`oss_pop_total_cum` | Double | Cumulative covered population after this pick.

#### 7. Symbolic Representation

Categorised by `oss_selected`: selected = green star markers (size scaled by rank), not selected = small grey circles. Per-step coverage curve is printed to the log — export this to chart cumulative coverage vs K.

#### 8. Interpretation Guide

The per-step log shows diminishing returns — the first 2–3 sites typically cover 60–80% of demand; additional sites add progressively less. Use this curve to justify the K budget. The "top 10 uncovered demand units" log entry identifies the hardest-to-reach populations — these may need mobile response rather than fixed shelters.

#### Academic References

Toregas, C., Swain, R., ReVelle, C. & Bergman, L. (1971). "The location of emergency service facilities." Operations Research, 19(6), 1363–1373. DOI: 10.1287/opre.19.6.1363

Church, R.L. & ReVelle, C.S. (1974). "The maximal covering location problem." Papers of the Regional Science Association, 32, 101–118.

Cornuéjols, G., Fisher, M.L. & Nemhauser, G.L. (1977). "Location of bank accounts to optimize float." Management Science, 23(8), 789–810.

Daskin, M.S. (2013). Network and Discrete Location: Models, Algorithms, and Applications. 2nd ed., Wiley.

### Evacuation Shelter Arrival Summary

Processing ID: `planx_urban_resilience:evacuation_shelter_arrival_summary`

#### 1. Overview

Post-processes the Evacuation Time Simulation origin outputs into a per-shelter arrival table with optional per-timestep/minute breakdown and a standalone HTML report. The core operational question it answers: "Which shelters fill first, and which receive arrivals late into the evacuation window?" The algorithm bins each origin's `evac_min` (total evacuation time, including queue delay when timestep mode is active) into timestep-width intervals and aggregates arrival counts per shelter per bin. The output HTML report includes a stacked-bar timeline visualisation showing the arrival profile of each shelter.

#### 2. Theoretical Background

Cumulative arrival curve analysis is a standard tool in emergency operations planning (FEMA, 2019; NIST, 2015). The key metrics — P50 arrival time, P95 arrival time, and time-to-50%-capacity — operationalise the concepts of shelter service speed and catchment balance. A shelter that reaches 90% capacity within the first 10 minutes of an evacuation has a compressed arrival profile: its catchment population is both nearby and unimpeded. A shelter that reaches only 30% capacity after 40 minutes has either a distant catchment, severe congestion on access routes, or both. The arrival-curve shape — steep initial ramp, long tail, bimodal — carries diagnostic information about the road network topology and shelter distribution that a single average access-time figure cannot capture.

Key assumptions.

- This tool is a pure post-processor — it re-bins and aggregates `evac_min` values already computed upstream, adding no new routing or capacity logic of its own; every assumption and limitation of Evacuation Time Simulation (perfect compliance, no shadow evacuation, hard capacity caps) carries through unchanged.
- TIMESTEP_MINUTES here should match the bin width used in the upstream simulation for the P50/P95 figures to be operationally meaningful — a mismatched bin width still produces a chart, but one whose bins don't correspond to the simulation's own decision points.
- A shelter whose final `cumulative_pct` falls short of 100% signals unassigned or queue-stalled origins in the PARENT simulation, not an error in this summarising step — always trace such gaps back to the simulation's own `qa_flag` field rather than debugging this tool in isolation.

When to use vs. when NOT to use. Use it immediately after any Evacuation Time Simulation run, especially in timestep-queue mode, to translate the raw per-origin table into the shelter-level operational question planners actually ask: which shelters are overwhelmed, and when. Do NOT use it on an instant-assignment run (TIMESTEP_MINUTES = 0 upstream) expecting a meaningful temporal arrival curve — with no queueing, every assigned origin's `evac_min` reduces to pure travel time and the "arrival profile" collapses to a simple travel-time histogram, losing the congestion story this tool is built to surface.

The engine is in `processing/reporting/evacuation_arrival_summary.py`.

#### 3. Mathematical Formulation

$$\text{bin}(t) = \left\lfloor \frac{\text{evac\_min}}{\Delta} \right\rfloor \tag{1}$$ $$\text{arrivals}(s, b) = \sum_{o \in \text{origins}} [\text{shelter}(o) = s \land \text{bin}(t_o) = b] \tag{2}$$ $$\text{cumulative\_pct}(s, t) = 100 \cdot \frac{\sum_{b=0}^{t} \text{arrivals}(s, b)}{\sum_{b=0}^{B} \text{arrivals}(s, b)} \tag{3}$$

where \(\Delta\) is the timestep interval in minutes, \(t_o =\) `evac_min` for origin \(o\), \(s\) is the assigned shelter, and \(B\) is the maximum timestep bin. The summary log also prints P50 and P95 cumulative-percentage arrival times per shelter — the evacuation-operations equivalents of the P50/P95 metrics from the parent simulation.

$$\text{P50}(s) = \min \left\{ t : \text{cumulative\_pct}(s, t) \ge 50 \right\} \tag{4}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Evacuation origins | Vector (Point) | Yes | Output of Evacuation Time Simulation. Must have `evac_shelter`, `evac_min`, `evac_pop` fields.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`EVAC_ORIGINS` | Vector (Point) | — | Evacuation Time Simulation origin output layer.
`SHELTER_FIELD` | Field (String/ID) | evac_shelter | Field identifying the assigned shelter per origin.
`TIME_FIELD` | Field (Numeric) | evac_min | Total evacuation time per origin (minutes).
`POP_FIELD` | Field (Numeric) | evac_pop | Population assigned to each origin.
`TIMESTEP_MINUTES` | Double | 5.0 | Bin width for arrival-count aggregation. Should match the timestep used in Evacuation Time Simulation.
`OUTPUT_HTML` | File | — | Path for the HTML arrival-curve report.
`OUTPUT_TABLE` | Vector (NoGeometry) | — | Per-shelter per-timestep long-format table for further analysis.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Arrival table (QGIS sink) | `shelter_id`, `timestep_bin`, `arrival_count`, `arrival_pop`, `cumulative_pct` | Long-format: one row per shelter per timestep bin. `cumulative_pct` is the percentage of that shelter's total assigned population that has arrived by the end of this bin.
HTML report | — | Standalone HTML with arrival-curve chart per shelter and summary statistics table.

#### 7. Symbolic Representation

The HTML report uses a stacked-bar chart where each shelter is one bar subdivided by timestep-bin colour (early bins = dark green, late bins = orange/red). Shelters with long red tails are the congestion-affected ones needing either capacity expansion or road-network improvements. In the QGIS table, graduate by `cumulative_pct` at the critical timestep (e.g., 30 minutes) with a RdYlGn (reversed) ramp.

#### 8. Interpretation Guide

Time-to-50%-capacity < 5 minutes: the shelter's catchment is highly localised and uncongested — an efficient assignment. Time-to-90%-capacity > 40 minutes: the shelter's assigned population is taking too long to arrive — either the catchment is too large (geographically distant origins), too congested (bottleneck edges), or both. Cross-reference with the edge-load output from Evacuation Time Simulation: are the edges leading to this shelter at >100% congestion? Shelters with final cumulative_pct < 100%: some assigned origins have `evac_min = ∞` (unassigned or stalled in the queue) — check the parent simulation's `qa_flag` field. Bimodal arrival curves: two distinct peaks suggest two distinct catchment sub-populations — one nearby (first peak) and one distant (second peak); consider splitting the distant catchment to a closer shelter. Use the arrival-summary table to feed the per-shelter P95 arrival time into Optimal Shelter Siting as a coverage-gap diagnostic: shelters with high P95 times are candidates for additional nearby sites.

Workflow integration. Run Evacuation Time Simulation first. Feed its origin output here. Use the HTML report in a planning workshop to identify the 2–3 worst-performing shelters. Then return to the simulation: adjust `ASSIGN_ORDER` or add candidate shelters via Optimal Shelter Siting, re-run, and compare arrival profiles.

#### Academic References

FEMA. (2019). CPG 101: Developing and Maintaining Emergency Operations Plans, Version 3.0. Federal Emergency Management Agency, Washington, DC. DOI: 10.1007/978-3-030-04624-8_5

NIST. (2015). Community Resilience Planning Guide for Buildings and Infrastructure Systems. NIST Special Publication 1190. DOI: 10.6028/NIST.SP.1190

Yperman, I. (2007). The Link Transmission Model for Dynamic Network Loading. PhD thesis, KU Leuven.

Daganzo, C.F. (1994). "The cell transmission model: a dynamic representation of highway traffic consistent with the hydrodynamic theory." Transportation Research Part B, 28(4), 269–287. DOI: 10.1016/0191-2615(94)90002-7

Church, R.L. & ReVelle, C.S. (1974). "The maximal covering location problem." Papers of the Regional Science Association, 32, 101–118. DOI: 10.1007/BF01942293

Daskin, M.S. (2013). Network and Discrete Location: Models, Algorithms, and Applications. 2nd ed., Wiley. DOI: 10.1002/9781118033883

Cova, T.J. & Johnson, J.P. (2003). "A network flow model for lane-based evacuation routing." Transportation Research Part A, 37(7), 579–604. DOI: 10.1016/S0965-8564(03)00007-7

Southworth, F. (1991). "Regional evacuation modeling: a state-of-the-art review." Oak Ridge National Laboratory, ORNL/TM-11740.

Murray-Tuite, P. & Wolshon, B. (2013). "Evacuation transportation modeling: an overview of research, development, and practice." Transportation Research Part C, 27, 25–45. DOI: 10.1016/j.trc.2012.11.005

Stepanov, A. & Smith, J.M. (2009). "Multi-objective evacuation routing in transportation networks." European Journal of Operational Research, 198(2), 435–446. DOI: 10.1016/j.ejor.2008.09.025

## 6. Air: Exposure Screening

The Air group provides a planning-support exposure grid combining road proximity, industrial/emission-source proximity, sensitive-receptor density, and green-buffer mitigation. It is a spatial screening index, not an atmospheric dispersion model.

### Air Quality / Urban Exposure (screening)

Processing ID: `planx_urban_resilience:air_quality_exposure_screening`

#### 1. Overview

Scores each grid cell for air-quality exposure using four additive components: road proximity risk (distance to major traffic corridors), emission-source proximity (industrial/point sources), sensitive-receptor density (schools, hospitals, housing clusters), and green-buffer mitigation (tree belts and green patches that reduce exposure). The composite subtracts the green-buffer term — green infrastructure reduces the score.

#### 2. Theoretical Background

Academic lineage. Land-use regression (LUR) grew out of the 1990s realisation that intra-urban air pollution varies far more over tens of metres than city-wide monitoring networks could ever capture — Briggs et al. (1997) showed that a handful of GIS-derivable proximity variables (distance to major roads, traffic volume, land-use mix) could predict a large share of that fine-grained NO₂ variation without needing a dispersion model, founding a research tradition later scaled to continental level by projects such as ESCAPE (Hoek et al., 2008, and successors). This tool follows the LUR logic directly: proximity to roads and point sources stands in for the regression coefficients a full LUR study would fit statistically. The green-buffer mitigation term is a distinct, deliberately subtractive component grounded in Nowak, Crane & Stevens' (2006) US Forest Service study, which quantified urban tree canopy removing measurable particulate mass from ambient air. That subtractive design is not universally supported, however: Vos, Maiheu, Vankerkom & Janssen (2013) used CFD modelling of street canyons to show that dense roadside vegetation can reduce local ventilation and increase street-level pollutant concentration under certain canopy-porosity and canyon-geometry conditions — the mitigation effect this tool assumes is a canopy-scale, open-area finding (Nowak et al.), and does not automatically transfer to enclosed street-canyon plantings.

Key assumptions.

- This is a distance-decay proximity model, not an emissions-dispersion model — it has no wind direction, wind speed, atmospheric stability class, or terrain-channelling term, none of which a Gaussian-plume or CFD dispersion model would omit.
- Green-buffer mitigation is a flat area-share subtraction, independent of species, canopy density, deposition velocity, or canyon geometry — per the Vos et al. (2013) finding above, a dense planting in a narrow street canyon could plausibly worsen, not improve, street-level exposure, which this model cannot represent.
- Emission "sources" are scored by proximity alone with no source-strength weighting — a small workshop and a large refinery at the same distance score identically.

When to use vs. when NOT to use. Use it for first-pass environmental-justice screening (which sensitive receptors sit closest to traffic and industrial corridors), for siting new green buffers for co-benefit prioritisation in open, non-canyon contexts, and for comparing relative burden across a study area. Do NOT use it for regulatory air-quality compliance determination, for health-risk quantification (which needs modelled or monitored pollutant concentrations from AERMOD, CALPUFF, or an equivalent dispersion model), or to justify dense roadside tree planting as a pollution-mitigation measure in an enclosed street canyon without first checking the canyon-ventilation literature.

The engine is in `processing/air/air_quality_exposure.py`.

#### 3. Mathematical Formulation

$$\text{air\_score} = \frac{w_r \cdot R + w_s \cdot S + w_e \cdot E}{\max(w_r + w_s + w_e, 0.0001)} - w_g \cdot G \cdot 100 \tag{1}$$

where \(R\) = road proximity score (0–100), \(S\) = source proximity score, \(E\) = sensitive-receptor score (count × 25), \(G\) = green-buffer area share, and the final subtraction can reduce but not zero the score.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Study area | Vector (Polygon) | Yes | Grid extent.
Major roads / traffic | Vector (Line) | Yes | Traffic corridors.
Emission sources | Vector (Point/Polygon) | No | Industrial / point sources.
Sensitive receptors | Vector (Point/Polygon) | No | Schools, hospitals, housing.
Green buffers | Vector (Polygon) | No | Tree belts, green screens.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`STUDY_AREA` | Vector (Polygon) | — | Study area.
`ROADS` | Vector (Line) | — | Major roads.
`EMISSION_SOURCES` | Vector | (optional) | Emission sources.
`SENSITIVE` | Vector | (optional) | Sensitive receptors.
`GREEN_BUFFERS` | Vector (Polygon) | (optional) | Green buffers.
`CELL_SIZE` | Double | 100.0 | Grid cell size.
`ROAD_DISTANCE` | Double | 300.0 | Road influence distance (m).
`SOURCE_DISTANCE` | Double | 600.0 | Source influence distance (m).
`ROAD_WEIGHT` | Double | 0.35 | Road proximity weight.
`SOURCE_WEIGHT` | Double | 0.25 | Source proximity weight.
`SENSITIVE_WEIGHT` | Double | 0.25 | Sensitive receptor weight.
`BUFFER_WEIGHT` | Double | 0.15 | Green buffer mitigation weight.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Air exposure grid | `cell_id`, `road_score`, `source_score`, `sens_count`, `green_share`, `air_score`, `risk_class` | Full grid.
Exposed sensitive receptors | `asset_type`, `air_score`, `risk_class` | Sensitive receptors in high-exposure cells (≥55).

#### 7. Symbolic Representation

Graduated by `air_score`, Purples ramp, 5-class natural breaks. Exposed receptors: red markers. Overlay with the heat-risk grid to find cells that are both hot AND polluted — double environmental burden.

#### 8. Interpretation Guide

air_score ≥ 75: cells within 50 m of a major road, downwind of an industrial source, and lacking green buffers — the environmental-justice hotspots. Cross-reference with Social Vulnerability Index to test the double-burden hypothesis. Feed exposed receptors into Recommended Actions Report for curated intervention checklists.

#### Academic References

Briggs, D.J. et al. (1997). "Mapping urban air pollution using GIS: a regression-based approach." International Journal of Geographical Information Science, 11(7), 699–718.

Nowak, D.J., Crane, D.E. & Stevens, J.C. (2006). "Air pollution removal by urban trees and shrubs in the United States." Urban Forestry & Urban Greening, 4(3–4), 115–123.

Vos, P.E.J., Maiheu, B., Vankerkom, J. & Janssen, S. (2013). "Improving local air quality in cities: to tree or not to tree?" Environmental Pollution, 183, 113–122. DOI: 10.1016/j.envpol.2012.10.021

## 7. Drought: Green Infrastructure Stress

The Drought group ranks parks, green patches, and tree-canopy polygons by drought and thermal stress sensitivity using impervious context, distance to water, patch size, and tree density. It is an ecological planning-support index, not a plant-physiology or irrigation model.

### Drought and Green Infrastructure Stress

Processing ID: `planx_urban_resilience:green_infrastructure_drought_stress`

#### 1. Overview

For each green-infrastructure polygon, computes four stress components: impervious context (share of impervious surface in a buffer around the patch), water distance (distance to nearest blue infrastructure), patch size sensitivity (smaller patches = more vulnerable to edge effects and drought), and tree density deficit (sparse tree cover within the patch). The weighted composite is a 0–100 stress score. Patches scoring ≥55 are written to a priority-intervention layer.

#### 2. Theoretical Background

Academic lineage. MacArthur & Wilson's (1967) theory of island biogeography — developed to explain species counts on literal oceanic islands — was adapted decades later by landscape and urban ecologists to any habitat "island" surrounded by an inhospitable matrix, including a city park surrounded by pavement: smaller patches have a higher edge-to-core ratio, so a larger share of their area is exposed to the microclimate of the surrounding matrix rather than buffered by their own core. In an urban setting that surrounding matrix is impervious surface, and McDonald et al. (2008) documented that impervious cover within roughly 100–200 m of a green patch measurably raises its local temperature and moisture stress — the mechanism this tool's `imperv_ctx` context-buffer term screens for directly. Nowak et al.'s (2008) i-Tree Eco field protocol supplied the practical convention for sampling tree density used as this tool's fourth stress component.

Key assumptions.

- This is an ecological screening index built from geometry and land-cover context alone — there is no soil-moisture measurement, no actual evapotranspiration or precipitation-deficit data, and no species-specific drought tolerance.
- Patch-size sensitivity is a min-max rank relative to the supplied green-infrastructure layer — adding one very large park to the input set re-ranks every other patch's `size_score`, even though nothing about those other patches changed physically.
- Tree count uses a flat linear penalty (100 − count × 10, floored at 0) with no distinction of canopy size, species, or age; a layer with no `TREES` input at all leaves every patch's tree-density term at its worst-case value (100) unless the tree weight is zeroed.

When to use vs. when NOT to use. Use it to prioritise irrigation and tree-planting budget across many patches at once, to flag small isolated patches most exposed to edge-effect stress, and for teaching landscape-ecology patch-context concepts with a fully transparent formula. Do NOT use it to diagnose actual plant water stress (that needs soil-moisture sensors or a remote-sensing NDVI/NDWI time series), for species-selection decisions, or for precise irrigation scheduling — this tool ranks relative vulnerability across a portfolio of patches, it does not measure the physiological state of any one of them.

The engine is in `processing/drought/green_infrastructure_stress.py`.

#### 3. Mathematical Formulation

$$\text{stress\_score} = \frac{w_i \cdot I + w_w \cdot W + w_s \cdot S + w_t \cdot T}{\max(w_i + w_w + w_s + w_t, 0.0001)} \tag{1}$$ $$S = 100\left(1 - \frac{A - A_{\min}}{A_{\max} - A_{\min}}\right) \quad \text{(small-patch penalty)} \tag{2}$$ $$T = \text{clamp}(100 - \text{tree\_count} \times 10) \quad \text{(low-tree-density penalty)} \tag{3}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Green infrastructure / parks | Vector (Polygon) | Yes | Patches to evaluate.
Study area | Vector (Polygon) | No | Optional filter — patches outside are skipped.
Impervious surfaces | Vector (Polygon) | No | Roads / hardscape.
Water / blue infrastructure | Vector (Line/Polygon) | No | Lakes, streams.
Tree points | Vector (Point) | No | Individual tree locations.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`GREEN` | Vector (Polygon) | — | Green infrastructure patches.
`STUDY_AREA` | Vector (Polygon) | (optional) | Study-area filter.
`IMPERVIOUS` | Vector (Polygon) | (optional) | Impervious surfaces.
`WATER` | Vector | (optional) | Water / blue infrastructure.
`TREES` | Vector (Point) | (optional) | Tree points.
`MAX_WATER_DISTANCE` | Double | 500.0 | Maximum water-access distance for scoring.
`IMPERVIOUS_WEIGHT` | Double | 0.35 | Impervious context weight.
`WATER_WEIGHT` | Double | 0.25 | Water distance weight.
`SIZE_WEIGHT` | Double | 0.25 | Small-patch sensitivity weight.
`TREE_WEIGHT` | Double | 0.15 | Low tree density weight.

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
Green infrastructure stress index | `imperv_ctx`, `water_dist`, `size_score`, `tree_count`, `stress_score`, `stress_class` | All patches with component and composite scores.
High-priority patches | (same schema) | Patches with stress_score ≥ 55 — the intervention shortlist.

#### 7. Symbolic Representation

Graduated by `stress_score`, YlOrBr ramp, 5-class natural breaks. High-priority patches: darker outline, 0.8 mm stroke. Overlay with the heat-risk grid to identify patches that are both stressed AND in hot areas — these need both irrigation and shade-tree planting.

#### 8. Interpretation Guide

stress_score ≥ 75: small, isolated patches surrounded by impervious surfaces, far from water, with sparse trees — acute drought vulnerability. tree_count = 0: the patch has no tree points at all — a planting opportunity. Feed high-priority patches into Cost-Benefit Analyzer to rank irrigation and tree-planting interventions by cost-effectiveness. Cross-reference with Population-Weighted Exposure (using nearby population polygons) to quantify the human population dependent on each stressed green space.

#### Academic References

MacArthur, R.H. & Wilson, E.O. (1967). The Theory of Island Biogeography. Princeton University Press.

McDonald, R.I. et al. (2008). "The implications of current and future urbanization for global protected areas and biodiversity conservation." Biological Conservation, 141(6), 1695–1703.

Nowak, D.J. et al. (2008). "A ground-based method of assessing urban forest structure and ecosystem services." Arboriculture & Urban Forestry, 34(6), 347–358.

## 8. Synthesis: Multi-Hazard, Adaptation & Priority

The Synthesis group is the intellectual core of the suite — 16 algorithms that compose, compare, prioritise, and stress-test resilience scores. They fall into four sub-themes: spatial joins (Join Scores to Planning Units brings hazard-layer outputs onto a common polygon layer), composite indices (Climate Adaptation Priority, Multi-Hazard Composite, Recovery Capacity, Hazard Frequency), equity and climate adjustment (Equity-Adjusted Priority, Climate Projection Overlay), and prioritisation (Risk-Recovery Matrix, Scenario Sensitivity, Population-Weighted Exposure, Critical Infrastructure Exposure, Vulnerability Surface IDW).

### Join Hazard Scores to Planning Units

Processing ID: `planx_urban_resilience:join_scores_to_planning_units`

#### 1. Overview

Spatially joins up to five hazard/score layers onto planning-unit polygons by taking the maximum score among intersecting features. This is the standard "collect and align" step after running hazard modules: join the Heat, Flood, Social, Air, and Drought score fields onto a common neighbourhood or grid-cell layer, then feed the result into Multi-Hazard Composite or Adaptation Priority Synthesis. The output planning units preserve all original attributes and accumulate new numeric score columns ready for composite-index construction.

#### 2. Theoretical Background

Academic lineage. The max-intersection spatial join is a conservative aggregation strategy grounded in the precautionary principle of environmental risk assessment (Kriebel et al., 2001; Stirling, 2007). When a planning unit intersects multiple source features with different scores, the maximum is taken — if any part of the unit is exposed to a high score, the whole unit inherits that score. This avoids the dilution problem of areal-weighted averaging, where a small high-hazard sliver (e.g., a flood-prone stream corridor crossing a large census tract) is averaged away by the tract's predominantly low-hazard area (Gotway & Young, 2002) — an instance of the wider Modifiable Areal Unit Problem (MAUP, Openshaw, 1984; Fotheringham & Wong, 1991), where the choice of aggregation unit changes the apparent result. The cost is potential overestimation: a unit with 90% low-risk area and 10% high-risk area is classified entirely at the high-risk level.

Key assumptions.

- Maximum-intersection is a deliberate one-sided choice — it never underestimates a unit's exposure, but it can overestimate a large unit's overall risk based on a small intersecting sliver.
- Each of the five source layers is joined independently; there is no cross-layer normalisation, so a "80" from one hazard module and a "80" from another are only comparable if both modules use the same 0–100 scale convention (all of this suite's hazard tools do, by design).
- A NULL output means "no intersecting feature," not "zero risk" — the two are easy to conflate in a subsequent composite calculation if NULLs are not handled explicitly.

When to use vs. when NOT to use. Use it as the standard "collect and align" step immediately after running any combination of hazard modules, before Multi-Hazard Composite Index or Climate Adaptation Priority Synthesis. Do NOT use it where an areal-weighted average is the more defensible choice (e.g., aggregating a fine-grained hazard grid onto large administrative units for a headline statistic) — pre-process with zonal statistics in QGIS or GRASS (Flowerdew & Green, 1992, review areal-interpolation alternatives) instead of relying on the max-intersection default.

The engine is in `processing/synthesis/join_scores_to_units.py`.

#### 3. Mathematical Formulation

$$\text{score}(u) = \max\left(\left\{\text{clamp}(f_s) \mid f \in S, \text{geom}(f) \cap \text{geom}(u) \neq \emptyset \right\} \cup \{0\}\right) \tag{1}$$

where \(u\) is a planning unit, \(S\) is the joined score source layer, \(f_s\) is the numeric score field value on feature \(f\), and \(\text{clamp}\) bounds the result to \([0, 100]\). If no source feature intersects the unit (or all intersecting features have NULL scores), the output field is NULL for that unit. The max operator is applied independently per source layer; no cross-layer normalisation is performed.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector (Polygon) | Yes | Target layer for joins. All original attributes preserved.
Score source layers (1–5) | Vector (Point/Polygon/Line) | At least 1 | Layers with a numeric 0–100 score field. Lines and points are supported; intersection is tested against the full geometry.

Performance note. The join iterates every source feature for every planning unit — complexity is O(units × features). For large datasets (>10,000 features per source), pre-process source layers to clip to the planning-unit extent, or use a spatial index layer join in the QGIS toolbox.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`UNITS` | Vector (Polygon) | — | Planning-unit polygons.
`SOURCE1`–`SOURCE5` | Vector (Point/Polygon/Line) | (optional) | Up to 5 score layers to join.
`FIELD1`–`FIELD5` | Field (Numeric) | (optional) | Score field on each source layer.
`OUT1`–`OUT5` | String | score_N | Output field name (e.g. heat_score). Defaults to score_1, score_2, etc.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`{OUT1}`–`{OUT5}` | Double | Maximum intersecting score per source layer. NULL if no feature intersects or all scores are NULL.
(all original fields) | (preserved) | Planning-unit attributes are passed through unchanged.

#### 7. Symbolic Representation

After joining, the planning-unit layer carries multiple score columns. Visualise each score independently with a graduated renderer before building composites — this reveals spatial concordance/discordance between hazard dimensions. Recommended: Apply Resilience Symbology with preset "Risk score 0–100" for each joined column.

#### 8. Interpretation Guide

NULL score fields: the planning unit's geometry does not intersect any feature in that source layer. For grid-based source layers (e.g., Heat Risk Grid), this typically means the unit lies outside the study-area extent used when generating the grid. For vector-based sources, check that both layers share the same CRS and that source features cover the full extent of the planning units. All scores = 0 on the same feature: the source layers may be using a neutral default for missing data — verify the source field names are correct. Cross-score consistency check: sort planning units by each joined score and compare the top-10 lists. If the same units dominate all hazards, the study area has a concentrated multi-hazard problem; if the lists are disjoint, the hazards are spatially independent and the composite must respect that separation.

#### Academic References

Kriebel, D., Tickner, J., Epstein, P., Lemons, J., Levins, R., Loechler, E.L., Quinn, M., Rudel, R., Schettler, T. & Stoto, M. (2001). "The precautionary principle in environmental science." Environmental Health Perspectives, 109(9), 871–876. DOI: 10.1289/ehp.01109871

Gotway, C.A. & Young, L.J. (2002). "Combining incompatible spatial data." Journal of the American Statistical Association, 97(458), 632–648. DOI: 10.1198/016214502760047140

Stirling, A. (2007). "Risk, precaution and science: towards a more constructive policy debate." EMBO Reports, 8(4), 309–315. DOI: 10.1038/sj.embor.7400953

Goodchild, M.F. (2011). "Scale in GIS: an overview." Geomorphology, 130(1–2), 5–9. DOI: 10.1016/j.geomorph.2010.10.004

Openshaw, S. (1984). "The modifiable areal unit problem." Concepts and Techniques in Modern Geography, 38. Geo Books, Norwich.

Fotheringham, A.S. & Wong, D.W.S. (1991). "The modifiable areal unit problem in multivariate statistical analysis." Environment and Planning A, 23(7), 1025–1044. DOI: 10.1068/a231025

Flowerdew, R. & Green, M. (1992). "Developments in areal interpolation methods and GIS." The Annals of Regional Science, 26(1), 67–78. DOI: 10.1007/BF01581481

Cutter, S.L., Boruff, B.J. & Shirley, W.L. (2003). "Social vulnerability to environmental hazards." Social Science Quarterly, 84(2), 242–261. DOI: 10.1111/1540-6237.8402002

### Climate Adaptation Priority Synthesis

Processing ID: `planx_urban_resilience:climate_adaptation_priority_synthesis`

#### 1. Overview

Combines up to six 0–100 hazard/deficit score fields already joined onto a common polygon layer into a single weighted `adapt_score`. Outputs the composite, a priority class, the top three driving hazards (with their scores), and a list of missing fields.

#### 2. Theoretical Background

Academic lineage. This is a linear weighted-sum multi-criteria decision analysis (MCDA) model — the simplest and most transparent form of the additive-value family that dominates practical, defensible-to-a-planning-board index construction, and which follows the same aggregation logic as the ISO 14044 lifecycle impact assessment convention (weighted-sum characterisation across impact categories). Weight defaults reflect a balanced-all-hazards prior: heat 0.20, flood 0.20, social 0.20, air 0.15, access 0.15, drought 0.10 — chosen so no single hazard dominates by construction, leaving the weights as the explicit, auditable policy choice rather than a black-box output.

Key assumptions.

- Linear weighted-sum assumes full compensability — a very low flood score can offset a very high heat score. Multi-criteria methods that avoid this (e.g. outranking methods like ELECTRE/PROMETHEE) exist but trade transparency for complexity; this suite deliberately stays with the simpler, auditable form throughout.
- Missing fields are excluded and their weight redistributed across the present fields — a unit with only 2 of 6 hazard fields populated still gets a 0–100 `adapt_score`, but it is not comparable in reliability to a unit with all 6 fields present (check `missing`).
- All six input fields must already be on a common 0–100 scale — this tool performs no independent normalisation of its own.

When to use vs. when NOT to use. Use it as the suite's default single-number adaptation-priority ranking once hazard scores are joined via Join Hazard Scores to Planning Units. Do NOT use it where the breakdown of which hazard drives the score matters more than the single number — use Multi-Hazard Composite Index instead, which keeps the diagnostic breakdown (dominant hazard, diversity, data gaps) visible rather than collapsing it away.

The engine is in `processing/synthesis/adaptation_priority_synthesis.py`.

#### 3. Mathematical Formulation

$$\text{adapt\_score} = \frac{\sum_{k} w_k \cdot \text{clamp}(s_k)}{\sum_{k : w_k > 0, f_k \neq \emptyset} w_k} \tag{1}$$

Missing fields are excluded; their weight is redistributed across present fields.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector (Polygon) | Yes | Typically the output of Join Hazard Scores to Planning Units.
Hazard score fields (up to 6) | Numeric field | At least 1 | Heat / flood / social / air / access / drought, each 0–100. Any subset may be supplied.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`UNITS` | Vector (Polygon) | — | Joined risk layer.
`HEAT_FIELD`…`DROUGHT_FIELD` | Field (Numeric) | (optional) | Six hazard score fields.
`WEIGHT_HEAT` | Double | 0.20 | Heat weight.
`WEIGHT_FLOOD` | Double | 0.20 | Flood weight.
`WEIGHT_SOCIAL` | Double | 0.20 | Social vulnerability weight.
`WEIGHT_AIR` | Double | 0.15 | Air exposure weight.
`WEIGHT_ACCESS` | Double | 0.15 | Accessibility deficit weight.
`WEIGHT_DROUGHT` | Double | 0.10 | Green infrastructure stress weight.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`adapt_score` | Double | Weighted composite 0–100.
`adapt_class` | String | Immediate (≥75) / High (55–74) / Medium (35–54) / Monitor (<35).
`drivers` | String | Top 3 contributing hazard names with scores, e.g. "flood:82.3, heat:67.1, social:54.0".
`missing` | String | Comma-separated list of hazard fields with zero weight or absent from the layer.

#### 7. Symbolic Representation

Graduated by `adapt_score`, RdYlGn ramp (reversed), 5-class natural breaks. Add labels showing the `drivers` field for the top 10 units.

#### 8. Interpretation Guide

adapt_class = Immediate: units where multiple hazards score ≥75 simultaneously. drivers: tells you which hazard(s) dominate — "flood:92.1, heat:45.0" means the unit is a flood priority, not a heat priority. Use this to assign hazard-specific interventions. Feed into Equity-Adjusted Priority to re-rank by vulnerability. Feed into Risk-Recovery Priority Matrix with `rc_score` to find high-hazard + low-recovery gaps.

### Multi-Hazard Composite Index

Processing ID: `planx_urban_resilience:multi_hazard_composite_index`

#### 1. Overview

Combines N hazard score fields (each 0–100) per planning unit into a single composite with diagnostic transparency: which hazard dominates?, how diverse is the stress profile?, and how many data gaps exist? The Adaptation Priority Synthesis collapses everything to one score; this algorithm keeps the breakdown visible.

#### 2. Theoretical Background

Academic lineage. The multi-hazard index draws on the IPCC AR5 "reasons for concern" framework, which established that climate risks must be assessed jointly rather than hazard-by-hazard, since a place facing three moderate stresses can be more fragile than one facing a single severe stress. Shannon's (1948) information-theoretic entropy measure — originally developed for quantifying uncertainty in a communication signal — is repurposed here as a diversity index: it transforms the hazard profile into a 0–100 measure where 0 means a single hazard explains everything (concentrated risk, one clear intervention target) and 100 means all hazards contribute equally (diffuse, multi-stressed risk, no single fix suffices). This diversity axis is exactly what a single weighted-sum score (as in Climate Adaptation Priority Synthesis) discards — two units can share an identical `mh_score` while having completely different risk profiles, one concentrated and one diffuse.

Key assumptions.

- Entropy is computed on the relative weighted contribution of each hazard (\(p_i\)), not on the raw scores — a unit where every hazard field is null except one still computes a diversity of 0 (correctly, single-hazard), but a unit with mostly-low scores can still show high diversity if what little risk exists is spread evenly.
- \(\ln N\) normalises by the number of fields supplied in this run, so `mh_diversity` is only comparable across units analysed with the same field count — adding a 5th hazard field to one subset of units and not another breaks direct diversity comparison between them.
- Like Adaptation Priority Synthesis, this is a linear weighted-sum for the score itself — full compensability between hazards applies to `mh_score`, even though the diversity index is designed to flag when that compensability is misleading.

When to use vs. when NOT to use. Use it whenever the diagnostic breakdown — which hazard dominates, how diffuse the stress is, how much data is missing — matters as much as the headline number, and especially before deciding between a single-hazard versus a multi-hazard intervention strategy. Do NOT use it where a single, simple composite is all that is needed for reporting — Climate Adaptation Priority Synthesis is the leaner tool for that; do not compare `mh_diversity` values computed from different numbers of input hazard fields without accounting for the \(\ln N\) normalisation difference.

The engine is in `processing/synthesis/multi_hazard_composite.py`.

#### 3. Mathematical Formulation

$$\text{mh\_score} = \frac{\sum_i w_i \cdot s_i}{\sum_i w_i \cdot [s_i \neq \text{null}]} \tag{1}$$ $$\text{mh\_diversity} = 100 \cdot \frac{-\sum_i p_i \ln p_i}{\ln N}, \quad p_i = \frac{w_i \cdot s_i}{\sum_j w_j \cdot s_j} \tag{2}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector (Polygon/Point) | Yes | Typically the output of Join Hazard Scores to Planning Units.
Hazard fields | Numeric field (multiple) | At least 2 | Any number of 0–100 hazard/score fields; diversity is only meaningful with 2 or more.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon/Point) | — | Planning units with hazard score fields.
`HAZARD_FIELDS` | Field (Numeric, multiple) | — | At least 2 hazard score fields.
`WEIGHTS` | String | (empty = equal) | CSV weights matching the field order.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`mh_score` | Double | Weighted mean of hazard scores 0–100.
`mh_class` | String | Very High / High / Moderate / Low.
`mh_dominant` | String | Field name with the largest weighted contribution.
`mh_diversity` | Double | Shannon entropy 0–100. 0 = single-hazard, 100 = perfectly multi-stressed.
`mh_drivers` | String | Top 3 contributing field names (CSV).
`mh_data_gap` | Integer | Number of hazard fields with null/non-numeric values.

#### 7. Symbolic Representation

Graduated by `mh_score`, RdYlGn (reversed). Bivariate option: `mh_score` × `mh_diversity` via Bivariate Choropleth Symbology — high-score + low-diversity = concentrated single-hazard risk; high-score + high-diversity = diffuse multi-hazard stress (arguably harder to address).

#### 8. Interpretation Guide

mh_dominant: the hazard to target first. mh_diversity ≥ 70: the unit is genuinely multi-stressed — a single-hazard intervention won't suffice. mh_data_gap > 0: the composite is based on incomplete data; the missing hazards are not factored in. Units combining high `mh_score` with high `mh_diversity` are the most complex cases — feed them into Scenario Sensitivity Analysis to test whether their rank is stable under weight uncertainty.

#### Academic References

Shannon, C.E. (1948). "A mathematical theory of communication." Bell System Technical Journal, 27(3), 379–423.

### Equity-Adjusted Adaptation Priority

Processing ID: `planx_urban_resilience:equity_adjusted_priority`

#### 1. Overview

Re-ranks adaptation priority by multiplying a hazard/composite score (0–100) by an equity factor derived from the Social Vulnerability Index. Units that are both high-hazard and high-vulnerability rise to the top. The equity weight w controls amplification: w = 0 returns the raw hazard score; w = 1 doubles the score at SVI = 100.

#### 2. Theoretical Background

Academic lineage. The equity adjustment follows the distributive-justice framework for climate adaptation (Shi et al., 2016), which argues from a substantial body of environmental-justice literature that adaptation resources should be allocated proportionally to both physical exposure AND social vulnerability — treating a hazard score alone as the ranking criterion implicitly assumes every unit has equal capacity to respond, which the Social Vulnerability Index (Cutter et al., 2003) exists specifically to show is false. The multiplicative form `factor = 1 + w × (SVI/100)` is a deliberately simple amplifier: at w = 0 the output equals the raw hazard score (no equity adjustment at all, a neutral default), and at w = 1 a fully vulnerable unit (SVI = 100) has its priority doubled relative to an identical-hazard, zero-vulnerability unit.

Key assumptions.

- The adjustment is multiplicative, not additive — a unit with a very low hazard score gains very little absolute priority even at high SVI, since there is little to amplify. This is a deliberate design choice: the tool re-ranks among already-hazardous units by vulnerability, it does not manufacture new priority from vulnerability alone.
- w is a single global parameter applied uniformly — there is no mechanism to vary the equity weight by hazard type or by planning-unit sub-group.
- SVI must already be computed and joined onto the same layer (typically via Social Vulnerability Index then Join Hazard Scores to Planning Units) — this tool performs no vulnerability computation of its own.

When to use vs. when NOT to use. Use it whenever a hazard/composite ranking needs to be re-ordered to reflect that the same physical hazard falls harder on already-vulnerable communities — the standard equity pass after any composite-index step. Do NOT use it as a substitute for engaging affected communities in the actual weight choice (w) — the "right" amount of equity weighting is a policy decision, not a technical one, and this tool only makes that choice auditable, it does not make it for you.

The engine is in `processing/synthesis/equity_adjusted_priority.py`.

#### 3. Mathematical Formulation

$$\text{factor} = 1 + w \cdot \frac{\text{SVI}}{100} \tag{1}$$ $$\text{eq\_adjusted\_score} = \text{clamp}\left(100 \cdot \frac{\text{hazard} \cdot \text{factor}}{100 \cdot (1 + w)}\right) \tag{2}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector | Yes | Must carry both a hazard score and an SVI field.
Hazard/composite field | Numeric field | Yes | 0–100 scale, e.g. adapt_score or mh_score.
SVI field | Numeric field | Yes | 0–100, typically from Social Vulnerability Index.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector | — | Planning units.
`HAZARD_FIELD` | Field (Numeric) | — | Hazard/composite score 0–100.
`SVI_FIELD` | Field (Numeric) | — | Social vulnerability score 0–100.
`EQUITY_WEIGHT` | Double | 0.5 | 0 = ignore SVI, 1 = double at SVI=100.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`eq_hazard` | Double | Copy of source hazard score.
`eq_svi` | Double | Copy of source SVI.
`eq_factor` | Double | 1 + w × SVI/100.
`eq_adjusted_raw` | Double | Hazard × factor.
`eq_adjusted_score` | Double | Normalised 0–100.
`eq_priority_class` | String | Very High / High / Moderate / Low.

#### 7. Symbolic Representation

Side-by-side maps: raw hazard score (OrRd) vs equity-adjusted score (same ramp). The units that change colour the most are the equity story — map them with labels showing both scores.

#### 8. Interpretation Guide

Units where `eq_adjusted_score` > `eq_hazard` by >10 points are the equity findings: physically moderate hazard but high social vulnerability pushes them up the priority list. Use w = 0.5 (default) for balanced prioritisation; use w = 1.0 for a strongly equity-first ranking.

#### Academic References

Shi, L. et al. (2016). "Roadmap towards justice in urban climate adaptation research." Nature Climate Change, 6(2), 131–137. DOI: 10.1038/nclimate2841

### Climate Projection Overlay

Processing ID: `planx_urban_resilience:climate_projection_overlay`

#### 1. Overview

Applies a literature-based amplification factor to a today's hazard/composite score so planners can see the unit-level impact of a warming scenario. Offline-first — no CMIP/downscaled rasters required. Built-in scenarios: Stable (×1.00), Mild +1.5°C ~2050 (×1.15), Moderate +2°C ~2070 (×1.30), High +3°C ~2100 (×1.60), plus a custom override. Outputs include projected score, delta, and delta class.

#### 2. Theoretical Background

Academic lineage. This tool trades physical downscaling fidelity for accessibility: rather than requiring a CMIP/regional-climate-model raster stack most planning studios cannot obtain or process, it applies a single literature-derived amplification factor to today's already-computed hazard score. The amplification factors are drawn from the IPCC AR6 Working Group I regional fact sheets (IPCC, 2021), which project heatwave frequency increases of roughly 1.3–1.6× per degree of warming and flood magnitude increases of roughly 1.1–1.5× — figures this tool packages into four built-in scenarios plus a custom override. This "apply a scalar to a screening index" approach sits deliberately at the opposite end of the sophistication spectrum from a downscaled climate projection; it trades precision for the ability to run entirely offline, in seconds, on any hazard score this suite produces.

Key assumptions.

- A single scalar factor cannot represent how climate change reshapes the spatial pattern of a hazard, only its overall magnitude — the geographic distribution of who is most exposed today is assumed to persist unchanged into the projected scenario, which is frequently false (e.g. sea-level rise and precipitation-pattern shifts change WHERE flooding occurs, not just how much).
- The built-in factors are generic, cross-hazard averages from IPCC regional fact sheets, not hazard-specific or location-specific climate model output — for any hazard where precision matters, replace the default with a CUSTOM_FACTOR derived from a regional climate assessment for the actual study area.
- The clamp at 100 means a unit already at or near the ceiling shows little or no visible delta even under an aggressive scenario — the tool cannot represent "already maximally at risk, and getting worse" beyond the 0–100 scale's ceiling.

When to use vs. when NOT to use. Use it for rapid, offline-first sensitivity screening — "how much worse does this look under +2°C versus +3°C" — and for classroom or workshop settings without access to downscaled climate data. Do NOT use it as a substitute for genuine climate-hazard modelling in a technical study; where the decision stakes justify it, commission location- and hazard-specific downscaled projections instead of relying on a generic scalar.

Because this is a generic overlay, users should run it once per hazard with hazard-specific custom factors. The engine is in `processing/synthesis/climate_projection_overlay.py`.

#### 3. Mathematical Formulation

$$\text{cp\_projected} = \text{clamp}(\text{today} \times f) \tag{1}$$ $$\text{cp\_delta} = \text{cp\_projected} - \text{today} \tag{2}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector | Yes | Any layer with a 0–100 hazard/composite score field.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector | — | Scored layer with a 0–100 field.
`SCORE_FIELD` | Field (Numeric) | — | Today's score.
`SCENARIO` | Enum | Moderate (+2°C) | Pre-set or Custom.
`CUSTOM_FACTOR` | Double | 1.30 | Override factor when scenario = Custom.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`cp_today` | Double | Validated today's score.
`cp_scenario` | String | Scenario label.
`cp_factor` | Double | Amplification factor used.
`cp_projected` | Double | Today × factor, clamped 0–100.
`cp_delta` | Double | Projected − today.
`cp_delta_class` | String | Improved / Stable / Slight increase / Notable increase / Major increase.

#### 7. Symbolic Representation

Graduated by `cp_delta`, diverging RdBu ramp (blue = improved/stable, red = major increase), 5-class. Overlay with today's score as a hatch pattern for the compounding effect: high today + high delta = the worst future.

#### 8. Interpretation Guide

cp_delta_class = Major increase (>25 points): units that cross a risk-class threshold under the scenario — e.g. from Moderate (45) to High (72). These are the units where climate change qualitatively changes the risk profile. Run for all four built-in scenarios and compare the "Major increase" counts to show the non-linear escalation of risk.

#### Academic References

IPCC. (2021). Climate Change 2021: The Physical Science Basis. Contribution of Working Group I to the Sixth Assessment Report. Cambridge University Press.

### Recovery Capacity Index

Processing ID: `planx_urban_resilience:recovery_capacity_index`

#### 1. Overview

Measures the opposite side of resilience — how well a unit can bounce back after a shock. Composites four offline-derivable proxies: land-use diversity (HHI-based from multiple share fields), redundancy (road-access count, scaled), green coverage (0–1 or 0–100), and social cohesion (0–100 proxy, e.g. home-ownership rate). Missing sub-scores are excluded from the weighted mean. Outputs each sub-score, the composite, a class, and a data-gap count.

#### 2. Theoretical Background

Academic lineage. The index operationalises the "adaptive capacity" dimension of the IPCC AR5 vulnerability framework (IPCC, 2014), which frames vulnerability as a function of exposure, sensitivity, AND adaptive capacity — the resilience literature's answer to "exposure and sensitivity aren't the whole story; some places bounce back faster than others." Diversity borrows the Hirschman-Herfindahl Index from industrial-organisation economics, where it originally measured market concentration, and repurposes it here as a land-use-mix measure following Cervero & Kockelman's (1997) "3Ds" (density, diversity, design) framework for the built environment — the same logic as its economic origin: many small, evenly-sized land-use shares (like many small competing firms) signal a healthier, more mixed local economy than one dominant use. Redundancy follows the "multiple paths" principle from network resilience theory (Gao et al., 2016): a location reachable by several routes recovers service faster after any one route is disrupted. Cutter, Ash & Emrich (2014) is the direct empirical source for treating green coverage and social cohesion as validated recovery-capacity indicators.

Key assumptions.

- All four sub-scores are optional and independently excludable — a unit scored on only one of the four (e.g. only redundancy) still produces an `rc_score`, but `rc_data_gap` must be checked before treating it as comparable to a fully-scored unit.
- REDUNDANCY_SCALE (default 25.0) is a saturation constant, not an empirically fitted parameter — a location with 25 or more road-access points scores the maximum 100 on redundancy regardless of whether it actually has 25 or 250; adjust the scale to match the actual access-count range in your study area.
- Diversity, redundancy, green, and cohesion are treated as independently additive contributors — the model does not represent interaction effects (e.g. high diversity might matter less if redundancy is already very low, since diverse local land uses are of little help if nobody can physically reach them).

When to use vs. when NOT to use. Use it as the deliberate counterpart to any hazard/risk score — resilience planning needs both "how exposed" and "how well can this place recover" to be useful, and this tool supplies the second half. Feed both into Risk-Recovery Priority Matrix. Do NOT use it as a complete recovery-capacity assessment on its own — it uses four offline-derivable, geometry- and attribute-based proxies specifically because they require no survey data; a full community-resilience assessment (economic capital, institutional capacity, social capital surveys) needs primary data collection this tool does not attempt to replace.

The engine is in `processing/synthesis/recovery_capacity_index.py`.

#### 3. Mathematical Formulation

$$\text{rc\_diversity} = 100 \cdot \frac{1 - \sum_i (s_i / \sum s)^2}{1 - 1/n} \tag{1}$$ $$\text{rc\_score} = \frac{\sum_k w_k \cdot \text{sub}_k}{\sum_k w_k \cdot [\text{sub}_k \neq \text{null}]} \tag{2}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector | Yes | Any combination of the four optional sub-score inputs below.
Land-use share fields | Numeric fields (multiple) | No | e.g. residential/commercial/industrial/green shares.
Road-access count field | Numeric field | No | Number of road connections per unit.
Green coverage field | Numeric field | No | 0–1 or 0–100.
Social cohesion proxy field | Numeric field | No | 0–100, e.g. home-ownership rate.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector | — | Planning units.
`DIVERSITY_FIELDS` | Field (Numeric, multiple) | (optional) | Land-use share fields.
`REDUNDANCY_FIELD` | Field (Numeric) | (optional) | Road-access count.
`REDUNDANCY_SCALE` | Double | 25.0 | Saturates at 100/scale items.
`GREEN_FIELD` | Field (Numeric) | (optional) | Green coverage 0–1 or 0–100.
`COHESION_FIELD` | Field (Numeric) | (optional) | Social cohesion proxy 0–100.
`WEIGHTS` | String | "1,1,1,1" | CSV weights: diversity, redundancy, green, cohesion.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`rc_diversity` | Double | HHI-based land-use diversity 0–100. NULL if no share fields.
`rc_redundancy` | Double | Scaled access count 0–100.
`rc_green` | Double | Green coverage 0–100.
`rc_cohesion` | Double | Social cohesion proxy 0–100.
`rc_score` | Double | Weighted composite 0–100 (high = better recovery).
`rc_class` | String | Very High (≥80) / High / Moderate / Low / Very Low (<20).
`rc_data_gap` | Integer | Number of missing sub-scores.

#### 7. Symbolic Representation

Graduated by `rc_score`, Greens ramp (high = good recovery), 5-class natural breaks. Pair with Risk-Recovery Priority Matrix using this as the recovery field.

#### 8. Interpretation Guide

rc_score < 20 (Very Low): units with zero or one sub-score — they lack the economic, infrastructural, and social resources to self-recover. rc_class = Very High (≥80): well-connected, diverse, green neighbourhoods — they can absorb shocks. The gap between Climate Adaptation Priority (risk side) and Recovery Capacity (response side) is the resilience deficit — feed both into Risk-Recovery Priority Matrix.

#### Academic References

Cutter, S.L., Ash, K.D. & Emrich, C.T. (2014). "The geographies of community disaster resilience." Global Environmental Change, 29, 65–77.

Cervero, R. & Kockelman, K. (1997). "Travel demand and the 3Ds: density, diversity, and design." Transportation Research Part D, 2(3), 199–219.

Gao, J., Barzel, B. & Barabási, A.L. (2016). "Universal resilience patterns in complex networks." Nature, 530, 307–312.

### Risk-Recovery Priority Matrix

Processing ID: `planx_urban_resilience:risk_recovery_priority_matrix`

#### 1. Overview

Combines a hazard/adaptation score (0–100) with a recovery capacity score (0–100) into a single priority: high hazard + low recovery → highest priority. Uses the same multiplicative-deficit form as Equity-Adjusted Priority: priority = hazard × (1 + w × deficit/100), where deficit = 100 − recovery. Normalised back to 0–100.

#### 2. Theoretical Background

Academic lineage. This is a bivariate risk matrix — the standard decision-support tool in ISO 31000 risk management, where a two-axis (likelihood × consequence, or here hazard × recovery-deficit) grid replaces a single number specifically so a decision-maker can see WHY a unit is high-priority, not just THAT it is. The multiplicative form follows the "risk = hazard × vulnerability / coping capacity" convention used in the UNDRR Global Assessment Report framework (UNDRR, 2019): recovery capacity functions as an inverse vulnerability term, so a location's coping/recovery capacity discounts (or, when low, amplifies) its raw hazard exposure into a priority score. This is structurally the same multiplicative-amplifier pattern as Equity-Adjusted Priority, applied here to recovery capacity instead of social vulnerability — the two tools are siblings, and nothing prevents chaining them (equity-adjust first, then recovery-adjust the result, or vice versa).

Key assumptions.

- The two input scores (hazard and recovery) are assumed independent — in reality they can correlate (well-resourced areas often also have lower physical exposure), which this multiplicative form does not detect or correct for.
- Recovery capacity is treated as a single scalar — typically the composite `rc_score` from Recovery Capacity Index, which already collapses four sub-dimensions into one number; any nuance about WHICH recovery dimension is weakest is lost by the time it reaches this tool.
- Like the equity-adjustment tool, RECOVERY_WEIGHT is a single global amplification parameter, not something this tool derives empirically — treat the default (0.5) as a starting point for discussion, not a validated constant.

When to use vs. when NOT to use. Use it to triage a long list of planning units into the four classic risk-matrix quadrants (Act now / Prepare / Support recovery / Monitor) once both a hazard and a recovery score exist for the same units. Do NOT use it where the two input scores are not truly independent measurements — check for correlation between your hazard and recovery fields first, since a spuriously high multiplicative priority can result from two correlated inputs both moving together rather than genuinely compounding risk.

The engine is in `processing/synthesis/risk_recovery_priority.py`.

#### 3. Mathematical Formulation

$$\text{deficit} = 100 - \text{recovery\_score} \tag{1}$$ $$\text{rr\_priority\_score} = \text{clamp}\left(100 \cdot \frac{\text{hazard} \cdot (1 + w \cdot \frac{\text{deficit}}{100})}{100 \cdot (1 + w)}\right) \tag{2}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector | Yes | Must carry both a hazard/adaptation score and a recovery-capacity score field.
Hazard/adaptation field | Numeric field | Yes | 0–100, e.g. adapt_score.
Recovery capacity field | Numeric field | Yes | 0–100, e.g. rc_score.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector | — | Planning units with both fields.
`HAZARD_FIELD` | Field (Numeric) | — | Hazard / adaptation score 0–100.
`RECOVERY_FIELD` | Field (Numeric) | — | Recovery capacity score 0–100.
`RECOVERY_WEIGHT` | Double | 0.5 | 0 = ignore recovery, 1 = double at recovery=0.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`rr_hazard` | Double | Copy of hazard score.
`rr_recovery` | Double | Copy of recovery score.
`rr_deficit` | Double | 100 − recovery.
`rr_factor` | Double | 1 + w × deficit/100.
`rr_priority_score` | Double | Normalised 0–100.
`rr_priority_class` | String | Immediate / High / Medium / Monitor.

#### 7. Symbolic Representation

Graduated by `rr_priority_score`, YlOrRd ramp, 5-class natural breaks. Add labels showing `rr_hazard` and `rr_recovery` for the top 10 units. Alternatively, use Bivariate Choropleth Symbology with risk and recovery axes for a 3×3 matrix.

#### 8. Interpretation Guide

rr_priority_class = Immediate: high hazard AND low recovery — the intervention gap where resources matter most. High hazard + High recovery: exposed but resilient — monitoring may suffice. Use the matrix to triage a long list of planning units into four quadrants: Act now (↑↓), Prepare (↑↑), Support recovery (↓↓), Monitor (↓↑).

#### Academic References

UNDRR. (2019). Global Assessment Report on Disaster Risk Reduction. United Nations Office for Disaster Risk Reduction, Geneva.

### Hot-Spot Cluster Analysis (Getis-Ord Gi*)

Processing ID: `planx_urban_resilience:hotspot_analysis_getis_ord`

#### 1. Overview

Tags each feature as a statistically significant hot spot (cluster of high values), cold spot (cluster of low values), or not significant using the Getis-Ord Gi* local statistic. Supports inverse-distance and fixed-distance spatial weights. Includes Benjamini-Hochberg FDR-adjusted p-values and class labels to control for multiple testing across n simultaneous local tests. Pure Python implementation — no numpy dependency.

#### 2. Theoretical Background

Academic lineage. Getis & Ord (1992) introduced local spatial-association statistics to answer a question global statistics like Moran's I cannot: not just "is there clustering somewhere in this dataset" but "WHERE, specifically, is it clustered?" Ord & Getis (1995) then worked out the statistic's exact distributional properties under randomisation, making rigorous significance testing possible rather than relying on approximate normal-theory assumptions. Gi* (as opposed to the original Gi) includes the feature itself in its own neighbourhood sum — a small but consequential choice that makes Gi* behave better at the edges of a dataset and is now the standard variant used in practice. Because this suite runs Gi* on every feature simultaneously (n independent local significance tests), the naive p-values understate the true false-positive risk — Benjamini & Hochberg's (1995) False Discovery Rate (FDR) correction, one of the most widely adopted multiple-testing corrections across all of statistics, controls the expected proportion of false discoveries among the features flagged significant.

Key assumptions.

- Gi* assumes the value field is at least approximately normally distributed for the z-score interpretation to hold cleanly — highly skewed inputs (e.g. a field with a handful of extreme outliers) can distort results; consider a transform (log, rank) for strongly skewed fields.
- The spatial-weights choice (inverse-distance vs. fixed-distance binary) and the DISTANCE band materially change which features are flagged — there is no single "correct" distance band, and running the tool at 2–3 different bands to see whether hot spots are stable is standard practice, not optional diligence.
- `gi_class` (raw) will always flag more features as significant than `gi_class_fdr` (FDR-adjusted) — reporting the raw class alongside FDR without saying which is which risks overstating confidence.

When to use vs. when NOT to use. Use it to answer "where specifically are the significant clusters of high or low values" on any 0–100 score this suite produces — composite risk, SVI, recovery capacity, anything numeric. Do NOT use it when outliers (a high value surrounded by low values, or vice versa) are the object of interest rather than clusters — Gi* is built to find clusters, not outliers; use LISA Local Moran's I for that, since its HL/LH quadrants are specifically designed to expose exactly those outlier cases.

The engine is in `processing/synthesis/hotspot_analysis.py`.

#### 3. Mathematical Formulation

$$G_i^* = \frac{\sum_j w_{ij} x_j - \bar{x} \sum_j w_{ij}}{s \sqrt{\frac{n \sum_j w_{ij}^2 - (\sum_j w_{ij})^2}{n-1}}} \tag{1}$$

where \(s\) is the global standard deviation, \(w_{ij}\) is the spatial weight (inverse-distance or binary), and self is included (Gi* not Gi).

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (Polygon/Point) | Yes | Minimum 4 valid features with the value field populated.
Value field | Numeric field | Yes | Any numeric field to test for spatial clustering.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector | — | Scored polygon/point layer. Minimum 4 valid features.
`VALUE_FIELD` | Field (Numeric) | — | Value to analyse for clustering.
`DISTANCE` | Double | 500.0 | Distance band in map units.
`WEIGHT_METHOD` | Enum | Inverse distance | Inverse distance (1/d, row-standardised) or Fixed-distance binary.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`gi_zscore` | Double | Standardised Gi* statistic. |z| > 1.65 ≈ 90% confidence.
`gi_pvalue` | Double | Two-sided p-value from normal CDF.
`gi_p_fdr` | Double | Benjamini-Hochberg FDR-adjusted p-value.
`gi_class` | String | Hot-99 / Hot-95 / Hot-90 / Not significant / Cold-90 / Cold-95 / Cold-99.
`gi_class_fdr` | String | Same labels from FDR-adjusted p-values — prefer this for reporting.

#### 7. Symbolic Representation

Categorised by `gi_class_fdr`: Hot-99 = dark red, Hot-95 = red, Hot-90 = light red, Not significant = grey, Cold-90 = light blue, Cold-95 = blue, Cold-99 = dark blue. Use on any 0–100 score: composite, SVI, recovery capacity.

#### 8. Interpretation Guide

Hot-99 (FDR): features whose local mean is significantly higher than the global mean at p < 0.01 after FDR correction — the strongest spatial-concentration signal. Not significant (FDR): features where the clustering evidence is weak after correcting for multiple tests. The raw `gi_class` will always show more significant features than `gi_class_fdr` — FDR is more conservative. Run Gi* on the same score with different distance bands (250, 500, 1000 m) to explore the scale of clustering.

#### Academic References

Getis, A. & Ord, J.K. (1992). "The analysis of spatial association by use of distance statistics." Geographical Analysis, 24(3), 189–206.

Ord, J.K. & Getis, A. (1995). "Local spatial autocorrelation statistics: distributional issues and an application." Geographical Analysis, 27(4), 286–306.

Benjamini, Y. & Hochberg, Y. (1995). "Controlling the false discovery rate." Journal of the Royal Statistical Society: Series B, 57(1), 289–300.

### LISA Local Moran's I (cluster + outlier)

Processing ID: `planx_urban_resilience:lisa_local_moran`

#### 1. Overview

Companion to Hot-Spot Gi* — adds outlier detection to cluster detection. Tags each feature as HH (high-high cluster), LL (low-low cluster), HL (high-low outlier — a high-value feature in a low-value neighbourhood), LH (low-high outlier), or Not significant. The HL and LH classes expose under-served pockets Gi* cannot identify. Uses Anselin (1995) analytic randomization moments for significance and Benjamini-Hochberg FDR adjustment.

#### 2. Theoretical Background

Academic lineage. Anselin (1995) introduced Local Indicators of Spatial Association (LISA) as a general class of statistics satisfying two properties: each feature gets its own local statistic, and the sum of all local statistics is proportional to the corresponding global statistic. Local Moran's I is the LISA decomposition of the classical global Moran's I into per-feature contributions \(I_i = z_i \sum_j w_{ij} z_j\) — this is what lets an analyst move from "the whole map is spatially autocorrelated" to "these specific features are." Crucially, Anselin's framework classifies each feature into one of four quadrants by comparing its own standardised value \(z_i\) against its neighbourhood's spatial lag, which is what makes LISA able to detect outliers (HL, LH) as well as clusters (HH, LL) — a capability the Gi* statistic, built purely to find clusters, structurally lacks.

Key assumptions.

- The HH/LL/HL/LH quadrant classification depends on the significance threshold (α = 0.05 by default, shown as `lisa_quadrant`) — features near the significance boundary can flip quadrant with small data changes; prefer the FDR-adjusted `lisa_quad_fdr` for reporting, per the same multiple-testing logic as Hot-Spot Gi*.
- Randomisation-based significance (Anselin, 1995, eq. 13–14) assumes the value field's higher moments (specifically kurtosis) are estimated reliably from the sample — with very few features, these moment estimates themselves become unstable, undermining the significance test they feed into.
- Like Gi*, the spatial-weights choice and DISTANCE band are analyst decisions that materially change which features are flagged as outliers versus clusters; there is no universally correct choice.

When to use vs. when NOT to use. Use it as the companion to Hot-Spot Gi* whenever outlier detection matters — the HL/LH classes surface exactly the "surprising" units (a well-resourced pocket in an under-served district, or vice versa) that a pure hot-spot analysis cannot see. Do NOT use it in isolation when only cluster strength/extent is the question and outliers are not of interest — Gi*'s continuous z-score is a more direct answer to "how strongly clustered" without LISA's added HL/LH classification overhead.

The engine is in `processing/synthesis/lisa_local_moran.py`.

#### 3. Mathematical Formulation

$$I_i = z_i \cdot \sum_{j \neq i} w_{ij} z_j \quad \text{(row-standardised weights)} \tag{1}$$ $$z(I_i) = \frac{I_i - E[I_i]}{\sqrt{\text{Var}[I_i]}} \tag{2}$$

Quadrant: z_i > 0, lag > 0 → HH; z_i < 0, lag < 0 → LL; z_i > 0, lag < 0 → HL; z_i < 0, lag > 0 → LH.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (Polygon/Point) | Yes | Same requirements as Hot-Spot Gi* — minimum 4 valid features.
Value field | Numeric field | Yes | Any numeric field to test for local clusters and outliers.

#### 5. Parameters

Same as Hot-Spot Gi*: `INPUT`, `VALUE_FIELD`, `DISTANCE`, `WEIGHT_METHOD`.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`lisa_z` | Double | Standardised value z_i = (x_i − μ)/σ.
`lisa_lag` | Double | Spatial lag Σ_j w_ij z_j.
`lisa_I` | Double | Local Moran's I = z_i × lag.
`lisa_pvalue` | Double | Two-sided p-value from randomization moments.
`lisa_p_fdr` | Double | FDR-adjusted p-value.
`lisa_quadrant` | String | HH / LL / HL / LH / Not significant (α=0.05).
`lisa_quad_fdr` | String | Same, using FDR-adjusted p-values — prefer this for reporting.

#### 7. Symbolic Representation

Categorised by `lisa_quad_fdr`: HH = dark red, LL = dark blue, HL = pink (high in a low neighbourhood), LH = light blue (low in a high neighbourhood), Not significant = light grey. The HL and LH outliers are the map's most policy-relevant features — label them with both the feature's own value and its neighbourhood's mean.

#### 8. Interpretation Guide

HL (high-low outlier): a high-value unit surrounded by low-value neighbours — in resilience context, this is a well-resourced pocket in an under-served district. LH (low-high outlier): a low-value unit surrounded by high-value neighbours — the equity gap Gi* cannot see. Feed HL and LH units into Equity-Adjusted Priority to see whether the equity adjustment changes their rank. The Processing log prints a breakdown of quadrant counts for both raw and FDR-adjusted significance.

#### Academic References

Anselin, L. (1995). "Local indicators of spatial association — LISA." Geographical Analysis, 27(2), 93–115.

### Population-Weighted Exposure

Processing ID: `planx_urban_resilience:population_weighted_exposure`

#### 1. Overview

Translates a 0–100 hazard score into a human-impact estimate: how many people live inside each risk class? Population comes from either a field on the scored layer or a separate point/polygon layer. Per-unit outputs: `pop_total`, `pop_exposed` (= score/100 × pop), `exposure_intensity`, `exposure_class`. The Processing log prints a study-area summary: total population, breakdown by risk class, and top 10 units by exposed population.

#### 2. Theoretical Background

Academic lineage. Population-weighted exposure is the standard bridge from physical hazard to human impact in the disaster-risk literature (UNDRR, 2019): a hazard score alone tells a planner nothing about how many people are actually affected, which is usually the number that drives political and budgetary attention. The linear exposure model (`exposed = score/100 × pop`) treats the hazard score as a probability-like weight on the population — a first-order approximation that is transparent and defensible for screening, at the cost of assuming risk scales exactly linearly with score, which most underlying hazard models (themselves already screening-level approximations) cannot actually guarantee.

Key assumptions.

- The relationship between hazard score and actual harm is assumed linear — a score of 80 is assumed to expose exactly twice the "risk-weighted" population of a score of 40, which is a modelling convenience, not an empirically validated dose-response curve for any specific hazard.
- Population figures (whether from a field on the same layer or a separate population layer) are treated as a snapshot — the tool has no temporal or diurnal population variation (e.g. daytime workplace population vs. nighttime residential population), which can matter substantially for hazards with a strong time-of-day dependence.
- When population comes from a separate layer, the spatial join method (area-weighted vs. centroid-based) determines how population is apportioned to hazard units — check which convention your population source uses before comparing results across studies.

When to use vs. when NOT to use. Use it to translate any 0–100 hazard or composite score into a headline human-impact number for reporting, and to re-rank units where moderate hazard but very high population density can outrank high hazard with low population — a finding this module exists specifically to surface. Do NOT use it where the hazard-to-harm relationship is known to be strongly non-linear (e.g. flood depth-damage curves, which are typically convex, not linear) — for those, a hazard-specific dose-response function should replace this tool's generic linear assumption.

The engine is in `processing/synthesis/population_weighted_exposure.py`.

#### 3. Mathematical Formulation

$$\text{pop\_exposed} = \frac{\text{score}}{100} \times \text{pop\_total} \tag{1}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored planning units | Vector (Polygon) | Yes | Must have a 0–100 score field.
Population source | Field on same layer, or separate layer | Yes (one of the two) | Either a population field on the scored layer, or a separate population point/polygon layer with its own count field.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon) | — | Scored planning units.
`SCORE_FIELD` | Field (Numeric) | — | Score 0–100.
`POP_FIELD` | Field (Numeric) | (optional) | Option A: population on the same layer.
`POP_LAYER` | Vector | (optional) | Option B: separate population layer.
`POP_LAYER_FIELD` | Field (Numeric) | (optional) | Population field on Option B layer.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`pop_total` | Double | Population in this unit.
`pop_exposed` | Double | Score/100 × pop_total — expected exposed population.
`exposure_intensity` | String | Very High / High / Moderate / Low (based on score).
`exposure_class` | String | Duplicate of intensity for symbology compatibility.

#### 7. Symbolic Representation

Graduated by `pop_exposed`, OrRd ramp, 5-class natural breaks. The study-area summary table in the log is formatted for direct paste into a report.

#### 8. Interpretation Guide

pop_exposed > 5 000: large exposed populations — the humanitarian-priority units. The log's "Top units by exposed population" table is the audience-ready headline. Compare `pop_exposed` rankings with the raw `score` rankings: units with moderate hazard but very high population may rank higher on exposure than on hazard — a classic finding that justifies this module's existence.

### Multi-Period Hazard Frequency Aggregation

Processing ID: `planx_urban_resilience:hazard_frequency_aggregation`

#### 1. Overview

Collapses 2–8 temporal hazard snapshots (e.g., flood model runs for 2020/2030/2050, or seismic Monte Carlo across multiple magnitudes) into one composite score per planning unit. Captures both frequency (how many periods scored above threshold) and intensity (mean/max/min across periods). Three aggregation modes: Mean of all periods, Maximum, or Frequency-weighted mean (mean × freq/N — units high in both many periods AND high intensity rise to the top).

#### 2. Theoretical Background

Multi-period hazard aggregation addresses the temporal compounding problem in risk assessment (IPCC, 2012; Zscheischler et al., 2018). A single hazard snapshot (e.g., a 100-year flood map at current sea level) captures spatial variation at one point in time but misses the temporal persistence dimension: a unit that scores 80 in every period represents a fundamentally different risk profile from one that scores 95 in one period and 30 in all others, even if their mean scores are identical. The frequency-weighted mean aggregation mode operationalises the IPCC AR5 "Reasons for Concern" framework (IPCC, 2014), where both the magnitude and the frequency of climate impacts determine the risk level. The default threshold of 50 corresponds to the Moderate/High breakpoint in the suite's standard classification, ensuring that "above-threshold" represents at least a "High" classification. The max-intersection spatial join (same conservative convention as Join Scores to Planning Units) ensures that the unit inherits the highest score from each source layer.

Key assumptions.

- All source layers must share the same score field name — the tool has no mechanism to reconcile different field-naming conventions across snapshots automatically.
- The threshold τ (default 50) is a single global cutoff applied identically to every period; a hazard whose "significant" threshold genuinely shifts over time (e.g. as building codes or defences improve) cannot be represented without re-running with a different τ.
- Frequency-weighted mean penalises single-period spikes by design — a genuinely catastrophic one-off event (a single extreme flood year) will score lower on this composite than a chronically moderate hazard, even though the single event might be more consequential in absolute terms. Use Max mode specifically when worst-case, not average, preparedness is the planning question.

When to use vs. when NOT to use. Use it whenever 2–8 temporal or scenario snapshots of the same hazard exist and need collapsing into one composite that respects both intensity and persistence. Do NOT use it to combine genuinely different hazards (heat and flood, say) into one number — that is what Multi-Hazard Composite Index is for; this tool is specifically for the SAME hazard across different time periods or scenarios.

The engine is in `processing/synthesis/hazard_frequency.py`.

#### 3. Mathematical Formulation

$$\text{freq}(u) = \sum_{k=1}^{N} \left[ s_k(u) \ge \tau \right] \tag{1}$$ $$\bar{s}(u) = \frac{1}{N} \sum_{k=1}^{N} s_k(u) \tag{2}$$ $$s_{\text{composite}}(u) = \begin{cases} \bar{s}(u) & \text{Mean mode} \\ \max_k s_k(u) & \text{Max mode} \\ \bar{s}(u) \cdot \dfrac{\text{freq}(u)}{N} & \text{Frequency-weighted mean} \end{cases} \tag{3}$$

where \(N\) is the number of source layers (2–8), \(s_k(u)\) is the maximum intersecting score from source layer \(k\) at planning unit \(u\), \(\tau\) is the frequency threshold (default 50), and \([\cdot]\) is the Iverson bracket. All scores are clamped to \([0, 100]\). The frequency-weighted mean penalises units that are high in only a few periods: a unit with mean 80 and freq = 1/5 gets a composite of 16, while a unit with mean 80 and freq = 5/5 keeps 80.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector (Polygon) | Yes | Target polygons for aggregation.
Source hazard layers | Vector (Polygon), 2–8 layers | Yes | Each layer must have the same score field name. Score values clamped to 0–100.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`UNITS` | Vector (Polygon) | — | Planning units.
`SOURCES` | Multiple layers | — | 2–8 polygon score layers (e.g., flood_2020, flood_2030, flood_2050).
`SCORE_FIELD` | String | "score" | Score field name (same on all sources).
`THRESHOLD` | Double | 50.0 | Frequency threshold (0–100). A source layer "counts" when its score at this unit ≥ threshold.
`AGG_MODE` | Enum | Frequency-weighted | Mean / Max / Frequency-weighted mean.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`hf_frequency` | Integer | Count of source layers with score ≥ threshold at this unit.
`hf_mean` | Double | Mean score across all N sources.
`hf_max` | Double | Maximum score across sources.
`hf_min` | Double | Minimum score across sources.
`hf_composite` | Double | Aggregated composite per chosen mode (0–100).
`hf_class` | String | Very High (≥75) / High (55–74) / Moderate (35–54) / Low (<35).

#### 7. Symbolic Representation

Graduated by `hf_composite`, YlOrRd ramp, 5-class natural breaks. For temporal analysis, create a small-multiples layout: one map per source period in a row, with the composite map below. Use labels on `hf_frequency` to annotate composite values so the viewer sees both "how bad" and "how persistent".

#### 8. Interpretation Guide

hf_frequency = N (all periods above threshold): persistent, chronic exposure — these units need structural, long-term adaptation, not just emergency response. hf_frequency = 1, hf_max ≥ 75: a single-period spike — investigate whether this is a real trend driven by a specific scenario assumption or a model artefact. Compare the mean and max: if max ≫ mean, the unit has high inter-period variability (one bad year, several mild years). If max ≈ mean, the exposure is stable across periods. Frequency-weighted mean (default): use this for prioritisation when both persistence and intensity matter — it is the most conservative composite for identifying units that are chronically exposed. Mean mode: use when you want to compare average exposure independent of persistence. Max mode: useful for worst-case preparedness planning.

Workflow integration. Feed the output into Climate Projection Overlay for a second dimension of temporal stress-testing, or into Scenario Sensitivity Analysis to test whether the rank ordering is stable when you vary the threshold τ.

#### Academic References

IPCC. (2012). Managing the Risks of Extreme Events and Disasters to Advance Climate Change Adaptation (SREX). Cambridge University Press. DOI: 10.1017/CBO9781139177245

IPCC. (2014). Climate Change 2014: Impacts, Adaptation, and Vulnerability. Part A: Global and Sectoral Aspects. Cambridge University Press.

Zscheischler, J., Westra, S., van den Hurk, B.J.J.M., Seneviratne, S.I., Ward, P.J., Pitman, A., AghaKouchak, A., Bresch, D.N., Leonard, M., Wahl, T. & Zhang, X. (2018). "Future climate risk from compound events." Nature Climate Change, 8(6), 469–477. DOI: 10.1038/s41558-018-0156-3

AghaKouchak, A., Chiang, F., Huning, L.S., Love, C.A., Mallakpour, I., Mazdiyasni, O., Moftakhari, H., Papalexiou, S.M., Ragno, E. & Sadegh, M. (2020). "Climate extremes and compound hazards in a changing world." Annual Review of Earth and Planetary Sciences, 48, 519–548. DOI: 10.1146/annurev-earth-071719-055228

Ward, P.J., Jongman, B., Aerts, J.C.J.H., Bates, P.D., Botzen, W.J.W., Diaz Loaiza, A., Hallegatte, S., Kind, J., Kwadijk, J., Scussolini, P. & Winsemius, H.C. (2017). "A global framework for future costs and benefits of river-flood protection in urban areas." Nature Climate Change, 7(9), 642–646. DOI: 10.1038/nclimate3350

Hallegatte, S., Green, C., Nicholls, R.J. & Corfee-Morlot, J. (2013). "Future flood losses in major coastal cities." Nature Climate Change, 3(9), 802–806. DOI: 10.1038/nclimate1979

UNDRR. (2019). Global Assessment Report on Disaster Risk Reduction. United Nations Office for Disaster Risk Reduction, Geneva.

Kreibich, H., Di Baldassarre, G., Vorogushyn, S., Aerts, J.C.J.H., Apel, H., Aronica, G.T., Arnbjerg-Nielsen, K., Bouwer, L.M., Bubeck, P., Caloiero, T. et al. (2017). "Adaptation to flood risk: results of international paired flood event studies." Earth's Future, 5(10), 953–965. DOI: 10.1002/2017EF000606

### Vulnerability Surface (IDW interpolation)

Processing ID: `planx_urban_resilience:vulnerability_surface_idw`

#### 1. Overview

Interpolates sparse point measurements (sensor readings, survey scores, social-cohesion samples) into a continuous surface as polygon grid cells using inverse-distance weighting (IDW). Parameters: IDW power (higher = sharper local peaks), optional search radius, and optional neighbour cap. Pure Python — no raster dependencies.

#### 2. Theoretical Background

Academic lineage. Shepard (1968) introduced inverse-distance weighting as one of the earliest general-purpose spatial interpolators, decades before geostatistical kriging became computationally practical — its enduring appeal is that it requires no variogram fitting or distributional assumptions, only a distance function and a power parameter, which is exactly why it remains the default "quick surface from scattered points" tool across GIS software. It is a deterministic interpolator: values at unobserved locations are a distance-weighted average of observed values, with no represented uncertainty (contrast with kriging, which produces a prediction variance alongside the estimate). The power parameter controls locality: p = 1 gives gentle, smooth gradients; p = 4 gives sharp peaks that decay quickly around each sample, effectively giving near neighbours almost total control over the estimate.

Key assumptions.

- IDW assumes spatial dependence decays smoothly and isotropically (equally in all directions) with distance — it cannot represent directional trends (e.g. a value that varies systematically along a coastline) or genuine spatial discontinuities (a value that jumps sharply across an administrative or physical boundary).
- The surface is an exact interpolator: at a sample point itself, the estimate equals the observed value exactly, which can produce a visually "spiky" or bullseye-pattern surface with too few points or too high a power.
- IDW has no cross-validation or error estimate built in — unlike kriging, there is no statistically principled way to know how much to trust the interpolated value at any given cell without a separate validation step (e.g. holding out points and comparing predicted vs. actual).

When to use vs. when NOT to use. Use it for a fast, dependency-free surface from scattered point measurements (sensor readings, survey scores, social-cohesion samples) where an approximate continuous picture is more useful than no picture at all. Do NOT use it where a rigorous uncertainty estimate matters for the decision being made — a proper geostatistical method (kriging) is the standard alternative when prediction confidence needs to be quantified, not just the point estimate; also avoid it with strongly clustered, unevenly distributed sample points without first checking `vs_n_points` for extrapolation gaps.

The engine is in `processing/synthesis/vulnerability_surface_idw.py`.

#### 3. Mathematical Formulation

$$\hat{z}(x) = \frac{\sum_i w_i z_i}{\sum_i w_i}, \quad w_i = \frac{1}{d(x, x_i)^p} \tag{1}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Source points | Vector (Point) | Yes | Sparse measurements to interpolate.
Value field | Numeric field | Yes | The quantity to interpolate.
Study area | Vector (Polygon) | Yes | Clips the output grid to this extent.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`POINTS` | Vector (Point) | — | Source point layer.
`VALUE_FIELD` | Field (Numeric) | — | Value to interpolate.
`STUDY` | Vector (Polygon) | — | Study-area mask.
`CELL_SIZE` | Double | 50.0 | Output cell size.
`POWER` | Double | 2.0 | IDW power. Higher = sharper local peaks.
`RADIUS` | Double | 0 (unlimited) | Search radius. 0 = use all points.
`MAX_NEIGHBOURS` | Integer | 12 | Max neighbours per cell. 0 = no cap.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`cell_id` | Integer | Sequential cell ID.
`vs_value` | Double | Interpolated value.
`vs_class` | String | Very High / High / Moderate / Low.
`vs_n_points` | Integer | Number of source points contributing to this cell.

#### 8. Interpretation Guide

vs_n_points = 0: no source point within radius — the cell is an extrapolation gap. Reduce IDW power or increase radius. vs_n_points = 1: the cell's value equals the single nearest point — an exact-interpolation artefact; consider increasing radius.

#### Academic References

Shepard, D. (1968). "A two-dimensional interpolation function for irregularly-spaced data." Proceedings of the 1968 ACM National Conference, 517–524.

### Critical Infrastructure Exposure

Processing ID: `planx_urban_resilience:critical_infrastructure_exposure`

#### 1. Overview

Crosses a hazard score layer with a critical-facility layer (schools, hospitals, fire stations, power/water assets) to produce a combined exposure score. Each facility's hazard score is sampled at its centroid using spatial containment against hazard polygons. A user-supplied criticality field (numeric, any scale) is min-max normalised to 0–100 across all facilities. The combined score = (hazard/100) × criticality_norm × 100, producing a joint measure that elevates facilities that are both highly exposed AND highly critical — the right shortlist for retrofit and hardening programmes. The Processing log prints the top 10 most exposed facilities and the mean combined score by facility class.

#### 2. Theoretical Background

Academic lineage. Critical infrastructure exposure analysis bridges two parallel literatures: natural hazard risk assessment (where exposure is "what is in harm's way") and infrastructure systems engineering (where criticality is "what function is lost if this asset fails"). The multiplicative form — hazard × criticality — follows the standard risk equation \(R = H \times V \times E\) codified in the UNDRR Global Risk Assessment Framework (UNDRR, 2019) and the IPCC AR5 risk framework (IPCC, 2014). The criticality half of the equation draws on the infrastructure-interdependency literature that emerged after Rinaldi, Peerenboom & Kelly (2001) formalised how physical, cyber, geographic, and logical dependencies let a single facility failure cascade through connected systems — a hospital is critical not only for the care it delivers directly, but because power, water, and road-network failures elsewhere can knock it out too. Buldyrev et al.'s (2010) demonstration that interdependent networks can fail catastrophically even when each network alone would be robust is the theoretical extreme case this tool's simple multiplicative score is a tractable, planning-scale proxy for; Ouyang's (2014) review surveys the far more complex simulation-based alternatives (agent-based, network-flow, economic input-output models) that a full interdependency study would use instead of this tool's single-snapshot multiplicative score.

Key assumptions.

- The min-max normalisation of the criticality field is a pragmatic choice — it makes the algorithm usable with any user-supplied criticality scale (1–5, 1–100, monetary replacement value, population served) without prior knowledge of the scale bounds, but it also means `ci_criticality` values are only meaningful relative to the other facilities in this run, not as an absolute criticality measure.
- The centroid-based hazard sampling is conservative: a facility is considered exposed if its geographic centre lies inside a hazard polygon — this avoids the complexity of partial-overlap weighting while ensuring that polygon facilities (e.g., a hospital campus spanning multiple blocks) are assessed at a representative location, at the cost of the containment-vs-intersection edge case documented in the warning callout below.
- This tool computes a single-snapshot, single-facility exposure score — it has no representation of the cascading, network-level interdependency failures that Buldyrev et al. (2010) and the broader interdependency literature (Rinaldi et al., 2001; Ouyang, 2014) describe. A facility scoring "Low" here could still be knocked out by an interdependency failure this tool cannot see.

When to use vs. when NOT to use. Use it for a fast, transparent first-pass shortlist of which critical facilities most need hardening or retrofit attention, combining physical hazard exposure with a planner-supplied criticality judgement. Do NOT use it as a substitute for a genuine infrastructure-interdependency study where cascading failure across connected systems (power, water, transport, communications) is the actual concern — that requires the network-simulation methods Ouyang (2014) reviews, well beyond this tool's single-facility scoring scope.

The engine is in `processing/synthesis/critical_infrastructure_exposure.py`.

#### 3. Mathematical Formulation

$$H_i = \text{score}(\text{polygon containing centroid}(f_i)) \quad \text{or } 0 \text{ if no polygon contains centroid} \tag{1}$$ $$c_{\text{range}} = \max(c_i) - \min(c_i) \quad \text{(or 1.0 if all equal)} \tag{2}$$ $$c_{\text{norm}, i} = 100 \cdot \frac{c_i - \min(c_i)}{c_{\text{range}}} \tag{3}$$ $$E_i = \frac{H_i}{100} \cdot c_{\text{norm}, i} \cdot 100 = H_i \cdot \frac{c_{\text{norm}, i}}{100} \tag{4}$$

where \(H_i\) is the hazard score (0–100) at facility \(i\)'s centroid, \(c_i\) is the raw criticality value, and \(E_i\) is the combined exposure score (0–100). If no hazard polygon contains the facility centroid (e.g., the facility lies outside the hazard model extent), \(H_i = 0\).

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Hazard score layer | Vector (Polygon) | Yes | Must have a numeric 0–100 score field. Polygons form a continuous coverage of the study area for accurate point-in-polygon containment.
Critical facilities | Vector (Point/Polygon) | Yes | Facilities to evaluate. Points: centroid is the point itself. Polygons: centroid of the polygon geometry.
Criticality field | Field (Numeric) | Optional | Any numeric scale. If omitted, all facilities get the default criticality.
Name/label field | Field | Optional | Used in the log's "Top 10 most exposed facilities" table.
Class field | Field (String) | Optional | Used for grouped mean-exposure statistics by class (e.g., "schools average 65, hospitals 40").

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`HAZARD` | Vector (Polygon) | — | Hazard score layer.
`HAZARD_FIELD` | Field (Numeric) | — | Score field 0–100.
`FACILITIES` | Vector (Point/Polygon) | — | Critical facility layer.
`CRITICALITY_FIELD` | Field (Numeric) | (optional) | Facility criticality field. Any scale — normalised to 0–100 using min/max across all facilities.
`CRITICALITY_DEFAULT` | Double | 1.0 | Default criticality when no field or no per-feature value is supplied.
`NAME_FIELD` | Field | (optional) | Facility name/label for the log's top-10 table.
`CLASS_FIELD` | Field (String) | (optional) | Facility class (e.g., school, hospital, fire_station) for grouped statistics.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`ci_hazard` | Double | Hazard score at the facility centroid (0–100).
`ci_criticality` | Double | Raw criticality value from the facility's field.
`ci_combined` | Double | Combined exposure score = (hazard/100) × criticality_norm × 100. Range 0–100.
`ci_exposure_class` | String | Critical (≥75) / High (55–74) / Moderate (35–54) / Low (<35).

#### 7. Symbolic Representation

Graduated by `ci_combined`, OrRd ramp, 5-class natural breaks. For facilities, use circles scaled by `ci_combined` (2–8 mm) with dark stroke. Label the top 5 facilities by name from the log. Overlay with the hazard layer at 30% opacity for context. The log's "Mean combined score by facility class" table is suitable for direct inclusion in a report.

#### 8. Interpretation Guide

ci_exposure_class = Critical (≥75): a facility that is both in a high-hazard zone AND highly critical — the top retrofit/hardening priority. ci_hazard high but ci_combined moderate: a critical facility in a low-hazard area — its criticality is normalised but its location is favourable. This is a resilience asset, not a priority target. ci_hazard and ci_criticality both are uniformly distributed: all facilities get similar combined scores; the min-max normalisation compresses diversity. In this case, try using a wider criticality scale (e.g., 1–100 instead of 1–5) to spread the distribution. Cross-reference with Social Vulnerability Index: facilities serving high-SVI populations that score Critical on exposure are the equity+exposure double priority. Feed the top-N facilities into Cost-Benefit Analyzer to rank retrofit options by cost-effectiveness.

Containment vs. intersection. Hazard score is sampled by polygon containment of the facility centroid, not by intersection. If facilities span multiple hazard zones (e.g., a large hospital campus crossing a flood score = 90 and a flood score = 30 polygon), only the centroid's containing polygon determines the score. For multi-polygon facilities, consider converting to points at each building footprint centroid before running this algorithm.

#### Academic References

UNDRR. (2019). Global Assessment Report on Disaster Risk Reduction. United Nations Office for Disaster Risk Reduction, Geneva.

IPCC. (2014). Climate Change 2014: Impacts, Adaptation, and Vulnerability. Part A: Global and Sectoral Aspects. Cambridge University Press.

NIST. (2015). Community Resilience Planning Guide for Buildings and Infrastructure Systems. NIST Special Publication 1190. DOI: 10.6028/NIST.SP.1190

Rinaldi, S.M., Peerenboom, J.P. & Kelly, T.K. (2001). "Identifying, understanding, and analyzing critical infrastructure interdependencies." IEEE Control Systems Magazine, 21(6), 11–25. DOI: 10.1109/37.969131

Ouyang, M. (2014). "Review on modeling and simulation of interdependent critical infrastructure systems." Reliability Engineering & System Safety, 121, 43–60. DOI: 10.1016/j.ress.2013.06.040

FEMA. (2018). Seismic Performance Assessment of Buildings, Volume 1 — Methodology (FEMA P-58-1), Second Edition. Washington, DC.

Pederson, P., Dudenhoeffer, D., Hartley, S. & Permann, M. (2006). Critical Infrastructure Interdependency Modeling: A Survey of U.S. and International Research. Idaho National Laboratory, INL/EXT-06-11464.

Cutter, S.L., Ash, K.D. & Emrich, C.T. (2014). "The geographies of community disaster resilience." Global Environmental Change, 29, 65–77. DOI: 10.1016/j.gloenvcha.2014.08.005

Buldyrev, S.V., Parshani, R., Paul, G., Stanley, H.E. & Havlin, S. (2010). "Catastrophic cascade of failures in interdependent networks." Nature, 464, 1025–1028. DOI: 10.1038/nature08932

FEMA. (2003). HAZUS-MH MR4 Technical Manual. Federal Emergency Management Agency, Washington, DC.

### Scenario Sensitivity Analysis

Processing ID: `planx_urban_resilience:scenario_sensitivity`

#### 1. Overview

Stress-tests composite scores by varying component weights via Latin Hypercube Sampling (LHS). Each weight may vary by ± the perturbation fraction around its nominal value. Per unit, computes the baseline composite (nominal weights), mean, standard deviation, p05/p95, range, and a 0–100 stability score (100 = identical across all samples, 0 = highly sensitive). Units with low stability change rank under reasonable weight uncertainty — flag them for qualitative review.

#### 2. Theoretical Background

Academic lineage. Latin Hypercube Sampling (McKay, Beckman & Conover, 1979) is a stratified Monte Carlo method developed originally for computer-experiment design: it guarantees the full range of each input dimension is represented exactly once per stratum, exploring the space far more efficiently than brute-force random sampling for the same sample budget. In multi-criteria decision analysis, weight sensitivity is widely recognised as the primary source of rank uncertainty (Saltelli et al., 2004) — every composite tool in this suite (Adaptation Priority Synthesis, Multi-Hazard Composite, Recovery Capacity Index) asks the user to supply weights that are, in the end, policy judgements rather than measured constants, and this tool is the suite's answer to "how much does the ranking actually depend on getting those judgements exactly right?"

Key assumptions.

- Each weight is perturbed independently within ±PERTURB of its nominal value, then the full sample vector is renormalised to sum to 1 — weights are not assumed independent in reality (raising one conceptually should lower others), but the LHS design explores the space as if they were, which is a simplification of the true weight-elicitation problem.
- Stability is derived purely from the standard deviation of the composite across samples (`stability = 100 × (1 − std/25)`, floored at 0) — it says nothing about whether the unit's RANK relative to other units is stable, only whether its own score is. Two units can each have high stability individually while still swapping relative rank order under perturbation.
- SAMPLES (default 100) trades runtime for estimate precision — very low values (e.g. 20) can produce noisy p05/p95 estimates; 100–500 is the recommended range per the parameter table below.

When to use vs. when NOT to use. Use it after any weighted composite (Adaptation Priority, Multi-Hazard Composite, Recovery Capacity) to flag which units' rankings are robust versus fragile before committing to a final priority list — units with low stability deserve qualitative review, not blind trust in their composite score. Do NOT use it as a substitute for actually engaging stakeholders on the "right" weights — sensitivity analysis tells you HOW MUCH the weight choice matters, it does not tell you what the weights SHOULD be.

The engine is in `processing/synthesis/scenario_sensitivity.py`.

#### 3. Mathematical Formulation

$$w_{k}^{(s)} = \frac{w_k^{lo} + u_k^{(s)} \cdot (w_k^{hi} - w_k^{lo})}{\sum_j w_j^{(s)}}, \quad w_k^{lo} = w_k(1-\rho),\ w_k^{hi} = w_k(1+\rho) \tag{1}$$ $$C^{(s)} = \frac{\sum_k w_k^{(s)} \cdot \text{clamp}(s_k)}{\sum_{k : s_k \neq \text{null}} w_k^{(s)}} \tag{2}$$ $$\text{ss\_stability} = \text{clamp}\left(100 \cdot \left(1 - \frac{\sigma(C)}{25}\right)\right) \tag{3}$$

where \(u_k^{(s)} \in [0,1]\) is the Latin-Hypercube draw for weight \(k\) in sample \(s\), \(\rho\) is PERTURB, \(w_k\) is the nominal weight, \(C^{(s)}\) is the composite score for that sample's renormalised weight vector, and \(\sigma(C)\) is the standard deviation of the composite across all SAMPLES draws.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector | Yes | Any layer with 2 or more component score fields, e.g. the output of Join Hazard Scores to Planning Units.
Component score fields | Numeric fields (multiple) | Yes, at least 2 | The same fields that feed a downstream composite tool.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector | — | Planning units with ≥2 score fields.
`COMPONENT_FIELDS` | Field (Numeric, multiple) | — | Component score fields.
`WEIGHTS` | String | (empty = equal) | Nominal weight CSV.
`PERTURB` | Double | 0.5 | Perturbation fraction. 0 = fixed, 1 = ±100%.
`SAMPLES` | Integer | 100 | LHS sample count. 100–500 recommended.
`SEED` | Integer | 42 | Random seed for reproducibility.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`ss_baseline` | Double | Composite with nominal weights.
`ss_mean` | Double | Mean across LHS samples.
`ss_std` | Double | Standard deviation.
`ss_p05`, `ss_p95` | Double | 5th/95th percentile.
`ss_range` | Double | p95 − p05.
`ss_stability` | Double | 0–100. 100 = score unchanged under any weight perturbation.
`ss_samples` | Integer | Number of LHS draws used.

#### 8. Interpretation Guide

ss_stability < 50: the unit's rank is fragile — changing weights by ±50% shifts its score by more than 12.5 points on average. Flag these units in the report as "rank uncertain; review qualitatively." ss_stability ≥ 90: the unit's score is robust to weight choice — high-confidence prioritisation.

#### Academic References

McKay, M.D., Beckman, R.J. & Conover, W.J. (1979). "A comparison of three methods for selecting values of input variables." Technometrics, 21(2), 239–245.

Saltelli, A. et al. (2004). Sensitivity Analysis in Practice. Wiley.

### Risk Trend Hot-Spot (Gi* x Delta score)

Processing ID: `planx_urban_resilience:risk_trend_hotspot`

#### 1. Overview

Fuses temporal trend (delta score between two snapshots) with spatial clustering (Getis-Ord Gi* on the later snapshot) into a single label per unit. The nine-class typology — Rising / Stable / Cooling hot spot, Rising / Stable / Cooling cold spot, Hot/Cold no cluster, Not significant — answers the planner question: "which neighbourhoods are both rising in risk AND clustered with other high-risk areas?". This is the suite's early-warning algorithm: a "Rising hot spot" label means the unit is in a spatially concentrated high-value cluster AND its score is increasing.

#### 2. Theoretical Background

The algorithm operates at the intersection of two distinct analytical traditions: temporal trend analysis (longitudinal studies using paired snapshots; Diggle, 2014) and local spatial autocorrelation (Getis & Ord, 1992). The fusion approach follows the space-time scan statistic literature (Kulldorff, 1997) but uses a simpler two-stage method: first compute the hot-spot significance on the most recent data, then annotate with the temporal trajectory. This avoids the computational complexity of full space-time scan statistics while preserving the key operational insight: a hot spot that is rising represents an emerging threat; a hot spot that is cooling represents a successful intervention or natural hazard decline. The 1-point delta tolerance (adjustable) distinguishes genuine change from rounding noise. The significance threshold used is |z| \(\ge\) 1.96, corresponding to a two-sided p < 0.05 under the standard normal approximation.

Key assumptions.

- The two-stage design (hot-spot significance on the later snapshot, then annotate with trend) means the significance test itself is purely spatial and purely cross-sectional — it does not test whether the CHANGE is spatially clustered, only whether the current level is. A unit can be a "Rising hot spot" even if its neighbours are not also rising, as long as they are also currently high.
- The 9-class typology depends on both a hard significance cutoff (|z| ≥ 1.96) and a delta tolerance (ε, default 1.0) — units near either boundary can flip class with small data changes, the same edge-sensitivity caveat that applies to any classification built on hard thresholds.
- Only two time points are used (later score + delta) — the tool cannot distinguish a genuinely accelerating trend from a single noisy jump between two snapshots; three or more snapshots would be needed to fit an actual trend line.

When to use vs. when NOT to use. Use it as the suite's early-warning tool once at least two temporal snapshots of the same score exist (ideally via Resilience Time-Series Tracker) — it is the one tool in the suite designed specifically to answer "where is risk both clustered AND getting worse right now." Do NOT use it with only a single snapshot (it requires a delta by construction), or where the two snapshots span a period short enough that normal year-to-year noise could be mistaken for a genuine trend — check the delta tolerance ε against the known measurement noise of the underlying hazard score before trusting a "Rising"/"Cooling" label at face value.

The engine is in `processing/synthesis/risk_trend_hotspot.py`.

#### 3. Mathematical Formulation

$$G_i^* = \frac{\sum_j w_{ij} x_j - \bar{x} \sum_j w_{ij}}{s \sqrt{\frac{n \sum_j w_{ij}^2 - (\sum_j w_{ij})^2}{n-1}}} \tag{1}$$ $$w_{ij} = \begin{cases} 1 & i = j \\ d_{ij}^{-1} & i \neq j, d_{ij} \le r \end{cases} \tag{2}$$ $$\text{sig}_i = [|G_i^*| \ge 1.96] \tag{3}$$ $$\text{rising}_i = [\Delta_i > \varepsilon], \quad \text{falling}_i = [\Delta_i < -\varepsilon] \tag{4}$$ $$\text{label}_i = f(G_i^*, \text{sig}_i, \text{rising}_i, \text{falling}_i) \tag{5}$$

where \(x_j\) is the later-snapshot score, \(\bar{x}\) is the global mean, \(s\) is the global standard deviation, \(r\) is the distance band, \(\Delta_i\) is the per-unit delta (later − earlier), and \(\varepsilon\) is the delta tolerance (default 1.0). The labeling function \(f\) maps the (z-score sign, significance, trend) tuple to one of 9 classes.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Input layer | Vector (Polygon/Point) | Yes | Must have both a later-snapshot score field and a delta field.
Later-snapshot score field | Field (Numeric) | Yes | The current-period score (0–100). Drives the Gi* hot-spot computation.
Delta field | Field (Numeric) | Yes | Later score − earlier score (any numeric range).

Pairing note. The ideal input is the output of Resilience Time-Series Tracker with `ts_later_score` as the later-score field and `ts_delta` as the delta field.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon/Point) | — | Layer with later-score and delta fields.
`LATER_SCORE` | Field (Numeric) | — | Later-snapshot score (0–100).
`DELTA_FIELD` | Field (Numeric) | — | Delta field (later − earlier).
`DISTANCE` | Double | 500.0 | Distance band for Gi* neighbourhood (map units).
`DELTA_TOL` | Double | 1.0 | Delta tolerance — units with |delta| ≤ this are considered "Stable".

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`trh_gi_z` | Double | Getis-Ord Gi* z-score. |z| ≥ 1.96 = significant at p < 0.05.
`trh_gi_significant` | Integer | 1 if |z| ≥ 1.96, else 0.
`trh_delta` | Double | Input delta value (copied for convenience).
`trh_label` | String | 9-class label: Rising hot spot / Stable hot spot / Cooling hot spot / Rising cold spot / Stable cold spot / Cooling cold spot / Hot, no cluster / Cold, no cluster / Not significant.

#### 7. Symbolic Representation

Categorised by `trh_label`: Rising hot spot = dark red (#b30000), Stable hot spot = red (#e34a33), Cooling hot spot = orange (#fc8d59), Rising cold spot = dark blue (#313695), Stable cold spot = blue (#4575b4), Cooling cold spot = light blue (#91bfdb), Hot no cluster = light red, Cold no cluster = light blue, Not significant = light grey (#cccccc). The Log prints a summary table of label counts for direct report inclusion.

#### 8. Interpretation Guide

Rising hot spot: the strongest early-warning signal — the unit is in a statistically significant high-value cluster AND its score is increasing. These are the immediate-intervention priority units. Cooling hot spot: a clustered high-value unit with a declining score — evidence that an intervention or natural hazard cycle is reducing risk. Document these as success cases. Rising cold spot: a clustered low-value unit with an increasing score — the early stage of what could become a hot spot if the trend continues. Flag for monitoring. Cooling cold spot: clustered low value, still declining — possibly abandonment or disinvestment. Cross-reference with Social Vulnerability Index to check whether declining risk reflects genuine improvement or population displacement. Not significant: the spatial clustering evidence is weak at the 95% level — this unit's score is not spatially structured; its trend is an independent local phenomenon. Run with different distance bands (250 m, 500 m, 1000 m) to test the scale sensitivity of the cluster labels.

Workflow integration. Feed the "Rising hot spot" units into Cost-Benefit Analyzer for cost-effective intervention ranking. Pair with Scenario Comparison / Delta Map for a side-by-side before/after visualisation of the units that changed most.

#### Academic References

Getis, A. & Ord, J.K. (1992). "The analysis of spatial association by use of distance statistics." Geographical Analysis, 24(3), 189–206. DOI: 10.1111/j.1538-4632.1992.tb00261.x

Kulldorff, M. (1997). "A spatial scan statistic." Communications in Statistics — Theory and Methods, 26(6), 1481–1496. DOI: 10.1080/03610929708831995

Diggle, P.J. (2014). Statistical Analysis of Spatial and Spatio-Temporal Point Patterns. 3rd ed., CRC Press. DOI: 10.1201/b15326

Ord, J.K. & Getis, A. (1995). "Local spatial autocorrelation statistics: distributional issues and an application." Geographical Analysis, 27(4), 286–306. DOI: 10.1111/j.1538-4632.1995.tb00912.x

Anselin, L. (1995). "Local indicators of spatial association — LISA." Geographical Analysis, 27(2), 93–115. DOI: 10.1111/j.1538-4632.1995.tb00338.x

Rey, S.J. & Anselin, L. (2010). "PySAL: a Python library of spatial analytical methods." In M.M. Fischer & A. Getis (eds.), Handbook of Applied Spatial Analysis, Springer, pp. 175–193. DOI: 10.1007/978-3-642-03647-7_11

Cressie, N. (1993). Statistics for Spatial Data. Revised ed., Wiley. DOI: 10.1002/9781119115151

IPCC. (2021). Climate Change 2021: The Physical Science Basis. Cambridge University Press.

Cutter, S.L. & Finch, C. (2008). "Temporal and spatial changes in social vulnerability to natural hazards." Proceedings of the National Academy of Sciences, 105(7), 2301–2306. DOI: 10.1073/pnas.0710375105

Benjamini, Y. & Hochberg, Y. (1995). "Controlling the false discovery rate." Journal of the Royal Statistical Society: Series B, 57(1), 289–300. DOI: 10.1111/j.2517-6161.1995.tb02031.x

## 10. Economics: Cost-Benefit, Lifecycle & Optimisation

Three algorithms that move the suite from "where is the risk?" to "what does the response cost, and in what order should we act?". Together they form a budget-allocation pipeline: score interventions → rank by benefit/cost → identify the Pareto frontier → compute lifecycle NPV.

### Cost-Benefit Analyzer (planning prioritisation)

Processing ID: `planx_urban_resilience:cost_benefit_analyzer`

#### 1. Overview

Ranks planning units by cost-effectiveness using a transparent two-input model: cost = unit_cost × area (or per-feature for points), benefit = score × reduction_factor × population_weight. The B/C ratio drives a 1-N priority rank. The Processing log prints the cumulative-benefit curve: "to capture 80% of benefit you need to fund 25% of units."

#### 2. Theoretical Background

Academic lineage. Benefit-cost ratio ranking is the standard, textbook first-pass prioritisation method in public-sector economic appraisal (Boardman et al., 2018) — simpler than full net-present-value analysis (see the sibling Lifecycle Cost Calculator for that), but transparent and fast enough to rank hundreds of candidate interventions in one pass. Benefit here is modelled as a product of three factors — the hazard score being addressed, a reduction factor (how much of that risk the intervention actually removes), and a population weight (how many people benefit) — following the standard "avoided damage" logic of infrastructure cost-benefit appraisal: an intervention's benefit is proportional to how much risk it removes, weighted by how many people that removed risk affects.

Key assumptions.

- The reduction factor (DEFAULT_REDUCTION, default 0.7) is a single global assumption unless overridden per-feature — it represents "this class of intervention removes roughly 70% of the addressed risk," a planning-level estimate, not an engineering-validated performance figure for any specific intervention design.
- Benefit is linear in population weight and in score — the same caveats as Population-Weighted Exposure apply: this is a modelling convenience, not a validated dose-response relationship.
- The B/C ratio says nothing about absolute budget — a unit with a very high ratio but a tiny absolute cost and tiny absolute benefit may still be a poor use of a large capital programme; pair this tool's rank with the absolute cb_cost and cb_benefit values, not the ratio alone.

When to use vs. when NOT to use. Use it for fast, transparent first-pass prioritisation across many candidate interventions when only rough unit-cost and reduction-factor estimates are available. Do NOT use it where a rigorous total-cost-of-ownership comparison across interventions with different lifespans and maintenance profiles is needed — use Lifecycle Cost Calculator's discounted NPV for that; do not use the B/C ratio alone to allocate a fixed budget without also checking Pareto-Optimal Selector, since ratio-ranking and Pareto-frontier selection can disagree when absolute costs vary widely across candidates.

#### 3. Mathematical Formulation

$$\text{cost} = uc \times \max(A, 0) \tag{1}$$ $$\text{benefit} = s \times rf \times \max(pop, 1) \tag{2}$$ $$\text{ratio} = \frac{\text{benefit}}{\text{cost}} \quad (\text{cost} = 0, \text{benefit} > 0 \Rightarrow \text{free win, ranked first}) \tag{3}$$

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (Point/Polygon) | Yes | Must have a 0–100 score field. Cost defaults to area × unit cost for polygons, or a flat per-feature cost for points.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector | — | Scored layer.
`SCORE_FIELD` | Field (Numeric) | — | Score 0–100.
`UNIT_COST` | Double | 100.0 | Default unit cost (currency/m² for polygons, /feature for points).
`UNIT_COST_FIELD` | Field (Numeric) | (optional) | Per-feature cost override.
`DEFAULT_REDUCTION` | Double | 0.7 | Reduction factor. 0.7 = intervention removes 70% of risk.
`REDUCTION_FIELD` | Field (Numeric) | (optional) | Per-feature reduction override.
`POP_FIELD` | Field (Numeric) | (optional) | Population weight field.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`cb_cost` | Double | Estimated intervention cost.
`cb_benefit` | Double | Estimated benefit (score × reduction × pop).
`cb_ratio` | Double | Benefit / cost. NULL if cost = 0 and benefit = 0.
`cb_priority_rank` | Integer | 1-N rank (1 = best B/C ratio).

#### 8. Interpretation Guide

The cumulative-benefit curve in the log is the budget-justification exhibit. Free-win features (cost = 0, benefit > 0) are ranked first — these are the no-regret actions. Feed `cb_priority_rank == 1` features into Pareto-Optimal Selector to find the cost–benefit frontier.

#### Academic References

Boardman, A.E. et al. (2018). Cost-Benefit Analysis: Concepts and Practice. 5th ed., Cambridge University Press.

### Pareto-Optimal Intervention Selector

Processing ID: `planx_urban_resilience:pareto_optimal_selector`

#### 1. Overview

Identifies the Pareto frontier of a (cost, benefit) trade-off via non-dominated sorting (NSGA-II style). Each feature receives: `pareto_rank` (1 = first frontier), `pareto_dominated` (count of features that strictly dominate it), `pareto_dominates` (count it dominates). Picking everything with rank = 1 guarantees no objectively better alternative exists — no other feature is both cheaper AND more beneficial. The algorithm is O(N^2) in the number of features; for typical planning-unit layers of 100–2,000 features it completes in under a second.

#### 2. Theoretical Background

Pareto optimality, named after the Italian economist Vilfredo Pareto (1848–1923), is the cornerstone of multi-objective optimisation. The non-dominated sorting algorithm follows the NSGA-II framework of Deb et al. (2002), applied deterministically to an existing feature set. The domination relation is strict: \(a\) dominates \(b\) iff \(a\) is no worse on both objectives AND strictly better on at least one. The frontier-peeling after dominance counting is equivalent to the recursive non-dominated sorting of Kung et al. (1975).

Key assumptions.

- Only two objectives are considered (cost, benefit) — real intervention decisions often have more dimensions (equity impact, co-benefits, political feasibility) that this tool cannot represent; treat the Pareto frontier as a shortlist for further qualitative judgement, not a final answer.
- Domination is strict and binary — a feature that is 0.1% worse on cost and identical on benefit is still "dominated," even though the practical difference may be negligible; consider this when a very large number of features cluster near the frontier.
- The frontier reflects the cost and benefit values AS SUPPLIED — if those come from Cost-Benefit Analyzer's simplified linear model, the Pareto frontier inherits all of that model's assumptions and limitations.

When to use vs. when NOT to use. Use it after Cost-Benefit Analyzer to move from a single ratio-based ranking to the full efficient frontier — rank-1 features are optimal under ANY linear cost-benefit weighting, which is a stronger guarantee than a single B/C ratio ranking provides. Do NOT use it as the sole selection criterion when non-cost/benefit factors (equity, political feasibility, co-benefits) are decisive — the frontier answers "which options are efficient," not "which options should be funded."

The engine is in `processing/synthesis/pareto_selector.py`.

#### 3. Mathematical Formulation

$$a \succ b \iff (c_a \le c_b \land b_a \ge b_b) \land (c_a < c_b \lor b_a > b_b) \tag{1}$$ $$D_i = |\{j \neq i : j \succ i\}| \tag{2}$$

where \(c\) is cost (lower better), \(b\) is benefit (higher better), and \(D_i\) is the count of features that strictly dominate feature \(i\). Frontier rank \(R_i\) is computed by iteratively peeling the current Pareto frontier (features with no remaining dominator) from the set, assigning rank = 1, 2, 3, ... to successive peels.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (Point/Polygon) | Yes | Must have a cost field and a benefit field, e.g. the output of Cost-Benefit Analyzer.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Point/Polygon) | — | Layer with cost and benefit fields.
`COST_FIELD` | Field (Numeric) | — | Cost field (lower = better).
`BENEFIT_FIELD` | Field (Numeric) | — | Benefit field (higher = better).

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`pareto_rank` | Integer | Frontier rank: 1 = first (best), 2 = next skin, etc.
`pareto_dominated` | Integer | Global count of features that strictly dominate this one.
`pareto_dominates` | Integer | Global count of features this one strictly dominates.

#### 8. Interpretation Guide

pareto_rank = 1: the budget-bounded best pick set — these features are optimal under any linear cost–benefit weighting scheme. pareto_rank > 3: strictly worse options; fund only if budget exhausts first two frontiers. pareto_dominated ≥ 100: this feature is dominated by many — poor choice regardless of framing. The Processing log prints the feature count per rank; typical distribution is ~5–15% in rank 1. Feed the Cost-Benefit Analyzer output (`cb_cost`, `cb_benefit`) into this algorithm to identify interventions on the Pareto frontier.

#### Academic References

Deb, K., Pratap, A., Agarwal, S. & Meyarivan, T. (2002). "A fast and elitist multiobjective genetic algorithm: NSGA-II." IEEE Transactions on Evolutionary Computation, 6(2), 182–197. DOI: 10.1109/4235.996017

Kung, H.T., Luccio, F. & Preparata, F.P. (1975). "On finding the maxima of a set of vectors." Journal of the ACM, 22(4), 469–476. DOI: 10.1145/321906.321910

Miettinen, K. (1999). Nonlinear Multiobjective Optimization. Springer. DOI: 10.1007/978-1-4615-5563-6

Boardman, A.E., Greenberg, D.H., Vining, A.R. & Weimer, D.L. (2018). Cost-Benefit Analysis: Concepts and Practice. 5th ed., Cambridge University Press.

Malczewski, J. & Rinner, C. (2015). Multicriteria Decision Analysis in Geographic Information Science. Springer. DOI: 10.1007/978-3-540-74757-4

Chankong, V. & Haimes, Y.Y. (1983). Multiobjective Decision Making: Theory and Methodology. North-Holland.

Coello Coello, C.A., Van Veldhuizen, D.A. & Lamont, G.B. (2007). Evolutionary Algorithms for Solving Multi-Objective Problems. 2nd ed., Springer.

Pareto, V. (1906). Manuale di Economia Politica. Societa Editrice Libraria, Milano.

### Lifecycle Cost Calculator (25-year NPV)

Processing ID: `planx_urban_resilience:lifecycle_cost_calculator`

#### 1. Overview

Per planning unit, computes discounted Net Present Value over a configurable horizon (default 25 years) including upfront capex, annual opex, periodic maintenance events, and end-of-life replacement. Outputs the six NPV components plus an equivalent flat annual cost. Optional area scaling multiplies per-m^2 values by polygon area. This is the "total cost of ownership" companion to the Cost-Benefit Analyzer: it answers "what does this intervention cost over its full life?" rather than "which intervention gives the best return per euro?".

#### 2. Theoretical Background

Lifecycle cost analysis (LCCA) is the standard engineering-economics method for comparing long-term investment alternatives (Fuller & Petersen, 1996; ISO 15686-5:2017). The Net Present Value (NPV) framework discounts all future cash flows to their present-value equivalent using a discount rate that reflects the time value of money and, in public-sector applications, the social discount rate (typically 3–7% as recommended by the European Commission, 2014, and OMB Circular A-94). The annualisation formula converts the lump-sum NPV into an equivalent uniform annual cost via the capital recovery factor (also known as the annuity factor), enabling fair comparison between interventions with different lifespans. The default 25-year horizon and 4% discount rate follow the convention for urban infrastructure projects in temperate-climate OECD countries (Boardman et al., 2018). The 5-year maintenance interval default reflects typical green-infrastructure maintenance cycles (irrigation upgrades, canopy planting, drainage desilting).

Key assumptions.

- The discount rate is a single constant applied for the entire horizon — real public discount rates can vary over time (as HM Treasury's Green Book, 2020, itself recommends declining long-term rates for very long horizons), which this tool does not represent.
- Maintenance and replacement events are modelled as fixed-interval, fixed-cost recurring items — real maintenance schedules and costs are rarely perfectly periodic or perfectly predictable; treat the output as an expected-value planning estimate, not a guaranteed budget schedule.
- AREA_SCALE assumes per-m² costs scale linearly with polygon area — economies of scale (larger installations often cost less per unit area) are not represented.

When to use vs. when NOT to use. Use it whenever comparing interventions with genuinely different lifespans, maintenance intensities, or replacement cycles on a like-for-like basis — this is exactly the comparison a simple upfront-cost figure cannot make fairly. Do NOT use it for a fast first-pass screening across many candidates where only rough unit costs are available — Cost-Benefit Analyzer's simpler ratio model is faster to populate and sufficient for that stage; reserve this tool's more data-hungry, more rigorous NPV analysis for the shortlist that survives first-pass screening.

The engine is in `processing/synthesis/lifecycle_cost.py`.

#### 3. Mathematical Formulation

$$\text{NPV} = C + \sum_{t=1}^{H} \frac{O}{(1+r)^t} + \sum_{t \in M} \frac{m}{(1+r)^t} + \sum_{t \in R} \frac{q}{(1+r)^t} \tag{1}$$ $$\text{annualised} = \text{NPV} \times \frac{r}{1 - (1+r)^{-H}} \tag{2}$$ $$\text{opex\_npv} = \begin{cases} O \cdot H & r = 0 \\ O \cdot \frac{1 - (1+r)^{-H}}{r} & r > 0 \end{cases} \tag{3}$$

where \(C\) is upfront capex, \(O\) is annual opex, \(m\) is maintenance cost every \(M\) years, \(q\) is replacement cost every \(R\) years, \(H\) is the analysis horizon, and \(r\) is the annual discount rate. All costs are multiplied by polygon area when `AREA_SCALE` is enabled for polygon input layers.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Planning units | Vector (Polygon/Point) | Yes | Must have a capex field; opex, maintenance and replacement fields are optional.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon/Point) | — | Planning units.
`CAPEX_FIELD` | Field (Numeric) | — | Upfront capital cost.
`OPEX_FIELD` | Field (Numeric) | (optional) | Annual O&M cost.
`MAINT_INTERVAL` | Integer | 5 | Years between maintenance events. 0 = none.
`MAINT_COST` | Double | 0.0 | Default maintenance cost.
`MAINT_COST_FIELD` | Field (Numeric) | (optional) | Per-feature override.
`REPL_YEARS` | Integer | 0 | Replacement cycle. 0 = none.
`REPL_COST` | Double | 0.0 | Default replacement cost.
`REPL_COST_FIELD` | Field (Numeric) | (optional) | Per-feature override.
`HORIZON` | Integer | 25 | Analysis horizon (years).
`DISCOUNT` | Double | 4.0 | Discount rate (% per year).
`AREA_SCALE` | Boolean | False | Multiply per-m^2 values by polygon area.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`lc_capex_npv` | Double | Upfront capex (already present value).
`lc_opex_npv` | Double | Sum of discounted annual opex.
`lc_maintenance_npv` | Double | Sum of discounted maintenance events.
`lc_replacement_npv` | Double | Sum of discounted replacements.
`lc_total_npv` | Double | Total NPV = capex + opex + maintenance + replacement.
`lc_annualised` | Double | Equivalent flat annual cost over the horizon.

#### 8. Interpretation Guide

The Processing log prints the sum of `lc_total_npv` across all features — the programme-level cost. The `lc_annualised` field is the most communicable number: "this intervention costs EUR X per year, flat, for 25 years." Use it to compare interventions with different horizons and maintenance profiles on a like-for-like basis. lc_maintenance_npv > lc_capex_npv: the intervention is maintenance-heavy — the lifecycle costs are dominated by recurring expenses, not upfront investment. This is common in green infrastructure (regular irrigation, pruning, replanting) and should be accounted for in municipal O&M budgets. DISCOUNT = 0%: use only for undiscounted break-even analysis; for public-sector economic appraisal, 3–5% is standard.

#### Academic References

Fuller, S.K. & Petersen, S.R. (1996). Life-Cycle Costing Manual for the Federal Energy Management Program. NIST Handbook 135. DOI: 10.6028/NIST.HB.135

European Commission. (2014). Guide to Cost-Benefit Analysis of Investment Projects. Directorate-General for Regional and Urban Policy.

Boardman, A.E., Greenberg, D.H., Vining, A.R. & Weimer, D.L. (2018). Cost-Benefit Analysis: Concepts and Practice. 5th ed., Cambridge University Press. DOI: 10.1017/9781108235594

ISO. (2017). ISO 15686-5:2017 — Buildings and constructed assets — Service life planning — Part 5: Life-cycle costing. International Organization for Standardization.

U.S. Office of Management and Budget. (1992). Circular A-94: Guidelines and Discount Rates for Benefit-Cost Analysis of Federal Programs. Washington, DC.

HM Treasury. (2020). The Green Book: Central Government Guidance on Appraisal and Evaluation. London. DOI: 10.1108/9781787562820-024

NIST. (2015). Community Resilience Planning Guide for Buildings and Infrastructure Systems. NIST Special Publication 1190. DOI: 10.6028/NIST.SP.1190

FEMA. (2018). Seismic Performance Assessment of Buildings, Volume 1 — Methodology (FEMA P-58-1), Second Edition. Washington, DC.

## 9. Spatial Statistics: Clusters, Outliers & Surfaces

Four algorithms for local spatial analysis. Hot-Spot Gi* (Getis-Ord) and LISA Local Moran's I identify statistically significant clusters and outliers in any 0–100 resilience score. Risk Trend Hot-Spot fuses temporal delta with spatial clustering. Vulnerability Surface IDW interpolates sparse point measurements into continuous polygon grids. All are pure Python — no compiled dependencies. Full documentation for each appears under Group 8 (Synthesis) where they are also registered in the Processing provider.

## R. Reporting, Visualisation & QA

The Reporting group provides ten algorithms for output styling, comparison, export, and communication. They transform the numeric scores from the hazard and synthesis modules into studio-ready deliverables: HTML/Markdown reports, PDF atlas pages, styled QGIS layers, 3D GeoJSON exports, correlation matrices, and curated intervention checklists.

### Resilience Time-Series Tracker

Processing ID: `planx_urban_resilience:resilience_time_series`

#### 1. Overview

Joins two snapshots of a scored layer (e.g., 2020 baseline and 2025 update) on a stable unit ID field. Emits per-unit delta, percent change, trend (Improved / Stable / Worsened), and optional target gap. Geometry is taken from the later snapshot. This is the suite's primary longitudinal-analysis tool — it converts two static maps into a change map suitable for trend analysis, before/after intervention evaluation, and policy-target tracking.

#### 2. Theoretical Background

Longitudinal spatial analysis of risk indicators has become standard practice in climate adaptation monitoring (Cutter & Finch, 2008; Preston et al., 2011). The paired-snapshot approach used here is a two-period panel design — the simplest longitudinal design that can detect direction of change but not acceleration or non-linear trajectories. The trend classification uses a ±1 point deadband around zero to suppress reporting of sub-meaningful fluctuations caused by rounding or data-update noise. The percent change metric normalises the raw delta by the baseline, enabling comparison across units with different baseline scores (a +10 delta from a baseline of 20 is a +50% change; the same +10 from a baseline of 80 is only +12.5%). The optional target gap field operationalises the "distance to policy target" concept from the Sendai Framework's monitoring system (UNDRR, 2019).

Key assumptions.

- A two-period design can detect DIRECTION of change but not acceleration or non-linear trajectories — three or more snapshots (paired with Risk Trend Hot-Spot) would be needed to distinguish a steadily worsening trend from a one-off spike.
- The ID join assumes both snapshots share a genuinely stable unit-identification scheme — boundary redraws, unit splits/merges, or ID-scheme changes between snapshots surface as spurious "No match" results, not as errors.
- Percent change is undefined (NULL) when the baseline is at or near zero, since dividing by a near-zero baseline produces meaningless or infinite ratios — use the absolute delta for those units instead.

When to use vs. when NOT to use. Use it as the primary longitudinal tool whenever two genuine time-separated snapshots of the same scored layer exist — before/after intervention evaluation, multi-year monitoring, policy-target tracking. Do NOT use it to compare two different SCENARIOS at the same point in time (baseline vs. proposed) — that is a cross-sectional comparison, not longitudinal; use Scenario Comparison / Delta Map for that distinct question instead.

The engine is in `processing/reporting/time_series_tracker.py`.

#### 3. Mathematical Formulation

$$\Delta_i = s_i^{(t_1)} - s_i^{(t_0)} \tag{1}$$ $$\text{pct}_i = 100 \cdot \frac{\Delta_i}{s_i^{(t_0)}} \quad \text{if } |s_i^{(t_0)}| > 10^{-6} \text{, else NULL} \tag{2}$$ $$T_i = \begin{cases} \text{Improved} & \Delta_i < -1.0 \\ \text{Stable} & -1.0 \le \Delta_i \le 1.0 \\ \text{Worsened} & \Delta_i > 1.0 \end{cases} \tag{3}$$ $$G_i = s_i^{(t_1)} - \tau \quad \text{(target gap, positive = above target)} \tag{4}$$

where \(s_i^{(t_0)}\) is the earlier-snapshot score, \(s_i^{(t_1)}\) is the later-snapshot score, and \(\tau\) is the policy target value (default 0 = not used).

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Earlier snapshot | Vector (any geometry) | Yes | Must have an ID field and a numeric score field (0–100 preferred).
Later snapshot | Vector (any geometry) | Yes | Must have the same ID scheme and a numeric score field.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`EARLIER` | Vector (any geometry) | — | Earlier-snapshot layer.
`EARLIER_ID` | Field | — | Stable unit ID field on the earlier layer.
`EARLIER_SCORE` | Field (Numeric) | — | Earlier score field (0–100).
`LATER` | Vector (any geometry) | — | Later-snapshot layer.
`LATER_ID` | Field | — | Stable unit ID field on the later layer (must match earlier IDs).
`LATER_SCORE` | Field (Numeric) | — | Later score field (0–100).
`TARGET_VALUE` | Double | 0.0 | Policy target value. Positive gap = score exceeds target (i.e., risk is above the policy goal).

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`ts_earlier_score` | Double | Score from the earlier snapshot (clamped). NULL if no matching ID.
`ts_later_score` | Double | Score from the later snapshot (clamped).
`ts_delta` | Double | Later − earlier. NULL if no match.
`ts_delta_pct` | Double | 100 × delta / earlier. NULL if earlier ≈ 0 or no match.
`ts_trend` | String | Improved / Stable / Worsened / No match.
`ts_target_gap` | Double | Later − target. Positive = above target (risk too high).

#### 7. Symbolic Representation

Graduated by `ts_delta`, diverging RdBu ramp (blue = improved, white = stable, red = worsened), 5–7 class equal-interval classification centred on zero. Alternatively, categorised by `ts_trend`: Improved = green, Stable = grey, Worsened = red. The delta map is the project's core before/after exhibit.

#### 8. Interpretation Guide

ts_trend = No match: the unit ID in the later layer was not found in the earlier layer (new units, boundary changes, or ID scheme mismatch). Check ID field correspondence. ts_delta_pct > 50%: a dramatic change — verify whether this is a genuine risk change or a data collection methodology change between snapshots. ts_target_gap > 0 AND ts_trend = Worsened: the unit is moving away from the policy target — the most concerning trajectory. Feed `ts_later_score` and `ts_delta` into Risk Trend Hot-Spot for the fused temporal × spatial label. Feed the full output into Scenario Comparison for before/after delta mapping.

#### Academic References

Cutter, S.L. & Finch, C. (2008). "Temporal and spatial changes in social vulnerability to natural hazards." Proceedings of the National Academy of Sciences, 105(7), 2301–2306. DOI: 10.1073/pnas.0710375105

Preston, B.L., Yuen, E.J. & Westaway, R.M. (2011). "Putting vulnerability to climate change on the map: a review of approaches, benefits, and risks." Sustainability Science, 6(2), 177–202. DOI: 10.1007/s11625-011-0129-1

UNDRR. (2019). Global Assessment Report on Disaster Risk Reduction. United Nations Office for Disaster Risk Reduction, Geneva.

Füssel, H.M. (2007). "Vulnerability: a generally applicable conceptual framework for climate change research." Global Environmental Change, 17(2), 155–167. DOI: 10.1016/j.gloenvcha.2006.05.002

Adger, W.N. (2006). "Vulnerability." Global Environmental Change, 16(3), 268–281. DOI: 10.1016/j.gloenvcha.2006.02.006

IPCC. (2014). Climate Change 2014: Impacts, Adaptation, and Vulnerability. Part A: Global and Sectoral Aspects. Cambridge University Press.

Cutter, S.L., Boruff, B.J. & Shirley, W.L. (2003). "Social vulnerability to environmental hazards." Social Science Quarterly, 84(2), 242–261. DOI: 10.1111/1540-6237.8402002

Birkmann, J., Cardona, O.D., Carreno, M.L., Barbat, A.H., Pelling, M., Schneiderbauer, S., Kienberger, S., Keiler, M., Alexander, D., Zeil, P. & Welle, T. (2013). "Framing vulnerability, risk and societal responses: the MOVE framework." Natural Hazards, 67(2), 193–211. DOI: 10.1007/s11069-013-0558-5

### Scenario Comparison / Delta Map

Processing ID: `planx_urban_resilience:resilience_scenario_comparison`

#### 1. Overview

Compares two 0–100 score fields on the same layer (typically baseline vs proposed after intervention, or two alternative planning scenarios). Outputs delta (scenario − baseline), absolute delta, percent change, change class (Improved / Stable / Worsened), and an action hint. The meaningful-change threshold (default 5 points) filters out noise-level fluctuations from genuine interventions. This is the suite's primary decision-support comparison tool: it quantifies the spatial impact of a proposed intervention and identifies units where the scenario produces a meaningful difference.

#### 2. Theoretical Background

Scenario comparison is a core methodology in sustainability assessment and spatial planning (Swart et al., 2004; Xiang & Clarke, 2003). The delta-map approach — subtracting two spatial fields and mapping the residuals — follows the "comparative statics" tradition in spatial economics and the before/after control-impact (BACI) design in environmental impact assessment (Smith, 2014). The default 5-point threshold for meaningful change eliminates the "small change problem" identified by Openshaw & Taylor (1979): without a threshold, every unit shows some delta, most of which is noise, and the resulting map is uninformative. A 5-point delta on a 0–100 scale represents roughly one-half of a standard classification band, which is both statistically detectable and operationally meaningful.

Key assumptions.

- Both scores must already be on the same layer at the same point in time — this is a cross-sectional (same-moment) comparison of two alternatives, not a longitudinal (across-time) comparison; use Resilience Time-Series Tracker for the latter.
- The threshold (default 5.0) is a fixed global cutoff — for hazards whose scores are naturally noisier or more volatile, a larger threshold may be warranted to avoid classifying measurement noise as a genuine "Worsened" or "Improved" result.
- The action hint is a generic, class-derived suggestion, not a substitute for actual planning judgement — "Review scenario drivers" does not identify WHICH driver changed, only that some meaningful change occurred.

When to use vs. when NOT to use. Use it as the standard tool for quantifying the spatial impact of a proposed intervention against a baseline, or for comparing two alternative planning scenarios side by side. Do NOT use it where the two fields being compared are not truly on the same 0–100 scale and methodology — comparing scores computed by two different tools or two different weight configurations can produce a delta that reflects a methodology difference, not a genuine scenario difference.

The engine is in `processing/reporting/scenario_comparison.py`.

#### 3. Mathematical Formulation

$$\Delta_i = b_i^{(s)} - b_i^{(0)} \tag{1}$$ $$\text{abs\_}\Delta_i = |\Delta_i| \tag{2}$$ $$\text{pct}_i = \begin{cases} 100 \cdot \dfrac{\Delta_i}{b_i^{(0)}} & b_i^{(0)} \neq 0 \\ 0 & \text{otherwise} \end{cases} \tag{3}$$ $$C_i = \begin{cases} \text{Improved} & \Delta_i \le -\tau \\ \text{Stable} & -\tau < \Delta_i < \tau \\ \text{Worsened} & \Delta_i \ge \tau \end{cases} \tag{4}$$

where \(b_i^{(0)}\) is the baseline score, \(b_i^{(s)}\) is the scenario score, and \(\tau\) is the meaningful-change threshold (default 5.0). The action hint is derived from the change class: Improved → "Document and replicate", Worsened → "Review scenario drivers", Stable → "Monitor".

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Input layer | Vector (any geometry) | Yes | Must have both a baseline and a scenario score field on the same layer.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`BASELINE_FIELD` | Field (Numeric) | — | Baseline score field (0–100).
`SCENARIO_FIELD` | Field (Numeric) | — | Scenario/proposed score field (0–100).
`THRESHOLD` | Double | 5.0 | Meaningful-change threshold. Deltas with |delta| < threshold are classified as Stable.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`base_score` | Double | Baseline score (validated).
`scen_score` | Double | Scenario score (validated).
`delta` | Double | Scenario − baseline.
`abs_delta` | Double | Absolute value of delta.
`pct_change` | Double | 100 × delta / baseline (0 if baseline = 0).
`change_cls` | String | Improved / Stable / Worsened.
`action_hint` | String | Document and replicate / Review scenario drivers / Monitor.

#### 7. Symbolic Representation

Graduated by `delta`, diverging RdBu ramp (blue = improved, white = stable, red = worsened), 5–7 class classification centred on zero. The delta map is the project's core before/after exhibit. For presentations, create a side-by-side layout: baseline map (left), scenario map (centre), delta map (right).

#### 8. Interpretation Guide

change_cls = Improved with large negative delta (e.g., −30): the scenario/intervention substantially reduces risk — these are the success-story units to document and replicate. change_cls = Worsened with positive delta: the scenario increases risk relative to the baseline — review the scenario parameters; this may indicate a realistic future-worsening (e.g., climate projection) or a modelling error. change_cls = Stable: the intervention does not alter the risk profile enough to cross the threshold — either the intervention is too weak, the unit's baseline score is too far from the threshold zone, or both. Percent change vs absolute delta: use percent change for ranking (a +10 delta from a baseline of 20 = +50% is more dramatic than +10 from 80 = +12.5%); use absolute delta for spatial mapping (it preserves the interpretable 0–100 scale). Feed the output into Bivariate Choropleth Symbology with baseline and delta as axes to show "where was it high AND got worse".

#### Academic References

Swart, R.J., Raskin, P. & Robinson, J. (2004). "The problem of the future: sustainability science and scenario analysis." Global Environmental Change, 14(2), 137–146. DOI: 10.1016/j.gloenvcha.2003.10.002

Xiang, W.N. & Clarke, K.C. (2003). "The use of scenarios in land-use planning." Environment and Planning B, 30(6), 885–909. DOI: 10.1068/b2945

Smith, E.P. (2014). "BACI design." In A.H. El-Shaarawi & W.W. Piegorsch (eds.), Encyclopedia of Environmetrics, 2nd ed., Wiley. DOI: 10.1002/9780470057339.vab001.pub2

Openshaw, S. & Taylor, P.J. (1979). "A million or so correlation coefficients: three experiments on the modifiable areal unit problem." In N. Wrigley (ed.), Statistical Applications in the Spatial Sciences, Pion, pp. 127–144.

Rounsevell, M.D.A. & Metzger, M.J. (2010). "Developing qualitative scenario storylines for environmental change assessment." Wiley Interdisciplinary Reviews: Climate Change, 1(4), 606–619. DOI: 10.1002/wcc.63

Mahmoud, M., Liu, Y., Hartmann, H., Stewart, S., Wagener, T., Semmens, D., Stewart, R., Gupta, H., Dominguez, D., Dominguez, F. et al. (2009). "A formal framework for scenario development in support of environmental decision-making." Environmental Modelling & Software, 24(7), 798–808. DOI: 10.1016/j.envsoft.2008.11.010

van Vuuren, D.P., Edmonds, J., Kainuma, M., Riahi, K., Thomson, A., Hibbard, K., Hurtt, G.C., Kram, T., Krey, V., Lamarque, J.F. et al. (2011). "The representative concentration pathways: an overview." Climatic Change, 109, 5–31. DOI: 10.1007/s10584-011-0148-z

IPCC. (2021). Climate Change 2021: The Physical Science Basis. Cambridge University Press.

### Scenario Planning Template

Processing ID: `planx_urban_resilience:scenario_planning_template`

#### 1. Overview

Creates an editable baseline/proposed score worksheet from any scored layer. Computes a scenario score by applying an expected reduction percentage to the baseline, then adds intervention labels, scenario metadata, expected reduction, draft status, and notes fields. The output is a QGIS vector layer that can be edited directly in the attribute table or exported to CSV for spreadsheet-based planning. This is the suite's studio-drafting tool: planners sketch intervention scenarios directly in QGIS, edit the proposed scores per unit, and then compare baseline vs scenario via Scenario Comparison / Delta Map.

#### 2. Theoretical Background

The template follows the participatory scenario planning methodology developed in land-use and climate adaptation studies (Patel et al., 2007; Kok et al., 2006). Rather than prescribing specific intervention magnitudes, the template empowers the planner to estimate expected reductions per planning unit based on local knowledge of costs, feasibility, and community context. The built-in intervention-type catalogue (green cooling, flood storage, shelter improvement, social support, air mitigation, drought retrofit, custom) maps to the six hazard families in the suite, ensuring each intervention is linked to a specific risk dimension. The expected reduction percentage is applied uniformly as a starting hypothesis; the planner then edits individual units to reflect heterogeneity.

Key assumptions.

- The uniform-reduction starting hypothesis is deliberately naive — it assumes every unit responds identically to the intervention, which is almost never true in practice; the template exists specifically to be edited unit-by-unit, not run once and trusted as-is.
- The tool performs no spatial or engineering validation of the proposed scores — a planner could enter an unrealistic scen_score (e.g. below what any real intervention could achieve) and the tool will not flag it; that judgement is left entirely to the planner's expertise.
- `edit_status` always initialises to "draft" regardless of how carefully the reduction percentage was chosen — it is a workflow marker, not a data-quality indicator.

When to use vs. when NOT to use. Use it as the starting point for any studio scenario-drafting exercise where planners need an editable worksheet rather than an automated optimiser. Do NOT use it as a substitute for genuine intervention-impact modelling — the expected-reduction percentages are planner-supplied hypotheses, not validated engineering or ecological performance figures; for hazard-specific reduction estimates, ground the EXPECTED_REDUCTION value in the relevant hazard tool's own literature (e.g. Bowler et al., 2010, for green-cooling magnitude) rather than guessing.

The engine is in `processing/reporting/scenario_template.py`.

#### 3. Mathematical Formulation

$$\text{base\_score}_i = \max(0, \min(100, v_i)) \quad \text{(from field or default)} \tag{1}$$ $$\text{scen\_score}_i = \max\left(0, \min\left(100, \text{base\_score}_i \cdot \left(1 - \frac{r}{100}\right)\right)\right) \tag{2}$$

where \(v_i\) is the existing baseline score value (if a base_score_field is provided; otherwise the default_base_score), and \(r\) is the expected reduction percentage (default 15%). The formula linearly scales: a 15% reduction on a baseline of 80 yields a scenario score of 68; on a baseline of 40 yields 34.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Input layer | Vector (any geometry) | Yes | Existing planning units or scored layer. If a baseline score field is supplied, its values are used; otherwise the default base score applies to all units.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (any geometry) | — | Planning units or scored layer.
`BASE_SCORE_FIELD` | Field (Numeric) | (optional) | Existing baseline score field. If omitted, all units use the default base score.
`DEFAULT_BASE_SCORE` | Double | 50.0 | Default baseline score when no field is provided.
`SCENARIO_NAME` | String | "Scenario A" | Scenario label (e.g., "Proposed Green Corridor").
`INTERVENTION_TYPE` | Enum | Green cooling | Intervention category: Green cooling / Flood storage / Shelter improvement / Social support / Air mitigation / Drought retrofit / Custom.
`EXPECTED_REDUCTION` | Double | 15.0 | Expected score reduction (%). Applied uniformly to all units as a starting hypothesis.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`scenario` | String | Scenario name (user-supplied).
`interv_type` | String | Selected intervention type (plain text).
`base_score` | Double | Baseline score (from field or default, clamped 0–100).
`scen_score` | Double | Computed scenario score = base_score × (1 − reduction/100), clamped 0–100.
`exp_reduce` | Double | Expected reduction percentage.
`edit_status` | String | Always "draft" — enables filtering for units that need expert review.
`notes` | String | Default note: "Review and edit scenario score before comparison."

#### 8. Interpretation Guide

The template is a studio drafting tool, not an automated optimiser. The expected reduction percentage provides a uniform starting point. Planners should then: (1) filter by `edit_status = 'draft'` to see units needing review, (2) edit `scen_score` manually for units where the uniform reduction is unrealistic (e.g., units with existing green infrastructure may have a smaller marginal reduction), (3) update `notes` with site-specific justification, (4) change `edit_status` to "reviewed" when done. After editing, run the output through Scenario Comparison / Delta Map to visualise the spatial impact of the proposed intervention package. For multi-scenario studios, run the template multiple times with different intervention types and scenario names, then compare all proposed scenarios side-by-side.

#### Academic References

Patel, M., Kok, K. & Rothman, D.S. (2007). "Participatory scenario construction in land use analysis: an insight into the experiences created by an approach in southern India." Land Use Policy, 24(3), 546–561. DOI: 10.1016/j.landusepol.2006.01.002

Kok, K., Patel, M., Rothman, D.S. & Quaranta, G. (2006). "Multi-scale narratives from an IA perspective: Part II. Participatory local scenario development." Futures, 38(3), 285–311. DOI: 10.1016/j.futures.2005.07.006

Swart, R.J., Raskin, P. & Robinson, J. (2004). "The problem of the future: sustainability science and scenario analysis." Global Environmental Change, 14(2), 137–146. DOI: 10.1016/j.gloenvcha.2003.10.002

Boardman, A.E., Greenberg, D.H., Vining, A.R. & Weimer, D.L. (2018). Cost-Benefit Analysis: Concepts and Practice. 5th ed., Cambridge University Press. DOI: 10.1017/9781108235594

IPCC. (2014). Climate Change 2014: Impacts, Adaptation, and Vulnerability. Part A: Global and Sectoral Aspects. Cambridge University Press.

Xiang, W.N. & Clarke, K.C. (2003). "The use of scenarios in land-use planning." Environment and Planning B, 30(6), 885–909. DOI: 10.1068/b2945

NIST. (2015). Community Resilience Planning Guide for Buildings and Infrastructure Systems. NIST Special Publication 1190. DOI: 10.6028/NIST.SP.1190

UNISDR. (2015). Sendai Framework for Disaster Risk Reduction 2015–2030. United Nations, Geneva.

### Resilience Score HTML Report

Processing ID: `planx_urban_resilience:resilience_score_html_report`

#### 1. Overview

Generates a standalone HTML report from any scored polygon or point layer with a 0–100 score field. Contents: summary cards (feature count, average score, score range, high-priority count), a class-distribution table, and a top-drivers table parsed from an optional notes/driver field. The file opens in any browser — no QGIS, network connection, or external asset needed to view it.

#### 2. Theoretical Background

Design rationale. This is a deliberately lightweight, dependency-free reporting tool, not a statistical model — its purpose is communication, not analysis. The report follows the well-established "front-load the summary" convention from data-visualisation and technical-communication practice (Tufte, 1983): the four headline cards appear before any table, so a reader gets the essential numbers in the first few seconds without scrolling. The class-distribution and driver tables are simple frequency counts (a Python `Counter` tally), deliberately not re-computed or re-classified — the report reflects exactly what is already in the source layer's fields, so any error in an upstream score or class field will show up here unchanged, which is intentional: this tool is a mirror of the data, not an independent check on it.

Key assumptions.

- The report is a snapshot of the layer's field values at generation time — it does not re-run any upstream hazard or synthesis algorithm, so if the source layer is later edited, the HTML file becomes stale and must be regenerated.
- Driver/notes text is parsed by splitting on commas and semicolons — a driver field using a different separator convention (e.g. pipe-delimited) will not tally correctly and needs reformatting first.
- The high-priority threshold (≥55) is hard-coded to match this suite's standard risk-class convention (High starts at 55) — it does not adapt to a different classification scheme the source layer might use.

When to use vs. when NOT to use. Use it for a quick, shareable, no-QGIS-required summary to send to a stakeholder or embed in a webpage. Do NOT use it where a narrative, prose explanation is needed — it produces only cards and tables, no interpretive text; for that, use Resilience Studio Brief instead, which generates full narrative sections around the same underlying statistics.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (any geometry) | Yes | Must have a numeric 0–100 score field.
Class field | Field | No | Any categorical field; tallied into the class-distribution table.
Driver/notes field | Field | No | Comma- or semicolon-separated labels; tallied into the top-drivers table (top 12 shown).

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (any geometry) | — | Scored layer.
`SCORE_FIELD` | Field (Numeric) | — | 0–100 score field.
`CLASS_FIELD` | Field | (optional) | Class/category field.
`DRIVER_FIELD` | Field | (optional) | Driver/notes field.
`TITLE` | String | "PlanX Urban Resilience Report" | Report title.
`OUTPUT_HTML` | File destination | — | Path for the generated HTML file.

#### 6. Output Description

One self-contained HTML file: four summary cards (feature count, average score, score range, high-priority count ≥55), a class-distribution table, a top-12-drivers table, and a footnote reporting the count of features with missing scores.

#### 8. Interpretation Guide

A large "missing score values" footnote count is itself a data-quality finding — it means a meaningful share of the layer never got scored by the upstream tool and the summary statistics are based on an incomplete subset. Treat the top-drivers table as a quick sanity check: if one driver dominates overwhelmingly, the underlying composite may be less "multi-hazard" in this study area than expected — cross-check against Multi-Hazard Composite Index's `mh_diversity` field for a rigorous version of the same question.

#### Academic References

Tufte, E.R. (1983). The Visual Display of Quantitative Information. Graphics Press, Cheshire, CT.

### Resilience Studio Brief (Markdown)

Processing ID: `planx_urban_resilience:resilience_studio_brief_markdown`

#### 1. Overview

Generates a first-draft executive-summary Markdown document from a scored layer: study context, executive summary, key-metrics table, class distribution, dominant drivers, suggested next actions, and a standing limitations section. The Markdown renders natively on GitHub/GitLab, in any Markdown editor, or can be pandoc-converted to PDF/DOCX for formal submission.

#### 2. Theoretical Background

Design rationale. This tool sits one step up the communication ladder from Resilience Score HTML Report: same underlying statistics (average, range, class tally, driver tally), but rendered as narrative prose with an executive-summary paragraph and a rule-based suggested-actions list, following the standard structure of a planning-studio memo or policy brief — context, summary, evidence, recommended next steps, caveats. The suggested-actions list is generated by simple conditional rules (e.g. "if average score ≥ 55, prioritise near-term design over monitoring"), not by any statistical inference — it is a template for a planner to edit, not an automated recommendation engine. The standing limitations section is included on every run by design, so a first-draft brief can never be mistaken for a finished, caveat-free document.

Key assumptions.

- The executive-summary paragraph and action suggestions are template-generated from simple thresholds on the same average/count statistics as the HTML report — they carry no additional analysis beyond what a reader could compute themselves from the key-metrics table.
- Like the HTML report, this is a snapshot of the source layer's current field values, not a live or re-computed analysis — regenerate after any upstream edit.
- The brief is explicitly labelled a "first draft" in its own limitations section — it is designed to be edited before submission, not distributed as-is.

When to use vs. when NOT to use. Use it whenever a narrative document (not just tables) is needed for a studio submission, internal memo, or stakeholder briefing — it saves the "blank page" step of drafting a first version. Do NOT use it as a finished, publication-ready document without review — treat every generated sentence as a draft a domain expert should read, verify, and very likely rewrite before it reaches an external audience.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (any geometry) | Yes | Same requirements as Resilience Score HTML Report.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (any geometry) | — | Scored layer.
`SCORE_FIELD` | Field (Numeric) | — | 0–100 score field.
`CLASS_FIELD` | Field | (optional) | Class/category field.
`DRIVER_FIELD` | Field | (optional) | Driver/notes field.
`TITLE` | String | "PlanX Urban Resilience Studio Brief" | Document title.
`STUDY_CONTEXT` | String (multi-line) | generic placeholder | Free-text paragraph describing the study area/purpose.
`OUTPUT_MD` | File destination | — | Path for the generated Markdown file.

#### 6. Output Description

One Markdown file with sections: Study Context, Executive Summary, Key Metrics (table), Class Distribution, Dominant Drivers/Notes, Suggested Next Actions, and Limitations.

#### 8. Interpretation Guide

Edit the Study Context section first — the default placeholder text is generic and should be replaced with the actual study area, timeframe, and purpose before the brief leaves the studio. Treat the Suggested Next Actions list as conversation starters for a design review, not as a finished recommendation.

### Recommended Actions Report (Markdown)

Processing ID: `planx_urban_resilience:recommended_actions_report`

#### 1. Overview

Turns a scored layer with optional class and dominant-hazard fields into a structured Markdown report: class distribution, top-N units table, and curated intervention checklists per hazard family (heat: cool roofs, green corridors, reflective pavements; flood: bioswales, detention basins, raised plinths; seismic: retrofit, soft-storey bracing; social: community centres, early-warning systems; air: green screens, traffic filtering; drought: drip irrigation, drought-tolerant species; emergency: satellite shelters, pre-positioned supplies). Each checklist item is a plain-language action with a rationale sentence.

#### 2. Theoretical Background

Design rationale. Unlike the two general-purpose report tools above, this one embeds a hard-coded intervention catalogue — one curated checklist of 3–4 concrete actions per hazard family, drawn from common urban-resilience planning practice rather than from a single citable study (the individual interventions listed, e.g. bioswales for flood or soft-storey seismic bracing, are each independently well-documented in hazard-specific engineering and planning literature, but the catalogue itself is this plugin's own curation, not a reproduction of any one external framework). The hazard family for each unit is detected by simple case-insensitive substring matching against the dominant-hazard field (e.g. any value containing "flood" maps to the flood catalogue) — a transparent, auditable rule rather than a fuzzy classifier, with an explicit generic fallback for unmatched or unrecognised hazard labels.

Key assumptions.

- Hazard-family detection is a substring match on the dominant-hazard field's text — a field using unexpected terminology (e.g. "inundation" instead of "flood") will not match and falls back to the generic checklist; check the field's actual values against the catalogue's keys (heat, flood, seism, social, air, drought, emergency) if a unit unexpectedly gets generic advice.
- The intervention catalogue is fixed and identical for every project — it has no awareness of local context, budget, regulatory environment, or site constraints; every listed action needs local validation before being treated as a real proposal.
- Only ONE dominant hazard per unit drives the checklist — a unit facing two comparably severe hazards (see Multi-Hazard Composite Index's `mh_diversity`) still gets only its single dominant hazard's checklist here.

When to use vs. when NOT to use. Use it to jump-start an intervention-brainstorming session with a concrete, hazard-specific starting checklist rather than a blank page. Do NOT use it as a substitute for site-specific engineering or ecological design — every checklist item is a generic category of intervention ("structural retrofit programme"), not a designed, costed, or feasibility-checked proposal for any specific unit.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (any geometry) | Yes | Must have a numeric score field.
Class field | Field | No | Risk-class field for grouping.
Dominant-hazard field | Field | No | Text field matched against the built-in catalogue keys.
Unit-name field | Field | No | Used to label units in the top-N table instead of feature IDs.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (any geometry) | — | Scored layer.
`SCORE_FIELD` | Field (Numeric) | — | 0–100 score field.
`CLASS_FIELD` | Field | (optional) | Class field.
`HAZARD_FIELD` | Field | (optional) | Dominant-hazard field.
`UNIT_NAME_FIELD` | Field | (optional) | Display-name field for the top-N table.
`TOP_N` | Integer | — | Number of highest-scored units to list.
`PROJECT` | String | (optional) | Project/study name for the report header.
`OUTPUT` | File destination | — | Path for the generated Markdown file.

#### 6. Output Description

One Markdown file: class distribution (with colour-coded emoji markers), a top-N units table, and one intervention checklist per hazard family actually present among the top-scoring units — each checklist item paired with a one-sentence rationale.

#### 8. Interpretation Guide

If every unit falls back to the generic checklist, check the dominant-hazard field's actual text values against the catalogue keys — a mismatch there is the most common reason for an unexpectedly generic-only report. Use the per-hazard checklists as a facilitation tool in a stakeholder workshop: ask which of the 3–4 listed actions is most feasible for this specific site, rather than treating the list as a ranked or prioritised recommendation.

### Apply Resilience Symbology

Processing ID: `planx_urban_resilience:apply_resilience_symbology`

#### 1. Overview

Applies pre-configured QGIS categorised or graduated renderers to the most common resilience output types: 0–100 risk scores (5-class custom ramp: Low #2A9D8F teal, Moderate #E9C46A amber, High #F4A261 orange, Very High #D62828 red), access classes (Covered/Watch/Underserved/Critical/Unreachable), scenario change classes (Improved/Stable/Worsened), and adaptation priority classes (Monitor/Medium/High/Immediate). Optional QML export for reusable cartographic presets.

#### 2. Theoretical Background

Design rationale. Every tool in this suite that produces a 0–100 score or a fixed set of class labels uses the SAME breakpoints and class names throughout (e.g. every risk score classifies at 35/55/75, every "Improved/Stable/Worsened" tool uses the identical three labels) — this tool exists specifically to exploit that consistency, applying one hard-coded, shared colour vocabulary across the whole plugin so a reader who learns "red means Very High risk" once never has to re-learn it for a different tool's output. The four colour ramps are deliberately colourblind-conscious choices (teal-to-red rather than green-to-red, avoiding the classic red-green confusion for the most common form of colour vision deficiency) applied consistently rather than left to QGIS's arbitrary default ramp assignment.

Key assumptions.

- The four presets assume the target field's values or labels EXACTLY match what the preset expects (0–100 numeric for the risk-score preset; the exact string labels "Covered"/"Watch"/etc. for the access-class preset) — a field with different label spelling or a different numeric range will not classify correctly and needs to be reconciled with the suite's naming convention first.
- Styling is applied in-place to the live QGIS layer, not to a new output layer — the algorithm has a side effect (repainting the map) in addition to (or instead of) producing a file, which is unusual for a Processing algorithm and worth remembering when scripting a batch of runs.

When to use vs. when NOT to use. Use it as the last step after any hazard, accessibility, scenario, or synthesis tool to get instant, consistent, presentation-ready symbology without manually configuring a renderer. Do NOT use it on a field whose classification scheme differs from this suite's convention (e.g. a different risk-class threshold set) — style that field manually or adapt its class labels to match one of the four presets first.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Layer to style | Vector (any geometry) | Yes | Any layer already loaded in the QGIS project.
Score or class field | Field | Yes | Must match the chosen preset's expected value type (numeric 0–100 for the risk-score preset, exact class-label strings for the others).

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`LAYER` | Vector layer | — | Layer to style (styled in place).
`FIELD` | Field | — | Score or class field to drive the renderer.
`PRESET` | Enum | Risk score 0-100 | Risk score 0-100 / Access class / Scenario change class / Adaptation priority class.
`REFRESH` | Boolean | True | Trigger a map canvas repaint after styling.
`OUTPUT_QML` | File destination | (optional) | Path to export the style as a reusable QML file.

#### 6. Output Description

No new layer or fields are created — the input layer's renderer is replaced in place. If `OUTPUT_QML` is supplied, a standalone QML style file is written for reuse on other layers or projects.

### Bivariate Choropleth Symbology

Processing ID: `planx_urban_resilience:bivariate_choropleth_symbology`

#### 1. Overview

Classifies polygon units into Low/Mid/High on two axes (risk and vulnerability) using tertile breaks, then applies a Stevens-style 3×3 bivariate palette via a QGIS categorised renderer. Adds `bv_class`, `bv_risk_bin`, and `bv_vuln_bin` fields so the symbology survives layer-style export. The 9-colour palette is the standard bivariate matrix: (R=risk axis, V=vulnerability axis; L/M/H = Low/Mid/High).

#### 2. Theoretical Background

Academic lineage. Bivariate choropleth mapping — encoding two variables simultaneously through a 2D colour matrix rather than showing two separate single-variable maps — was popularised by cartographers in the 1970s specifically to answer questions a single-variable map structurally cannot: not just "where is risk high" or "where is vulnerability high" separately, but "where are BOTH high at once," which is exactly the equity-and-exposure double-burden question this suite's own Equity-Adjusted Priority tool answers numerically. Stevens-style palettes (the specific 3×3 teal/grey/blue-purple scheme used here) are a widely adopted convention because the two colour dimensions (hue for one axis, saturation/lightness for the other) remain visually separable even for readers with moderate colour vision deficiency, unlike a naive red-green 2D blend.

Key assumptions.

- Tertile breaks (33rd/67th percentile) are computed FROM THE SUPPLIED LAYER'S OWN VALUE RANGE — the same extent-relative caveat that applies to the Social Vulnerability Index's min-max normalisation: re-running on a different extent changes where the Low/Mid/High boundaries fall, even for identical underlying values.
- A 3×3 matrix is a deliberate resolution limit — finer binning (4×4, 5×5) becomes difficult for most readers to decode visually; if finer resolution is needed, a different visualisation (e.g. a scatter plot of the two raw fields) communicates better than a bivariate choropleth.
- The two axes are assumed to be independently meaningful 0–100-style scores — feeding in two fields with very different scales or distributions can produce tertile bins that don't correspond to any intuitive threshold.

When to use vs. when NOT to use. Use it whenever the story is the INTERSECTION of two conditions — high hazard AND high vulnerability, high score AND high delta — since that is precisely what a bivariate map shows at a glance that two side-by-side single-variable maps do not. Do NOT use it when the two variables are strongly correlated (check with Multi-Hazard Correlation Matrix first) — a bivariate map of two nearly-identical fields wastes the second colour dimension on redundant information and a single-variable map communicates the same finding more simply.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Input layer | Vector (Polygon) | Yes | Must have two independent 0–100-style numeric fields.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon) | — | Polygon layer with two score fields.
`RISK_FIELD` | Field (Numeric) | — | First (row) axis, e.g. a composite hazard score.
`VULN_FIELD` | Field (Numeric) | — | Second (column) axis, e.g. Social Vulnerability Index.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`bv_risk_bin` | Integer | 0/1/2 = Low/Mid/High on the risk axis (tertile break).
`bv_vuln_bin` | Integer | 0/1/2 = Low/Mid/High on the vulnerability axis.
`bv_class` | String | Combined label, e.g. "R-H_V-H" (high risk, high vulnerability) — also drives the categorised renderer applied automatically.

### Resilience PDF Atlas

Processing ID: `planx_urban_resilience:resilience_pdf_atlas`

#### 1. Overview

Generates a multi-page QgsLayout PDF atlas from a scored layer. Presets: A3 standard, A4 compact, A3 studio (300 dpi). Pages include a cover sheet, study-area overview map, and one map per risk class with automated legend, scale bar, and title block. The atlas is a QGIS print layout — open it in the Layout Manager to fine-tune before final export.

#### 2. Theoretical Background

Design rationale. This tool automates the standard cartographic "map book" or atlas convention long used in planning documents: a cover sheet establishing context, an overview map orienting the reader to the whole study area, then successive detail pages that each isolate one subset of the data (here, one risk class per page) so a reader can focus on one story at a time rather than parsing every risk level simultaneously on a single crowded map. Building the atlas via QGIS's native `QgsLayout` engine (rather than a rasterised image export) means every page remains a first-class, editable QGIS print layout — a planner can open it in the Layout Manager afterward and adjust any element before final export, exactly the same as an atlas built by hand.

Key assumptions.

- Page count scales with the number of DISTINCT values in the class field — a field with many fine-grained classes (or one accidentally left as a continuous numeric field rather than a small set of class labels) can generate an unwieldy number of pages; use a coarse classification (the suite's standard 4–5 class convention) for the class field, not a raw score.
- The 300 dpi "Studio" preset produces significantly larger PDF files and longer render times than the two lower-resolution presets — reserve it for final print production, not for quick iterative review.
- Legend, scale bar, and title-block placement are automated defaults tuned for a typical single-study-area extent — very elongated or multi-part study areas may need manual layout adjustment after generation.

When to use vs. when NOT to use. Use it to produce a print-ready, multi-page deliverable straight from a scored QGIS layer without manually building a print layout from scratch. Do NOT use it for a quick on-screen check of a single result — the HTML report tools (Resilience Score HTML Report, Resilience Studio Brief) are faster to generate and view for that purpose; reserve the PDF atlas for the polished, presentation-ready deliverable stage.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Scored layer | Vector (Polygon/Point) | Yes | Must have a 0–100 numeric score field; an optional class field drives per-class page splitting.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon/Point) | — | Scored layer.
`SCORE_FIELD` | Field (Numeric) | — | 0–100 score field, classified with the pure-Python engine also used by other tools (equal interval / quantile / Jenks-style natural breaks).
`CLASS_FIELD` | Field (String) | (optional) | Drives one detail page per distinct class value.
`TITLE` | String | "PlanX Urban Resilience — Atlas" | Cover-page title.
`SUBTITLE` | String | generic screening disclaimer | Cover-page subtitle.
`AUTHOR` | String | (optional) | Author/institution credit line.
`PROJECT` | String | (optional) | Project/study-area name.
`METHOD` | Enum | Quantile | Cartographic classification method: Equal interval / Quantile / Natural breaks (Jenks).
`ATLAS_PRESET` | Enum | A3 Standard (200 dpi) | A3 Standard / A4 Compact (150 dpi) / A3 Studio HQ (300 dpi).
`OUTPUT` | File destination | — | Output PDF path.

#### 6. Output Description

One multi-page PDF: a cover sheet (title/subtitle/author/project), a study-area overview map with the full graduated-colour classification, and one detail page per distinct class value (when a class field is supplied), each with its own legend, scale bar, and title block.

### Multi-Hazard Correlation Matrix

Processing ID: `planx_urban_resilience:multi_hazard_correlation_matrix`

#### 1. Overview

Computes pairwise Pearson and Spearman correlations between N numeric score fields (2–12) on the same layer. Outputs a standalone HTML report with a colour-coded heatmap, top-10 correlations table, and strength interpretation, plus a long-format QGIS table sink for further analysis. Pure Python — no numpy/scipy dependency. Operational use: spot redundancy ("flood and seismic scores move together in this city — pick one for the composite") or surprise divergences ("heat is uncorrelated with everything — it captures a unique spatial dimension").

#### 2. Theoretical Background

Correlation analysis between hazard scores serves as a diagnostic step before building a multi-hazard composite index. Highly correlated hazard fields (|r| > 0.80) contribute redundant spatial information: including both overweights that risk dimension and inflates the composite's sensitivity to shared measurement errors (OECD, 2008). The Pearson coefficient measures linear association; the Spearman rank coefficient measures monotonic association and is robust to outliers and non-normality (Spearman, 1904). Reporting both allows the analyst to distinguish between genuinely linear relationships and rank-consistent but non-linear ones (e.g., a threshold effect where flood and heat correlate only above a certain score). The engine implements both correlations in pure Python using the standard product-moment formulas, with ties in Spearman ranks handled by average-rank assignment.

Key assumptions.

- Correlation measures association, not causation — two hazards correlating strongly could share a genuine causal driver (e.g. both depend on elevation), or could simply co-vary by coincidence in this particular study area; do not over-interpret a strong r as proof of a mechanistic link.
- Pairwise (not listwise) exclusion of missing values means each cell in the matrix may be computed from a different, and possibly quite different, sample size (`n_pairs`) — always check `n_pairs` alongside the correlation coefficient itself before trusting a strong correlation computed from very few valid pairs.
- Both coefficients assume the paired observations are spatially independent for standard-error interpretation, which spatial data essentially never satisfies (spatial autocorrelation means neighbouring units are not independent draws) — treat the reported strength labels as descriptive summaries, not as formal hypothesis-test results.

When to use vs. when NOT to use. Use it as a diagnostic step before building any multi-hazard composite — running it first tells you which input fields are redundant and which are genuinely independent, informing both weight choices and which fields to include at all. Do NOT use it to select composite weights automatically — a low correlation with everything else does not by itself justify a high weight; that remains a planning judgement the correlation matrix only informs, not decides.

The engine is in `processing/reporting/correlation_matrix.py`.

#### 3. Mathematical Formulation

$$r_P = \frac{\sum (x_i - \bar{x})(y_i - \bar{y})}{\sqrt{\sum (x_i - \bar{x})^2 \sum (y_i - \bar{y})^2}} \tag{1}$$ $$\rho_S = r_P(\text{rank}(x), \text{rank}(y)) \quad \text{(ties receive average rank)} \tag{2}$$ $$n_{\text{pairs}} = |\{i : x_i \neq \text{null} \land y_i \neq \text{null}\}| \tag{3}$$

where pairs with missing values in either field are excluded listwise from that pairwise correlation. The heatmap colour interpolation maps \(r \in [-1, 0]\) to blue tones and \(r \in [0, 1]\) to red tones, with grey for NULL pairs. Strength interpretation labels: |r| ≥ 0.7 = strong, ≥ 0.4 = moderate, ≥ 0.2 = weak, < 0.2 = negligible.

#### 4. Input Data Requirements

Input | Type | Required | Notes
--- | --- | --- | ---
Input layer | Vector (Polygon/Point) | Yes | Must have 2–12 numeric score fields. Missing values are excluded pairwise.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon/Point) | — | Layer with multiple numeric score fields.
`FIELDS` | Field (Numeric, multiple) | — | 2–12 fields to correlate pairwise.
`PROJECT` | String | (optional) | Project/study name for the HTML report title.
`OUTPUT_HTML` | File | — | Path for the HTML heatmap report.
`OUTPUT_TABLE` | Table (sink) | — | Long-format QGIS table (field_a, field_b, pearson_r, spearman_r, n_pairs).

#### 6. Output Description

Output | Key Fields | Description
--- | --- | ---
HTML report | — | Standalone HTML with Pearson heatmap, Spearman heatmap, top-10 correlations table, and legend.
Long-format table | `field_a`, `field_b`, `pearson_r`, `spearman_r`, `n_pairs` | One row per pair (upper triangle only). Null if < 3 valid pairs.

#### 8. Interpretation Guide

|r| > 0.80, both Pearson and Spearman: near-redundant — the two hazards carry essentially the same spatial information. Consider dropping one from the composite or using their mean. Pearson high, Spearman low: the relationship is driven by a few extreme values — check for outliers driving the linear correlation. r ≈ 0: the hazards are spatially independent — keeping both in the composite adds genuine information. r < -0.50: inverse relationship (e.g., flood-prone lowlands vs heat-prone ridgetops in the same city) — the composite must handle this divergence explicitly. n_pairs varying across the table: different fields have different missing-data patterns — fields with high missing rates (< 80% of total features) should be flagged as incomplete for composite construction.

#### Academic References

Spearman, C. (1904). "The proof and measurement of association between two things." The American Journal of Psychology, 15(1), 72–101. DOI: 10.2307/1412159

Pearson, K. (1895). "Note on regression and inheritance in the case of two parents." Proceedings of the Royal Society of London, 58, 240–242. DOI: 10.1098/rspl.1895.0041

OECD. (2008). Handbook on Constructing Composite Indicators: Methodology and User Guide. OECD Publishing. DOI: 10.1787/9789264043466-en

Nardo, M., Saisana, M., Saltelli, A., Tarantola, S., Hoffman, A. & Giovannini, E. (2005). Handbook on Constructing Composite Indicators. OECD Statistics Working Papers, 2005/03. DOI: 10.1787/533411815016

Kendall, M.G. (1938). "A new measure of rank correlation." Biometrika, 30(1–2), 81–93. DOI: 10.1093/biomet/30.1-2.81

Cutter, S.L., Boruff, B.J. & Shirley, W.L. (2003). "Social vulnerability to environmental hazards." Social Science Quarterly, 84(2), 242–261. DOI: 10.1111/1540-6237.8402002

IPCC. (2014). Climate Change 2014: Impacts, Adaptation, and Vulnerability. Part A. Cambridge University Press.

UNDRR. (2019). Global Assessment Report on Disaster Risk Reduction. United Nations Office for Disaster Risk Reduction, Geneva.

### 3D Risk Volume Export

Processing ID: `planx_urban_resilience:risk_3d_volume_export`

#### 1. Overview

Converts a 0–100 scored polygon layer into a GeoJSON file in EPSG:4326 with `extrusion_height` (= score x scale_factor) and `color_hex` attributes per feature. Three colour ramp choices: Yellow-Red (sequential), Blue-Red (diverging), or Greys. An optional base-height field is additive — useful when polygons already carry building-height attributes and the risk extrusion should stack on top. The GeoJSON drops straight into PlanX 3D City Viewer, Three.js, deck.gl, or Cesium for interactive 3D exploration — the suite's most dramatic visual output, transforming a choropleth map into a city silhouette where the tallest, most saturated blocks are the worst hot spots.

#### 2. Theoretical Background

3D visualisation of risk data builds on the cartographic extrusion metaphor formalised in geovisualisation research (MacEachren et al., 2004; Slocum et al., 2009). Extruding polygons by a risk score exploits the human visual system's sensitivity to height variation (Ware, 2012): equal risk differences are perceived as equal vertical displacement, making the 3D view a direct physicalisation of the risk surface. The CRS transformation to EPSG:4326 (WGS 84) ensures compatibility with all major web-mapping libraries (deck.gl, Cesium, Mapbox) which expect geographic coordinates. The colour-ramp encoding is redundant with height — providing two visual channels for the same variable — which improves interpretability for viewers who may not perceive height accurately (due to perspective, occlusion, or colour vision deficiency).

Key assumptions.

- Extrusion height is a purely visual encoding, not a physical building height (unless BASE_HEIGHT_FIELD carries real building heights and is deliberately used additively) — a viewer unfamiliar with the convention could mistake the risk extrusion for an actual 3D city model of built form.
- MAX_HEIGHT (default 100 m) is a display-tuning parameter with no inherent meaning — the same score always maps to the same relative height within one export, but the absolute height is arbitrary and must be chosen to look reasonable at the study area's actual scale (very large or very small extents may need a different MAX_HEIGHT to avoid needle-thin or building-sized-and-indistinguishable extrusions).
- The color and height channels both encode the SAME variable — this tool has no mechanism to show two different variables via colour and height simultaneously (that would need this suite's Bivariate Choropleth Symbology approach adapted to 3D, which does not exist here).

When to use vs. when NOT to use. Use it for the suite's most dramatic stakeholder-facing visual, especially when a study area's 2D choropleth has become too familiar or too easy for a non-technical audience to skim past. Do NOT use it as a replacement for the 2D choropleth in an analytical or technical report — 3D perspective introduces occlusion (tall extrusions can hide shorter ones behind them) and makes precise value comparison harder than a flat map; keep both views side by side, as the Interpretation Guide below recommends.

The engine is in `processing/reporting/risk_3d_export.py`.

#### 3. Mathematical Formulation

$$\text{norm}_i = \frac{\min(\max(s_i, 0), 100)}{100} \tag{1}$$ $$h_i = h_{\max} \cdot \text{norm}_i + b_i \tag{2}$$ $$\text{color}_i = \begin{cases} f_{\text{YlRd}}(\text{norm}) & \text{(sequential)} \\ f_{\text{BuRd}}(\text{norm}) & \text{(diverging)} \\ f_{\text{Greys}}(\text{norm}) & \text{(greyscale)} \end{cases} \tag{3}$$

where \(s_i\) is the score (0–100), \(h_{\max}\) is the maximum extrusion height (default 100 m), \(b_i\) is the optional base height, and the colour functions are piecewise-linear interpolations through anchor colours. Polygons are reprojected from the source CRS to EPSG:4326 via QGIS's coordinate transform.

#### 5. Parameters

Parameter | Type | Default | Description
--- | --- | --- | ---
`INPUT` | Vector (Polygon) | — | Scored polygon layer.
`SCORE_FIELD` | Field (Numeric) | — | Score field (0–100).
`MAX_HEIGHT` | Double | 100.0 | Maximum extrusion height in metres for score = 100.
`BASE_HEIGHT_FIELD` | Field (Numeric) | (optional) | Base height field in metres (added to extrusion).
`COLOR_RAMP` | Enum | Yellow-Red | Colour ramp: Yellow-Red / Blue-Red / Greys.
`OUTPUT_GEOJSON` | File | — | Output GeoJSON file path.

#### 6. Output Description

Field | Type | Description
--- | --- | ---
`extrusion_height` | Double | Total extrusion height = base_height + max_height x (score/100).
`color_hex` | String | CSS hex colour (e.g., #fdbb84) from the selected ramp.
`score_norm` | Double | Normalised score = score/100 (0 to 1).

#### 8. Interpretation Guide

The 3D view is most effective with max_height = 100 m and a study area of 2–5 km extent. For presentation: use Yellow-Red for single-hazard risk; use Blue-Red (diverging) for delta maps (blue = improved, red = worsened). The 3D view is a complement to, not a replacement for, the 2D choropleth — use both in a report side by side. The GeoJSON metadata block records the source score field, maximum height, colour ramp, and feature count for provenance tracking.

#### Academic References

MacEachren, A.M., Gahegan, M., Pike, W., Brewer, I., Cai, G., Lengerich, E. & Hardisty, F. (2004). "Geovisualization for knowledge construction and decision support." IEEE Computer Graphics and Applications, 24(1), 13–17. DOI: 10.1109/MCG.2004.1255801

Slocum, T.A., McMaster, R.B., Kessler, F.C. & Howard, H.H. (2009). Thematic Cartography and Geovisualization. 3rd ed., Pearson.

Ware, C. (2012). Information Visualization: Perception for Design. 3rd ed., Morgan Kaufmann. DOI: 10.1016/C2009-0-62432-6

Tufte, E.R. (1990). Envisioning Information. Graphics Press.

Brewer, C.A. (2005). Designing Better Maps: A Guide for GIS Users. ESRI Press.

Bertin, J. (1983). Semiology of Graphics: Diagrams, Networks, Maps. University of Wisconsin Press.

NIST. (2015). Community Resilience Planning Guide for Buildings and Infrastructure Systems. NIST Special Publication 1190. DOI: 10.6028/NIST.SP.1190

Dollner, J. & Walther, M. (2003). "Real-time expressive rendering of city models." Proceedings of the 7th International Conference on Information Visualization (IV'03), 245–250. DOI: 10.1109/IV.2003.1217985

## Appendix A: Data Sources

The Urban Resilience suite is data-agnostic — every algorithm accepts generic vector and raster inputs. Below are the input types each module expects and common open-data sources for each.

Data Type | Used By | Common Open Sources
--- | --- | ---
Building footprints (polygon) | Seismic, Heat, Flood | OpenStreetMap (QuickOSM plugin), Microsoft Building Footprints, national cadastral portals
Road network (line) | Emergency (all), Air, Flood | OpenStreetMap, national road agencies
DEM (raster) | Flood | SRTM 30m, ALOS AW3D30, Copernicus GLO-30, national LIDAR portals
Green space / tree canopy (polygon) | Heat, Drought | OpenStreetMap (landuse=*, natural=*), Copernicus Urban Atlas, i-Tree Canopy
Water bodies (polygon/line) | Heat, Flood, Drought | OpenStreetMap, Copernicus Water Bodies
Population / demographics | Social, Population-Weighted, Evacuation | National statistical institutes, WorldPop, Global Human Settlement Layer (GHSL)
Shelters / safe assembly (point/polygon) | Emergency (all) | Municipal emergency plans, OpenStreetMap (amenity=social_facility, emergency=*), Red Cross / UNHCR data
Industrial / emission sources (point/polygon) | Air | National environmental agencies, E-PRTR (European Pollutant Release and Transfer Register)
Critical facilities (point/polygon) | Critical Infrastructure | OpenStreetMap (amenity=school/hospital/fire_station/police), national infrastructure databases

CRS requirement. All vector inputs must share a common projected coordinate system (e.g. UTM zone or national grid). Results from unprojected geographic CRS (EPSG:4326) are geometrically meaningless — distances, areas, and buffers will be in decimal degrees, not metres. Reproject all layers before running any algorithm.

## Appendix B: Symbolization

The suite's default symbology recommendations follow a consistent visual grammar:

Data Type | Renderer | Ramp | Classes | Notes
--- | --- | --- | --- | ---
Hazard score 0–100 (heat, flood, air, composite, adaptation) | Graduated (quantile or natural breaks) | YlOrRd | 5 | Higher score = warmer colour = more risk. This is the suite's primary visual convention.
Access / evacuation time | Graduated | RdYlGn (reversed) | 5 | Green = fast/covered, red = slow/critical. The reverse of the hazard convention.
Recovery capacity | Graduated | Greens | 5 | High = good recovery (green), the opposite valence to hazard scores.
Social vulnerability | Graduated (quantile) | YlOrBr | 5 | Darker = more vulnerable.
Delta / change scores | Graduated | RdBu (diverging) | 5–7 | Blue = improved, white = stable, red = worsened. Centre the classification on zero.
Gi* hot/cold spots | Categorised | Custom 7-class | 7 | Hot-99=#b30000, Hot-95=#e34a33, Hot-90=#fc8d59, NS=#cccccc, Cold-90=#91bfdb, Cold-95=#4575b4, Cold-99=#313695.
LISA clusters + outliers | Categorised | Custom 5-class | 5 | HH=#d7191c, LL=#2c7bb6, HL=#fdae61, LH=#abd9e9, NS=#eeeeee.
Bivariate (risk × vulnerability) | Categorised | Stevens 3×3 | 9 | See Bivariate Choropleth Symbology algorithm for exact hex codes.
Buildings / critical facilities (exposed) | Categorised or graduated | OrRd (by score) | 3–5 | Red markers for exposed features in high-score cells.
Network edges (criticality / congestion) | Graduated | OrRd (criticality) or YlOrRd (congestion) | 5 | Line width scaled by score: 0.3 mm (low) to 2.0 mm (high).

## Appendix C: Glossary

Term | Definition
--- | ---
Adaptation priority | A composite score (0–100) synthesising multiple hazard dimensions into a single intervention-prioritisation index. Higher = more urgent.
B/C ratio | Benefit-to-cost ratio. A dimensionless measure of intervention efficiency. Values > 1 mean benefits exceed costs; higher values mean more efficient interventions.
Betweenness (edge) | The number of shortest paths that traverse a given edge. High-betweenness edges are network bottlenecks.
Benjamini-Hochberg (BH) FDR | A procedure that controls the false discovery rate across multiple simultaneous hypothesis tests. More conservative than raw p-values; preferred when reporting Gi* or LISA results.
Cascading hazard | A chain where a primary hazard (earthquake) triggers secondary consequences (debris → road blockage → accessibility loss).
Dijkstra (multi-source) | A single-run shortest-path algorithm initialised from all destination nodes simultaneously rather than one at a time. O(E log V) for the entire graph.
Equity weight | A parameter (0–1) controlling how much social vulnerability amplifies adaptation priority. 0 = no adjustment; 1 = SVI=100 doubles the score.
Getis-Ord Gi* | A local spatial autocorrelation statistic that identifies statistically significant clusters of high values (hot spots) or low values (cold spots).
IDW (Inverse Distance Weighting) | A deterministic spatial interpolation method where the value at an unobserved point is a distance-weighted average of nearby observed values.
Latin Hypercube Sampling (LHS) | A stratified Monte Carlo method that divides each input dimension into equal-probability intervals, ensuring the full range is explored with fewer samples than random sampling.
LISA (Local Moran's I) | A decomposition of the global Moran's I statistic into per-feature contributions, revealing both clusters (HH, LL) and outliers (HL, LH).
MCLP (Maximal Covering Location Problem) | The facility-location problem: select K sites to maximise the population covered within a fixed distance. Greedy solution is (1 − 1/e)-approximate.
Monte Carlo | A simulation method that draws random samples from probability distributions to estimate outcomes. In the seismic module, each building's collapse is a Bernoulli draw with probability P_collapse.
NPV (Net Present Value) | The sum of all discounted future cash flows over an analysis horizon. A positive NPV means the investment's benefits exceed its costs in present-value terms.
Pareto frontier | The set of options where no other option is both cheaper AND more beneficial. Rank-1 features on the frontier are objectively optimal trade-offs.
Recovery capacity | A composite index (0–100) measuring a unit's ability to self-recover after a shock. Higher = better recovery potential. The complement of vulnerability.
Shannon entropy (diversity) | A measure of how evenly distributed a set of values is. Used in the Multi-Hazard Composite to score whether a unit is single-hazard-dominated (0) or multi-stressed (100).
SVI (Social Vulnerability Index) | A normalised 0–100 composite of demographic indicators (elderly, children, disability, low-income, population density) reflecting a community's susceptibility to hazard impacts.

## Appendix D: Complete Bibliography

Anselin, L. (1995). "Local indicators of spatial association — LISA." Geographical Analysis, 27(2), 93–115.

Benjamini, Y. & Hochberg, Y. (1995). "Controlling the false discovery rate." Journal of the Royal Statistical Society: Series B, 57(1), 289–300.

Beven, K.J. & Kirkby, M.J. (1979). "A physically based, variable contributing area model of basin hydrology." Hydrological Sciences Journal, 24(1), 43–69.

Boardman, A.E. et al. (2018). Cost-Benefit Analysis: Concepts and Practice. 5th ed., Cambridge University Press.

Bowler, D.E. et al. (2010). "Urban greening to cool towns and cities: a systematic review." Landscape and Urban Planning, 97(3), 147–155.

Brandes, U. (2001). "A faster algorithm for betweenness centrality." Journal of Mathematical Sociology, 25(2), 163–177.

Briggs, D.J. et al. (1997). "Mapping urban air pollution using GIS: a regression-based approach." International Journal of Geographical Information Science, 11(7), 699–718.

Cardinal, J. et al. (2011). "A unified framework for rich routing problems." Computers & Operations Research, 38(5), 831–843.

Cervero, R. & Kockelman, K. (1997). "Travel demand and the 3Ds: density, diversity, and design." Transportation Research Part D, 2(3), 199–219.

Church, R.L. & ReVelle, C.S. (1974). "The maximal covering location problem." Papers of the Regional Science Association, 32, 101–118.

Cornuéjols, G., Fisher, M.L. & Nemhauser, G.L. (1977). "Location of bank accounts to optimize float." Management Science, 23(8), 789–810.

Cutter, S.L., Ash, K.D. & Emrich, C.T. (2014). "The geographies of community disaster resilience." Global Environmental Change, 29, 65–77.

Cutter, S.L., Boruff, B.J. & Shirley, W.L. (2003). "Social vulnerability to environmental hazards." Social Science Quarterly, 84(2), 242–261. DOI: 10.1111/1540-6237.8402002

Cutter, S.L. & Finch, C. (2008). "Temporal and spatial changes in social vulnerability to natural hazards." Proceedings of the National Academy of Sciences, 105(7), 2301–2306.

Daskin, M.S. (2013). Network and Discrete Location: Models, Algorithms, and Applications. 2nd ed., Wiley.

Dijkstra, E.W. (1959). "A note on two problems in connexion with graphs." Numerische Mathematik, 1, 269–271.

European Commission. (2014). Guide to Cost-Benefit Analysis of Investment Projects. Directorate-General for Regional and Urban Policy.

FEMA. (2003). HAZUS-MH MR4 Technical Manual. Federal Emergency Management Agency, Washington, DC.

FEMA. (2018). Seismic Performance Assessment of Buildings, Volume 1 — Methodology (FEMA P-58-1), Second Edition. Washington, DC.

Fisher, P.F. & Tate, N.J. (2006). "Causes and consequences of error in digital elevation models." Progress in Physical Geography, 30(4), 467–489.

Freeman, L.C. (1977). "A set of measures of centrality based on betweenness." Sociometry, 40(1), 35–41.

Gao, J., Barzel, B. & Barabási, A.L. (2016). "Universal resilience patterns in complex networks." Nature, 530, 307–312.

Getis, A. & Ord, J.K. (1992). "The analysis of spatial association by use of distance statistics." Geographical Analysis, 24(3), 189–206.

Girvan, M. & Newman, M.E.J. (2002). "Community structure in social and biological networks." Proceedings of the National Academy of Sciences, 99(12), 7821–7826.

Goretti, A. & Sarli, V. (2006). "Road network and damaged buildings in urban areas: short and long-term interaction." Bulletin of Earthquake Engineering, 4(2), 159–175. DOI: 10.1007/s10518-006-9004-3

Hansen, W.G. (1959). "How accessibility shapes land use." Journal of the American Institute of Planners, 25(2), 73–76.

IPCC. (2014). Climate Change 2014: Impacts, Adaptation, and Vulnerability. Part A: Global and Sectoral Aspects. Cambridge University Press.

IPCC. (2021). Climate Change 2021: The Physical Science Basis. Contribution of Working Group I to the Sixth Assessment Report. Cambridge University Press.

Luo, W. & Wang, F. (2003). "Measures of spatial accessibility to health care in a GIS environment." Environment and Planning B, 30(6), 865–884.

MacArthur, R.H. & Wilson, E.O. (1967). The Theory of Island Biogeography. Princeton University Press.

McDonald, R.I. et al. (2008). "The implications of current and future urbanization for global protected areas and biodiversity conservation." Biological Conservation, 141(6), 1695–1703.

McKay, M.D., Beckman, R.J. & Conover, W.J. (1979). "A comparison of three methods for selecting values of input variables." Technometrics, 21(2), 239–245.

NIST. (2015). Community Resilience Planning Guide for Buildings and Infrastructure Systems. NIST Special Publication 1190.

Nowak, D.J., Crane, D.E. & Stevens, J.C. (2006). "Air pollution removal by urban trees and shrubs in the United States." Urban Forestry & Urban Greening, 4(3–4), 115–123.

Nowak, D.J. et al. (2008). "A ground-based method of assessing urban forest structure and ecosystem services." Arboriculture & Urban Forestry, 34(6), 347–358.

Oke, T.R. (1982). "The energetic basis of the urban heat island." Quarterly Journal of the Royal Meteorological Society, 108(455), 1–24.

Ord, J.K. & Getis, A. (1995). "Local spatial autocorrelation statistics: distributional issues and an application." Geographical Analysis, 27(4), 286–306.

Radke, J. & Mu, L. (2000). "Spatial decompositions, modeling and mapping service regions to predict access to social programs." Geographic Information Sciences, 6(2), 105–112.

Saltelli, A. et al. (2004). Sensitivity Analysis in Practice. Wiley.

Shepard, D. (1968). "A two-dimensional interpolation function for irregularly-spaced data." Proceedings of the 1968 ACM National Conference, 517–524.

Shi, L. et al. (2016). "Roadmap towards justice in urban climate adaptation research." Nature Climate Change, 6(2), 131–137. DOI: 10.1038/nclimate2841

Stewart, I.D. & Oke, T.R. (2012). "Local Climate Zones for urban temperature studies." Bulletin of the American Meteorological Society, 93(12), 1879–1900. DOI: 10.1175/BAMS-D-11-00019.1

Tehrany, M.S., Pradhan, B. & Jebur, M.N. (2014). "Flood susceptibility mapping using a novel ensemble weights-of-evidence and support vector machine models in GIS." Journal of Hydrology, 512, 332–343.

Toregas, C., Swain, R., ReVelle, C. & Bergman, L. (1971). "The location of emergency service facilities." Operations Research, 19(6), 1363–1373. DOI: 10.1287/opre.19.6.1363

UNDRR. (2019). Global Assessment Report on Disaster Risk Reduction. United Nations Office for Disaster Risk Reduction, Geneva.

Vos, P.E.J., Maiheu, B., Vankerkom, J. & Janssen, S. (2013). "Improving local air quality in cities: to tree or not to tree?" Environmental Pollution, 183, 113–122. DOI: 10.1016/j.envpol.2012.10.021

Yperman, I. (2007). The Link Transmission Model for Dynamic Network Loading. PhD thesis, KU Leuven.

PlanX Urban Resilience — Academic Reference Manual · v2.1.1 · 43 Algorithms
Yusuf Eminoğlu · August 2026 · GitHub