PlanX

Urban Analytics Studio — Comprehensive Academic Reference Manual

Covering PlanX v4.10.1 · 69 Algorithms · 19 Tool Groups

August 2026

Yusuf Eminoğlu · github.com/YusufEminoglu/PlanX

How to Use This Manual

This manual is the definitive academic reference for every algorithm in PlanX — Urban Analytics Studio (v4.10.1). It is written for urban planners, spatial analysts, researchers, and students who need to understand not just what a tool produces, but why the method works, how to configure it correctly, and what the results mean in a real planning context.

Structure of each algorithm entry

Every one of the 69 algorithms is documented to an identical eight-section template:

  1. Overview — what problem it solves and how, in one page
  2. Theoretical Background — the academic lineage, key contributors, assumptions
  3. Mathematical Formulation — every equation with term-by-term explanation
  4. Input Data Requirements — what data you need, where to get it, how to prepare it
  5. Parameters — every dialog tab, every input, with recommendations per scenario
  6. Output Description — every output field, its meaning, units, and typical ranges
  7. Symbolic Representation — how to style the results in QGIS for publication
  8. Interpretation Guide — how to read the results, common pitfalls, planning actions

Academic references

Each algorithm section concludes with a curated list of academic references — the foundational papers, textbooks, and technical reports that underpin the method. Where a DOI exists, it is provided as a clickable link to the published version. Conference proceedings, institutional reports, and older monographs that do not assign DOIs are cited with full bibliographic detail and, where available, a stable institutional URL. All references have been selected to support both theoretical understanding and applied planning practice.

Typographic conventions

Note on CRS. Unless otherwise stated, all tools that operate on metric distances require a projected coordinate reference system (units in metres). Geographic CRS (degrees) will produce nonsensical results or raise an error. Use a local UTM zone or an equal-area projection appropriate for your study region.

Introduction to PlanX

PlanX — Urban Analytics Studio is a comprehensive geospatial analysis platform delivered as a QGIS Processing provider. Its 69 algorithms span the full urban-planning workflow: from understanding the existing city (network analysis, morphology, microclimate) through diagnosing problems (equity, accessibility, walkability) to designing and evaluating interventions (optimization, growth simulation, scenario comparison).

Architecture

PlanX follows a strict three-layer architecture:

  1. Algorithm layer (algorithms/alg_*.py) — thin QGIS Processing wrappers that define parameters, validate inputs, and format outputs. They contain no analytical logic.
  2. Engine layer (engine/*.py) — pure NumPy/SciPy modules that implement every method. They contain no QGIS imports and can be tested, profiled, and reasoned about independently.
  3. Plugin shell (planx.py, provider.py, studio_dock.py) — the QGIS integration surface: menus, docks, the tool browser, and the dashboard.

This separation means the mathematics described in this manual corresponds directly to the NumPy implementation in engine/ — what you read here is what runs.

The 19 tool groups

#GroupToolsDomain
1Network Analysis6Graph construction, shortest paths, service areas, criticality
2Centrality & Space Syntax2Closeness, betweenness, angular integration & choice
3Urban Morphology4Building shape, tessellation, density, street form
4Accessibility115-minute multi-amenity access scores
5Microclimate10Solar, shadow, wind, heat, noise, emissions, air quality
6Plan Standards & QA3Land-use balance, facility adequacy, density grids
7Reporting & Dashboard7Reports, scenarios, snapshots, ranking, audits
8Optimization5Facility location, allocation, land-use Pareto fronts
9Equity3Gini, Theil, Lorenz curves, demographic cross-tabs
10Walkability4Audit scores, slope comfort, street environment, route quality
11Transit3GTFS import, frequency, travel-time access (RAPTOR)
12Visibility3Viewshed, isovist field, landmark exposure
13Population & Housing4Cohort-component projection, housing needs, capacity
14Green Infrastructure2Park access hierarchy, connectivity (PC/dPC)
15Urban Growth3Land-cover change, CA simulation, sprawl metrics
16Cycling2Level of Traffic Stress, low-stress connectivity islands
17Hazard Screening3Flow accumulation, HAND inundation, flood exposure
18Travel Demand3Trip generation, gravity distribution, mode split
19Seismic Risk1Seismic debris estimation

Design principles

1. Network Analysis

The Network Analysis group provides the foundational graph infrastructure for the entire PlanX platform. Streets are modelled as an undirected primal graph where junctions are nodes and street segments are edges weighted by length (or a user-specified cost field). On this graph, the group builds the six classic transport-network queries: preparation (topological cleaning and node assignment), origin–destination cost matrices and routes, service areas (isochrones), nearest-facility allocation, and link criticality screening.

All six tools share a single Dijkstra engine (engine/paths.py) that uses scipy.sparse.csgraph when available and a pure-Python heapq kernel otherwise, with identical results asserted by unit tests. The engine tracks predecessor nodes and edge identifiers through the shortest-path tree, enabling full route-geometry reconstruction — not just costs.

Prepare Network

Processing ID: planx:preparenetwork

Overview

Converts raw street centreline layers into a topologically clean, routable network graph. This is the mandatory first step before any other network tool in PlanX: it snaps endpoints, assigns node IDs, computes edge lengths, and builds the adjacency structure that every downstream algorithm consumes. Without this preparation, crossing lines that appear connected visually will be treated as separate, unconnected edges — producing broken routes and nonsensical isochrones.

Theoretical Background

Academic lineage

Transport network analysis inherits from graph theory, whose formal foundations were laid by Leonhard Euler in 1736 with the Koenigsberg bridge problem — the first demonstration that the geometry of connection, not the geometry of distance, governs whether a network can be traversed. The modern computational treatment begins with Dijkstra's shortest-path algorithm (Dijkstra, 1959), which introduced the priority-queue relaxation paradigm that remains the backbone of every routing engine six decades later. In the GIS domain, network preparation was systematised through the 1990s and early 2000s as transportation agencies digitised their road inventories: the key contributions are the planar enforcement rules of early GIS topology engines (ARC/INFO, circa 1982), the generalised network data model of Goodchild (1998), and Curtin's (2007) comprehensive review that codified the distinction between geometric line layers and topological networks.

More recently, the explosion of OpenStreetMap (founded 2004) as a global road dataset made network preparation a routine prerequisite rather than a specialist task — but also introduced new challenges: OSM contributors draw crossing roads that do not share a node, double-digitised carriageways, and dangling stubs. The field has converged on a standard pipeline: (1) snap endpoints within a tolerance, (2) split lines at mutual intersections, (3) build an adjacency structure. This is the pipeline implemented in PlanX, and it is functionally equivalent to the v.clean tool in GRASS GIS, the pgRouting topology builder, and NetworkX's from_edgelist — but runs in pure NumPy with an R-tree spatial index so that no external network library is required.

Key assumptions

When to use vs. when NOT to use

Use Prepare Network when: you have a raw street-centreline layer and intend to run any downstream network analysis (shortest paths, catchments, centrality). The tool is mandatory before OD Cost Matrix, OD Routes, Service Areas, Nearest Facility, Link Criticality, Network Centrality, and Space Syntax. In practice, Prepare Network should be the first step in every PlanX project that involves the street grid.

Do NOT use when: (a) the input is already a verified topological network (e.g. a pgRouting export, a previous Prepare Network output with intact node_a/node_b fields); (b) the analysis does not involve routing — building metrics, solar radiation, and demographic analysis do not need a prepared network graph; (c) the input contains grade-separated crossings that geometry alone cannot resolve, and you lack a level attribute to split on — in this case, manually split the input at the known bridge/underpass locations first.

Mathematical Formulation

Given a set of $m$ polylines $\{P_1, \ldots, P_m\}$ where each polyline is a sequence of coordinate pairs $P_i = \{(x_1, y_1), \ldots, (x_{k_i}, y_{k_i})\}$, the algorithm constructs a primal graph $G = (V, E)$ through the following sequence of operations.

Node set $V$ — snapping and deduplication. The raw endpoints of every polyline segment form a multiset of coordinate pairs. Two endpoints $p, q$ are snapped together when their Euclidean distance falls within the tolerance $\varepsilon$. The implementation uses an R-tree spatial index: for each unprocessed endpoint $p$, a bounding-box query of radius $\varepsilon$ retrieves nearby endpoints; all within tolerance are merged to their centroid. Formally:

$$\|p - q\|_2 \leq \varepsilon \quad\Rightarrow\quad p, q \text{ share the same node } v \in V \tag{3}$$

where $\varepsilon$ is the snapping tolerance (default: 1.0 map unit). This step transforms $O(m^2)$ naive pairwise checking into $O(m \log m)$ spatial-index lookups. Nodes are assigned consecutive integer IDs $0, 1, \ldots, n-1$ after deduplication.

Edge set $E$ — splitting at mutual intersections. For every pair of segments that intersect at a point $p_{int}$ not already a node, both segments are split at $p_{int}$, and $p_{int}$ is added to $V$. This ensures that the resulting graph is planarised: every edge intersection corresponds to a node. Each edge $e = (u, v)$ carries a weight equal to either its geometric length or a user-specified cost field value:

$$w(e) = \ell_e \quad \text{or} \quad w(e) = f_{\text{cost}}(e) \tag{2}$$

where $\ell_e$ is the Euclidean length of the segment in map units, computed as the sum of distances between consecutive vertices along the polyline:

$$\ell_e = \sum_{t=1}^{k-1} \sqrt{(x_{t+1} - x_t)^2 + (y_{t+1} - y_t)^2} \tag{1}$$

When a cost field $f_{\text{cost}}(e)$ is provided (e.g. travel time in seconds), the edge weight becomes that field's value. This is critical for time-based routing: a short but slow street (e.g. a pedestrian lane with stairs) can carry a higher cost than a longer but faster one. The cost must be additive — a sum over segments, not an average or a rate. Speed fields (km/h) must be converted to travel time (length/speed) before use.

Adjacency — CSR representation. The graph is stored as a compressed sparse row (CSR) tuple $(indptr, indices, data)$ where:

Because the graph is undirected, each edge $(u, v)$ produces two adjacency entries: $(u \rightarrow v)$ and $(v \rightarrow u)$, both with the same weight. The CSR format is directly consumable by SciPy's csgraph.dijkstra for the C-accelerated fast path, and by PlanX's pure-Python heapq Dijkstra kernel for the fallback — both producing numerically identical results.

Edge multiplicity. If two segments share the same endpoint pair $(u, v)$ (parallel edges, e.g. dual carriageways digitised as separate lines), both are retained as separate adjacency entries. The Dijkstra kernels correctly handle parallel edges: during relaxation, each entry is considered independently, and the one yielding the lower cumulative cost wins. The predecessor-tracking variant (shortest_path_tree) records which specific edge was traversed, enabling correct route reconstruction even with parallel edges — a capability that distinguishes it from simpler CSR Dijkstra implementations.

Complexity. With $m$ input polylines producing $n$ nodes and $e$ edges after splitting, the preparation runs in $O(m \log m + e)$ time dominated by the R-tree spatial-index construction and the intersection tests. For a typical urban network of 10,000 street segments, preparation completes in under 5 seconds on commodity hardware.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesRoad centreline layer. Must be in a projected CRS (metres). Crossing lines must share a node at the intersection — if they don't, use the snapping tolerance or split them first.

Where to obtain: OpenStreetMap (via QuickOSM, Overpass Turbo, or the OSM Downloader plugin), municipal GIS portals, national mapping agencies. For OSM, download the highway key with values motorway, trunk, primary, secondary, tertiary, residential, living_street, pedestrian, footway, cycleway, path.

Preprocessing: Remove duplicate geometries, fix invalid geometries (Fix Geometries tool), ensure lines are not multipart (use Multipart to Singleparts). Dead-end stubs are fine; the graph handles degree-1 nodes naturally.

Parameters

ParameterTypeDefaultDescription
NETWORKVector layer (Line)Input street centreline layer. The tool expects simple linestrings; multipart geometries are not handled.
COST_FIELDField (Numeric)— (optional)If provided, edge weights use this field instead of geometric length. Use for travel time (seconds) or impedance-weighted analysis.
TOLERANCEDouble1.0Snapping tolerance in map units. Endpoints closer than this are merged into a single junction. Increase for messy data (2–5 m), decrease for precise data (0.1 m). Too large a tolerance collapses distinct intersections.
OUTPUTVector layer (Line)Prepared network with node IDs and segment lengths as attributes.

Output Description

FieldTypeDescription
node_aIntegerSource node ID of the segment
node_bIntegerTarget node ID of the segment
lengthDoubleGeometric length of the segment in map units (typically metres)
costDoubleTravel cost (equals length unless a cost field was specified)

Symbolic Representation

Style the output as a thin line (0.3 mm) in dark grey. Nodes (junctions) can be visualised by extracting the unique endpoints and styling as small circles (1 mm). Colour-code by node_a if you need to trace connectivity. The prepared network is an intermediate product — its visual quality matters less than its topological correctness.

Interpretation Guide

Verifying preparation quality

The prepared network is an intermediate product — its visual quality matters less than its topological correctness. Three diagnostic checks should be performed before proceeding to downstream analyses:

  1. Node degree histogram. In a typical urban grid, most junctions have degree 3 (T-intersection) or degree 4 (cross-intersection). Degree-2 nodes represent polyline vertices that were not split — they are interior points along a segment, not true intersections. Degree-1 nodes are dead-ends (cul-de-sac terminations or network-edge stubs). A histogram heavy on degree-1 nodes with no cul-de-sac geography suggests undersnapping — endpoints that should have merged but did not. A histogram with degree 5+ nodes suggests oversnapping — distinct intersections collapsed into one.
  2. Service area quick test. Run a single Service Areas analysis with a 500 m break on one centrally located facility. The resulting catchment should form a contiguous, roughly isotropic blob with no gaps or spikes. Gaps indicate disconnected segments within the buffer; spikes along a single street indicate the only connected corridor. If the catchment resembles a starburst along one road but ignores large adjacent areas, the snapping tolerance was too low — crossing streets look connected visually but are not in the graph.
  3. Edge count sanity check. The number of edges in the output should equal the number of input segments plus the number of intersection splits. If it grows by more than 10–15%, the input probably had many false crossings (unresolved grade separations); if it shrinks, duplicate geometries were present in the input.

Cost field selection

The choice between geometric length and a custom cost field is the single most important decision at the preparation stage, because it carries through to every downstream analysis. Length-based routing answers the question "how far is it on the map?" Time-based routing answers "how long does it take?" — and in urban environments with heterogeneous street types, the two can diverge dramatically:

Common misinterpretations

Cross-references

The prepared network's node_a, node_b, and cost fields are consumed by every downstream tool in Group 1 (Network Analysis) and Group 2 (Centrality & Space Syntax). The CSR graph built during preparation is serialised internally and reused across sessions when the input layer's file path and modification timestamp match — this cache avoids repeated preparation of the same network. For slope-aware or speed-aware routing, compute the cost field in a preprocessing step (Walking Slope Comfort or an external field calculator) and pass it to Prepare Network as COST_FIELD.

Academic References

Curtin, K.M. (2007). "Network Analysis in Geographic Information Science: Review, Assessment, and Projections." Cartography and Geographic Information Science, 34(2), 103–111. DOI: 10.1559/152304007781002163

Dijkstra, E.W. (1959). "A Note on Two Problems in Connexion with Graphs." Numerische Mathematik, 1(1), 269–271. DOI: 10.1007/BF01386390

Barabasi, A.-L. & Albert, R. (1999). "Emergence of Scaling in Random Networks." Science, 286(5439), 509–512. DOI: 10.1126/science.286.5439.509

Rodrigue, J.-P., Comtois, C. & Slack, B. (2017). The Geography of Transport Systems. 4th ed., Routledge. textbook, no DOI assigned] Available at: transportgeography.org

2. Centrality & Space Syntax

This group quantifies the structural position of every street segment within the network — how central or peripheral it is, how much through-movement it attracts, and how easily it can be reached. The two tools are complementary: Network Centrality works on the primal graph with metric (Euclidean/length) costs, computing the four canonical centrality measures — closeness, betweenness, straightness, and eigenvector. Space Syntax works on the segment dual graph with angular costs (turn degree ÷ 90), computing the Hillier space-syntax measures of angular integration (to-movement potential) and angular choice (through-movement potential), including the size-normalised NACH and NAIN indices.

Taken together, these two tools provide the configurational diagnosis of a street network — the single most influential analysis in the PlanX suite for master-plan evaluation, because street configuration, once built, persists for centuries.

Network Centrality

Processing ID: planx:networkcentrality

Overview

Computes four classic graph-centrality indices for every node in a prepared street network: closeness (how near a node is to all others), betweenness (how many shortest paths pass through a node), straightness (how direct routes from a node are, comparing Euclidean to network distance), and eigenvector centrality (how connected a node is to other well-connected nodes, via power iteration). Each measure can be computed at multiple metric radii to separate local from global structure.

The tool uses Brandes' (2001) $O(nm + n^2 \log n)$ algorithm for betweenness with radius limiting and optional source sampling for large networks. Closeness follows the Wasserman–Faust normalisation so that values are comparable across networks of different sizes.

Theoretical Background

Academic lineage

Centrality is one of the oldest and most studied concepts in network science. Its modern form traces to Alex Bavelas (1948), who first formalised the mathematical properties of group structures at MIT's Group Networks Laboratory under Kurt Lewin, showing that the position of a person within a communication network determined their influence and efficiency. Linton Freeman (1977) unified the proliferating measures into a coherent framework, establishing that three distinct structural properties — degree (activity), closeness (efficiency of access), and betweenness (control over flows) — capture fundamentally different dimensions of being "central." Phillip Bonacich (1972) introduced the eigenvector formulation: a node's centrality is proportional to the sum of its neighbours' centralities, capturing the recursive notion that being connected to a central node is more valuable than being connected to a peripheral one.

In urban studies, centrality measures translate directly into spatial quantities with economic meaning. Porta, Crucitti & Latora (2006a, 2006b) were the first to apply the full set of centrality indices to street networks at city scale under the label Multiple Centrality Assessment (MCA). Their landmark finding: self-organised (historically grown) cities exhibit scale-free centrality distributions similar to those found in biological and social networks, while planned cities do not — the planning act truncates the power-law tail. Sevtsuk & Mekonnen (2012) operationalised this as a GIS toolbox (Urban Network Analysis for ArcGIS), adding building-level weighting, so that two neighbouring buildings on the same street segment receive different reach scores if one has more residents. The PlanX implementation follows the same primal-graph, metric-cost paradigm but runs entirely within QGIS using the embedded Dijkstra and Brandes kernels.

Key assumptions

When to use vs. when NOT to use

Use Network Centrality when: you need a multi-dimensional diagnosis of the network's structural organisation (not the demand-weighted consequences — use Link Criticality for that); when comparing the spatial structure of different cities or different neighbourhoods within a city; when identifying candidate locations for new services (high closeness nodes), new transit corridors (high betweenness edges), or bypasses (high betweenness nodes that are also bottlenecks).

Do NOT use when: (a) you need segment-level (not node-level) centrality — use Space Syntax; (b) you need demand-weighted criticality — use Link Criticality; (c) the network is very small (under 100 nodes) and the ranking is driven by edge effects rather than structure — buffer your study area with at least one neighbourhood's worth of extra network; (d) you need directed (in/out) centrality for transport planning — the undirected primal graph does not distinguish directions.

Mathematical Formulation

Closeness (Wasserman–Faust):

$$C_C(i) = \frac{N_r(i) - 1}{N - 1} \cdot \frac{N_r(i) - 1}{\sum_{j \in V_r(i)} d(i, j)} \tag{4}$$

where $d(i,j)$ is the shortest-path distance, $V_r(i)$ is the set of nodes reachable from $i$ within radius $r$, and $N_r(i) = |V_r(i)|$. The first factor is the Wasserman–Faust correction for partial reachability; the second is the raw closeness (inverse mean distance).

Betweenness (Brandes, 2001):

$$C_B(v) = \sum_{\substack{s,t \in V \\ s \neq v \neq t \\ d(s,t) \leq r}} \frac{\sigma_{st}(v)}{\sigma_{st}} \tag{3}$$

where $\sigma_{st}$ is the number of shortest paths from $s$ to $t$, and $\sigma_{st}(v)$ is the number of those paths that pass through $v$.

Straightness:

$$C_S(i) = \frac{1}{N_r(i) - 1} \sum_{j \in V_r(i),\, j \neq i} \frac{\|p_i - p_j\|_2}{d(i, j)} \tag{2}$$

Eigenvector centrality:

$$C_E(i) = \frac{1}{\lambda} \sum_{j \in \mathcal{N}(i)} C_E(j) \tag{1}$$

where $\lambda$ is the largest eigenvalue of the adjacency matrix $A + I$, and $\mathcal{N}(i)$ is the set of neighbours of node $i$. The power iteration converges when $\|x^{(k+1)} - x^{(k)}\|_\infty < 10^{-6}$.

Input Data Requirements

InputTypeRequiredNotes
NetworkVector lines (prepared)YesOutput of Prepare Network. Must have node_a, node_b, and cost fields.

Parameters

ParameterTypeDefaultDescription
NETWORKVector layer (Line)Prepared street network from the Prepare Network tool.
RADIIString"n"Metric radii as comma-separated values; "n" = global (no radius limit). Use 400–800 m for neighbourhood-scale, 2000+ m for city scale.
SAMPLEDouble1.0Fraction of nodes used as betweenness sources (0–1). At 1.0 all nodes are sources (exact); at 0.1 a random 10% sample is used, giving an unbiased estimate at ~10× speed on large networks.
OUTPUTVector layer (Point)Node layer with centrality scores. One row per junction node.

Output Description

FieldTypeDescription
closeness_{r}DoubleWasserman–Faust closeness. 0–1; higher = more accessible. Low values signal peripheral/isolated nodes.
betweenness_{r}DoubleBrandes betweenness. Raw count; heavily right-skewed. Normalise by $(N-1)(N-2)$ for comparability.
straightness_{r}DoubleMean Euclidean/network ratio. 0–1; 1 = perfectly straight radial routes; <0.3 = highly circuitous.
eigenvector_{r}DoubleEigenvector centrality after power iteration. Relative scale; the absolute values are less important than the ranking.

(One set per radius; suffix is the radius label, e.g. closeness_800, betweenness_n.)

Symbolic Representation

Map betweenness with a graduated renderer using a sequential multi-hue ramp (Viridis or Inferno) in natural-breaks classification (5–7 classes). Closeness benefits from the same treatment. Straightness works well with a diverging ramp (RdYlGn) centred at the median. Eigenvector centrality is best visualised with quantile classification (5 classes) to expose the rank structure — the top quintile marks the spatial core. Always set the minimum symbol size to 0.5 mm and scale to 3 mm at maximum, with 30–50% opacity to handle overplotting in dense areas.

Interpretation Guide

Numerical benchmarks

Spatial pattern reading

The four-measure matrix

Each measure answers a different planning question, and the four together provide a complete structural diagnosis:

MeasurePlanning questionHigh = good forHigh = bad for
Closeness"Where is the centre?"Service siting, emergency response(no downside — high closeness is always desirable)
Betweenness"Where does movement concentrate?"Retail, transit corridors, street lifeSeverance, noise, pollution (when extreme)
Straightness"How direct are routes from here?"Everywhere — low straightness is a tax on all tripsHigh straightness on one street at the cost of low everywhere else = severance by design
Eigenvector"Where is the structural core?"Land value, investment stabilityOverconcentration of activity, heat island core

Cross-references

Cross-reference with Space Syntax: metric betweenness correlates with angular choice (NACH), and metric closeness correlates with angular integration (NAIN). The correlation is typically $r \approx 0.7$–$0.85$ in grid cities, falling to $r \approx 0.5$ in cities with irregular geometry or organic street patterns. Discrepancies between the metric and angular pictures reveal where the geometry of the grid matters beyond simple distance — a long, straight boulevard may score high on metric closeness but low on angular integration if it turns sharply relative to the surrounding grid, forcing large angular costs at its ends.

Feed high-betweenness nodes into Link Criticality as OD origins/destinations to test how vulnerable the network's busiest corridors are. Overlay betweenness on Walkability Audit scores — streets with high betweenness but low walkability are the corridors where pedestrian infrastructure investment yields the highest safety return per metre.

Common misinterpretations

Academic References

Bavelas, A. (1948). "A Mathematical Model for Group Structures." Human Organization, 7(3), 16–30. DOI: 10.17730/humo.7.3.f4033344851gl053

Bonacich, P. (1972). "Factoring and Weighting Approaches to Status Scores and Clique Identification." Journal of Mathematical Sociology, 2(1), 113–120. DOI: 10.1080/0022250X.1972.9989806

Brandes, U. (2001). "A Faster Algorithm for Betweenness Centrality." Journal of Mathematical Sociology, 25(2), 163–177. DOI: 10.1080/0022250X.2001.9990249

Crucitti, P., Latora, V. & Porta, S. (2006). "Centrality in Networks of Urban Streets." Chaos, 16, 015113. DOI: 10.1063/1.2150162

Freeman, L.C. (1977). "A Set of Measures of Centrality Based on Betweenness." Sociometry, 40(1), 35–41. DOI: 10.2307/3033543

Porta, S., Crucitti, P., & Latora, V. (2006). "The Network Analysis of Urban Streets: A Primal Approach." Environment and Planning B: Planning and Design, 33(5), 705–725. DOI: 10.1068/b32045

Sevtsuk, A. & Mekonnen, M. (2012). "Urban Network Analysis: A New Toolbox for ArcGIS." Revue Internationale de Geomatique, 22(2), 287–305. DOI: 10.3166/rig.22.287-305

Wasserman, S. & Faust, K. (1994). Social Network Analysis: Methods and Applications. Cambridge University Press. DOI: 10.1017/CBO9780511815478

Space Syntax (Segment Angular Analysis)

Processing ID: planx:spacesyntax

Overview

Performs segment angular analysis — the modern space syntax workflow — directly on road centreline segments. Unlike traditional axial-map space syntax, this method uses the segment dual graph: each street segment between junctions becomes a node, and connections between segments carry an angular cost equal to the turn angle in degrees divided by 90 (straight = 0, right angle = 1). Shortest paths minimise this angular cost, effectively finding the least-turning routes through the city.

For every specified radius, the tool computes six measures per segment: NC (node count reached), TD (angular total depth), MD (angular mean depth), NAIN (normalised angular integration — to-movement potential), CH (raw angular choice — through-movement count), and NACH (normalised angular choice). The normalised measures follow Hillier, Yang & Turner (2012) and are size-independent, enabling comparison across cities of different sizes.

Theoretical Background

Academic lineage

Space syntax theory, pioneered by Bill Hillier and colleagues at the Bartlett School of Architecture, University College London, since the early 1970s, posits that the spatial configuration of a city — the pattern of connections and barriers in its street network — is the primary generator of movement, land-use patterns, and social encounters. This is the "theory of natural movement" (Hillier et al., 1993): the grid itself, more than any land-use designation or planning policy, determines where people walk, drive, and gather. The founding text, The Social Logic of Space (Hillier & Hanson, 1984), demonstrated that buildings and settlements across cultures and centuries follow configurational rules that are mathematically describable — space is not a neutral container but an active structuring force.

The evolution of the method has three generations. Each generation solved a limitation of the previous one while introducing its own:

  1. Axial analysis (1970s–1990s). The fewest and longest lines of sight covering all open public space, connected at their intersections. The axial map is the line-of-sight representation: each axial line is a node in the dual graph, and connections carry unit cost. Axial depth — the number of axial steps from one line to another — was the first configurational measure. The method is elegant but suffers from two fundamental problems: it requires subjective judgement in drawing the axial lines (two practitioners drawing the same neighbourhood produce different maps), and it is resolution-dependent — denser areas generate more axial lines, which inflates integration scores, making cross-city comparison impossible.
  2. Segment angular analysis (Turner, 2001). Rather than drawing the fewest-and-longest lines, the axial map is broken at every intersection into segments — the street pieces between junctions. Connections between segments are weighted by turn angle divided by 90 degrees, so continuing straight costs 0, a right-angle turn costs 1, and a U-turn costs 2. Shortest paths minimise this angular cost, effectively finding the least-turning routes through the city. This single change:
    • Removes subjectivity — the segment map is determined by the geometry of intersections, not a practitioner's judgement,
    • Captures the cognitive reality that people prefer straight routes and penalise sharp turns,
    • Is resolution-independent — adding intermediate shape points along a straight segment does not change the angular cost,
    • Subsumes axial analysis: if every turn costs 1 (regardless of angle), the result is the axial topology; if costs are proportional to turn angle, the result is the more nuanced angular model.
  3. Normalised measures — NACH and NAIN (Hillier, Yang & Turner, 2012). The raw measures from segment angular analysis — integration (inverse mean depth) and choice (angular betweenness) — are inherently size-dependent. A larger network has more segments, longer paths, and therefore systematically higher choice counts and systematically lower integration values, even if the structural pattern is identical. NACH and NAIN normalise away size by dividing choice and integration respectively by terms derived from the total depth. The constants (exponent 1.2 for NAIN, +1/+3 shift for NACH, +2/+2 for NAIN denominator) were calibrated by Hillier, Yang & Turner (2012) to maximise the correlation with observed pedestrian and vehicular movement rates across 50 cities worldwide. This made NACH and NAIN the first space-syntax measures that could be compared numerically across cities of different sizes — a landmark for quantitative urban morphology.

Key assumptions

When to use vs. when NOT to use

Use Space Syntax when: you need the definitive configurational diagnosis of a street network — the structural complement to land-use, density, and demographic analyses; when comparing alternative masterplan layouts (run twice, difference the NACH/NAIN fields); when identifying the foreground movement grid (top 10–20% NACH) and the background residential fabric; when the question is about the spatial logic of the city — why does this street attract pedestrians while its parallel twin two blocks away does not.

Do NOT use when: (a) you need only metric (distance) measures of centrality — use Network Centrality; (b) the street network lacks a prepared topology — segments that cross without a shared node produce a disconnected segment graph, and the output will be nonsensical; always run Prepare Network first; (c) the study area is very small (under 100 segments) — edge effects dominate at this scale, and the NACH/NAIN normalisation was calibrated on city-scale networks of thousands of segments; (d) you are working entirely within a superblock or gated community where movement is constrained by gates and access controls that the configurational model cannot account for.

Mathematical Formulation

Segment dual graph. Given $n$ street segments with coordinates, the segment graph is built by the engine (engine/graphs.py). Two segments $(u, v)$ are adjacent if they share an endpoint. The angular cost of turning from segment $u$ to segment $v$ is:

$$c(u, v) = \frac{\theta_{uv}}{90^\circ} \tag{6}$$

where $\theta_{uv} \in [0^\circ, 180^\circ]$ is the deflection angle between the last direction vector of $u$ and the first direction vector of $v$ (taking the smaller angle, since the graph is undirected). Internal polyline curvature within each segment is also accumulated — the angular cost along a segment equals the sum of turn angles between its consecutive sub-segments divided by 90°.

Angular shortest paths. For each source segment $s$, a modified Dijkstra algorithm computes the minimum angular-cost path to every other segment $t$ within metric radius $r$ (if specified). The result is the angular distance matrix $d_{ang}(s, t)$.

Angular total depth (TD) and node count (NC):

$$TD(s) = \sum_{t \in V_r(s)} d_{ang}(s, t) \qquad NC(s) = |V_r(s)| \tag{5}$$

Angular mean depth (MD):

$$MD(s) = \frac{TD(s)}{\max(NC(s) - 1, 1)} \tag{4}$$

Angular choice (CH) — raw betweenness:

$$CH(s) = \sum_{u \neq s \neq v} \frac{\sigma_{uv}^{ang}(s)}{\sigma_{uv}^{ang}} \tag{3}$$

where $\sigma_{uv}^{ang}$ is the number of least-angular-cost paths between $u$ and $v$, and $\sigma_{uv}^{ang}(s)$ counts those passing through $s$. Since the graph is undirected, each unordered pair $(u,v)$ is counted twice; the engine divides by 2 to obtain pair-based choice.

NAIN — Normalised Angular INtegration (to-movement potential):

$$NAIN(s) = \frac{(NC(s) + 2)^{1.2}}{TD(s) + 2} \tag{2}$$

The constants +2 in numerator and denominator prevent division by zero for isolated segments. The exponent 1.2 was calibrated by Hillier, Yang & Turner (2012) to maximise correlation with observed movement rates across 50 cities. High NAIN = easy to arrive at — the segment is a destination magnet.

NACH — Normalised Angular CHoice (through-movement potential):

$$NACH(s) = \frac{\log_{10}(CH(s) + 1)}{\log_{10}(TD(s) + 3)} \tag{1}$$

The constants +1 and +3 keep the ratio real and finite (choice can be zero; total depth is at least 1). High NACH = heavily used as a through route — the segment is in the foreground movement grid.

Both NACH and NAIN are dimensionless and approximately invariant to system size — a segment in a small town and a segment in a metropolis with the same configurational role will have similar NACH/NAIN values.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector lines (prepared)YesMust be pre-processed with Prepare Network first. Crossing lines must share nodes. Must be in a projected CRS (metres) — radii are measured in map units.

Parameters

ParameterTypeDefaultDescription
NETWORKVector layer (Line)Street network, output of Prepare Network. Lines must share nodes at intersections or the segment graph will be disconnected.
RADIIString"800, n"Comma-separated metric radii in map units. n (or global, inf) = no radius limit — global analysis. Typical values: 400–800 m (pedestrian/neighbourhood), 1200–2000 m (cycling/district), 5000+ m or n (city-region). Multiple radii let you read the multi-scale structure: a main street that is strong at all radii is resilient; one strong only at 800 m is a neighbourhood centre; one strong only at n is a car-oriented arterial.
OUTPUTVector layer (Line)Street segments with syntax attributes. The output has the original street geometry plus all per-radius fields.
Performance warning: Global radius (n) on networks with more than ~20,000 segments may be slow — the engine issues a warning. For very large networks, use metric radii or subset the study area. Always include at least one radius of network beyond your actual study area: segments near the edge have artificially low values because the radius "sees" fewer segments than it would if the network extended further. This edge effect is a universal space-syntax issue — the tool does not correct for it.

Output Description

FieldTypeTypical RangeDescription
connectivityInteger1–8Number of segments connected to this segment at each endpoint (degree of the segment node in the dual graph).

Per radius (suffix is the radius label, e.g. _800, _n):

FieldTypeTypical RangeDescription
NC_{r}Double1–NNode count: how many other segments are reachable within this radius. Low NC at small radius = isolated cul-de-sac.
TD_{r}Double≥0Angular total depth: sum of least-turn costs to all reachable segments. Raw diagnostic; prefer MD, NAIN.
MD_{r}Double0–~10Angular mean depth: average angular cost to reachable segments. Low MD = straight, continuous routes radiate from here.
NAIN_{r}Double0–~2.5Normalised Angular INtegration: to-movement potential. High = easy to arrive at — centres, destinations. The city mean is typically 0.4–0.8.
CH_{r}Double0–~N²Raw angular choice (pair-based betweenness). Heavy-tailed right skew — map NACH instead. Diagnostic only.
NACH_{r}Double0–~1.6Normalised Angular CHoice: through-movement potential. City mean ~0.7–1.1; max ~1.2–1.6 (Hillier, Yang & Turner 50-city benchmarks). Values above 1.4 are the foreground super-grid.

Symbolic Representation

NACH and NAIN are the two primary map products.

Interpretation Guide

The dual reading: NACH × NAIN

The real power of space syntax lies in reading NACH and NAIN together on the same segment. The cross-classification yields four urban typologies:

High NAIN (easy to arrive)Low NAIN (hard to reach)
High NACH
(heavy through-movement)
Live high street — the city's signature mixed-use corridor. People pass through AND stop. Retail, restaurants, street life. Planning action: protect and enhance the public realm; widen footpaths; restrict cars at peak pedestrian hours. Pass-through corridor — a movement channel that nobody uses as a destination. Traffic pressure, severance risk, noise, poor air quality. Often a major arterial or ring road. Planning action: add pedestrian crossings; plant street trees; rezone adjacent parcels for active frontage to convert it toward a live street over time.
Low NACH
(light through-movement)
Calm local centre — a neighbourhood square, local high street, or community hub. Easy to reach but only by those who intend to. Planning action: preserve the intimate scale; add seating and planting; resist chain retail that would drive up NACH and erode the calm. Residential background / enclave — the quiet fabric where people live. This is the city's largest spatial type by length. Planning action: monitor NAIN — if it stays low at every radius, the neighbourhood is an enclave and needs better connections to the surrounding grid; if NAIN rises at larger radii, it is locally quiet but regionally accessible — a desirable balance.

Multi-radius reading

Run at least three radii (e.g. 400, 1200, n) and compare the same segment across radii:

Masterplan evaluation

Run Space Syntax twice — once on the existing network, once on the proposed masterplan network — and compute the difference in NACH and NAIN per segment (or per spatial unit). Segments whose NAIN rises significantly are winners — the masterplan connects them. Segments whose NAIN drops are losers — the masterplan severs or bypasses them. A quarter whose NAIN stays low at every radius in the proposed network will persist as an enclave regardless of land-use mix or density — the spatial configuration itself prevents integration.

Benchmarks from the 50-city study

Hillier, Yang & Turner (2012) established these approximate ranges from their analysis of 50 urban systems worldwide:

MetricLowTypicalHigh
City-mean NACH~0.7 (disconnected sprawl)~0.85–1.0~1.1 (dense orthogonal grid)
Maximum NACH~1.2~1.4~1.6 (super-grid arteries)
Mean NAIN~0.3~0.5–0.7~0.9

Your city's values will differ — these are comparative benchmarks, not normative targets. An organic medieval centre and a planned modernist grid can both be excellent places to live despite very different scores.

Combining with other PlanX tools. Feed NACH into the Walkability Audit as a proxy for the "connectivity" component at city scale. Overlay high-NACH segments with the Link Criticality output to identify corridors that are both structurally dominant AND fragile. Use NACH/NAIN as candidate weights in the Land-Use Allocation Optimizer to favour configurational centres for mixed-use zoning.

Academic References

Hillier, B. & Hanson, J. (1984). The Social Logic of Space. Cambridge University Press. DOI: 10.1017/CBO9780511597237

Hillier, B., Penn, A., Hanson, J., Grajewski, T., & Xu, J. (1993). "Natural Movement: Or, Configuration and Attraction in Urban Pedestrian Movement." Environment and Planning B: Planning and Design, 20(1), 29–66. DOI: 10.1068/b200029

Turner, A. (2001). "Angular Analysis." In: Peponis, J., Wineman, J. & Bafna, S. (eds.), Proceedings of the 3rd International Space Syntax Symposium, Atlanta, GA, pp. 30.1–30.11. conference proceedings] Available at: UCL Discovery

Hillier, B. & Iida, S. (2005). "Network and Psychological Effects in Urban Movement." In: Cohn, A.G. & Mark, D.M. (eds.), Spatial Information Theory (COSIT 2005), LNCS 3693, pp. 475–490. Springer. DOI: 10.1007/11556114_30

Crucitti, P., Latora, V. & Porta, S. (2006). "Centrality Measures in Spatial Networks of Urban Streets." Physical Review E, 73, 036125. DOI: 10.1103/PhysRevE.73.036125

Porta, S., Crucitti, P. & Latora, V. (2006). "The Network Analysis of Urban Streets: A Dual Approach." Physica A, 369(2), 853–866. DOI: 10.1016/j.physa.2005.12.063

Hillier, B., Yang, T., & Turner, A. (2012). "Normalising Least Angle Choice in Depthmap — and How It Opens Up New Perspectives on the Global and Local Analysis of City Space." Journal of Space Syntax, 3(2), 155–193. journal does not assign DOIs] Available at: UCL Discovery

OD Cost Matrix

Processing ID: planx:odmatrix

Overview

Computes the full origin–destination cost matrix over the street network using the embedded Dijkstra engine — no external routing plugin or server. For every origin–destination pair, it reports the shortest-path network cost, the straight-line (Euclidean) distance, and the detour ratio (network ÷ Euclidean). Optional desire lines render the matrix as straight-line glyphs styled by cost or detour.

This is the foundational input for gravity models, accessibility studies, facility siting, and any analysis that needs to know "how far is A from B on the real network."

Theoretical Background

Academic lineage

The OD cost matrix is the most basic product of a transportation network model: it answers the question "how far apart are these places?" for every pair of interest. The concept originates in traffic analysis zone (TAZ) planning, formalised during the post-war boom in urban transportation modelling. The landmark Chicago Area Transportation Study (CATS, 1955–1962) established the four-step travel-demand model (trip generation, distribution, modal split, assignment) that remains the framework for metropolitan transport planning worldwide. The OD cost matrix feeds the distribution step — trip distribution models (gravity, intervening opportunities, and later entropy-maximising and logit-based formulations) all require a cost or impedance matrix as their primary input (Ortuzar & Willumsen, 2011). Wilson's (1967) entropy-maximising derivation of the doubly-constrained gravity model was a theoretical breakthrough, showing that the gravity model is the most probable trip distribution consistent with known constraints — not merely an analogy to Newtonian physics.

In GIS, the OD cost matrix was for many years computed by external routing engines (ESRI Network Analyst, pgRouting, Google Distance Matrix API). PlanX brings this capability inside QGIS using the same Dijkstra engine that powers all other network tools. The many-to-many shortest-path computation runs one Dijkstra tree per origin, reusing the tree to reach all destinations simultaneously — far more efficient than running Dijkstra once per OD pair. This exact approach is implemented in the engine's many_to_many kernel (see engine/paths.py), which passes the CSR adjacency directly to SciPy's csgraph.dijkstra when available and falls back to a pure-Python heapq implementation otherwise, with identical results verified by unit tests.

Key assumptions

When to use vs. when NOT to use

Use OD Cost Matrix when: you need a complete pairwise impedance matrix for gravity models, accessibility indices, facility location optimisation, or statistical analysis of travel costs. The detour ratio makes it a powerful barrier-effect screening tool — the OD pair with the highest detour ratio often pinpoints the single best location for a new bridge or street link.

Do NOT use when: (a) you need actual route geometries — use OD Routes instead; (b) you are computing a single origin's catchment — use Service Areas which provides partial-edge trimming and pedshed ratios; (c) you need assignment-aware routing (each traveller affects congestion) — this requires an iterative traffic-assignment model, which PlanX does not provide; (d) the OD set is very large (thousands of origins x thousands of destinations) with no cutoff — the output table grows as $O(m \cdot n)$ and will overwhelm QGIS. Always set a CUTOFF for large OD sets, or aggregate origins and destinations to zone centroids first.

Mathematical Formulation

Network cost. For each origin $i$ and destination $j$, the engine computes the shortest-path distance $d_{net}(i,j)$ by Dijkstra's algorithm on the primal graph $G = (V, E)$ with edge weights $w(e)$ (geometric length or a user-specified additive cost field). Each origin snap point is mapped to its nearest network node $n_i^O$; similarly each destination snap point maps to $n_j^D$. The many-to-many kernel then runs Dijkstra from each unique origin node, producing a full shortest-path tree per origin:

$$d_{net}(i,j) = \min_{\text{path}(n_i^O \to n_j^D)} \sum_{e \in \text{path}} w(e) \tag{4}$$

The engine adds the straight-line snap distance $d_{snap}$ from the original point coordinates to the network node, so the total cost from origin geometry to destination geometry is:

$$D_{ij} = d_{snap}(O_i \to n_i^O) + d_{net}(n_i^O, n_j^D) + d_{snap}(n_j^D \to D_j) \tag{3}$$

Euclidean distance (as the crow flies) between the original point coordinates:

$$d_{euc}(i,j) = \sqrt{(x_i - x_j)^2 + (y_i - y_j)^2} \tag{2}$$

Detour ratio: the dimensionless measure of how much farther the network forces travel compared to the straight line:

$$detour(i,j) = \frac{D_{ij}}{d_{euc}(i,j)} \tag{1}$$

Many-to-many computational kernel. The engine function paths.many_to_many(indptr, adj, weights, n, sources, cutoff) implements the computation. For each source node $s$, it runs a single Dijkstra pass that computes distances to all $n$ nodes. For $m$ unique origin nodes, the cost is $O(m \cdot (n \log n + e))$ in the SciPy fast path and $O(m \cdot (n \log n + e))$ in the heapq fallback — the difference is a constant factor (~5-10x), not the asymptotic complexity. The cutoff parameter prunes the search: expansion stops when the cumulative cost exceeds the cutoff, which both accelerates the computation and limits memory use for large networks.

All-pairs special case. When no separate destination layer is provided, the origins also serve as destinations. Self-pairs $(i, i)$ are automatically excluded (a point's distance to itself is not meaningful for gravity models and would introduce a zero diagonal that biases calibration).

Unreachable handling. A destination $j$ is unreachable from origin $i$ if: (a) the source and destination nodes belong to different connected components of the graph, or (b) the shortest path exceeds the cutoff and expansion was pruned. In either case, the pair is not included in the output — the matrix is sparse by design, containing only reachable pairs within the cutoff. The count of missing pairs per origin is a direct measure of network fragmentation.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesPrepared network recommended. Projected CRS (metres).
OriginsVector (any geometry)YesPoint/centroid locations. Each snaps to nearest network node.
DestinationsVector (any geometry)NoIf empty, origins serve as destinations (all-pairs among origins).
Cost fieldNumeric fieldNoMust be additive per segment (travel time, not speed). NULL = 0 = free segment.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network. Prepared network recommended.
ORIGINSVector (Any)Origin features. Use population centroids, building points, or zone centres.
ORIGIN_IDFieldField uniquely identifying each origin (appears in output as origin_id).
DESTINATIONSVector (Any)(optional)Destination features. Leave empty to compute all pairs among origins.
DEST_IDField(optional)Field identifying each destination. Falls back to origin ID field.
COST_FIELDField (Numeric)(empty = length)Additive per-segment cost column. Use a time field (minutes) for travel-time matrices. Must be ≥ 0 and complete.
CUTOFFDouble0 (unlimited)Maximum cost; pairs exceeding this are excluded. 0 = no limit. Essential for large OD sets to keep output manageable.
MATRIXTable outputOD matrix table (no geometry).
LINESVector (Line)(optional)Straight desire lines between OD pairs. Useful for visualisation.

Output Description

FieldTypeDescription
origin_idStringOrigin identifier from the ID field
dest_idStringDestination identifier
net_costDoubleShortest-path cost (metres, or cost-field units). The operative number.
euclid_mDoubleStraight-line distance in metres
detourDoubleNetwork ÷ Euclidean ratio. ~1.0–1.2 direct; ≥1.4 = major barrier. Only meaningful with length cost (not time).

Symbolic Representation

Desire lines styled by detour ratio with a diverging colour ramp (RdYlGn, reversed: green = direct, red = circuitous) and natural-breaks classification (5 classes) produce the most immediately informative map. Style by net_cost with a sequential ramp (Viridis) for a raw-distance view. Line width proportional to the inverse of detour (thick = efficient connections). At city scale, set line opacity to 20–30% to prevent overplotting.

Interpretation Guide

Numerical benchmarks

Spatial pattern reading

Visualise the matrix as a desire-line map: each OD pair is a straight line coloured by detour ratio. The spatial pattern reveals:

Cross-references

The OD cost matrix is the primary impedance input for the Gravity Distribution model (the $c_{ij}$ term in the gravity formula), for the Mode Split tool's distance decay functions, and for any Location-Allocation analysis where a precomputed cost matrix avoids repeated routing. The net_cost values can also be used as a standalone accessibility indicator: the mean net_cost to a category of destinations (e.g. all hospitals) is a simpler but coarser alternative to the Multi-Amenity Access Score for single-category access studies.

Common misinterpretations

Academic References

Ortuzar, J. de D. & Willumsen, L.G. (2011). Modelling Transport. 4th ed., Wiley. DOI: 10.1002/9781119993308

Dijkstra, E.W. (1959). "A Note on Two Problems in Connexion with Graphs." Numerische Mathematik, 1(1), 269–271. DOI: 10.1007/BF01386390

Wilson, A.G. (1967). "A Statistical Theory of Spatial Distribution Models." Transportation Research, 1(3), 253–269. DOI: 10.1016/0041-1647(67)90035-4

Rodrigue, J.-P., Comtois, C. & Slack, B. (2017). The Geography of Transport Systems. 4th ed., Routledge. textbook, no DOI assigned] Available at: transportgeography.org

OD Routes (Shortest Paths)

Processing ID: planx:odroutes

Overview

Computes actual shortest-path route geometries over the street network from origins to destinations. Unlike the OD Cost Matrix (which returns only costs), this tool reconstructs the precise sequence of street segments traversed — turn by turn — for every origin–destination pair. Each route includes the network cost, Euclidean distance, detour ratio, and the number of edges traversed. Optional straight desire lines complement the route geometries.

The tool supports k-nearest destination filtering and a cost cutoff to keep output manageable for large OD sets.

Theoretical Background

Academic lineage

Route reconstruction from a shortest-path tree is the standard predecessor-tracking technique — a natural extension of Dijkstra's algorithm (1959) and an integral part of every routing engine since the first in-vehicle navigation systems of the 1980s. During Dijkstra's algorithm, each node records which predecessor node and which edge were used to reach it with the minimum cost. After the tree is built, a path from source $s$ to target $t$ is reconstructed by walking backwards from $t$ through the predecessor chain to $s$, collecting the edge IDs along the way. The PlanX implementation uses the shortest_path_tree kernel in engine/paths.py, which is deliberately a pure-Python heapq implementation — the SciPy accelerated kernel cannot track which specific parallel edge was taken, so predecessor tracking uses the guarantee of identical distances but with full edge-identity preservation.

The route-reconstruction problem gains practical significance at urban scale because of parallel edges — dual carriageways, service roads, and slip lanes that share the same endpoint pair $(u, v)$ but have different geometry, different names, and different attributes. A Dijkstra implementation that only records predecessor nodes cannot distinguish which parallel edge was traversed; one that records predecessor edges can. The PlanX engine records both, ensuring that every reconstructed route follows the exact geometry of the streets it traverses.

Key assumptions

When to use vs. when NOT to use

Use OD Routes when: you need the actual street-level itinerary, not just the cost; when visualising movement corridors through route bundling; when k=1 routing to the nearest facility with full path geometry (functionally equivalent to Nearest Facility with routes enabled); when the analysis requires turn-by-turn inspection of which specific streets a trip uses.

Do NOT use when: (a) you need only the cost matrix without geometry — use OD Cost Matrix which is faster and produces a smaller output; (b) the OD set is very large (thousands of pairs) without a cutoff — each route is a multi-segment linestring that bloats the output; (c) k-nearest is set to a large value — for k >= 10, the route count per origin explodes and the map becomes unreadable.

Mathematical Formulation

Shortest-path tree. For each origin $s$, Dijkstra's algorithm computes:

Path reconstruction. Starting from target node $t$:

  1. Push edge $pred\_edge[t]$ onto the edge list
  2. Set $t \leftarrow pred\_node[t]$
  3. Repeat until $t = s$ (source reached) or predecessor is invalid
  4. Reverse the edge list to obtain travel order from origin to destination

Route geometry assembly. For each edge in the ordered list, the polyline coordinates are oriented so the first vertex is closest to the current position. Consecutive edges share the junction node, producing a contiguous linestring.

$$length\_m = \sum_{e \in route} \ell_e \tag{1}$$

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesPrepared network recommended. Projected CRS.
OriginsVector (any)YesOrigin locations snapped to nearest network node.
DestinationsVector (any)NoEmpty = origins serve as destinations.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network.
ORIGINSVector (Any)Origin features with ID field.
ORIGIN_IDFieldUnique identifier for each origin.
DESTINATIONSVector (Any)(optional)Destination features. Leave empty for all-pairs among origins.
DEST_IDField(optional)Destination ID field.
COST_FIELDField (Numeric)(empty = length)Additive per-segment cost. Use time_min for slope-aware routing.
CUTOFFDouble0 (unlimited)Maximum route cost. Essential for large networks.
K_NEARESTInteger0 (all)Keep only the k nearest destinations per origin. k=1 = nearest-service routes.
OUT_ROUTESVector (Line)Reconstructed street-level routes.
OUT_LINESVector (Line)(optional)Straight desire lines for OD visualisation.

Output Description

FieldTypeDescription
origin_idStringOrigin identifier
dest_idStringDestination identifier
kIntegerRank of this destination for this origin (1 = nearest, 2 = second-nearest, …)
net_costDoubleShortest-path cost along the route
euclid_mDoubleStraight-line distance in metres
detourDoubleNetwork ÷ Euclidean ratio
n_edgesIntegerNumber of street segments in the route

Symbolic Representation

Route geometries styled by flow bundling (overlaying many routes at low opacity, 15–30%) reveal the de-facto movement corridors — the demand-side complement to betweenness centrality. Use a sequential ramp (Viridis) on net_cost for a time/distance view. For k=1 output, style by dest_id as a categorical renderer to see de-facto catchment boundaries (the nearest-destination Voronoi equivalent on the network). Route line width 0.3–0.5 mm with 20% opacity works well for bundling.

Interpretation Guide

Academic References

Dijkstra, E.W. (1959). "A Note on Two Problems in Connexion with Graphs." Numerische Mathematik, 1(1), 269–271. DOI: 10.1007/BF01386390

Bast, H., Delling, D., Goldberg, A., Muller-Hannemann, M., Pajor, T., Sanders, P., Wagner, D. & Werneck, R.F. (2016). "Route Planning in Transportation Networks." In: Kliemann, L. & Sanders, P. (eds.), Algorithm Engineering, LNCS 9220, pp. 19–80. Springer. DOI: 10.1007/978-3-319-49487-6_2

Service Areas (Isochrones)

Processing ID: planx:serviceareas

Overview

Computes true network catchments around facilities with exact partial-edge reach: walking budgets end mid-street, so streets are trimmed at the precise point where the cost budget runs out. For each break distance, the tool produces trimmed street pieces (coloured by cost band), service area polygons (buffer, concave hull, or convex hull), and straight-line circles for comparison. The pedshed ratio (network catchment area ÷ circle area) is the classic measure of how much the street layout shrinks the reach promised by a given radius.

Theoretical Background

Academic lineage

Service area (isochrone) analysis is the spatial implementation of catchment-based planning standards: a regulation states that every residence must be within 500 m of a park, 800 m of a primary school, or 400 m of a bus stop. The straight-line (crow-flies) buffer that most GIS users draw to check compliance is systematically optimistic — it ignores the street network entirely. The systematic error was first quantified by Porta & Renne (2005), who introduced the pedshed ratio (catchment area divided by circle area) as a network-efficiency metric for walkability assessment. The concept was further formalised by Curtin (2007), who demonstrated that the network buffer — trimming streets at the exact budget point rather than at the nearest junction — is not a cosmetic refinement but a quantitative necessity at walking-scale budgets (below approximately 1,500 m).

The computational approach — multi-source Dijkstra — builds on the observation that labelling every reachable point with its nearest facility is mathematically equivalent to inserting a virtual super-source node connected to all facility entry points with zero-cost edges. This was first described for GIS by Dreyfus (1969) as an extension of Dijkstra's algorithm and became the standard approach with the rise of desktop GIS in the 1990s. PlanX's implementation extends this with partial-edge trimming: after the per-node costs are known, each edge is tested for partial reachability between its endpoints. For an edge $(u, v)$ where $d_u \leq B < d_v$, the reachable portion is the fraction $(B - d_u) / (d_v - d_u)$ of the edge length from $u$. This eliminates the block-length quantisation error that node-only isochrones suffer from.

Key assumptions

When to use vs. when NOT to use

Use Service Areas when: auditing compliance with distance-based planning standards; visualising the reach of a proposed facility site; computing the pedshed ratio as a network-efficiency indicator; producing masterplan exhibits with clear "served" vs. "unserved" delineation.

Do NOT use when: (a) you need demand-weighted allocation counts — use Nearest Facility instead; (b) the question is about the fastest (time-based) route rather than the shortest distance — use a time cost field from Walking Slope Comfort; (c) the radius is very large (over 5000 m) — the polygon may cover most of the study area and the pedshed ratio approaches 1.0 regardless of network quality, making it non-diagnostic; (d) you need individual routes for every origin-destination pair — use OD Routes.

Mathematical Formulation

Multi-source Dijkstra with entry costs. Each facility $f$ enters the network at its nearest point on the nearest edge $(e_f, t_f)$ where $t_f \in [0,1]$ is the position along the edge. Two node-offset entries are created for the edge's endpoints with the approach cost added:

$$cost_{entry}(a) = d_{snap} + t_f \cdot \ell_{e_f} \tag{4}$$ $$cost_{entry}(b) = d_{snap} + (1 - t_f) \cdot \ell_{e_f} \tag{3}$$

where $d_{snap}$ is the straight-line snap distance (added to the budget when cost is length). Dijkstra runs once from all facility entries simultaneously, producing per-node costs and winning-facility labels.

Partial-edge reach intervals. For an edge $(u, v)$ with cost $\ell$ and node costs $d_u, d_v$, the reachable portion within budget $B$ is the interval $[t_{min}, t_{max}]$ where the interpolated cost at position $t$ is ≤ $B$:

$$cost(t) = \min(d_u + t\ell,\; d_v + (1-t)\ell) \leq B \tag{2}$$

Pedshed ratio:

$$pedshed = \frac{A_{catchment}}{A_{circle}} = \frac{\text{network catchment area}}{\pi \cdot B^2} \tag{1}$$

Pedshed ≥ 0.6 = well-connected grid; 0.4–0.6 = average; < 0.4 = poor — the network delivers less than half the promised radius.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesPrepared network required. Projected CRS.
FacilitiesVector (any)YesFacility point locations. Each snaps to nearest point on nearest edge.
Cost fieldNumeric fieldNoAdditive per-segment cost for time-based catchments.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Prepared street network.
FACILITIESVector (Any)Facility locations. Use school points, park entrances, transit stops.
FACILITY_IDField(optional)Label field for per-facility output. Without it, facilities are numbered.
COST_FIELDField (Numeric)(empty = length)Use for time-based catchments (minutes). When cost ≠ length, circles read breaks as map-unit radii and pedshed mixes units.
BREAKSString"250, 500, 1000"Comma-separated distances/costs. Multiple breaks produce nested bands. Standard walking: 250, 500, 1000 m.
COMBINEEnumMerged only"Merged only" = one combined catchment (nearest facility wins). "Per facility + merged" adds individual facility catchments.
METHODEnumStreet bufferPolygon method: Street buffer (hugs network, cartographic precision); Concave hull (familiar isochrone blob); Convex hull (fastest, most generous).
BUFFERDouble30.0Street buffer width in map units. Wider = smoother but less precise at corners.
HULL_DETAILDouble0.3Concave hull tightness: 0 = tightest (follows streets), 1 = convex. Only used with Concave hull method.
RINGSBooleanFalseIf true, outputs difference rings (outer band minus inner) for clean band cartography.
EDGESVector (Line)Reached streets trimmed and coloured by cost band.
AREASVector (Polygon)Service area polygons (catchment geometry).
CIRCLESVector (Polygon)Straight-line catchments (circles of break radius) for comparison.
SUMMARYTablePedshed summary with circle area, network area, ratio, and reached street length per break per facility.

Output Description

OutputKey FieldsDescription
Edgesfacility, band, cost_from, len_mStreet pieces trimmed at budget. band = break value; len_m = actual piece length.
Areasfacility, break, rank, areaCatchment polygons. rank = break index (1 = smallest).
Circlesfacility, break, areaStraight-line circles of radius = break.
Summaryfacility, break, circle_area, net_area, pedshed, street_lenpedshed = net_area / circle_area. street_len = total reached street length.

Symbolic Representation

Overlay Circles (hollow, dashed outline, 0.5 mm stroke) on top of Areas (solid fill, 30–40% opacity, sequential colour by break). The gaps between circle and area are the visual story — they show exactly where the paper radius fails on the ground. This is the single strongest exhibit for a standards review. Use blue for circles and warm colours (OrRd) for catchments to emphasise the deficit. For the edges output, colour by band with a sequential ramp for a served-street map. The summary table's pedshed column ranks facilities by catchment efficiency — style as a bar chart in the QGIS layout.

Interpretation Guide

Academic References

Curtin, K.M. (2007). "Network Analysis in Geographic Information Science: Review, Assessment, and Projections." Cartography and Geographic Information Science, 34(2), 103–111. DOI: 10.1559/152304007781002163

Porta, S. & Renne, J.L. (2005). "Linking Urban Design to Sustainability: Formal Indicators of Social Urban Sustainability Field Research in Perth, Western Australia." Urban Design International, 10, 51–64. DOI: 10.1057/palgrave.udi.9000136

Handy, S.L. & Niemeier, D.A. (1997). "Measuring Accessibility: An Exploration of Issues and Alternatives." Environment and Planning A, 29(7), 1175–1194. DOI: 10.1068/a291175

Nearest Facility Allocation

Processing ID: planx:nearestfacility

Overview

Assigns every demand point (building, household, parcel centroid) to its nearest facility over the street network and reports each facility's total load. A single multi-source Dijkstra run resolves all assignments at once — every demand point is simultaneously claimed by its closest facility. Outputs include the allocated demand layer (with assigned facility label and network cost), optional spider/allocation lines, reconstructed network-path routes, and a per-facility load summary with demand count, mean cost, and maximum cost.

Theoretical Background

Academic lineage

Nearest-facility allocation solves the Voronoi problem on a network: instead of partitioning space into Euclidean regions (as a standard Thiessen/Voronoi diagram would), it partitions it by shortest-path distance. The concept extends the classical planar Voronoi diagram (Dirichlet, 1850; Voronoi, 1908) to graphs, where the "distance" is the shortest-path cost on the network rather than the Euclidean norm. The de-facto catchment boundaries — the lines where the nearest facility changes — rarely align with administrative boundaries, census tracts, or straight-line catchment circles. This is the fundamental insight of network-based accessibility planning: the street network, not the radius, determines who goes where (Talen & Anselin, 1998).

The computational technique — multi-source Dijkstra — was first described by Dreyfus (1969) as an extension of Dijkstra's algorithm: insert a virtual super-source node connected to all facility nodes with zero-cost edges, then run a single Dijkstra pass. All reachable nodes are labelled with the facility that first reached them (the one with the minimum cost). The implementation in engine/paths.py as multi_source uses a priority-queue initialised with all facility nodes at cost zero, each carrying its facility label. The relaxation rule is strict improvement only: if two facilities reach a node at exactly equal cost, the one already settled keeps it — the assignment is deterministic but the first-settled bias favours facilities appearing earlier in the input order.

The approach is $O((n + f) \log n + e)$ where $f$ is the number of facilities — a single Dijkstra pass replaces $f$ independent single-source passes. This makes nearest-facility allocation dramatically cheaper than OD routing: assigning 5,000 demand points to 50 facilities requires one Dijkstra pass, not 5,000.

Key assumptions

When to use vs. when NOT to use

Use Nearest Facility when: auditing school catchment assignment, healthcare service area planning, emergency-service station coverage analysis, or any application where the question is "which facility serves this location?" and facilities do not have hard capacity limits. Combine with the SUMMARY output to identify overloaded facilities (demand_n far above the mean) and underserved service gaps (facility = "").

Do NOT use when: (a) facilities have strict capacity limits — use Capacitated Allocation; (b) you need only the cost to the nearest facility, not the allocation label — the Multi-Amenity Access Score is faster for per-category proximity without spatial assignment; (c) the question is about optimal facility locations, not the allocation from existing facilities — use the Facility Location Optimizer.

Mathematical Formulation

Given $n$ demand locations and $f$ facility locations, each snapped to network nodes, the algorithm computes:

$$label(d) = \arg\min_{k \in \{1,\ldots,f\}} d_{net}(k, d) \tag{5}$$ $$cost(d) = \min_{k \in \{1,\ldots,f\}} d_{net}(k, d) \tag{4}$$

where $d_{net}(k,d)$ is the shortest-path distance from facility $k$ to demand $d$ on the graph. If multiple facilities share a node, the first one in the input order wins — assignments are deterministic.

Facility load:

$$load(k) = |\{d : label(d) = k\}| \tag{3}$$ $$mean\_cost(k) = \frac{1}{load(k)} \sum_{d: label(d)=k} cost(d) \tag{2}$$ $$max\_cost(k) = \max_{d: label(d)=k} cost(d) \tag{1}$$

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesPrepared network. Projected CRS.
Demand pointsVector (any)YesLocations to be allocated. Building centroids, parcel points, population-weighted points.
FacilitiesVector (any)YesFacility locations. Points snap to nearest network node.
Cost fieldNumeric fieldNoAdditive per-segment cost for time-based allocation. Walking Slope Comfort's time_fwd_min gives slope-aware catchments.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network.
DEMANDVector (Any)Demand point locations.
FACILITIESVector (Any)Facility locations with ID field.
FACILITY_IDFieldField identifying each facility in the output.
COST_FIELDField (Numeric)(empty = length)Additive per-segment cost. Use for time-based allocation.
CUTOFFDouble0 (unlimited)Maximum cost; demand beyond this is unallocated (facility = "", cost = −1).
OUTPUTVector (Point)Allocated demand with facility label and cost.
SPIDERVector (Line)(optional)Straight allocation lines from each demand to its facility.
ROUTESVector (Line)(optional)Reconstructed street-level routes. Slower but gives actual travel paths.
SUMMARYTablePer-facility load summary.

Output Description

OutputFieldsDescription
Allocated demandfacility, net_costAssigned facility label and network cost. facility = "" and net_cost = −1 = unreachable.
Allocation linesfacility, net_costStraight lines from demand to facility. Long bundles crossing other catchments = missing facility or network barrier.
Allocation routesdemand_i, facility, net_cost, length_mActual street-level paths. length_m = geometric length of the reconstructed route.
Facility summaryfacility, demand_n, mean_cost, max_costdemand_n = load. max_cost = worst-case trip (the number standards care about).

Symbolic Representation

Colour demand points by facility with a categorical renderer (random colours or a predefined palette) to see de-facto catchment boundaries — these are the network Voronoi regions. Style unallocated points (cost = −1) as red X markers to highlight service gaps. Allocation lines coloured by facility, with 30–40% opacity, reveal the spatial extent of each catchment. For the summary table, a bar chart of demand_n by facility identifies overloaded facilities at a glance.

Interpretation Guide

Academic References

Talen, E. & Anselin, L. (1998). "Assessing Spatial Equity: An Evaluation of Measures of Accessibility to Public Playgrounds." Environment and Planning A, 30(4), 595–613. DOI: 10.1068/a300595

DOI: 10.1080/01944361003766766

Dreyfus, S.E. (1969). "An Appraisal of Some Shortest-Path Algorithms." Operations Research, 17(3), 395–412. DOI: 10.1287/opre.17.3.395

Link Criticality (Network Robustness)

Processing ID: planx:linkcriticality

Overview

Ranks every street segment by how badly the network would suffer if that segment were lost — the road-network vulnerability view based on the Network Robustness Index (Scott et al., 2006; Jenelius et al., 2006). For a given origin–destination demand set, the tool first routes every pair on the intact network, then removes each segment in turn and re-routes, reporting the extra travel cost each removal forces plus any demand it cuts off entirely. Only segments that carry at least one shortest path are re-tested — the rest cannot change any route and score zero, which prunes the computation significantly.

Theoretical Background

Academic lineage

Network robustness analysis emerged from the intersection of transportation engineering and complex network theory in the early 2000s. The pivotal event was the 2001 Nisqually earthquake in Washington State, which damaged bridges and severed key road links, demonstrating that modern cities depend on a small number of critical links whose failure cascades far beyond the immediate damage zone. This spurred the first formal methodologies: the Network Robustness Index (NRI) by Scott, Novak, Aultman-Hall & Guo (2006), which introduced the concept of OD-weighted consequence measurement, and the importance/exposure framework of Jenelius, Petersen & Mattsson (2006), which separated link criticality into an importance component (how much travel uses the link) and an exposure component (how much travel is affected when it fails).

The field draws on two distinct intellectual traditions. The first is graph-theoretic vulnerability, rooted in the Erdős–Rényi and Barabási–Albert random-graph models: it asks which nodes or edges, when removed, maximally increase the average path length or fragment the network. The second is transport-system consequence analysis: it asks, for a given demand (the actual trips people make), which link failures impose the highest total detour cost. The two traditions sometimes conflict — a topologically central edge that carries few real trips ranks high by betweenness but low by NRI, and vice versa. The PlanX implementation follows the second tradition (consequence analysis) because planning decisions about bridge replacement and emergency routing should be driven by actual travel demand, not abstract topology.

Mattsson & Jenelius (2015) consolidated the field in their comprehensive review, distinguishing vulnerability (the susceptibility to disruption) from resilience (the ability to recover function after disruption). The NRI implemented in PlanX measures vulnerability; the n_disconnected field captures the most extreme form — total severance. These authors also highlighted that the most impactful single-step improvement to a vulnerability analysis is rarely a new algorithm but placing the OD demand on the trips that actually matter: emergency vehicle access routes, hazardous material corridors, and evacuation paths produce more actionable rankings than generic all-pairs matrices.

Key assumptions

When to use vs. when NOT to use

Use Link Criticality when: screening a road network for bridges, tunnels, and main arteries whose failure would impose the greatest detour cost; preparing a resilience investment priority list; identifying single points of failure (n_disconnected > 0) that warrant duplication or hardening; evaluating a proposed new link by comparing criticality scores before and after it is added — the links whose criticality drops most are those that the new link relieves.

Do NOT use when: (a) the analysis requires multi-link failure scenarios (earthquake damage to multiple bridges) — the tool tests one edge at a time; (b) you need a generic, demand-independent structural ranking of the network — use Network Centrality (betweenness) instead; (c) the network is very small (under 50 segments) and all-pairs routing is trivial — the tool's computational overhead is wasted; (d) you need real-time or near-real-time vulnerability assessment on a live traffic network — the exhaustive removal-test approach is designed for offline planning analysis, not operational use.

Mathematical Formulation

Baseline routing. For every OD pair $(o,d)$ in the demand set, compute $d_{net}(o,d)$ on the intact graph $G$. The base total is:

$$T_{base} = \sum_{o,d} d_{net}(o,d) \tag{3}$$

An edge $e$ is a candidate if it lies on at least one intact shortest path: $used\_by(e) = |\{(o,d) : e \in path(o,d)\}| > 0$.

Removal test. For each candidate edge $e$, route all OD pairs on $G \setminus \{e\}$:

$$extra\_cost(e) = \sum_{o,d} \max(0,\; d_{net}^{(e)}(o,d) - d_{net}(o,d)) \tag{2}$$

Note: removal can only lengthen paths, so $extra\_cost(e) \geq 0$. If the removal severs an OD pair entirely, $d_{net}^{(e)}(o,d)$ is infinite and the pair contributes to $n\_disconnected(e)$.

Criticality (NRI):

$$criticality(e) = \frac{extra\_cost(e)}{T_{base}} \tag{1}$$

Criticality is dimensionless, typically in $[0, 0.1]$ — losing a single segment rarely adds more than 10% to total travel, though extreme values occur for bridges and single-access connectors.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesPrepared network. Projected CRS. More segments = more removal tests.
OriginsVector (any)YesDemand origins. Place on the trips that matter (population to hospitals, depots to demand).
DestinationsVector (any)NoEmpty = origins serve as destinations (all-pairs).
Cost fieldNumeric fieldNoAdditive per-segment cost.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network.
ORIGINSVector (Any)Origin demand points.
DESTINATIONSVector (Any)(optional)Destination demand points. Empty = all-pairs among origins.
COST_FIELDField (Numeric)(empty = length)Additive per-segment cost column.
CUTOFFDouble0 (unlimited)Maximum cost for baseline routing. Pairs beyond cutoff are excluded from the demand set.
CRITICALVector (Line)Street segments with criticality scores.
Performance note: This tool runs Dijkstra $c + 1$ times, where $c$ is the number of candidate segments (those with used_by > 0). For a 10,000-segment network with 2,000 candidates and 500 origins, this is ~2,000 Dijkstra runs. Use a modest origin set (20–100 points) placed on the trips that actually matter for your resilience analysis — population centroids to hospital locations, for instance — rather than the entire building stock.

Output Description

FieldTypeDescription
edge_idLongUnique edge identifier in the graph
criticalityDoubleNRI: extra_cost / base_total. 0 = fully redundant. Top few percent = critical links.
extra_costDoubleAbsolute detour cost summed over all OD pairs (metres or cost-field units)
n_disconnectedLongNumber of OD pairs severed by removing this edge. Nonzero = genuine cut-edge.
used_byLongNumber of intact shortest paths using this edge. High use + low criticality = well-served redundancy.
length_mDoubleGeometric length of the segment in metres

Symbolic Representation

A graduated renderer on criticality with a sequential multi-hue ramp (Inferno or Plasma), natural-breaks classification (5–7 classes), and line width 0.3–0.8 mm scaled by criticality percentile. Edges with n_disconnected > 0 should use a distinct colour (red, 2 mm stroke) — these are the true single points of failure. The top 5% by criticality warrant labels with the edge ID. At city scale, filter to criticality ≥ 0.001 to declutter.

Interpretation Guide

Academic References

Scott, D.M., Novak, D.C., Aultman-Hall, L., & Guo, F. (2006). "Network Robustness Index: A New Method for Identifying Critical Links and Evaluating the Performance of Transportation Networks." Journal of Transport Geography, 14(3), 215–227. DOI: 10.1016/j.jtrangeo.2005.10.003

Jenelius, E., Petersen, T. & Mattsson, L.-G. (2006). "Importance and Exposure in Road Network Vulnerability Analysis." Transportation Research Part A, 40(7), 537–560. DOI: 10.1016/j.tra.2005.11.003

Mattsson, L.-G. & Jenelius, E. (2015). "Vulnerability and Resilience of Transport Systems — A Discussion of Recent Research." Transportation Research Part A, 81, 16–34. DOI: 10.1016/j.tra.2015.06.002

10. Walkability

The Walkability group translates the walkable-city agenda into four interconnected GIS tools. Walkability Audit scores every street segment 0–100 using the classic Frank et al. (2010) ingredients: intersection density, land-use mix, destinations, block length, and slope — each normalised with documented breakpoints and combined with editable weights. Walking Slope Comfort samples a DEM along each street and computes Tobler's-hiking-function travel times. Street Environment Comfort scores streets by the assets (trees, benches, lighting) and barriers (blank walls, vacant lots) around them using kernel-density estimates. Pedestrian Route Quality re-routes over the comfort-weighted network, reporting the detour, the length-weighted quality score, and the low-score share — the difference between the shortest path and the pleasantest one.

Walkability Audit

Processing ID: planx:walkability

Overview

Scores every street segment 0–100 for walkability using five classic ingredients from the walkability-index literature: intersection density (connectivity), land-use mix (normalised Shannon entropy of buffer areas), destination counts (POIs within radius), block length (mean street-segment length as a block-size proxy), and slope (segment gradient from an optional DEM). Each ingredient is normalised 0–100 with documented breakpoints; missing inputs are renormalised away so the tool degrades gracefully with partial data. The output is the definitive "existing conditions" exhibit for a pedestrian master plan.

Theoretical Background

The walkability index traces its lineage to urban planning's D-variables framework (Ewing & Cervero, 2010): Density, Diversity (land-use mix), Design (street connectivity), Destination accessibility, and Distance to transit. Frank et al. (2010) operationalised four of these for the Neighborhood Quality of Life Study. PlanX adapts this to the street-segment level — walkability is fundamentally a network property: a street is walkable not in isolation but by virtue of what it connects to. Breakpoints default to a comfortably walkable European urban fabric: 120 junctions/km², 80 m block length, 25 POIs, and 0% slope all score 100.

Mathematical Formulation

Intersection density. Junctions (degree ≥ 3) counted within radius $r$ of each segment midpoint:

$$int\_km2(s) = \frac{|\{j : \|p_j - m_s\| \leq r\}|}{\pi r^2 / 10^6} \qquad s\_inters(s) = 100 \cdot \min(1, int\_km2(s) / 120) \tag{2}$$

Land-use mix. Normalised Shannon entropy of buffer land-use areas:

$$mix(s) = \frac{-\sum_c p_c \ln p_c}{\ln N_{cat}} \in [0,1] \qquad s\_mix(s) = 100 \cdot mix(s) \tag{1}$$

Destinations. POI count within radius: $s\_dest(s) = 100 \cdot \min(1, n\_pois(s) / 25)$.

Block length (smaller-is-better): $s\_block(s) = 100 \cdot \max(0, \min(1, (400 - blk\_len(s)) / (400 - 80)))$.

Slope: $slope\_pct(s) = |z_{end} - z_{start}| / \ell_s \cdot 100$, then $s\_slope(s) = 100 \cdot \max(0, 1 - slope\_pct(s) / 10)$.

Composite: $walk\_score(s) = \sum w_c \cdot s_c(s) / \sum w_c$ over available components, with missing weights renormalised.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesPrepared, projected CRS (metres). Audit radius is in map units.
Land-use polygonsVector polygonsNoNeeds category field. Without it, mix is skipped.
Destinations/POIsVector pointsNoShops, schools, stops, parks. Counted within radius.
DEMRasterNo10–30 m resolution adequate. Sampled at segment endpoints.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network. Exploded segments.
LANDUSEVector (Polygon)(optional)Land-use for mix component.
CATEGORY_FIELDField(optional)Land-use category field.
POISVector (Point)(optional)Destination points.
DEMRaster(optional)DEM for slope.
RADIUSDouble400.0Audit radius in map units. 400 m = ~5-min walk.
WEIGHTSString"intersections=0.3, mix=0.25, destinations=0.25, blocklength=0.1, slope=0.1"Component weights. Renormalised over available components.
OUT_SEGMENTSVector (Line)Walkability-scored segments.

Output Description

FieldTypeRangeDescription
walk_scoreDouble0–100Composite. 70+ walkable; 50–70 friction; <40 car-dependent.
s_interDouble0–100Intersection density sub-score
s_mixDouble0–100Land-use mix sub-score (null if no data)
s_destDouble0–100Destination sub-score (null if no data)
s_blockDouble0–100Block length sub-score
s_slopeDouble0–100Slope sub-score (null if no DEM)
int_km2Double0–200+Raw junction density (junctions/km²)
mix_entDouble0–1Raw Shannon entropy
n_poisInteger0–NRaw POI count
blk_lenDouble20–500Raw mean block length (m)
slope_pctDouble0–15+Raw slope (%)

Symbolic Representation

Map walk_score with a diverging RdYlGn ramp (5 classes: 0–20, 20–40, 40–60, 60–80, 80–100). Green = walkable; red = car-dependent. Line width 0.5 mm. Map sub-scores as small multiples to reveal which deficiency drives each low-scoring corridor: low s_inter = coarse network (add cut-throughs); low s_mix = monofunctional zoning; low s_dest = nothing to walk TO; low s_block = oversized blocks; low s_slope = topography. Rank low-score corridors by adjacent population for the investment priority list.

Interpretation Guide

Walkability bands map to different remedies: low s_inter → new links and cut-throughs; low s_mix → permit corner commercial uses; low s_dest → ground-floor activation, kiosks, stops; low s_block → mid-block crossings; low s_slope → topography (route around, not fixable by zoning). A high-scoring segment next to very low ones reads as an island — walkability is a NETWORK property. Rerun after proposed interventions and difference the scores to show exactly which streets improve and by how much. Feed walk_score into Pedestrian Route Quality to find the pleasantest path, not just the shortest.

Academic References

Frank, L.D., Sallis, J.F., Saelens, B.E., Leary, L., Cain, K., Conway, T.L., & Hess, P.M. (2010). "The Development of a Walkability Index: Application to the Neighborhood Quality of Life Study." British Journal of Sports Medicine, 44(13), 924–933. DOI: 10.1136/bjsm.2009.058701

Ewing, R. & Cervero, R. (2010). "Travel and the Built Environment: A Meta-Analysis." Journal of the American Planning Association, 76(3), 265–294. DOI: 10.1080/01944361003766766

Appendix D: Complete Bibliography

Barabasi, A.-L. & Albert, R. (1999). "Emergence of Scaling in Random Networks." Science, 286(5439), 509–512. DOI: 10.1126/science.286.5439.509

Bast, H., Delling, D., Goldberg, A., Muller-Hannemann, M., Pajor, T., Sanders, P., Wagner, D. & Werneck, R.F. (2016). "Route Planning in Transportation Networks." In: Kliemann, L. & Sanders, P. (eds.), Algorithm Engineering, LNCS 9220, pp. 19–80. Springer. DOI: 10.1007/978-3-319-49487-6_2

Bavelas, A. (1948). "A Mathematical Model for Group Structures." Human Organization, 7(3), 16–30. DOI: 10.17730/humo.7.3.f4033344851gl053

Bonacich, P. (1972). "Factoring and Weighting Approaches to Status Scores and Clique Identification." Journal of Mathematical Sociology, 2(1), 113–120. DOI: 10.1080/0022250X.1972.9989806

Brandes, U. (2001). "A Faster Algorithm for Betweenness Centrality." Journal of Mathematical Sociology, 25(2), 163–177. DOI: 10.1080/0022250X.2001.9990249

Crucitti, P., Latora, V. & Porta, S. (2006). "Centrality Measures in Spatial Networks of Urban Streets." Physical Review E, 73, 036125. DOI: 10.1103/PhysRevE.73.036125

Crucitti, P., Latora, V. & Porta, S. (2006). "Centrality in Networks of Urban Streets." Chaos, 16, 015113. DOI: 10.1063/1.2150162

Curtin, K.M. (2007). "Network Analysis in Geographic Information Science: Review, Assessment, and Projections." Cartography and Geographic Information Science, 34(2), 103–111. DOI: 10.1559/152304007781002163

Dijkstra, E.W. (1959). "A Note on Two Problems in Connexion with Graphs." Numerische Mathematik, 1(1), 269–271. DOI: 10.1007/BF01386390

Dreyfus, S.E. (1969). "An Appraisal of Some Shortest-Path Algorithms." Operations Research, 17(3), 395–412. DOI: 10.1287/opre.17.3.395

Ewing, R. & Cervero, R. (2010). "Travel and the Built Environment: A Meta-Analysis." Journal of the American Planning Association, 76(3), 265–294. DOI: 10.1080/01944361003766766

Frank, L.D., Sallis, J.F., Saelens, B.E., Leary, L., Cain, K., Conway, T.L., & Hess, P.M. (2010). "The Development of a Walkability Index: Application to the Neighborhood Quality of Life Study." British Journal of Sports Medicine, 44(13), 924–933. DOI: 10.1136/bjsm.2009.058701

Freeman, L.C. (1977). "A Set of Measures of Centrality Based on Betweenness." Sociometry, 40(1), 35–41. DOI: 10.2307/3033543

Handy, S.L. & Niemeier, D.A. (1997). "Measuring Accessibility: An Exploration of Issues and Alternatives." Environment and Planning A, 29(7), 1175–1194. DOI: 10.1068/a291175

Hillier, B. & Hanson, J. (1984). The Social Logic of Space. Cambridge University Press. DOI: 10.1017/CBO9780511597237

Hillier, B., Penn, A., Hanson, J., Grajewski, T., & Xu, J. (1993). "Natural Movement: Or, Configuration and Attraction in Urban Pedestrian Movement." Environment and Planning B: Planning and Design, 20(1), 29–66. DOI: 10.1068/b200029

Hillier, B. & Iida, S. (2005). "Network and Psychological Effects in Urban Movement." In: Cohn, A.G. & Mark, D.M. (eds.), Spatial Information Theory (COSIT 2005), LNCS 3693, pp. 475–490. Springer. DOI: 10.1007/11556114_30

Hillier, B., Yang, T., & Turner, A. (2012). "Normalising Least Angle Choice in Depthmap — and How It Opens Up New Perspectives on the Global and Local Analysis of City Space." Journal of Space Syntax, 3(2), 155–193. journal does not assign DOIs] Available at: UCL Discovery

Jenelius, E., Petersen, T. & Mattsson, L.-G. (2006). "Importance and Exposure in Road Network Vulnerability Analysis." Transportation Research Part A, 40(7), 537–560. DOI: 10.1016/j.tra.2005.11.003

Mattsson, L.-G. & Jenelius, E. (2015). "Vulnerability and Resilience of Transport Systems — A Discussion of Recent Research." Transportation Research Part A, 81, 16–34. DOI: 10.1016/j.tra.2015.06.002

Ortuzar, J. de D. & Willumsen, L.G. (2011). Modelling Transport. 4th ed., Wiley. DOI: 10.1002/9781119993308

Porta, S. & Renne, J.L. (2005). "Linking Urban Design to Sustainability: Formal Indicators of Social Urban Sustainability Field Research in Perth, Western Australia." Urban Design International, 10, 51–64. DOI: 10.1057/palgrave.udi.9000136

Porta, S., Crucitti, P., & Latora, V. (2006). "The Network Analysis of Urban Streets: A Primal Approach." Environment and Planning B: Planning and Design, 33(5), 705–725. DOI: 10.1068/b32045

Porta, S., Crucitti, P. & Latora, V. (2006). "The Network Analysis of Urban Streets: A Dual Approach." Physica A, 369(2), 853–866. DOI: 10.1016/j.physa.2005.12.063

Scott, D.M., Novak, D.C., Aultman-Hall, L., & Guo, F. (2006). "Network Robustness Index: A New Method for Identifying Critical Links and Evaluating the Performance of Transportation Networks." Journal of Transport Geography, 14(3), 215–227. DOI: 10.1016/j.jtrangeo.2005.10.003

Sevtsuk, A. & Mekonnen, M. (2012). "Urban Network Analysis: A New Toolbox for ArcGIS." Revue Internationale de Geomatique, 22(2), 287–305. DOI: 10.3166/rig.22.287-305

Talen, E. & Anselin, L. (1998). "Assessing Spatial Equity: An Evaluation of Measures of Accessibility to Public Playgrounds." Environment and Planning A, 30(4), 595–613. DOI: 10.1068/a300595

Turner, A. (2001). "Angular Analysis." In: Peponis, J., Wineman, J. & Bafna, S. (eds.), Proceedings of the 3rd International Space Syntax Symposium, Atlanta, GA, pp. 30.1–30.11. conference proceedings] Available at: UCL Discovery

Wasserman, S. & Faust, K. (1994). Social Network Analysis: Methods and Applications. Cambridge University Press. DOI: 10.1017/CBO9780511815478

Wilson, A.G. (1967). "A Statistical Theory of Spatial Distribution Models." Transportation Research, 1(3), 253–269. DOI: 10.1016/0041-1647(67)90035-4

Walking Slope Comfort

Processing ID: planx:walkingslope

Overview

Profiles every street segment against a Digital Elevation Model (DEM) to compute slope statistics, comfort classes, and direction-aware walking times. The engine samples elevations at regular intervals along each segment and applies Tobler's hiking function — the canonical empirical model relating walking speed to terrain gradient. For each segment, it reports the length-weighted mean absolute slope, maximum slope, total vertical climb and descent, and estimated forward and reverse walking travel times. The output's time_fwd_min column is designed as a drop-in cost field for all PlanX network routing tools, enabling slope-aware catchments that price uphill walks honestly. The reverse time captures the directional asymmetry that flat-network models ignore.

The comfort classification uses editable breakpoints on the mean absolute slope percentage. The defaults (5%, 8%, 12%) map to established terrain-accessibility standards: 5% is the maximum running slope permitted for an accessible route under ADA/ABA guidelines without landings; 8.33% is the maximum for a short ramp with landings; and 12% is the threshold above which stairs or mechanical assistance is generally preferred. These are illustrative defaults — local standards and terrain conditions should override them.

Theoretical Background

Walking speed as a function of terrain gradient has been studied for over a century, from Naismith's rule-of-thumb (1892) for Scottish mountaineering to modern GPS-instrumented studies. The canonical empirical function in GIScience is Tobler's hiking function (Tobler, 1993):

$$v(m) = 6 \cdot \exp\left(-3.5 \cdot |m + 0.05|\right) \quad [\text{km/h}] \tag{5}$$

where $m$ is the signed slope gradient as a fraction ($m = dz/dx$, so $m = +0.10$ is a 10% uphill, $m = -0.05$ a 5% downhill). The function is empirically calibrated from hiker data and has three key properties:

  1. Peak speed: 6.0 km/h at $m = -0.05$ (a gentle 5% downhill), faster than level walking because gravity assists without braking.
  2. Asymmetry: downhill speeds exceed uphill speeds for the same absolute gradient — the function is not symmetric about $m = 0$.
  3. Exponential decay: speeds drop rapidly for steep grades. At $m = 0.10$ (10% uphill), speed is ~3.5 km/h; at $m = 0.20$ (20%), ~1.5 km/h — halving distance at double the time.

This asymmetry has profound implications for accessibility analysis. Consider a hill between a residential area and a train station: the uphill journey (home to station) takes substantially longer than the downhill return, yet standard network-cost models assume a symmetric edge weight. A catchment drawn with a flat-network budget of 800 m will systematically overestimate the reach from uphill origins and underestimate it from downhill origins. Walking Slope Comfort provides the direction-aware travel times needed to correct this error.

The method of profiling — sampling a raster at regular intervals along a polyline and computing length-weighted grade statistics — is standard in trail analysis and has been applied to urban networks by Iverson (1975) and others. The length-weighted mean ($\sum |dz| / \sum dx$) is preferred over the arithmetic mean because it correctly accounts for segments of varying length: a short, steep section contributes proportionally less to the mean than a long, gentle one.

The comfort classification breakpoints are grounded in accessibility standards but are illustrative. The Americans with Disabilities Act (ADA, 2010) specifies 5% (1:20) as the maximum running slope for an accessible route and 8.33% (1:12) for short ramps with landings. The UN Convention on the Rights of Persons with Disabilities (CRPD) uses similar thresholds internationally. At 12% mean slope, most jurisdictions would require stairs or mechanical assistance (lift, funicular). These breakpoints are user-editable: a municipality with steeper terrain (e.g., San Francisco, Istanbul) may adopt a more permissive classification calibrated to local topography.

Mathematical Formulation

Profile sampling. Given a polyline of length $L$ and a sample step $\Delta s$, the tool generates sample distances $d_k = k \cdot \Delta s$ for $k = 0, 1, \ldots, \lceil L / \Delta s \rceil$, with the final sample at $d = L$. At each distance, a point is interpolated along the geometry and the DEM is sampled at that coordinate:

$$z_k = \text{DEM}(x(d_k), y(d_k)) \tag{4}$$

Samples falling outside the DEM extent or on NoData cells are discarded. If fewer than two valid samples are obtained, the segment is treated as flat.

Grade statistics. For consecutive samples $k, k+1$, the signed grade (fraction) over interval $i$ is:

$$m_i = \frac{z_{i+1} - z_i}{d_{i+1} - d_i} \tag{3}$$

The length-weighted mean absolute grade is:

$$\bar{m} = \frac{\sum_{i} |m_i| \cdot \Delta d_i}{\sum_{i} \Delta d_i} \tag{2}$$

where $\Delta d_i = d_{i+1} - d_i$. The maximum absolute grade is $m_{\max} = \max_i |m_i|$. Total climb and descent ($\sum \max(0, \Delta z_i)$ and $\sum \max(0, -\Delta z_i)$ respectively) are reported in metres.

Direction-aware travel time. The forward travel time is computed segment by segment using Tobler's speed:

$$t_{\text{fwd}} = \sum_i \frac{\Delta d_i / 1000}{v(m_i)} \cdot 60 \quad [\text{minutes}] \tag{1}$$

where $v(m_i)$ is Tobler's speed in km/h for the signed grade $m_i$. The reverse travel time is computed identically but with the grade sign flipped and the intervals processed in reverse order: $t_{\text{rev}} = \sum_i (\Delta d_{n-i} / 1000) / v(-m_{n-i}) \cdot 60$. The effective mean speed from the Tobler profile is reported as tobler_fwd_kmh and tobler_rev_kmh — these differ from the flat-network assumption of constant walking speed and are the key diagnostics for slope-aware routing.

Why length-weighting matters. Consider a 200 m segment sampled at 10 m intervals: 18 intervals at 2% grade (180 m) and 2 intervals at 20% grade (20 m). The arithmetic mean grade is 3.8%, but the length-weighted mean — which controls the travel-time calculation — is $(180 \times 0.02 + 20 \times 0.20) / 200 = 3.8\%$ here (equal by coincidence because the length weights mirror the sample counts). In general, length-weighting prevents a few steep samples on a short section from dominating the segment-level statistic.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesExploded street segments in a projected CRS (metres). Multipart geometries are handled via the PlanX source_polylines utility which tessellates them.
Digital Elevation ModelRasterYesDEM at 5–30 m resolution. Coarser DEMs (30 m SRTM, ASTER) produce acceptable segment-mean slopes but miss short ramps. LiDAR-derived DEMs (1–2 m) capture kerb cuts and short grades. Must be in the same projected CRS as the network, or the tool will reproject on the fly.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network. Projected CRS required. Exploded segments (one row per street block) give the cleanest per-segment results.
DEMRasterDigital elevation model. Required — without it, the tool cannot proceed.
SAMPLE_STEPDouble10.0Profile sample spacing in map units (typically metres). Smaller values (2–5 m) capture short ramps but increase runtime; 10 m is adequate for segment-mean slope on urban blocks. At 2 m, a 200 m segment generates ~100 sample pairs.
BREAKSString"5,8,12"Comma-separated mean absolute slope % breakpoints for comfort classification. Must be strictly ascending. The tool produces len(breaks) + 1 classes. With the default three breakpoints, classes 1–4 receive labels: Comfortable, Moderate, Steep, Severe. With a different number of breakpoints, generic "Class N" labels are used.
OUTPUTVector (Line)Slope-profiled street segments with all derived fields.

Output Description

FieldTypeRangeDescription
slope_pctDouble0–30+Length-weighted mean absolute slope in percent. 0–5% is comfortable walking; 5–8% is noticeable but manageable; 8–12% is steep; >12% is stairs-preferred terrain.
max_pctDouble0–50+Maximum absolute slope across any sample interval. Even a single steep ramp within an otherwise gentle segment will raise this value — use it to identify discrete barriers.
climb_mDouble0–100+Total vertical climb in metres (sum of positive elevation changes). For a segment that undulates, climb + descent can exceed the net elevation difference.
descent_mDouble0–100+Total vertical descent in metres (sum of absolute negative elevation changes).
tobler_fwd_kmhDouble0–6Effective forward walking speed from the Tobler profile in km/h. The ratio of segment length to forward travel time.
tobler_rev_kmhDouble0–6Effective reverse walking speed. Equal to forward speed only on perfectly flat segments.
time_fwd_minDouble0–60+Forward walking time in minutes using Tobler's hiking function. Use this as the cost field for slope-aware routing.
time_rev_minDouble0–60+Reverse walking time. If time_fwd_min and time_rev_min differ substantially (e.g., 5 min forward, 3 min reverse on a 200 m segment), the grade is significant and directional routing is warranted.
comfort_classInteger1–4+Comfort class index starting at 1 (best). With default breaks: 1 = Comfortable (slope_pct ≤ 5), 2 = Moderate (≤ 8), 3 = Steep (≤ 12), 4 = Severe (>12).
class_labelStringHuman-readable label for the comfort class. With default three breaks: "Comfortable", "Moderate", "Steep", "Severe". With custom breakpoints: "Class 1", "Class 2", etc.

Symbolic Representation

Map slope_pct with a sequential multi-hue ramp (YlOrRd, 5 classes, natural breaks). Red segments (>8%) are walking barriers — these should be the narrowest line weight (0.3 mm) to avoid visual dominance. Map comfort_class as a categorical renderer with green (1), yellow (2), orange (3), red (4) for a standards-compliance heat map. For publication exhibits, use a two-panel layout: slope_pct on one map, time_fwd_min / time_rev_min ratio on the other to show the asymmetry effect.

Interpretation Guide

Primary uses

  1. Slope-aware catchments. Feed time_fwd_min as the cost field into OD Cost Matrix, Nearest Facility Allocation, Service Areas, or the 15-Minute City Access Score. This is the single most important step in calibrating a walking accessibility model for hilly cities. A catchment drawn with flat-network costs can overestimate uphill reach by 30–50% on steep terrain.
  2. Accessibility compliance audit. Map comfort_class ≥ 3 segments against the pedestrian network. These are the locations where wheelchair, stroller, or mobility-impaired continuity breaks. Overlay with census data on disability prevalence to identify the most exposed streets.
  3. Intervention prioritisation. Rank segments by $\text{climb\_m} \times \text{pedestrian\_volume}$ (from OD matrix bundles or pedestrian counts). A segment with 10 m climb and 1000 daily pedestrians is a higher priority for ramps, handrails, or a funicular than a 30 m climb segment with 10 pedestrians.

Diagnostic patterns

Cross-references with other PlanX tools

Pitfalls

Academic References

Tobler, W. (1993). "Three Presentations on Geographical Analysis and Modeling: Non-Isotropic Geographic Modeling; Speculations on the Geometry of Geography; and Global Spatial Analysis." NCGIA Technical Report, 93-1. University of California, Santa Barbara. technical report, no DOI assigned] Available at: eScholarship

Iverson, W.D. (1975). "Assessing terrain conditions for hiking and mountain recreation." Journal of Leisure Research, 7(1), 37–45.

Naismith, W.W. (1892). "Excursions. Cruach Ardran, Stobinian, and Ben More." Scottish Mountaineering Club Journal, 2(3), 136. historical mountaineering journal, no DOI assigned]

ADA Standards for Accessible Design (2010). Title III, 28 CFR Part 36, Section 405: Ramps. U.S. Department of Justice. regulatory standard, no DOI assigned] Available at: ada.gov

Moughtin, C. & Shirley, P. (2005). Urban Design: Green Dimensions. 2nd ed., Architectural Press. (Section 4.2: "Gradients and pedestrian movement" — practical urban-design treatment of slope thresholds.)

Street Environment Comfort

Processing ID: planx:streetcomfort

Overview

Scores every street segment 0–100 by aggregating the micro-scale environmental qualities that pedestrians experience at the street level. The tool models perception as a spatial kernel: assets and barriers do not affect only the point where they sit, but radiate influence over a distance — the bandwidth $h$ — that represents the pedestrian's awareness radius. Comfort assets (trees, benches, street lighting, active shopfronts) raise the score; comfort barriers (potholes, blank walls, vacant lots, construction hoardings) lower it. Optional raster layers (winter sun hours, heat risk index, road noise) add climatic and sensory dimensions.

Each component is aggregated via a kernel density estimate (KDE) at sample points along each segment, then min-max normalised to [0, 1] and orientation- flipped (higher density of barriers = lower comfort). The final index is a weighted mean of the oriented components, scaled 0–100. Missing components are gracefully excluded and weights renormalised; constant components (e.g., all segments have the same tree density) are dropped with a warning.

The index is relative to the study area: it ranks streets against each other, not against an absolute standard. A comfort score of 70 means the segment is in the 70th percentile of local conditions, not that it meets any external benchmark. This makes the tool an ideal comparative diagnostic for identifying the best and worst streets in a network, but unsuitable for compliance checking against a fixed standard.

Theoretical Background

Street-level environmental quality has been studied under several overlapping frameworks. Jan Gehl's decades of observational research in Copenhagen and elsewhere (Gehl, 2011) established that the "space between buildings" — the micro-scale qualities of pavements, facades, street furniture, and planting — determines whether a street invites lingering or merely permits passage. The concept of environmental affordance (Gibson, 1979; applied to urban design by Heft, 1988) argues that the physical environment does not determine behaviour but offers possibilities: a bench affords sitting, a tree affords shade, a blank wall affords nothing. The comfort score aggregates these affordances into a single continuous metric.

The kernel-density approach draws on spatial statistics (Silverman, 1986) and has been applied to urban perception in two influential papers. Ewing & Handy (2009) used expert panel ratings of street-view imagery to identify five urban design qualities — imageability, enclosure, human scale, transparency, and complexity — and then operationalised each with measurable spatial variables. Harvey et al. (2015) extended this with a streetscore algorithm that rated perceived safety from street-level images using a combination of computer vision features and crowd-sourced training data. The PlanX tool follows the same logic but uses vector-based KDE rather than image classification, making it applicable to any city with point-layer inventories (trees, lamps, benches) regardless of whether street-level imagery is available.

The kernel bandwidth $h$ is the critical calibration parameter. At $h = 50$ m (the default), a bench affects comfort out to roughly one block length — the range at which a pedestrian can see and decide to walk toward it. At $h = 25$ m, only very local assets count (the "immediate sidewalk" view). At $h = 100$ m, assets at the far end of a long block still contribute — relevant for assessing visual amenity rather than immediate physical comfort. The kernel shape controls how the influence decays with distance: Uniform (equal weight to $h$), Triangular (linear decay), Epanechnikov (quadratic decay, default — most statistical efficiency), Gaussian (exponential decay, tail extending beyond $h$ but truncated for computational practicality).

Mathematical Formulation

Kernel weights. For a candidate feature at distance $d$ from a sample point, with bandwidth $h > 0$ and normalised distance $u = d/h$:

$$K_{uniform}(u) = 1,\quad K_{tri}(u) = 1 - u,\quad K_{epan}(u) = 1 - u^2,\quad K_{gauss}(u) = e^{-4.5 u^2} \tag{5}$$

All kernels are truncated at $u = 1$ ($d > h \Rightarrow K = 0$). The Gaussian kernel uses $\sigma = h/3$, so that $h$ acts as an approximate $3\sigma$ cutoff.

Segment density. For a segment with sample points $\{\mathbf{s}_1, \ldots, \mathbf{s}_m\}$ and a candidate feature set $\{\mathbf{p}_1, \ldots, \mathbf{p}_k\}$ with weights $w_j$, the segment-level density for this component is:

$$\rho_{\text{seg}} = \frac{1}{m} \sum_{i=1}^{m} \sum_{j=1}^{k} w_j \cdot K\!\left(\frac{\|\mathbf{s}_i - \mathbf{p}_j\|_2}{h}\right) \tag{4}$$

Candidates are pre-filtered by a spatial index (QgsSpatialIndex) on the bounding box of the segment grown by $h$ — only features within reach are evaluated. Sample points are placed at regular intervals (default 10 m) with midpoint offset: the first sample is at $step/2$ from the segment start, avoiding endpoint duplication at shared junctions.

Min-max normalisation and orientation. For component $c$ with raw values $\rho_c(s)$ across $n$ segments, the normalised and oriented value is:

$$\hat{\rho}_c(s) = \begin{cases} \dfrac{\rho_c(s) - \min_s \rho_c}{\max_s \rho_c - \min_s \rho_c} & \text{if direction = +1 (comfort asset)} \\[10pt] 1 - \dfrac{\rho_c(s) - \min_s \rho_c}{\max_s \rho_c - \min_s \rho_c} & \text{if direction = -1 (barrier)} \end{cases} \tag{3}$$

If $\max = \min$ (the component is constant), it is dropped. Raster components are sampled at each sample point and the segment mean is used as $\rho_c(s)$.

Weighted comfort index. For used components $\mathcal{C}$ with weights $w_c$:

$$\text{comfort}(s) = 100 \cdot \frac{\sum_{c \in \mathcal{C}} w_c \cdot \hat{\rho}_c(s)}{\sum_{c \in \mathcal{C}} w_c} \quad \in [0, 100] \tag{2}$$

Component weights default to 1.0 each (equal) and are user-editable via a name=value syntax (e.g., positive=2, negative=1 doubles the asset weight). Absent components are renormalised away:

$$\text{comfort}(s) = 100 \cdot \min\!\left(1,\; \frac{\sum_c w_c \hat{\rho}_c(s)}{\sum_c w_c}\right) \tag{1}$$

The min-max normalisation makes the index scale-dependent on the study area extent. A comfort score of 50 means the segment is at the median of the observed range — if the entire area is pleasant, "median" is still pleasant. For cross-city comparison, use the same component layers and a consistent bandwidth.

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesProjected CRS. Does not need to be prepared (no graph operations).
Comfort assetsVector points (multi-layer)NoOne or more point layers. Each layer contributes to the positive component. Tree points, lamp posts, benches, waste bins, drinking fountains — any feature that improves the pedestrian experience. Each layer may have an optional weight field.
Comfort barriersVector points (multi-layer)NoOne or more point layers. Potholes, graffiti locations, construction sites, blank-wall centroids. With weight field, a larger or more severe barrier can count more.
Positive rasterRasterNoRaster where higher values are better for comfort (e.g., winter sun hours, vegetation index).
Negative rasterRasterNoRaster where higher values are worse for comfort (e.g., heat risk index, road noise dB, air pollution).

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network. Projected CRS required.
POSITIVEMultiple vector layers (Point)(optional)Comfort asset layers. Each layer's points are pooled; if multiple layers are given, all contribute to the positive component with the same bandwidth and kernel.
NEGATIVEMultiple vector layers (Point)(optional)Comfort barrier layers, pooled identically.
WEIGHT_FIELDString"" (empty)Name of a numeric field (must exist in the layer) providing per-feature weights. If empty or the field is absent, weight 1.0 is used. A large pothole (weight = 5) is five times as impactful as a small one (weight = 1).
RASTER_PLUSRaster(optional)Comfort-positive raster.
RASTER_MINUSRaster(optional)Comfort-negative raster.
BANDWIDTHDouble50.0Kernel bandwidth in map units (metres). 50 m = one block. 25 m = immediate sidewalk. 100 m = visual amenity at the street scale.
KERNELEnumEpanechnikov (2)Kernel shape: 0 = Uniform, 1 = Triangular, 2 = Epanechnikov, 3 = Gaussian. Epanechnikov is the MSE-optimal choice for KDE; Gaussian for smoother transitions.
SAMPLE_STEPDouble10.0Spacing between sample points along each segment in map units. Smaller = finer resolution, slower. Midpoint offset prevents double-counting at junctions.
WEIGHTSString"" (equal)Component weight string: positive=2, negative=1, raster_plus=1, raster_minus=1. Weights must be > 0. Absent components are renormalised out; weights for absent components are warned and ignored.
OUTPUTVector (Line)Comfort-scored street segments.

Output Description

FieldTypeRangeDescription
comfortDouble0–100Weighted comfort index. Higher = more comfortable. Mean and low-count (<25) are reported in the log.
pos_denDouble0+Raw density of comfort assets (kernel-weighted sum of asset weights at each sample, averaged over samples). Null if no positive layers given.
neg_denDouble0+Raw density of comfort barriers. Null if no negative layers given.
rplus_meanDoublevariableMean value of the positive raster at segment sample points. Null if no raster given or all samples fall outside the raster.
rminus_meanDoublevariableMean value of the negative raster. Null similarly.
n_samplesInteger1+Number of sample points evaluated on this segment. Short segments (< step) get one midpoint sample.

Symbolic Representation

Map comfort with a diverging RdYlGn ramp (5 quantile classes). Green = pleasant; red = hostile. Line width 0.4–0.7 mm scaled by the absolute deviation of comfort from the network median — the best and worst streets are the widest lines. The raw density fields (pos_den, neg_den) are diagnostic: map neg_den in red alone to see the barrier landscape, then overlay high-pos_den segments in green to identify streets where assets and barriers coexist — a complex reading that the composite score alone would average away.

Interpretation Guide

The two-factor diagnostic

Read comfort scores as a two-by-two matrix with pos_den and neg_den:

High pos_den (asset-rich)Low pos_den (asset-poor)
High neg_den
(barrier-heavy)
Contested street. A main road with trees AND noise, shops AND construction. The comfort score is middling but the street is not neutral — it is intense. Action: reduce barriers (noise barriers, construction management) to let the assets dominate. Neglected corridor. A service alley, an underpass, a vacant-strip arterial. Nothing good, plenty bad. Action: barrier removal first (lighting, surface repair), then asset planting.
Low neg_den
(barrier-light)
Pleasant street. A tree-lined residential avenue with benches. The comfort score ceiling. Action: maintain; protect from infill that would remove assets. Blank canvas. A clean but featureless street — no barriers but nothing to enjoy either. New developments often start here before planting matures. Action: add assets incrementally (street trees first — they deliver shade, beauty, and air quality in one intervention).

Cross-references with other PlanX tools

Pitfalls

Academic References

Gehl, J. (2011). Life Between Buildings: Using Public Space. 6th ed., Island Press. (The foundational observational study of micro-scale urban qualities that predict pedestrian presence and stationary activity.) architecture/design monograph, no DOI assigned]

Ewing, R. & Handy, S. (2009). "Measuring the Unmeasurable: Urban Design Qualities Related to Walkability." Journal of Urban Design, 14(1), 65–84. DOI: 10.1080/13574800802451155

Harvey, C., Aultman-Hall, L., Hurley, S.E., & Troy, A. (2015). "Effects of Skeletal Streetscape Design on Perceived Safety." Landscape and Urban Planning, 142, 18–28. DOI: 10.1016/j.landurbplan.2015.05.007

Silverman, B.W. (1986). Density Estimation for Statistics and Data Analysis. Chapman & Hall. (The canonical text on kernel density estimation, including the MSE optimality of the Epanechnikov kernel.) DOI: 10.1007/978-1-4899-3324-9

Gibson, J.J. (1979). The Ecological Approach to Visual Perception. Houghton Mifflin. (The theory of affordances — that the environment offers possibilities for action — which underpins the entire concept of "environmental comfort" as a measurable property.)

Pedestrian Route Quality

Processing ID: planx:routequality

Overview

Routes pedestrians over quality-weighted streets and reports what the walk is actually like — the complement to every shortest-path tool in PlanX. While OD Routes answers "how far is it," Pedestrian Route Quality answers "how good is the walk." The tool takes a street network with a per-segment quality score (0–100, typically the Walkability Audit output or Street Environment Comfort score) and a quality penalty $p$, reweights each segment's cost as:

$$\text{cost}_e = \ell_e \cdot \left(1 + p \cdot \frac{100 - \text{score}_e}{100}\right) \tag{5}$$

With penalty $p = 1$, a segment scoring 0 costs double its length; a segment scoring 50 costs 1.5x; a perfect-100 segment costs exactly its length. With $p = 0$, the quality-weighted cost collapses to the plain geometric length — the tool reproduces the shortest path. For every origin–destination pair, the tool reports the quality-optimal route's length, the plain shortest-path length, the detour ratio between them (the price of pleasantness), the length-weighted mean walk score along the route, the share of route length on low-scoring segments (below a user-set threshold), and the number of edges traversed.

The pairing mode offers two strategies: Nearest destination finds the best-quality route from each origin to its single nearest destination (by quality-weighted cost), the natural mode for "which facility would a quality-conscious pedestrian choose"; All pairs computes routes to every destination, the mode for catchment-quality profiling.

Theoretical Background

The concept of quality-weighted routing originates in the observation that pedestrians do not minimise distance alone. A large body of stated-preference and revealed-preference research (Guo & Loo, 2013; Borst et al., 2009; Agrawal et al., 2008) demonstrates that pedestrians accept detours — often 10–30% — to walk through pleasant, shaded, or active streets rather than along noisy arterials or through dark underpasses. The detour they accept is the revealed value of environmental quality: if pedestrians consistently walk 250 m extra to use a tree-lined street rather than a 750 m arterial, they value the environmental quality at roughly $250/750 = 0.33$ — a 33% premium on the shortest distance.

The quality-penalty formulation used here is a generalised cost model from transport economics (Ortúzar & Willumsen, 2011): each link's perceived cost is the sum of its objective cost (length) and a penalty proportional to its quality deficit. This is structurally identical to how congestion charges, tolls, or "traffic stress" penalties are added to link costs in multi-modal transport models. The penalty $p$ calibrates how much the pedestrian values quality relative to distance: $p = 0.5$ means a score-0 street costs only 1.5x its length (the pedestrian is only mildly quality-sensitive); $p = 2.0$ means a score-0 street costs 3x its length (the pedestrian is highly quality-averse and will detour substantially).

This approach differs from the more common composite index weighting in walkability studies (Frank et al., 2010; Leslie et al., 2007), which produce a single per-segment score but do not model route choice. PlanX separates the two steps: Walkability Audit (or Street Environment Comfort) produces the score; Pedestrian Route Quality answers the behavioural question — given these scores, where would people actually walk?

The connection to Safe Routes to School (SRTS) programmes is direct and operational. SRTS programmes in the US (authorised under federal transportation legislation since 2005) and similar "School Streets" initiatives in the UK and Europe aim to identify and improve the routes children take to school. The standard SRTS audit rates every block on ~10 safety and comfort criteria. Pedestrian Route Quality operationalises this at city scale: run home-to-school pairs (using building centroids or address points), rank routes by low_share (the fraction of the route on streets scoring below, say, 50), and the worst-ranked routes are the SRTS priority list — every block on these routes that falls below the threshold is a candidate for sidewalk widening, crossing improvements, lighting, or traffic calming.

Mathematical Formulation

Quality-weighted edge cost. For edge $e$ with geometric length $\ell_e$ and quality score $\text{score}_e \in [0, 100]$:

$$w_{\text{quality}}(e) = \ell_e \cdot \left(1 + p \cdot \frac{100 - \text{score}_e}{100}\right) \tag{4}$$

Edges without a score (NULL or missing) are treated as neutral: $\text{score}_e = 100$, so $w_{\text{quality}}(e) = \ell_e$ — they contribute their geometric cost and no penalty.

Quality-optimal path. Given origins $\{o_1, \ldots, o_m\}$ and destinations $\{d_1, \ldots, d_n\}$, the quality-optimal route from $o_i$ to $d_j$ minimises:

$$R_{quality}(i,j) = \arg\min_{path(i \to j)} \sum_{e \in path} w_{\text{quality}}(e) \tag{3}$$

This is solved by Dijkstra's algorithm on the quality-weighted adjacency matrix. The shortest-path (distance-only) route is solved on the original (unweighted) adjacency matrix, using the same engine (engine/paths.py) but with $\text{score}_e \equiv 100$ for all edges.

Detour ratio. For the quality-optimal route with total geometric length $L_{quality}$ and the shortest-path length $L_{shortest}$:

$$\text{detour} = \frac{L_{quality}}{L_{shortest}} \tag{2}$$

$\text{detour} \geq 1.0$ always (the shortest path cannot be longer than any other path). $\text{detour} = 1.0$ means the shortest path is also the best- quality path — the ideal outcome. $\text{detour} = 1.5$ means the pedestrian walks 50% further to avoid poor-quality streets.

Length-weighted mean score. For a route comprising edges $\{e_1, \ldots, e_k\}$ with lengths $\ell_1, \ldots, \ell_k$ and scores $s_1, \ldots, s_k$:

$$\overline{s} = \frac{\sum_{i=1}^{k} \ell_i \cdot s_i}{\sum_{i=1}^{k} \ell_i} \tag{1}$$

This is the experienced quality — unlike the simple mean over edges, a long edge with a low score weighs more heavily than a short edge.

Low-score share. The fraction of total route length on segments scoring below a user-defined threshold $s_{low}$ (default: 50):

$$\text{low\_share} = \frac{\sum_{i: s_i < s_{low}} \ell_i}{\sum_{i=1}^{k} \ell_i}$$

Input Data Requirements

InputTypeRequiredNotes
Street networkVector linesYesIdeally the Walkability Audit output. Must be in a projected CRS. The network is passed through build_node_graph so it does not need to be a prepared network — the engine builds the graph on the fly.
Walk-score fieldNumeric fieldNoPer-segment quality score 0–100 from Walkability Audit, Street Environment Comfort, or a custom score. If empty, all segments score 100 (neutral) and the tool reproduces the shortest path.
OriginsVector (any geometry)YesOrigin locations — home addresses, building centroids, zone centres. Snapped to nearest network node.
DestinationsVector (any geometry)YesDestination locations — schools, transit stations, parks, shops.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network with optional walk-score field. Projected CRS.
SCORE_FIELDField (Numeric)(optional)0–100 quality score per segment. Empty = all neutral (100). Use walk_score from Walkability Audit or comfort from Street Environment Comfort.
ORIGINSVector (Any)Origin features. Snapped to nearest network node via Euclidean proximity.
DESTINATIONSVector (Any)Destination features. Same snapping logic.
PAIRINGEnumNearest (0)0 = each origin to its single nearest destination (by quality-weighted cost). 1 = all pairs (each origin to every destination). Use nearest for SRTS and service-access studies; use all pairs for catchment-quality profiling.
PENALTYDouble1.0Quality penalty $p$. 0 = ignore quality (shortest path). 0.5 = mild quality-sensitivity. 1.0 = score-0 street costs double. 2.0 = strong aversion to poor streets.
LOW_THRESHOLDDouble50.0Score below which a segment is "low quality" for the low_share calculation. Set higher (e.g., 60) for a more conservative definition; lower (e.g., 30) for the genuinely hostile segments only.
OUT_ROUTESVector (Line)Quality-optimal route geometries with quality attributes.

Output Description

FieldTypeRangeDescription
originInteger1–MIndex of the origin feature (1-based).
destInteger1–NIndex of the destination feature (1-based).
length_mDouble0+Geometric length of the quality-optimal route in metres. This is the distance the pedestrian would actually walk.
shortest_mDouble0+Length of the shortest (distance-only) path for the same OD pair. The baseline.
detourDouble1.0–~3.0length_m / shortest_m. The price of pleasantness. 1.0 = no price; 1.3+ = substantial detour.
mean_scoreDouble0–100Length-weighted mean walk score along the quality-optimal route. The experienced quality.
low_shareDouble0–1Fraction of route length on segments scoring below LOW_THRESHOLD. 0 = the entire route is above threshold; 0.5 = half the route is on poor streets.
n_edgesInteger1+Number of street segments in the route.

Symbolic Representation

Map routes by detour with a diverging RdYlGn ramp (reversed: green = detour ~1.0, red = detour ≥ 1.4). Line width 0.3 mm, opacity 40% for bundling. The visual story is the divergence between the shortest and quality paths — where many routes take the same detour onto a pleasant alternative, that alternative street emerges as a thick green bundle even if the underlying street network is thin. For the "all pairs" mode, filter to a single origin and colour by dest categorically to see which destinations a quality-conscious pedestrian at that origin would prefer.

Interpretation Guide

The three diagnostic regimes

Regimedetourlow_shareInterpretation
Ideal~1.0~0The shortest path is already high-quality. No intervention needed — the network serves this OD pair well.
Escapable≥1.3~0The direct route is poor, but a good-quality alternative exists a reasonable detour away. The pedestrian can escape. Action: improve the direct route to close the quality gap — this is the highest-return investment because it eliminates the detour for every user on this corridor.
Trapped~1.0>0.3The direct route is poor AND there is no better alternative — every route from origin to destination forces the pedestrian onto low-quality streets. Action: this is the most urgent intervention case. The trapped routes identify missing-link investments: a new cut-through, a pedestrian bridge, or a street upgrade that creates a quality alternative where none exists.

Cross-references with other PlanX tools

Pitfalls

Academic References

Guo, Z. & Loo, B.P.Y. (2013). "Pedestrian Environment and Route Choice: Evidence from New York City and Hong Kong." Journal of Transport Geography, 28, 124–136. DOI: 10.1016/j.jtrangeo.2012.11.010

Agrawal, A.W., Schlossberg, M., & Irvin, K. (2008). "How Far, by Which Route and Why? A Spatial Analysis of Pedestrian Preference." Journal of Urban Design, 13(1), 81–98. DOI: 10.1080/13574800701804074

Leslie, E., Coffee, N., Frank, L., Owen, N., Bauman, A., & Hugo, G. (2007). "Walkability of Local Communities: Using Geographic Information Systems to Objectively Assess Relevant Environmental Attributes." Health & Place, 13(1), 111–122. DOI: 10.1016/j.healthplace.2005.11.001

Borst, H.C., de Vries, S.I., Graham, J.M.A., van Dongen, J.E.F., Bakker, I., & Miedema, H.M.E. (2009). "Influence of Environmental Street Characteristics on Walking Route Choice of Elderly People." Journal of Environmental Psychology, 29(4), 477–484. DOI: 10.1016/j.jenvp.2009.08.002

Ortúzar, J. de D. & Willumsen, L.G. (2011). Modelling Transport. 4th ed., Wiley. (Chapter 7: "Route Choice and Traffic Assignment" — the generalised-cost framework from which the quality-penalty model is derived.) DOI: 10.1002/9781119993308

3. Urban Morphology

The Urban Morphology group quantifies the physical form of the city at three scales: the building (shape metrics, orientation, courtyard index, shared-wall ratio), the plot (morphological tessellation as a cadastral proxy via Voronoi partitioning), and the block (Spacematrix density indicators GSI/FSI/OSR/L and street-network morphology via orientation entropy, meshedness indices, and junction typology).

Building Form Metrics

Processing ID: planx:buildingformmetrics

1. Theoretical Background

1.1 Academic Lineage

Building form metrics translate the geometric properties of individual footprint polygons into numerical descriptors of typology, construction era, and spatial organisation. The intellectual foundation draws from three traditions. First, classical Euclidean geometry contributed the isoperimetric inequality — the circle maximises area for a given perimeter, giving rise to the isoperimetric quotient (IPQ) as a compactness measure that has been used in urban morphology since at least the 1960s (Haggett & Chorley, 1969). Second, computational geometry furnished efficient algorithms for the minimum bounding rectangle via Toussaint's (1983) rotating calipers, enabling orientation and rectangularity to be computed in $O(k)$ time on a convex hull of $k$ vertices. Third, Batty & Longley (1994) demonstrated that fractal geometry could quantify the complexity of building footprints — their observation that organic urban fabric yields higher fractal dimensions than planned modernist fabric remains the standard interpretive frame.

1.2 Methodology Evolution

The systematic computation of building-level morphology metrics was catalysed by the release of open building footprint datasets (OpenStreetMap, national cadastral agencies) and the development of the momepy toolkit (Fleischmann, 2019). Prior to momepy, researchers computed these metrics in ad-hoc scripts; momepy standardised the vocabulary and computation. Fleischmann, Romice & Porta (2020) later provided the theoretical framework connecting single-building metrics to multi-scale urban form analysis, arguing that "spatial signatures" — combinations of metrics across scales — are the proper unit of morphological comparison rather than any single indicator.

1.3 Key Assumptions and Limitations

All metrics operate on the largest part of multi-polygon footprints, discarding detached annexes. Metrics are scale-invariant (ratios of areas and lengths) except area and perimeter which are absolute. The shared-wall computation uses a simple spatial index intersection test; it does not detect topological adjacency from a common cadastral polygon, so it may over-count walls separated by narrow gaps. The fractal dimension formula $D = 2\ln(P/4)/\ln(A)$ is valid for simple closed shapes but produces unreliable values for highly irregular perimeters below ~1 m². All metrics require a projected CRS; results in geographic coordinates are meaningless.

1.4 Use Cases

2. Mathematical Formulation

Let $R = \{(x_1, y_1), \ldots, (x_n, y_n)\}$ denote the exterior ring of a building footprint, with $x_{n+1} = x_1$ and $y_{n+1} = y_1$ for closure. Let interior rings (courtyards) be denoted $I_1, \ldots, I_m$. All metrics are computed in projected map units (metres).

$$ A_{ext} = \frac{1}{2}\left|\sum_{i=1}^{n} x_i y_{i+1} - x_{i+1} y_i\right| \tag{1} $$
$$ A = A_{ext} - \sum_{j=1}^{m} A(I_j), \qquad P = \sum_{i=1}^{n} \sqrt{(x_{i+1} - x_i)^2 + (y_{i+1} - y_i)^2} \tag{2} $$
$$ \text{IPQ} = \frac{4\pi A}{P^2} \in [0, 1], \qquad \text{Convexity} = \frac{A_{ext}}{A_{hull}}, \qquad \text{Rectangularity} = \frac{A_{ext}}{L \cdot W} \tag{3} $$
$$ \text{Elongation} = 1 - \frac{W}{L} \quad (L \geq W), \qquad D_{fractal} = \frac{2\ln(P/4)}{\ln(A_{ext})} \quad (P > 4,\; A_{ext} > 1) \tag{4} $$
$$ \text{SharedWall} = \min\left(1.0,\; \frac{\sum_{k \in N(i)} \ell(\partial B_i \cap \partial B_k)}{P}\right) \tag{5} $$

where $A_{hull}$ is the area of the convex hull computed via Andrew's monotone chain algorithm; $L$ and $W$ are the length and width of the minimum-area rotated rectangle obtained by the rotating calipers method on the convex hull; the orientation $\theta \in [0, 180)$ is the angle of $L$ from the positive $x$-axis; $D_{fractal}$ is set to 0 when the logarithm arguments are invalid; $\partial B_i$ denotes the boundary of building $i$; $N(i)$ is the set of neighbour buildings whose bounding box intersects building $i$; and $\ell$ measures the intersection length of boundary curves.

3. Parameters

ParameterTypeRequiredDefaultDescription
BUILDINGSVector (Polygon)YesBuilding footprints. Must use a projected CRS with metric units. Multi-polygons supported; metrics are computed on the largest part only.
OUTPUTVector (Polygon)Output layer with original attributes plus all 12 computed shape indicators.

4. Output Description

FieldTypeRangeDescription
area_m2Double$\geq 0$Net building area (exterior minus courtyards) in square metres
perim_mDouble$\geq 0$Exterior perimeter in metres
compactDouble$[0, 1]$Isoperimetric quotient. 1 = perfect circle, ~0.785 = square, decreasing with complexity
convexityDouble$[0, 1]$Ratio of exterior area to convex hull area. 1 = convex polygon
rectangDouble$[0, 1]$Ratio of exterior area to minimum rotated bounding rectangle area. ~1 = near-rectangular slab
elongationDouble$[0, 1)$$1 - W/L$. 0 = square plan; approaching 1 = long thin bar (row housing, industrial sheds)
orient_degDouble$[0, 180)$Orientation of the long axis in degrees from east (positive x). 0 = east-west; 90 = north-south
court_m2Double$\geq 0$Total courtyard area (sum of interior ring areas), in square metres
court_idxDouble$[0, 1)$Courtyard area / exterior area. > 0.05 = significant courtyard typology
fractalDouble$\sim$[1.0, 1.5]Fractal dimension. 1.0 = simple rectangle; > 1.3 = highly complex perimeter
cornersInteger$\geq 3$Number of vertices whose deflection angle exceeds 10°
sharedwallDouble$[0, 1]$Fraction of perimeter intersecting neighbouring buildings. 0 = detached; > 0.3 = attached fabric

5. Interpretation Guide

5.1 Benchmark Ranges

MetricDetached VillaRow HousePerimeter BlockSlab TowerIndustrial Shed
compact0.4–0.70.6–0.80.3–0.50.7–0.850.8–0.95
elongation0.2–0.50.6–0.850.1–0.30.1–0.30.7–0.95
sharedwall0.0–0.050.2–0.50.1–0.40.00.0–0.1
court_idx0.00.0–0.020.05–0.30.00.0
fractal1.05–1.151.02–1.081.05–1.201.01–1.051.01–1.03

5.2 Spatial Patterns

5.3 Cross-References

5.4 Common Pitfalls

6. Symbolic Representation

Recommended QGIS styling:

7. Literature

Fleischmann, M. (2019). "momepy: Urban Morphology Measuring Toolkit." Journal of Open Source Software, 4(43), 1807. DOI: 10.21105/joss.01807

Fleischmann, M., Romice, O. & Porta, S. (2020). "Measuring urban form: Overcoming terminological inconsistencies for a quantitative and comprehensive morphologic analysis of cities." Environment and Planning B, 48(8), 2133–2150. DOI: 10.1177/2399808320910444

Toussaint, G.T. (1983). "Solving geometric problems with the rotating calipers." Proceedings of IEEE MELECON '83, Athens, A10.02/1–4.

Batty, M. & Longley, P. (1994). Fractal Cities: A Geometry of Form and Function. Academic Press. ISBN: 978-0124555709.

Haggett, P. & Chorley, R.J. (1969). Network Analysis in Geography. Edward Arnold. ISBN: 978-0713154597.

Steadman, P. (2014). "Building types and built forms." Journal of Space Syntax, 5(1), 1–22.

Morphological Tessellation

Processing ID: planx:morphologicaltessellation

1. Theoretical Background

1.1 Academic Lineage

Morphological tessellation addresses a fundamental data problem in urban morphology: cadastral plot boundaries are often unavailable, proprietary, or inconsistent across jurisdictions, yet they are the natural analysis unit for density, coverage, and typology studies. The method implemented here follows the momepy toolkit (Fleischmann, 2019), which in turn builds on the concept of "spatial proximity polygons" from computational geometry. The core idea — partitioning space so that every point is assigned to its nearest building — originates from Voronoi (1908) but gained urban application through the work of Boffet & Rocca Serra (2001) on "morphological envelopes" and was systematised by Fleischmann, Romice & Porta (2020) into the standard momepy workflow: shrink buildings to prevent gap competition, densify boundaries to generate smooth partitioning lines, construct the Voronoi diagram, dissolve cells per building, and clip to a study area boundary.

1.2 Methodology and Key Decisions

Three design choices critically affect the output. Shrink distance: buildings are buffered inward before seeding to prevent adjacent buildings whose footprints nearly touch from competing over the narrow gap between them — without shrinking, the tessellation boundary bisects the gap, producing implausibly thin cells. A typical shrink of 0.4 m works for modern cadastral data; increase to 1–2 m for hand-digitised historic maps where buildings bleed into each other. Densify spacing: the Voronoi diagram is computed from boundary seed points, not from building centroids, so the cell boundaries can follow the shape of the building. Denser seeds produce cells that more closely approximate a setback-based plot proxy but increase computation time quadratically. Clip mask: cells at the study area edge are artificially truncated. For any cell-level statistic (mean area, Gini of cell sizes), exclude the outer ring of border-touching cells, or supply a study area generously larger than the analysis area.

1.3 Use Cases and Limitations

2. Mathematical Formulation

Let $B = \{b_1, \ldots, b_n\}$ be the set of building footprint polygons in a projected CRS. For each building $b_k$, let $\partial b_k$ denote its exterior ring. The workflow proceeds in five stages:

$$ b_k' = \text{buffer}(b_k, -d_{shrink}) \quad \text{(inward shrink by } d_{shrink} \text{ map units)} \tag{1} $$
$$ S_k = \text{densify}(\partial b_k', d_{densify}) \quad \text{(boundary points at spacing } \leq d_{densify} \text{)} \tag{2} $$

Let $P = \bigcup_{k=1}^{n} S_k$ be the set of all seed points, with each point $p \in P$ tagged by its originating building index $k(p)$. The standard Voronoi tessellation assigns each point in the plane to its nearest seed:

$$ V(p) = \{x \in \mathbb{R}^2 \mid \|x - p\| \leq \|x - q\| \;\forall q \in P,\; q \neq p\} \tag{3} $$

Cells belonging to the same building are dissolved (union), then clipped to the study area mask $M$:

$$ T_k = \bigcup_{p \in S_k} V(p), \qquad \hat{T}_k = T_k \cap M \tag{4} $$

where the study area mask $M$ is either a user-supplied boundary polygon or the convex hull of all building seed points buffered by $d_{limit}$:

$$ M = \begin{cases} \text{user\_boundary} & \text{if STUDY\_AREA provided} \\ \text{buffer}\bigl(\text{ConvexHull}(P),\; d_{limit}\bigr) & \text{otherwise} \end{cases} \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
BUILDINGSVector (Polygon)YesBuilding footprints. Must be in a projected CRS with metric units. At least 3 buildings required.
STUDY_AREAVector (Polygon)NoBoundary polygon to clip cells. When omitted, the convex hull of all buildings buffered by LIMIT is used. Supply a boundary generously larger than the analysis area to avoid edge artefacts.
SHRINKDouble0.4Inward buffer distance for building footprints in map units (metres). Prevents adjacent buildings from competing over narrow gaps. Increase for hand-digitised data; set to 0 for perfectly separated modern footprints. Minimum: 0.0.
DENSIFYDouble2.0Maximum spacing between seed points along building boundaries in map units. Smaller values produce smoother, more accurate cell boundaries at the cost of longer computation. Minimum: 0.1.
LIMITDouble100.0Buffer distance added to the auto-generated study area (convex hull of all buildings). Ignored when STUDY_AREA is provided. Minimum: 1.0.
OUTPUTVector (Polygon)Tessellation cells with original building attributes, cell_id, and cell_m2.

4. Output Description

FieldTypeDescription
cell_idLongIndex of the generating building (0-based). Used to join tessellation cells back to buildings.
cell_m2DoubleCell area in square metres after clipping to study area. Edge cells will have smaller values than interior cells.
(inherited)All attributes from the source building layer are copied to the corresponding cell.

5. Interpretation Guide

5.1 Benchmark Patterns

5.2 Cross-References

5.3 Common Pitfalls

6. Symbolic Representation

Recommended QGIS styling:

7. Literature

Fleischmann, M. (2019). "momepy: Urban Morphology Measuring Toolkit." Journal of Open Source Software, 4(43), 1807. DOI: 10.21105/joss.01807

Fleischmann, M., Romice, O. & Porta, S. (2020). "Measuring urban form: Overcoming terminological inconsistencies for a quantitative and comprehensive morphologic analysis of cities." Environment and Planning B, 48(8), 2133–2150. DOI: 10.1177/2399808320910444

Boffet, A. & Rocca Serra, S. (2001). "Identification of spatial structures within urban blocks for town characterisation." Proceedings of the 20th International Cartographic Conference, Beijing, 1974–1983.

Voronoi, G. (1908). "Nouvelles applications des parametres continus a la theorie des formes quadratiques." Journal fur die Reine und Angewandte Mathematik, 134, 198–287.

Aurenhammer, F. (1991). "Voronoi diagrams — a survey of a fundamental geometric data structure." ACM Computing Surveys, 23(3), 345–405. DOI: 10.1145/116873.116880

Spacematrix Density

Processing ID: planx:spacematrixdensity

1. Theoretical Background

1.1 Academic Lineage

The Spacematrix method was developed by Meta Berghauser Pont and Per Haupt at TU Delft, first published in their 2010 book Spacematrix: Space, Density and Urban Form and revised in an open-access 2023 edition. The method addresses a long-standing problem in urban planning: density is typically regulated through a single number (Floor Area Ratio, FAR, or Floor Space Index, FSI), but a given FSI can describe completely different urban forms depending on how much ground the building occupies (GSI) and how tall it is (L). By jointly analysing FSI, GSI, OSR (Open Space Ratio), and L (mean number of floors), the Spacematrix captures four independent degrees of freedom that together define the built density typology of any urban fabric.

1.2 The Spacematrix Diagram

The method's central analytical device is the Spacematrix diagram: a scatter plot with GSI on the horizontal axis and FSI on the vertical axis. Any built form occupies a single point. Lines of constant L radiate from the origin (FSI = L * GSI). Curves of constant OSR are hyperbolic. The diagram reveals that planning codes which only specify FSI and GSI implicitly fix L (since L = FSI/GSI), and vice versa: a plan demanding FSI 2.0 at GSI 0.2 silently demands 10 storeys (L = 10), which may conflict with height limits or neighbourhood character policies. OSR = (1 - GSI)/FSI measures the open space cost of each built square metre: OSR < 0.5 signals pressure on public space; OSR > 1.5 signals spacious suburban fabric.

1.3 Typology Classification

The 10-class Spacematrix label combines a height category (Low-rise: L < 3; Mid-rise: 3 <= L <= 6; High-rise: L > 6) with a coverage category (compact: GSI >= 0.35; moderate: 0.15 <= GSI < 0.35; spacious: GSI < 0.15). The three by three grid produces nine built classes plus "Unbuilt" for blocks with no intersection area. The labels provide a standardised vocabulary: "Mid-rise compact" describes the classic European perimeter block (4-6 storeys, 40-60% coverage); "High-rise spacious" describes tower-in-park Modernist schemes; "Low-rise spacious" describes detached suburban housing.

1.4 Key Assumptions and Limitations

2. Mathematical Formulation

Let $B = \{b_1, \ldots, b_N\}$ index a set of building footprint polygons, each with known floor count $n_i$. Let $A_k$ be the area of block (analysis unit) $k$. Let $A_{i,k}^{int} = \text{Area}(b_i \cap \text{block}_k)$ be the intersection area of building $i$ with block $k$. The Spacematrix indicators for block $k$ are:

$$ \text{GSI}_k = \frac{\sum_{i} A_{i,k}^{int}}{A_k} \in [0, 1] \tag{1} $$
$$ \text{FSI}_k = \frac{\sum_{i} A_{i,k}^{int} \cdot n_i}{A_k} \quad (\text{dimensionless}) \tag{2} $$
$$ \text{OSR}_k = \frac{1 - \text{GSI}_k}{\text{FSI}_k} \quad (\text{m}^2_{\text{open}} / \text{m}^2_{\text{floor}}) \tag{3} $$
$$ L_k = \frac{\text{FSI}_k}{\text{GSI}_k} \quad (\text{mean number of floors}) \tag{4} $$

The Spacematrix class label $C_k$ is determined by discretising GSI and L:

$$ C_k = \text{classify}(\text{GSI}_k, L_k) = \begin{cases} \text{"Unbuilt"} & \text{if } \text{FSI}_k = 0 \text{ or } \text{GSI}_k = 0 \\ \text{"Low-rise "} + f(\text{GSI}_k) & \text{if } L_k < 3 \\ \text{"Mid-rise "} + f(\text{GSI}_k) & \text{if } 3 \leq L_k \leq 6 \\ \text{"High-rise "} + f(\text{GSI}_k) & \text{if } L_k > 6 \end{cases} \tag{5} $$

where $f(\text{GSI}) = \text{"compact"}$ if $\text{GSI} \geq 0.35$, $\text{"moderate"}$ if $0.15 \leq \text{GSI} < 0.35$, and $\text{"spacious"}$ if $\text{GSI} < 0.15$.

3. Parameters

ParameterTypeRequiredDefaultDescription
BUILDINGSVector (Polygon)YesBuilding footprints. Must be in a projected CRS. Multi-polygons supported; area is computed on the geometry as stored.
LEVELS_FIELDField (Numeric)NoField containing the number of floors per building. When empty, DEFAULT_LEVELS is used for all buildings.
DEFAULT_LEVELSDouble2.0Default floor count applied when LEVELS_FIELD is empty or for buildings with invalid values. Minimum: 0.0. Use 1.0 for single-storey fabric; 2.0 for typical detached housing.
BLOCKSVector (Polygon)YesAnalysis unit polygons. Can be administrative blocks, census tracts, Morphological Tessellation cells, or any custom polygon layer. Must be in a projected CRS. Non-overlapping recommended.
OUTPUTVector (Polygon)Blocks with Spacematrix indicators and class labels appended.

4. Output Description

FieldTypeRangeDescription
b_countInteger$\geq 0$Number of buildings intersecting the block (may be fractional if buildings cross multiple blocks)
fp_m2Double$\geq 0$Total building footprint area within the block, in square metres
gfa_m2Double$\geq 0$Total gross floor area = $\sum$ footprint area $\times$ floors, in square metres
gsiDouble$[0, 1]$Ground Space Index = footprint area / block area. Coverage ratio. 0.35+ = compact urban fabric
fsiDouble$\geq 0$Floor Space Index (equivalent to FAR) = gross floor area / block area. Dimensionless density measure
osrDouble$\geq 0$Open Space Ratio = $(1-\text{GSI})/\text{FSI}$. m² of open space per m² of floor area. < 0.5 = pressure on public space
levelsDouble$\geq 0$Mean number of floors = FSI/GSI. The average height implied by the combined density and coverage
smx_classString10-class Spacematrix label: {Low, Mid, High}-rise {compact, moderate, spacious} + "Unbuilt"

5. Interpretation Guide

5.1 Benchmark Typologies

TypologyGSIFSILOSRLabel
Detached suburban0.05–0.150.1–0.31.5–2.51.5–5.0Low-rise spacious
Row housing0.20–0.400.5–1.22.5–3.50.5–1.5Low/Mid-rise compact
Perimeter block (European)0.35–0.551.5–3.04–60.2–0.5Mid-rise compact
Slab estate (Modernist)0.10–0.250.8–2.06–120.4–1.2High-rise moderate
Tower in park0.03–0.101.0–3.015–400.3–1.0High-rise spacious
Historic core0.50–0.802.0–5.03–80.05–0.25Low/Mid-rise compact

5.2 Reading the Spacematrix Diagram

Plot FSI versus GSI for all blocks in the study area. The arrangement of points reveals:

5.3 Cross-References

5.4 Common Pitfalls

6. Symbolic Representation

Recommended QGIS styling:

7. Literature

Pont, M.B. & Haupt, P. (2023). Spacematrix: Space, Density and Urban Form (Revised ed.). TU Delft OPEN Publishing. DOI: 10.59490/mg.38

Berghauser Pont, M. & Haupt, P. (2010). Spacematrix: Space, Density and Urban Form. NAi Publishers, Rotterdam. ISBN: 978-9056627423.

Berghauser Pont, M. & Haupt, P. (2007). "The relation between urban form and density." Urban Morphology, 11(1), 62–65.

Dovey, K. & Pafka, E. (2014). "The urban density assemblage: Modelling multiple measures." Urban Design International, 19(1), 66–76. DOI: 10.1057/udi.2013.13

Boyko, C.T. & Cooper, R. (2011). "Clarifying and re-conceptualising density." Progress in Planning, 76(1), 1–61. DOI: 10.1016/j.progress.2011.07.001

Fleischmann, M. (2019). "momepy: Urban Morphology Measuring Toolkit." Journal of Open Source Software, 4(43), 1807. DOI: 10.21105/joss.01807

Street Network Morphology

Processing ID: planx:streetnetworkmorphology

1. Theoretical Background

1.1 Academic Lineage

Street network morphology quantifies the structural properties of the street graph — bearing orientations, connectivity, and node typology — in a single analytical pass. The intellectual foundation draws from two traditions. First, graph theory provides the meshedness indices ($\alpha$, $\beta$, $\gamma$) developed by Kansky (1963) for transport network analysis and refined for planar graphs. These indices answer the question: how close is this network to being maximally connected, given its number of nodes? Second, Boeing (2019) introduced orientation entropy and orientation order as a systematic method to quantify how "grid-like" a street network is, using information theory: entropy measures the diversity of street bearings, and order normalises this against the theoretical limits of a perfect four-direction grid ($H_g = \ln 4$) and a completely uniform distribution ($H_{max} = \ln 36$ for 36 directional bins).

1.2 Methodology

Street bearings are computed as the compass angle of each edge's start-to-end vector, made bidirectional ($\theta$ and $\theta + 180^\circ$) because streets are undirected for morphological purposes. The bidirectional bearings are binned into 36 sectors of $10^\circ$ each, centred on $0^\circ$ (North). Boeing weights each bearing by its edge length, so long arterial segments contribute more to the orientation profile than short cul-de-sac stubs. The length-weighted histogram is converted to a probability distribution $p_b$, and Shannon entropy is computed. The orientation order $\phi$ normalises this entropy to $[0, 1]$: $\phi = 1$ for a perfect orthogonal grid (all edges in exactly 4 directions), $\phi \approx 0$ for a network whose bearings are uniformly distributed (random or organic).

Meshedness is measured by three classic planar-graph ratios. $\alpha$ (alpha index) measures the ratio of actual to maximum possible cycles (circuitry): an $\alpha$ of 0 means a tree (no cycles), while $\alpha$ approaching 1 means a maximally circuited planar network. $\beta$ (beta index) is edges per node — values below 1 indicate disconnected components, values near 2 indicate a fully connected planar grid. $\gamma$ (gamma index) measures the ratio of actual to maximum possible edges in a planar graph.

1.3 Use Cases and Limitations

2. Mathematical Formulation

Let $G = (V, E)$ be a planar graph representing the street network, with $n = |V|$ nodes and $e = |E|$ edges. Let $p$ be the number of connected components of $G$. Edge $j$ has length $\ell_j$ and bidirectional bearing $\theta_j \in [0, 360)$.

$$ p_b = \frac{\sum_{j \in \text{bin}(b)} \ell_j}{\sum_{j=1}^{e} \ell_j}, \qquad H = -\sum_{b=1}^{36} p_b \ln p_b \quad (\text{p_b > 0}) \tag{1} $$
$$ \phi = 1 - \left(\frac{H - H_g}{H_{max} - H_g}\right)^2, \quad H_{max} = \ln(36), \quad H_g = \ln(4) \tag{2} $$
$$ \alpha = \frac{e - n + p}{2n - 5}, \qquad \beta = \frac{e}{n}, \qquad \gamma = \frac{e}{3(n - 2)} \tag{3} $$
$$ \text{CulDeSacRatio} = \frac{|\{v \in V : \deg(v) = 1\}|}{n}, \qquad \text{IntDensity} = \frac{|\{v \in V : \deg(v) \geq 3\}|}{A_{hull}} \tag{4} $$
$$ \text{AvgSegLength} = \frac{1}{e}\sum_{j=1}^{e} \ell_j \quad (\text{metres}), \qquad \text{AvgDegree} = \frac{1}{n}\sum_{v \in V} \deg(v) = \frac{2e}{n} \tag{5} $$

where $A_{hull}$ is the area (in km²) of the convex hull of all network nodes, used as the denominator for intersection density. Connected components are identified via depth-first search on the adjacency representation. $\alpha$ is clamped to $[0, \infty)$ and $\gamma$ to $[0, \infty)$; negative values (possible when $n$ is very small) are set to 0.

3. Parameters

ParameterTypeRequiredDefaultDescription
NETWORKVector (Line)YesStreet network line layer. Must be in a projected CRS. Topologically connected (run Prepare Network first).
NODESVector (Point)Output: junction point layer with degree and typology classification.
SUMMARYVector (Table)Output: attribute-less table with 15 network-wide indicators as (metric, value) rows.

4. Output Description

4.1 Junction Layer (NODES)

FieldTypeDescription
node_idIntegerZero-based node index
degreeIntegerNumber of edges incident at this node. 1 = dead-end; 2 = continuation; 3+ = intersection
node_typeString"cul-de-sac" (degree=1), "continuation" (degree=2), or "intersection" (degree>=3)

4.2 Summary Table (SUMMARY)

MetricDescriptionExample Value
nodesTotal distinct intersection/endpoint nodes1247
edgesTotal street segments (edges in the graph)1893
componentsNumber of disconnected sub-graphs. 1 = fully connected network1
total_length_kmSum of all edge lengths in kilometres142.6
avg_segment_length_mMean edge length in metres. ~80-120 m supports pedestrian permeability95.3
avg_node_degreeMean degree across all nodes. ~1.4 = suburban tree; ~2.0 = dense grid1.87
intersections_deg3plusCount of true intersections (degree >= 3)412
culdesac_countCount of dead-end nodes (degree = 1)287
culdesac_ratioFraction of nodes that are dead-ends0.23
intersection_density_km2True intersections per km² of convex hull area35.6
alpha_meshednessAlpha index: degree of circuitry. 0 = tree; higher = more looped/redundant0.182
beta_indexBeta index: edges per node. ~1.0 = tree; ~2.0 = fully gridded1.52
gamma_indexGamma index: ratio of edges to maximum planar edges0.51
orientation_entropy_natsShannon entropy of length-weighted bearing distribution. ln(4)~1.386 = grid; ln(36)~3.584 = uniform2.14
orientation_orderNormalised orientation order [0,1]. ~1 = perfect grid; ~0 = random/organic0.62

5. Interpretation Guide

5.1 Benchmark Values

IndicesTree-like SuburbOrganic Medieval19th-C GridModernist SuperblockDense Downtown Grid
$\alpha$0.00–0.050.05–0.150.15–0.250.05–0.120.20–0.35
$\beta$1.05–1.201.30–1.551.55–1.801.25–1.451.70–1.95
$\phi$0.10–0.300.05–0.250.70–0.950.20–0.500.65–0.90
Cul-de-sac ratio0.30–0.600.05–0.150.02–0.100.10–0.250.02–0.08
Int. density/km²5–1530–8050–12010–2580–200

5.2 Spatial Patterns

5.3 Cross-References

5.4 Common Pitfalls

6. Symbolic Representation

Recommended QGIS styling:

7. Literature

Boeing, G. (2019). "Urban spatial order: street network orientation, configuration, and entropy." Applied Network Science, 4(1), 67. DOI: 10.1007/s41109-019-0189-1

Marshall, S. (2004). Streets and Patterns. Routledge. DOI: 10.4324/9780203589397

Kansky, K.J. (1963). "Structure of transportation networks." University of Chicago, Department of Geography, Research Paper No. 84.

Porta, S., Crucitti, P., & Latora, V. (2006). "The Network Analysis of Urban Streets: A Primal Approach." Environment and Planning B, 33(5), 705–725. DOI: 10.1068/b32045

Southworth, M. & Ben-Joseph, E. (2003). Streets and the Shaping of Towns and Cities. Island Press. ISBN: 978-1559639163.

4. Accessibility

Multi-Amenity Access Score (15-Minute City)

Processing ID: planx:multiamentiyaccess

1. Theoretical Background

1.1 Academic Lineage

The 15-minute city concept, popularised by Moreno et al. (2021) but with roots in Perry's (1929) "neighbourhood unit" and Doxiadis's (1968) "human community" scale, proposes that all daily needs — work, food, health, education, leisure, and culture — should be reachable within a 15-minute walk or cycle from home. The concept entered mainstream planning discourse during the COVID-19 pandemic, when restricted mobility revealed how unequally distributed local amenities are. This algorithm provides the quantitative indicator behind the concept: a composite score that measures, for every residential location, how many amenity categories are within walking distance on the real street network.

1.2 Methodology

The algorithm runs one multi-source Dijkstra shortest-path search per amenity category. All amenity points in a category are treated as source nodes with zero initial distance; the algorithm propagates outward on the street network, recording the shortest path distance from any amenity in that category to every network node. This is more efficient than running one-to-one Dijkstra for every origin-amenity pair and captures the "nearest facility of type X" semantics exactly. Travel time is computed as network distance divided by walking speed (default 4.8 km/h = 80 m/min, the conventional adult walking speed from transportation engineering). The composite score for each origin is the percentage of amenity categories whose nearest representative is within the time threshold.

1.3 The Composite Score

The score is intentionally simple: it counts categories, not amenities within categories. A location near one supermarket scores the same as a location near ten supermarkets for the "shops" category. This conservative design means the score measures minimum service diversity — the presence of each service type — rather than service abundance or quality. A score of 100 means every defined category has at least one representative within the threshold; it says nothing about capacity, quality, or choice. To capture abundance, run the tool separately with a shorter threshold or complement with the Facility Adequacy algorithm.

1.4 Key Assumptions and Limitations

2. Mathematical Formulation

Let $\mathcal{C} = \{C_1, \ldots, C_K\}$ be the set of amenity categories, each with a point layer of amenity locations. Let $G = (V, E)$ be the street network graph with edge lengths as costs, and let $v_{walk} = 4.8$ km/h be the walking speed. The distance-to-time conversion is:

$$ v_{m/min} = \frac{v_{walk} \cdot 1000}{60} = 80 \;\text{m/min} \quad (\text{at default } v_{walk} = 4.8) \tag{1} $$

For category $C_k$, let $S_k \subset V$ be the set of network nodes nearest to amenity locations in $C_k$. A multi-source Dijkstra from $S_k$ yields the shortest-path distance from any node $v \in V$ to the nearest amenity of category $k$:

$$ d_k(v) = \min_{s \in S_k} \text{dist}_{G}(v, s) \tag{2} $$

For origin $o$ with nearest network node $v_o$, the travel time to the nearest amenity of category $k$ is:

$$ t_{k,o} = \frac{d_k(v_o)}{v_{m/min}} \quad \text{if } d_k(v_o) \text{ is finite; } -1 \text{ otherwise} \tag{3} $$

The number of categories reachable within threshold $T$ (default 15 minutes) and the composite score are:

$$ n_{reach}(o) = \sum_{k=1}^{K} \mathbf{1}[0 \leq t_{k,o} \leq T], \qquad \text{score}(o) = 100 \cdot \frac{n_{reach}(o)}{K} \in [0, 100] \tag{4} $$

The population-weighted mean score, when a population field is provided:

$$ \bar{S}_{pop} = \frac{\sum_{o} \text{pop}_o \cdot \text{score}(o)}{\sum_{o} \text{pop}_o}, \qquad P_{full} = \frac{\sum_{o} \text{pop}_o \cdot \mathbf{1}[n_{reach}(o) = K]}{\sum_{o} \text{pop}_o} \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
ORIGINSVector (Any)YesDemand points (building centroids, address points, parcel centroids). Snapped to nearest network node.
NETWORKVector (Line)YesStreet network. Must be in a projected CRS. Topologically connected (run Prepare Network first).
AMENITIESMultiple layersYesOne or more point/polygon layers, each representing one amenity category. Layer names become field tokens (t_schools, t_parks, etc.).
POP_FIELDField (Numeric)NoPopulation count per origin. When provided, the log outputs population-weighted mean score, share with full access, and share with zero access.
SPEEDDouble4.8Walking speed in km/h. 4.8 = healthy adult; 3.6 = elderly/child; 5.0 = brisk. Minimum: 0.5.
THRESHOLDDouble15.0Time threshold in minutes. 15 = standard 15-minute city; 10 = dense urban core; 20 = suburban. Minimum: 1.0.
OUTPUTVector (Point)Output point layer with original attributes, per-category travel times, n_reach, and score.

4. Output Description

FieldTypeRangeDescription
t_<category>Double$-1$ to $2T$Walking minutes to the nearest amenity of that category. -1 = no amenity reachable within 2x threshold (abbreviated from layer name, e.g. t_schools)
n_reachInteger$[0, K]$Number of amenity categories with travel time $\leq$ threshold
scoreDouble$[0, 100]$Percentage of categories within threshold. 100 = all categories; 0 = none within threshold
(inherited)All attributes from the origin layer are preserved

5. Interpretation Guide

5.1 Score Benchmarks

5.2 Diagnosing the Cause

5.3 Cross-References

5.4 Common Pitfalls

6. Symbolic Representation

Recommended QGIS styling:

7. Literature

Moreno, C., Allam, Z., Chabaud, D., Gall, C., & Pratlong, F. (2021). "Introducing the '15-Minute City': Sustainability, Resilience and Place Identity in Future Post-Pandemic Cities." Smart Cities, 4(1), 93–111. DOI: 10.3390/smartcities4010006

Handy, S.L. & Niemeier, D.A. (1997). "Measuring Accessibility: An Exploration of Issues and Alternatives." Environment and Planning A, 29(7), 1175–1194. DOI: 10.1068/a291175

Vale, D. & Lopes, A.S. (2023). "Accessibility inequality across Europe: a comparison of 15-minute pedestrian accessibility in cities with 100,000 or more inhabitants." npj Urban Sustainability, 3, 55. DOI: 10.1038/s42949-023-00133-w

Perry, C.A. (1929). "The Neighborhood Unit." In: Regional Survey of New York and Its Environs, Vol. 7, pp. 22–140. Regional Plan of New York and Its Environs.

Geurs, K.T. & van Wee, B. (2004). "Accessibility evaluation of land-use and transport strategies: review and research directions." Journal of Transport Geography, 12(2), 127–140. DOI: 10.1016/j.jtrangeo.2003.10.005

Talen, E. & Anselin, L. (1998). "Assessing Spatial Equity: An Evaluation of Measures of Accessibility to Public Playgrounds." Environment and Planning A, 30(4), 595–613. DOI: 10.1068/a300595

Ewing, R. & Cervero, R. (2010). "Travel and the Built Environment: A Meta-Analysis." Journal of the American Planning Association, 76(3), 265–294. DOI: 10.1080/01944361003766766

5. Microclimate

Ten screening-quality environmental tools powered by an embedded NOAA solar position model (±0.5° accuracy) and UMEP-style array-shifting shadow casts. These are screening tools, not regulatory compliance models.

Shadow Casting (DSM)

Processing ID: planx:shadowcasting

1. Theoretical Background

1.1 Academic Lineage

Shadow casting from digital surface models (DSMs) is the computational core of urban solar-access analysis. The method implemented here follows Ratti & Richens (1999), who demonstrated that standard image-processing techniques — specifically iterative array shifting — could compute shadow patterns from raster DSMs orders of magnitude faster than ray-tracing, making city-scale shadow studies feasible on desktop computers. This approach was adopted by the Urban Multi-scale Environmental Predictor (UMEP) plugin (Lindberg et al., 2018), which remains the most widely used urban climate tool in QGIS. The solar position is computed using the NOAA simplified algorithm (Reda & Andreas, 2004), which achieves accuracy well under 0.5 degrees for the years 1900–2100 — more than sufficient for urban shadow studies where building height uncertainty is typically a larger error source.

1.2 The Array-Shifting Method

The algorithm works on the principle that a DSM cell $(x, y)$ is in shadow if any cell along the line of sight toward the sun, when projected forward, stands higher than the ground at $(x, y)$. Operationally, the DSM array is shifted toward the sun in discrete pixel steps. At step $i$, the shifted DSM is lowered by $i \cdot \Delta s \cdot \tan\alpha$, where $\Delta s$ is pixel size and $\alpha$ is solar altitude. If the lowered shifted surface at any step exceeds the original DSM at $(x, y)$, that cell is flagged as shadowed. The search radius is capped at $\min(\text{relief}/\tan\alpha,\; \text{raster\_diagonal})$ — beyond this distance, terrain features cannot cast a shadow on the cell because the ground itself intercepts the ray.

1.3 Use Cases and Limitations

2. Mathematical Formulation

Let $\mathbf{D} \in \mathbb{R}^{H \times W}$ be the DSM array with pixel size $\Delta s$ (metres). Let $\alpha$ be the solar altitude (degrees above horizon) and $A$ the solar azimuth (degrees clockwise from north, measured as compass bearing — N = 0, E = 90, S = 180). The unit vector toward the sun in array coordinates is:

$$ u_x = \sin A, \qquad u_y = \cos A, \qquad \Delta_{col}(i) = \lfloor i \cdot u_x \rceil, \qquad \Delta_{row}(i) = -\lfloor i \cdot u_y \rceil \tag{1} $$

The maximum search distance (in pixel steps) is:

$$ i_{max} = \min\left(\left\lceil\frac{\max(\mathbf{D}) - \min(\mathbf{D})}{\Delta s \cdot \tan\alpha}\right\rceil,\; \left\lceil\frac{\sqrt{H^2 + W^2} \cdot \Delta s}{\Delta s}\right\rceil\right) \tag{2} $$

At each step $i = 1, \ldots, i_{max}$, the shifted and lowered DSM is:

$$ \mathbf{D}^{(i)}(r, c) = \mathbf{D}(r + \Delta_{row}(i),\; c + \Delta_{col}(i)) - i \cdot \Delta s \cdot \tan\alpha \tag{3} $$

The shadow mask $\mathbf{S}$ is the Boolean array where any shifted surface exceeds the original:

$$ \mathbf{S}(r, c) = \mathbf{1}\!\left[\max_{i=1}^{i_{max}} \mathbf{D}^{(i)}(r, c) > \mathbf{D}(r, c) + 0.01\right] \tag{4} $$

NaN cells in the DSM are excluded from the shadow computation. If $\alpha \leq 0$ (sun below horizon), all cells are flagged as shadow.

$$ \text{ShadowedShare} = \frac{|\{(r, c) : \mathbf{S}(r, c) = 1 \land \mathbf{D}(r, c) \neq \text{NaN}\}|}{|\{(r, c) : \mathbf{D}(r, c) \neq \text{NaN}\}|} \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
DSMRasterYesDigital Surface Model including terrain + buildings. Must be in a projected CRS with metric pixel size. NaN cells are excluded from computation.
WHENDateTimeYesDate and local clock time at the site. Combined with UTC_OFFSET to derive solar position.
UTC_OFFSETDouble0.0Hours from UTC. Critical: 14:00 local time with +3 offset = 11:00 UTC. Range: [-14, 14].
MAX_SEARCHDouble0.0Maximum shadow casting distance in map units. 0 = auto-compute from DSM relief and solar altitude. Set a value to limit computation for very low sun.
OUTPUTRaster (Byte)Output raster: 1 = shadow, 0 = sunlit, 255 = NoData (DSM NaN cells).

4. Output Description

ValueMeaningInterpretation
0SunlitThe cell receives direct beam radiation at this instant
1In cast shadowThe cell is in shadow cast by terrain or buildings. It may still receive diffuse sky radiation
255NoDataThe DSM had NaN at this cell (water, raster edge). Excluded from computation

The log reports ShadowedShare — the fraction of valid DSM cells in shadow. This single number characterises a time-of-day: 0.15 at noon means 15% of the scene is shadowed; 0.85 near sunset means most of the scene is in shadow.

5. Interpretation Guide

5.1 Critical Planning Dates

5.2 Before/After Differencing

Run on existing DSM (baseline), run on proposed DSM (scenario). Subtract: cells where baseline=0 and scenario=1 are the NEW shadow cast by the proposal. These cells identify affected properties, gardens, and public spaces. The affected area in m² is the key exhibit for planning objections or approvals.

5.3 Cross-References

5.4 Common Pitfalls

6. Symbolic Representation

Recommended QGIS styling:

7. Literature

Lindberg, F., Grimmond, C.S.B., et al. (2018). "Urban Multi-scale Environmental Predictor (UMEP) — An integrated tool for city-based climate services." Environmental Modelling & Software, 99, 70–87. DOI: 10.1016/j.envsoft.2017.09.020

Reda, I. & Andreas, A. (2004). "Solar position algorithm for solar radiation applications." Solar Energy, 76(5), 577–589. DOI: 10.1016/j.solener.2003.12.003

Ratti, C. & Richens, P. (1999). "Urban Texture Analysis with Image Processing Techniques." In: Augenbroe, G. & Eastman, C. (eds.), Computers in Building, pp. 49–64. Springer. DOI: 10.1007/978-1-4615-5047-1_4

Ratti, C., Di Sabatino, S. & Britter, R. (2006). "Urban texture analysis with image processing techniques: winds and dispersion." Theoretical and Applied Climatology, 84, 77–90. DOI: 10.1007/s00704-005-0146-z

Rich, P.M. (1990). "Characterizing plant canopies with hemispherical photographs." Remote Sensing Reviews, 5(1), 13–29. DOI: 10.1080/02757259009532119

Muneer, T. (2004). Solar Radiation and Daylight Models (2nd ed.). Elsevier. ISBN: 978-0750659741.

Sky View Factor (DSM)

Processing ID: planx:skyviewfactor

1. Theoretical Background

1.1 Academic Lineage

Sky View Factor (SVF) is the fraction of the overlying hemisphere visible from a point on the ground — the single most important geometric control on urban microclimate. Oke (1981) first established the quantitative link between urban canyon geometry and nocturnal cooling rates, showing that SVF reduction from buildings is the primary cause of the urban heat island (UHI) through its effect on longwave radiation trapping. The computational method employed here follows Johnson & Watson (1984), who demonstrated that SVF can be computed from horizon elevation angles at equally spaced azimuths: $\text{SVF} = 1 - \frac{1}{N}\sum \sin^2\beta_i$, where $\beta_i$ is the maximum horizon elevation angle in direction $i$. This formulation avoids integration over the full hemisphere and is numerically identical to the geometric definition of the view factor from a differential planar surface element to the sky.

1.2 Physical Significance

SVF controls two opposing energy fluxes. During the day, high SVF means more solar radiation reaches the surface (heating), but also more longwave radiation escapes to the cold sky (cooling). At night, SVF is purely a cooling control: a surface with SVF = 0.9 radiates longwave energy to a sky at approximately -50 degrees C (effective sky temperature), while a surface with SVF = 0.3 radiates mostly to warm building walls at +20 to +30 degrees C, resulting in net nocturnal heating. This explains the classic observation that street canyons are 2-8 K warmer than nearby open country at night, but may be cooler during the day if deeply shaded.

1.3 Methodology and Key Choices

The algorithm scans the DSM outward from each cell in $N$ equally spaced azimuths (default 16). For each direction, it finds the maximum horizon elevation angle $\beta$ — the steepest angle from horizontal to any obstructing DSM cell along that ray, up to the search radius. The SVF per cell is then computed from the $N$ horizon angles. More directions produce smoother results (16 for screening, 32 for publication, 64 for research). The search radius must be large enough to capture the tallest obstruction that can affect the cell; for a 100 m tower, a radius of at least 100 m is needed. The default 100 m is appropriate for urban blocks; increase for tall-building districts or terrain with significant relief.

1.4 Use Cases and Limitations

2. Mathematical Formulation

For a cell at position $(r, c)$ in the DSM array with pixel size $\Delta s$, let $N$ be the number of equally spaced azimuth directions (default 16). For direction $d \in \{0, \ldots, N-1\}$, the scan direction is:

$$ \phi_d = \frac{2\pi d}{N}, \qquad u_x = \sin\phi_d, \qquad u_y = \cos\phi_d \tag{1} $$

The maximum terrain elevation encountered at step $i$ (distance $i \cdot \Delta s$) relative to the cell is:

$$ \tan\beta_i^{(d)} = \frac{\mathbf{D}(r + \Delta r_i, c + \Delta c_i) - \mathbf{D}(r, c)}{i \cdot \Delta s} \tag{2} $$

where $(\Delta r_i, \Delta c_i)$ are the integer row and column offsets at step $i$. The maximum horizon angle in direction $d$ over all steps $i = 1, \ldots, i_{max}$ is:

$$ \tan\beta_{max}^{(d)} = \max_{i=1}^{i_{max}} \left(\tan\beta_i^{(d)}, 0\right) \tag{3} $$

The sky view factor, using the identity $\sin^2(\arctan t) = t^2/(1+t^2)$, is:

$$ \text{SVF}(r, c) = 1 - \frac{1}{N}\sum_{d=0}^{N-1} \frac{(\tan\beta_{max}^{(d)})^2}{1 + (\tan\beta_{max}^{(d)})^2} \in [0, 1] \tag{4} $$

For a perfectly flat plane without obstructions ($\beta_{max}^{(d)} = 0$ for all $d$), SVF = 1.0. For a cell at the foot of an infinitely high wall ($\beta_{max}^{(d)} = 90^\circ$ for half the azimuths), SVF = 0.5. For a cell in a deep symmetric canyon, SVF asymptotically approaches:

$$ \text{SVF}_{canyon} \approx \cos^2(\arctan(H/W)) = \frac{W^2}{W^2 + H^2} \tag{5} $$

where $H$ is building height and $W$ is canyon width. A 20 m deep, 15 m wide canyon has theoretical SVF $\approx$ 0.36.

3. Parameters

ParameterTypeRequiredDefaultDescription
DSMRasterYesDigital Surface Model including terrain + buildings + vegetation. Must be in a projected CRS with metric pixel size.
DIRECTIONSInteger16Number of equally spaced azimuth scan directions. 16 = screening quality; 32 = publication; 64 = research (slow). Range: [4, 64].
RADIUSDouble100.0Maximum search distance in map units (metres). Must exceed the height of the tallest obstruction that can affect the cell. Minimum: 1.0.
OUTPUTRaster (Float32)Output raster: SVF values in [0, 1]. -9999 = NoData (NaN cells in DSM).

4. Output Description

Value RangeMorphologyMicroclimate Significance
0.95–1.00Open field, large plaza, rooftopMaximum nocturnal cooling (coldest at night). Full solar access but no shade. High diurnal temperature range
0.70–0.95Wide street, suburban, park edgeGood ventilation and daylight. Moderate cooling. Typical of low-rise suburbs
0.50–0.70Typical urban street, courtyardReduced longwave loss. Nighttime 1–3 K warmer than open field. Adequate daylight
0.30–0.50Dense urban canyon, narrow alleyStrong longwave trapping. 3–5 K warmer at night. Daylight restricted — supplementary lighting likely needed
0.10–0.30Very deep canyon, arcade, tunnelMaximum heat trapping. > 5 K nocturnal warming. Daylight negligible. These spaces function as covered passages

5. Interpretation Guide

5.1 Spatial Patterns to Identify

5.2 Cross-References

5.3 Common Pitfalls

6. Symbolic Representation

Recommended QGIS styling:

7. Literature

Oke, T.R. (1981). "Canyon geometry and the nocturnal urban heat island: Comparison of scale model and field observations." Journal of Climatology, 1(3), 237–254. DOI: 10.1002/joc.3370010304

Johnson, G.T. & Watson, I.D. (1984). "The Determination of View-Factors in Urban Canyons." Journal of Climate and Applied Meteorology, 23(2), 329–335. DOI: 10.1175/1520-0450(1984)023<0329:TDOVFI>2.0.CO;2

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

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

Grimmond, C.S.B., Potter, S.K., Zutter, H.N. & Souch, C. (2001). "Rapid methods to estimate sky-view factors applied to urban areas." International Journal of Climatology, 21(7), 903–913. DOI: 10.1002/joc.659

Hammerle, M., Gal, T., Unger, J. & Matzarakis, A. (2011). "Comparison of models calculating the sky view factor used for urban climate investigations." Theoretical and Applied Climatology, 105, 521–527. DOI: 10.1007/s00704-011-0402-3

Frontal Area Index

Processing ID: planx:frontalareaindex

1. Theoretical Background

1.1 Academic Lineage

Frontal Area Index ($\lambda_f$) and Plan Area Index ($\lambda_p$) are the standard urban roughness parameters introduced by Grimmond & Oke (1999) for characterising the aerodynamic properties of urban surfaces. Grimmond & Oke demonstrated that these two morphological parameters, computed from building footprint and height data, can predict the zero-plane displacement height ($z_d$) and roughness length ($z_0$) — the key parameters in boundary-layer wind models — with accuracy comparable to wind-tunnel measurements. The method has since become the primary morphological input to urban wind and dispersion models, including the operational ADMS-Urban and SIRANE models.

1.2 Physical Meaning

$\lambda_f$ measures the total wind-facing facade area of buildings per unit ground area. For a given wind direction $\theta$ (the compass bearing the wind comes FROM), each building contributes its projected width perpendicular to the wind, multiplied by its height: $F_i = w_{proj,i} \cdot h_i$. Summed over all buildings intersecting a grid cell and normalised by cell area, $\lambda_f$ quantifies how much the built fabric "catches" the wind — a sail-area analogy. $\lambda_p$ is simpler: total building footprint area per unit ground area (coverage). The ratio $\lambda_f / \lambda_p = \bar{h} \cdot \bar{w}_{proj} / \bar{A}_{fp}$ relates the aerodynamic to the geometric footprint of buildings.

1.3 Urban Ventilation Corridors

In urban climate planning, $\lambda_f$ maps are used to identify and protect ventilation corridors — continuous bands of low $\lambda_f$ aligned with the prevailing breeze direction. These corridors channel cooling air from rural surroundings (or large urban parks) into the city core. The operational workflow is: (a) determine the prevailing summer daytime wind direction from local meteorological data; (b) compute $\lambda_f$ with that direction; (c) identify continuous low-$\lambda_f$ paths connecting cool source areas to hot target areas; (d) keep new construction out of these paths in the zoning plan.

1.4 Use Cases and Limitations

2. Mathematical Formulation

Let $\mathcal{B}$ be the set of building footprint polygons, each with height $h_i$ (metres). Let the wind direction be $\theta$ degrees from North (the bearing the wind comes FROM). For building $i$ with exterior ring vertices $(x_j, y_j)$, the projected width perpendicular to the wind is:

$$ p_x = \cos\theta, \qquad p_y = -\sin\theta, \qquad w_{proj,i} = \max_{j}(x_j p_x + y_j p_y) - \min_{j}(x_j p_x + y_j p_y) \tag{1} $$

The frontal area of building $i$ is:

$$ F_i = w_{proj,i} \cdot h_i \quad (\text{m}^2) \tag{2} $$

For a grid cell $c$ with area $A_{cell}$, the intersection share of building $i$ is $s_{i,c} = \text{Area}(b_i \cap c) / \text{Area}(b_i)$. The frontal area index and plan area index for cell $c$ are:

$$ \lambda_f(c) = \frac{\sum_{i: b_i \cap c \neq \emptyset} F_i \cdot s_{i,c}}{A_{cell}}, \qquad \lambda_p(c) = \frac{\sum_{i: b_i \cap c \neq \emptyset} \text{Area}(b_i) \cdot s_{i,c}}{A_{cell}} \tag{3} $$

Only cells containing at least one building are emitted (sparse grid output). The roughness class thresholds are:

$$ \text{Class}(c) = \begin{cases} \text{"Open"} & \lambda_f < 0.1 \\ \text{"Moderate"} & 0.1 \leq \lambda_f < 0.3 \\ \text{"Blocked"} & \lambda_f \geq 0.3 \end{cases} \tag{4} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
BUILDINGSVector (Polygon)YesBuilding footprints. Must be in a projected CRS. Multi-polygons use the largest part only.
HEIGHT_FIELDField (Numeric)NoBuilding height in metres. When empty, DEFAULT_HEIGHT is used for all buildings.
DEFAULT_HEIGHTDouble6.0Default height in metres when no height field is provided. Typical: 3–6 m (1–2 storeys) for residential; 10–20 m for mixed-use. Minimum: 0.1.
WIND_DIRDouble0.0Wind direction in degrees from North (bearing wind comes FROM). 0 = north wind, 90 = east wind, 180 = south wind. Range: [0, 360].
CELL_SIZEDouble100.0Grid cell side length in map units. 100 m = neighbourhood-scale roughness; 50 m = street-scale. Minimum: 10.0.
OUTPUTVector (Polygon)Sparse grid of built cells with $\lambda_f$, $\lambda_p$, building count, and cell ID.

4. Output Description

FieldTypeDescription
cell_idIntegerZero-based grid cell identifier
b_countIntegerNumber of buildings intersecting the cell
lambda_fDoubleFrontal area index: wind-facing facade area per unit ground area. < 0.1 = open; > 0.3 = blocked
lambda_pDoublePlan area index: footprint coverage. Useful alongside $\lambda_f$ to separate dense-but-low from porous-but-tall fabric

5. Interpretation Guide

5.1 Reading the $\lambda_f$ / $\lambda_p$ Relationship

5.2 Ventilation Corridor Detection

5.3 Cross-References

6. Symbolic Representation

7. Literature

Grimmond, C.S.B. & Oke, T.R. (1999). "Aerodynamic Properties of Urban Areas Derived from Analysis of Surface Form." Journal of Applied Meteorology, 38(9), 1262–1292. DOI: 10.1175/1520-0450(1999)038<1262:APOUAD>2.0.CO;2

Ratti, C., Di Sabatino, S. & Britter, R. (2006). "Urban texture analysis with image processing techniques: winds and dispersion." Theoretical and Applied Climatology, 84, 77–90. DOI: 10.1007/s00704-005-0146-z

Ng, E., Yuan, C., Chen, L., Ren, C. & Fung, J.C.H. (2011). "Improving the wind environment in high-density cities by understanding urban morphology and surface roughness." Landscape and Urban Planning, 101(1), 59–74. DOI: 10.1016/j.landurbplan.2011.01.004

Macdonald, R.W., Griffiths, R.F. & Hall, D.J. (1998). "An improved method for the estimation of surface roughness of obstacle arrays." Atmospheric Environment, 32(11), 1857–1864. DOI: 10.1016/S1352-2310(97)00403-2

Oke, T.R., Mills, G., Christen, A. & Voogt, J.A. (2017). Urban Climates. Cambridge University Press. DOI: 10.1017/9781139016476

Sun Hours (DSM)

Processing ID: planx:sunhours

1. Theoretical Background

1.1 Academic Lineage

Sun hours — the accumulated duration of direct sunlight at a point over a day — is the fundamental metric for solar-access planning and rights-to-light assessment. The method extends the single-instant shadow casting of Ratti & Richens (1999) into a full-day integration: the DSM is swept at a fixed time interval (default 30 minutes), and for each step where the sun is above the horizon, a shadow mask is cast using the NOAA solar position algorithm and the UMEP array-shifting method. Cells that are sunlit at a given time step accumulate $\Delta t / 60$ hours. The result is a shadow-duration map that previously required batch-running the Shadow Casting tool 24-48 times.

1.2 Regulatory Context

Many jurisdictions specify minimum direct-sun criteria for habitable rooms and open spaces. The British "BRE Site Layout Planning for Daylight and Sunlight" guide recommends that at least one main window wall of a habitable room receive 2 hours of direct sun on March 21. The German DIN 5034 specifies minimum sun duration for playgrounds. The Chinese GB 50180 requires 2 hours on the winter solstice for residential buildings. The tool enables testing any of these by selecting the relevant date and comparing per-cell hours to the threshold.

1.3 Use Cases and Limitations

2. Mathematical Formulation

Let $\mathbf{D}$ be the DSM array with pixel size $\Delta s$. For a given date at latitude $\phi$, the day is swept at $n_{steps} = \lceil 24 \times 60 / \Delta t \rceil$ time steps, where $\Delta t$ is the interval in minutes. At step $k$, the local clock time is $t_k = (k + 0.5) \times 24 / n_{steps}$ hours. The solar altitude $\alpha_k$ and azimuth $A_k$ are computed from the NOAA algorithm at UTC time $t_k - \Delta_{UTC}$:

$$ \alpha_k, A_k = \text{sun\_position}(\text{year}, \text{month}, \text{day},\; t_k - \Delta_{UTC},\; \phi,\; \lambda) \tag{1} $$

The shadow mask $\mathbf{S}_k$ is computed for each step with $\alpha_k > 0$. The accumulated sun hours per cell are:

$$ \text{Hours}(r, c) = \frac{\Delta t}{60} \sum_{k=1}^{n_{steps}} \mathbf{1}[\alpha_k > 0] \cdot (1 - \mathbf{S}_k(r, c)) \quad (\text{hours}) \tag{2} $$

The site's unobstructed potential daylight is the total time the sun would be above the horizon without any building or terrain obstruction:

$$ \text{Daylight} = \frac{\Delta t}{60} \sum_{k=1}^{n_{steps}} \mathbf{1}[\alpha_k > 0] \quad (\text{hours}) \tag{3} $$

The ratio $\text{Hours}(r, c) / \text{Daylight}$ expresses the fraction of available sun that actually reaches the cell — a morphology-controlled solar access indicator independent of latitude and date:

$$ f_{sun}(r, c) = \frac{\text{Hours}(r, c)}{\text{Daylight}} \in [0, 1] \tag{4} $$
$$ \overline{\text{Hours}} = \frac{1}{|V|}\sum_{(r,c) \in V} \text{Hours}(r, c), \quad V = \{(r, c) : \mathbf{D}(r, c) \neq \text{NaN}\} \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
DSMRasterYesDigital Surface Model including terrain + buildings. Must be in a projected CRS with metric pixels.
DATEDateTimeYesDate for the study. Time of day component is ignored — the full day is swept. Use Dec 21 for worst-case winter; Mar 21 for equinox standard.
UTC_OFFSETDouble0.0Hours from UTC at the site. Required for correct sunrise/sunset times. Range: [-14, 14].
INTERVALDouble30.0Time step in minutes. 30 = screening balance of speed and accuracy; 15 = high accuracy; 60 = fast preview. Minimum: 5; maximum: 120.
MAX_SEARCHDouble0.0Maximum shadow casting distance in map units. 0 = auto-compute. Set a value to speed computation.
OUTPUTRaster (Float32)Sun hours raster. -9999 = NoData (NaN cells in DSM).

4. Output Description

Value RangeInterpretationPlanning Significance
$f_{sun} \approx 1.0$Unobstructed — cell receives essentially all available daylightRooftops, open fields. Good for PV, poor for thermal comfort without shade
$f_{sun} \approx 0.5$Half the day in shadow — typical of street-facing ground cells in mid-rise fabricAdequate for most uses. The 50% loss is purely from surrounding buildings
$f_{sun} < 0.25$Severely obstructed — less than one quarter of available daylight reaches the cellDeep canyons, narrow courtyards. Habitable-room windows here likely fail solar-access standards

5. Interpretation Guide

5.1 Regulatory Benchmarks

5.2 Seasonal Comparison

Run on both Dec 21 (winter solstice) and Jun 21 (summer solstice):

5.3 Cross-References

6. Symbolic Representation

7. Literature

Littlefair, P.J. (2001). "Daylight, sunlight and solar gain in the urban environment." Solar Energy, 70(3), 177–185. DOI: 10.1016/S0038-092X(00)00099-2

Ratti, C. & Richens, P. (1999). "Urban Texture Analysis with Image Processing Techniques." In: Computers in Building, pp. 49–64. Springer. DOI: 10.1007/978-1-4615-5047-1_4

Lindberg, F., Grimmond, C.S.B., et al. (2018). "Urban Multi-scale Environmental Predictor (UMEP)." Environmental Modelling & Software, 99, 70–87. DOI: 10.1016/j.envsoft.2017.09.020

Reda, I. & Andreas, A. (2004). "Solar position algorithm for solar radiation applications." Solar Energy, 76(5), 577–589. DOI: 10.1016/j.solener.2003.12.003

BRE (2011). Site Layout Planning for Daylight and Sunlight: A Guide to Good Practice (BR 209). BRE Press. ISBN: 978-1848061781.

Solar Irradiation (DSM)

Processing ID: planx:solarirradiation

1. Theoretical Background

1.1 Academic Lineage

Solar irradiation — the total radiant energy received per unit area — combines the geometric shadow computation with an atmospheric radiative transfer model. The beam (direct) component follows the ASHRAE clear-sky model (Masters, 2004), an empirical parameterisation widely used in building energy simulation: direct normal irradiance $DNI = A \exp(-k/\sin\beta)$ where the apparent extraterrestrial flux $A$ and optical depth $k$ vary seasonally. The diffuse component is modelled as isotropic ($DHI = C \cdot DNI$), a reasonable first-order assumption for clear-sky screening. Each cell's total irradiation sums beam (only when sunlit) plus diffuse weighted by the cell's sky view factor — a physically justified treatment that correctly reduces diffuse irradiance in street canyons.

1.2 Clear-Sky vs. All-Sky

The tool computes clear-sky irradiation — what the surface would receive under cloudless conditions. This is the correct basis for two screening tasks: (a) ranking roofs and spaces by their relative solar potential, because cloud cover affects all surfaces in a scene roughly equally (the ranking is preserved); (b) establishing an upper bound for PV yield, from which a cloudy-climate correction (typically 0.5–0.7) is applied. For absolute energy estimates, multiply clear-sky values by the local clearness index ($K_T$, the ratio of measured to clear-sky global radiation), available from TMY (Typical Meteorological Year) data or PVGIS.

1.3 Use Cases and Limitations

2. Mathematical Formulation

The ASHRAE clear-sky model parameters vary with day-of-year $n$:

$$ A(n) = 1160 + 75 \sin\left(\frac{2\pi (n - 275)}{365}\right) \;\text{W/m}^2, \quad k(n) = 0.174 + 0.035 \sin\left(\frac{2\pi (n - 100)}{365}\right) \tag{1} $$
$$ C(n) = 0.095 + 0.04 \sin\left(\frac{2\pi (n - 100)}{365}\right) \tag{2} $$

For solar altitude $\alpha$, the direct normal irradiance, beam horizontal, and diffuse horizontal components are:

$$ DNI = A(n) \cdot \exp\left(-\frac{k(n)}{\sin\alpha}\right), \quad I_{beam} = DNI \cdot \sin\alpha, \quad I_{diff} = C(n) \cdot DNI \tag{3} $$

At time step $k$, the irradiance contribution to cell $(r, c)$ is:

$$ E_k(r, c) = \begin{cases} I_{beam}^{(k)} + I_{diff}^{(k)} \cdot \text{SVF}(r, c) & \text{if sunlit (SVF mode)} \\ I_{beam}^{(k)} + I_{diff}^{(k)} & \text{if sunlit (no SVF)} \\ I_{diff}^{(k)} \cdot \text{SVF}(r, c) & \text{if shadowed (SVF mode)} \\ I_{diff}^{(k)} & \text{if shadowed (no SVF)} \end{cases} \tag{4} $$

The daily total irradiation (kWh/m²) and the unobstructed flat-ground reference are:

$$ \text{kWh}(r, c) = \frac{\Delta t}{60 \cdot 1000} \sum_{k=1}^{n_{steps}} E_k(r, c), \qquad \text{flat\_kWh} = \frac{\Delta t}{60 \cdot 1000} \sum_{k: \alpha_k > 0} (I_{beam}^{(k)} + I_{diff}^{(k)}) \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
DSMRasterYesDigital Surface Model. Must be in a projected CRS with metric pixel size.
DATEDateTimeYesDate for the study. For PV screening, use a winter date when solar access is the binding constraint. For heat exposure, use Jun 21.
UTC_OFFSETDouble0.0Hours from UTC at the site. Required for correct sun position timing. Range: [-14, 14].
INTERVALDouble30.0Time step in minutes. Smaller = more accurate but slower. 30 min is the recommended balance. Range: [5, 120].
USE_SVFBooleanTrueWhen checked, diffuse irradiance is weighted per cell by sky view factor (SVF computed in a preliminary pass). Recommended for urban scenes with canyons; disable for open-terrain or rooftop-only analysis.
SVF_RADIUSDouble100.0Search radius for the SVF pre-computation, in map units. Only used when USE_SVF is True. Minimum: 10.0.
MAX_SEARCHDouble0.0Maximum shadow casting distance. 0 = auto-compute from DSM relief. Set a value to bound computation time.
OUTPUTRaster (Float32)Daily irradiation in kWh/m². -9999 = NoData.

4. Output Description

ValueInterpretation
flat_kWh (log)Clear-sky daily total on an unobstructed horizontal surface. This is the upper bound — the reference against which every cell is compared.
Cell kWh/m²Actual clear-sky irradiation at the cell, including shadowing and (optionally) SVF-weighted diffuse. Ratio to flat_kWh = morphology efficiency.
Ratio > 0.9Essentially unobstructed — the cell receives >90% of what an open field would. Good roof for PV.
Ratio 0.5–0.7Heavily obstructed — surroundings cost 30–50% of potential yield. PV likely uneconomic here.

5. Interpretation Guide

5.1 Reading Against the Reference

The ratio cell / flat-ground reference is the honest number. It isolates morphological shadowing from atmospheric and seasonal effects. Two sites at different latitudes will have different flat_kWh values; the ratio tells you which site is more obstructed, independent of latitude. A cell at ratio 0.85 in London may produce less absolute kWh than a cell at ratio 0.6 in Cairo — but the London cell is a better PV location relative to its climate.

5.2 Seasonal Strategy

5.3 Cross-References

6. Symbolic Representation

7. Literature

Masters, G.M. (2004). Renewable and Efficient Electric Power Systems. Wiley. DOI: 10.1002/0471668826

Duffie, J.A. & Beckman, W.A. (2013). Solar Engineering of Thermal Processes (4th ed.). Wiley. DOI: 10.1002/9781118671603

Lindberg, F., Grimmond, C.S.B., et al. (2018). "Urban Multi-scale Environmental Predictor (UMEP)." Environmental Modelling & Software, 99, 70–87. DOI: 10.1016/j.envsoft.2017.09.020

Reda, I. & Andreas, A. (2004). "Solar position algorithm for solar radiation applications." Solar Energy, 76(5), 577–589. DOI: 10.1016/j.solener.2003.12.003

Suri, M. & Hofierka, J. (2004). "A New GIS-based Solar Radiation Model and Its Application to Photovoltaic Assessments." Transactions in GIS, 8(2), 175–190. DOI: 10.1111/j.1467-9671.2004.00174.x

Freitas, S., Catita, C., Redweik, P. & Brito, M.C. (2015). "Modelling solar potential in the urban environment: State-of-the-art review." Renewable and Sustainable Energy Reviews, 41, 915–931. DOI: 10.1016/j.rser.2014.08.060

Annual Solar Potential (DSM)

Processing ID: planx:annualsolar

1. Theoretical Background

1.1 Academic Lineage

Annual solar potential — the total clear-sky global irradiation summed over a full year — is the metric that matters for rooftop PV economics, building energy balance, and year-round outdoor thermal comfort. Computing it by shadow-casting all 365 days would be computationally prohibitive for city-scale DSMs. The standard solution, introduced by Klein (1977) and codified in Duffie & Beckman (2013), is the monthly-average-day method: for each month, one representative average day — the day whose solar declination is closest to the monthly mean — is computed with the full shadow-aware beam + SVF-weighted diffuse model, then scaled by the number of days in that month and summed across 12 months. Twelve day-sweeps stand in for 365, reducing computation by a factor of 30 while preserving within about 2% accuracy for annual totals at low-to-moderate latitudes.

1.2 The Klein Representative Days

The recommended average days (Jan 17, Feb 16, Mar 16, Apr 15, May 15, Jun 11, Jul 17, Aug 16, Sep 15, Oct 15, Nov 14, Dec 10) are those for which the daily extraterrestrial radiation on a horizontal surface is closest to the monthly mean. These dates are latitude-independent for practical purposes — the declination variations are symmetric around the solstices. The method is valid for latitudes between 60 degrees S and 60 degrees N; polar latitudes with prolonged darkness or midnight sun require full daily integration.

1.3 Use Cases and Limitations

2. Mathematical Formulation

Let $\text{daily}(r, c; y, m, d)$ be the clear-sky daily irradiation at cell $(r, c)$ for year $y$, month $m$, and day $d$, computed via the full shadow-aware + SVF-weighted model. Let $d_m^*$ be the Klein representative day for month $m$, and $N_m$ the number of days in month $m$:

$$ \text{Monthly}(r, c; m) = \text{daily}(r, c; y, m, d_m^*) \cdot N_m \quad (\text{kWh/m}^2) \tag{1} $$
$$ \text{Annual}(r, c) = \sum_{m=1}^{12} \text{Monthly}(r, c; m) \quad (\text{kWh/m}^2/\text{yr}) \tag{2} $$

The flat-ground annual reference (unobstructed, clear-sky) is:

$$ \text{flat\_annual} = \sum_{m=1}^{12} \text{flat\_daily}(y, m, d_m^*) \cdot N_m \tag{3} $$

The morphology efficiency — the share of available radiation that reaches the cell, isolating urban-form effects from climate — is:

$$ \eta_{morph}(r, c) = \frac{\text{Annual}(r, c)}{\text{flat\_annual}} \in [0, 1] \tag{4} $$

The scene mean per month, used to identify the peak month for heat exposure:

$$ \overline{\text{Monthly}}(m) = \frac{1}{|V|} \sum_{(r,c) \in V} \text{Monthly}(r, c; m) \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
DSMRasterYesDigital Surface Model. Must be in a projected CRS with metric pixel size.
YEARInteger2026Year for the computation. Affects leap-year day counts and the NOAA solar declination. Range: [1901, 2099].
UTC_OFFSETDouble0.0Hours from UTC. Affects the solar position timing for all 12 monthly sweeps. Range: [-14, 14].
INTERVALDouble60.0Time step in minutes. 60 min is the recommended balance for a full-year computation (12x faster than 5 min). Range: [5, 120].
USE_SVFBooleanTrueWhen checked, a single SVF pass is computed and reused for all 12 months. Recommended for urban scenes.
SVF_RADIUSDouble100.0Search radius for the SVF pre-computation. Only used when USE_SVF is True. Minimum: 10.0.
MAX_SEARCHDouble0.0Maximum shadow casting distance. 0 = auto-compute from DSM relief.
OUTPUTRaster (Float32)Annual irradiation in kWh/m²/yr. -9999 = NoData.
OUTPUT_MONTHLYRaster (Float32, 12-band)NoOptional 12-band raster with band names "January" through "December". Each band holds monthly kWh/m².

4. Output Description

OutputContentInterpretation
OUTPUT (annual raster)kWh/m²/yr per cellClear-sky annual total. Multiply by cloud correction (0.5–0.7) for approximate real-sky annual total
OUTPUT_MONTHLY (12-band)kWh/m² per month per bandSeasonal breakdown. Band 1 = January. Check which months deliver most energy for a given location
flat_annual (log)kWh/m²/yrUnobstructed flat-ground reference. All-cell values < this number. The ratio isolates morphological effects
Scene monthly means (log)kWh/m² per monthThree-letter abbreviations. The peak month identifies the seasonal heat-exposure maximum

5. Interpretation Guide

5.1 Reading Morphology Efficiency

5.2 PV Estimation Workflow

  1. Rank cells by annual kWh/m²/yr. Extract the top 20%.
  2. Filter by usable roof area (minimum contiguous patch size, e.g. 10 m²).
  3. Multiply by system factor $\eta_{sys} \approx 0.75$–$0.85$ (accounts for inverter, wiring, temperature, soiling losses).
  4. Multiply by local cloud correction $K_T \approx 0.5$–$0.7$ (the ratio of measured GHI to clear-sky GHI, from TMY data or PVGIS).
  5. Result: approximate annual AC energy per cell = $\text{Annual} \times \eta_{sys} \times K_T$ (kWh/m²/yr).

5.3 Seasonal Diagnostics (Monthly Bands)

5.4 Cross-References

6. Symbolic Representation

7. Literature

Klein, S.A. (1977). "Calculation of monthly average insolation on tilted surfaces." Solar Energy, 19(4), 325–329. DOI: 10.1016/0038-092X(77)90001-9

Duffie, J.A. & Beckman, W.A. (2013). Solar Engineering of Thermal Processes (4th ed.). Wiley. DOI: 10.1002/9781118671603

Masters, G.M. (2004). Renewable and Efficient Electric Power Systems. Wiley. DOI: 10.1002/0471668826

Lindberg, F., Grimmond, C.S.B., et al. (2018). "Urban Multi-scale Environmental Predictor (UMEP)." Environmental Modelling & Software, 99, 70–87. DOI: 10.1016/j.envsoft.2017.09.020

Freitas, S., Catita, C., Redweik, P. & Brito, M.C. (2015). "Modelling solar potential in the urban environment: State-of-the-art review." Renewable and Sustainable Energy Reviews, 41, 915–931. DOI: 10.1016/j.rser.2014.08.060

Reda, I. & Andreas, A. (2004). "Solar position algorithm for solar radiation applications." Solar Energy, 76(5), 577–589. DOI: 10.1016/j.solener.2003.12.003

Suri, M. & Hofierka, J. (2004). "A New GIS-based Solar Radiation Model and Its Application to Photovoltaic Assessments." Transactions in GIS, 8(2), 175–190. DOI: 10.1111/j.1467-9671.2004.00174.x

Heat Island Risk Grid

Processing ID: planx:heatriskgrid

1. Theoretical Background

1.1 Academic Lineage

The Urban Heat Island (UHI) effect — the observation that urban areas are 2–12 K warmer than their rural surroundings — was first systematically documented by Luke Howard (1833) and given its energetic explanation by Oke (1982), who showed that canyon geometry (SVF reduction) is the primary nocturnal mechanism and surface-cover properties (albedo, thermal admittance, vegetation moisture) dominate daytime UHI intensity. Stewart & Oke (2012) formalised this into the Local Climate Zone (LCZ) classification, which characterises urban landscapes by built fraction, building height, pervious surface fraction, and thermal admittance — the same variables the Heat Island Risk Grid computes per cell. The tool implements a weighted additive model of these four components, producing a fixed-scale 0–100 risk score directly comparable between scenarios and study areas.

1.2 The Fixed-Scale Design

Unlike percentile-based or data-driven normalisations that stretch the score to fit the data range, the fixed-scale approach anchors the endpoints to the theoretical extremes given the user's chosen weights: fully vegetated (or water-covered) land at zero building height maps to 0; fully built land at the reference height maps to 100. This means re-running after a planning intervention — adding a park, increasing building heights, introducing water features — yields directly comparable numbers. "The park drops 14 cells from Very High to Moderate" is a verifiable claim because the thresholds are fixed, not re-stretched to the new data.

1.3 Component Mechanisms

1.4 Use Cases and Limitations

2. Mathematical Formulation

For a grid cell $c$, let $b_c$, $g_c$, $w_c$ be the built, green, and water area fractions ($\in [0, 1]$), and $h_c$ the area-weighted mean building height (metres). Let $h_{ref}$ be the reference height for full height effect (default 20 m), and $w_{built}$, $w_{height}$, $w_{green}$, $w_{water}$ the user-defined component weights:

$$ h_{norm}(c) = \min\left(\frac{h_c}{h_{ref}},\; 1\right) \tag{1} $$
$$ \text{raw}(c) = w_{built} \cdot b_c + w_{height} \cdot h_{norm}(c) - w_{green} \cdot g_c - w_{water} \cdot w_c \tag{2} $$

The theoretical range of raw(c) is $[-\max(w_{green}, w_{water}),\; w_{built} + w_{height}]$, because a cell cannot simultaneously be fully green and fully water (they are disjoint surface covers). The normalised risk score is:

$$ \text{RS}(c) = 100 \cdot \frac{\text{raw}(c) - \text{raw}_{min}}{\text{raw}_{max} - \text{raw}_{min}} \in [0, 100] \tag{3} $$

where $\text{raw}_{min} = -\max(w_{green}, w_{water})$ and $\text{raw}_{max} = w_{built} + w_{height}$. With default weights (0.4, 0.2, 0.3, 0.1):

$$ \text{raw}_{min} = -0.3, \quad \text{raw}_{max} = 0.6, \quad \text{RS}(c) = 100 \cdot \frac{\text{raw}(c) + 0.3}{0.9} \tag{4} $$
$$ \text{RiskClass}(c) = \begin{cases} \text{"Low"} & \text{RS} < 25 \\ \text{"Moderate"} & 25 \leq \text{RS} < 50 \\ \text{"High"} & 50 \leq \text{RS} < 75 \\ \text{"Very High"} & \text{RS} \geq 75 \end{cases} \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
BUILDINGSVector (Polygon)YesBuilding footprints. Must be in a projected CRS.
HEIGHT_FIELDField (Numeric)NoBuilding height in metres. When empty, the height component weight is set to 0 (no height contribution).
GREENVector (Polygon)NoGreen/vegetated areas. Parks, gardens, street trees (as polygon coverage). Optional but strongly recommended.
WATERVector (Polygon)NoWater bodies. Lakes, rivers, ponds, fountains. Optional.
CELL_SIZEDouble100.0Grid cell side in map units. 100 m = neighbourhood; 50 m = block; 200 m = district. Minimum: 10.0.
H_REFDouble20.0Reference building height for full height effect. Heights above this value contribute no additional risk. Minimum: 1.0.
W_BUILTDouble0.4Weight of built fraction in the risk composite. Range: [0, 1].
W_HEIGHTDouble0.2Weight of normalised building height. Range: [0, 1].
W_GREENDouble0.3Weight of green cooling (subtractive). Range: [0, 1].
W_WATERDouble0.1Weight of water cooling (subtractive). Range: [0, 1].
OUTPUTVector (Polygon)Sparse grid of cells with at least one of built/green/water coverage. Contains risk score, class, and all component fractions.

4. Output Description

FieldTypeRangeDescription
cell_idInteger$\geq 0$Zero-based cell index
built_fracDouble$[0, 1]$Building footprint area fraction within the cell
green_fracDouble$[0, 1]$Green/vegetated area fraction within the cell
water_fracDouble$[0, 1]$Water body area fraction within the cell
mean_hDouble$\geq 0$Area-weighted mean building height within the cell (metres)
uhi_riskDouble$[0, 100]$Normalised UHI risk score. Fixed scale — directly comparable between scenarios
risk_classStringRisk class: Low (< 25), Moderate (25–50), High (50–75), Very High (>= 75)

5. Interpretation Guide

5.1 Reading Component Fractions for Remediation

5.2 Scenario Comparison

Because the scale is fixed, re-running after a mitigation intervention produces directly comparable numbers. The sentence "adding a 1 ha park drops 14 cells from Very High to Moderate" is legitimate because the thresholds (25, 50, 75) are anchored, not data-driven. To quantify impact: (a) run baseline; (b) edit the green/water/height layers; (c) re-run; (d) subtract uhi_risk between scenarios; (e) report the number of cells crossing class boundaries.

5.3 Cross-References

6. Symbolic Representation

7. Literature

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

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

Oke, T.R., Mills, G., Christen, A. & Voogt, J.A. (2017). Urban Climates. Cambridge University Press. DOI: 10.1017/9781139016476

Bowler, D.E., Buyung-Ali, L., Knight, T.M. & Pullin, A.S. (2010). "Urban greening to cool towns and cities: A systematic review of the empirical evidence." Landscape and Urban Planning, 97(3), 147–155. DOI: 10.1016/j.landurbplan.2010.05.006

Santamouris, M. (2014). "Cooling the cities — A review of reflective and green roof mitigation technologies to fight heat island and improve comfort in urban environments." Solar Energy, 103, 682–703. DOI: 10.1016/j.solener.2012.07.003

Road Noise Screening

Processing ID: planx:noisescreening

1. Theoretical Background

1.1 Academic Lineage

Road traffic noise is the most widespread environmental stressor in urban areas, with the World Health Organization (2018) estimating that at least 100 million Europeans are exposed to levels above the 55 dB(A) Lden threshold for annoyance and sleep disturbance. The Road Noise Screening tool implements a simplified version of the RLS-90 (Richtlinien fur den Larmschutz an Strassen, 1990) emission model, the German regulatory standard that underpins EU strategic noise mapping. The emission formula $L_{m,E} = 37.3 + 10\lg[M(1 + 0.082p)]$ at the 25 m reference distance is the RLS-90 mean level for light vehicles at 100 km/h, adapted to include heavy-vehicle correction. Propagation is free-field geometric spreading ($-20\lg r$, the point-source equivalent of a line-source after calibration), with a single fixed insertion loss where a building blocks the line of sight. This is a screening model — it correctly ranks exposure and locates hotspots but omits ground effect, air absorption, meteorology, and reflections, all of which are required for regulatory compliance.

1.2 Line-Source to Point-Source Calibration

A critical detail: road noise is a line source, not a point source. In the far field, a line source decays as $-10\lg r$ (3 dB per doubling of distance), while a point source decays as $-20\lg r$ (6 dB per doubling). The tool samples each road segment as multiple point sources, but each point source must be calibrated to reproduce the correct line-source level when summed. The calibration factor $10\lg(25 \cdot \text{seg\_len} / \pi)$ ensures that summing an infinite number of point samples along a straight road reproduces the RLS-90 mean level at 25 m. Each road is sampled at intervals of approximately 5 m (or larger, set by the cell size), so roads shorter than the sampling interval are represented by a single point source.

1.3 Building Screening

When a building layer is provided, a line-of-sight test is performed between each source sample and each receiver (grid cell). If the line-of-sight intersects any building polygon, the source contribution is reduced by the insertion loss (default 10 dB). This is a simple 2D screen — it treats buildings as infinitely tall solid barriers. In reality, sound diffracts over and around buildings, reducing the effective insertion loss. The 10 dB default is conservative (it overestimates attenuation, producing a "quiet-side" bias), which is intentional: if a cell reads below the WHO threshold with this simple screen, it is very likely below it in reality.

1.4 Use Cases and Limitations

2. Mathematical Formulation

Let road segment $j$ have hourly traffic volume $M_j$ (vehicles/hour, after applying the hourly factor) and heavy-vehicle share $p_j$ (percent). The RLS-90-style mean emission level at 25 m is:

$$ L_{m25}^{(j)} = 37.3 + 10\log_{10}\left[M_j \cdot (1 + 0.082 \cdot p_j)\right] \quad \text{dB(A)} \tag{1} $$

Each road segment is divided into $n_j$ point samples of length $\text{seg\_len}_j = \ell_j / n_j$ (metres). The level of one sample, calibrated to reproduce line-source behaviour when summed:

$$ L_{s}^{(j)} = L_{m25}^{(j)} + 10\log_{10}\left(\frac{25 \cdot \text{seg\_len}_j}{\pi}\right) \quad \text{dB(A)} \tag{2} $$

At receiver $(r, c)$, distance $d_{ij}$ from source $i$ of road $j$, the contribution with geometric spreading and optional screening loss $\Delta L_{screen}$ (default 10 dB) is:

$$ L_{ij}(r, c) = L_{s}^{(j)} - 20\log_{10}(\max(d_{ij}, d_{min})) - \mathbf{1}[\text{blocked}_{ij}] \cdot \Delta L_{screen} \tag{3} $$

The total level at the receiver is the energetic (incoherent) sum over all sources within the cutoff distance:

$$ L_{tot}(r, c) = 10\log_{10}\left(\sum_{i,j: d_{ij} \leq d_{cutoff}} 10^{L_{ij}(r, c) / 10}\right) \quad \text{dB(A)} \tag{4} $$

The population-weighted exposure summarises the public health burden:

$$ P_{\geq T} = \sum_{recipients\;\geq T} \text{pop}, \qquad T \in \{55, 65\} \text{ dB(A)} \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
ROADSVector (Line)YesRoad centreline layer. Must be in a projected CRS. Each segment needs a traffic volume field.
VOLUME_FIELDField (Numeric)YesTraffic volume per segment. Multiplied by HOURLY_FACTOR to obtain hourly volume M. 0 or missing = silent road (excluded).
HOURLY_FACTORDouble1.0Multiplier for the volume field. AADT → 1/24 = 0.0417 for average hour; peak hour factor = 0.10. Range: [0.0001, 10].
HEAVY_FIELDField (Numeric)NoHeavy-vehicle share in percent. When empty, HEAVY_PCT default is used for all roads.
HEAVY_PCTDouble5.0Default heavy-vehicle share in percent. 5% = typical urban arterial; 15% = freight route. Range: [0, 100].
BUILDINGSVector (Polygon)NoBuilding footprints for line-of-sight screening. Optional but essential for detecting quiet courtyards.
SCREEN_DBDouble10.0Insertion loss when a building blocks the line of sight, in dB. 10 = conservative screening; 5 = partial barrier. Range: [0, 30].
EXTENTExtentNoGrid bounding box. Empty = auto-extent from road layer + cutoff buffer.
CELLDouble10.0Grid cell side in map units. 10 m = facade-scale; 5 m = detailed. Minimum: 1.0.
CUTOFFDouble300.0Maximum source-receiver distance in map units. Sources beyond this are ignored. Minimum: 25.0.
RECEIVERSVector (Any)NoOptional receiver points (building centroids, address points). When provided, each receiver gets a dB level and noise band.
POP_FIELDField (Numeric)NoPopulation per receiver for exposure bands. Only used when RECEIVERS is provided.
OUTPUTRaster (Float32)Noise level grid in dB(A). -1 = NoData (excluded cells).
OUT_RECEIVERSVector (Point)NoOptional output: receiver points with dB level and exposure band label.

4. Output Description

FieldTypeDescription
db (receivers)DoubleTotal A-weighted sound level at the receiver, in dB(A). -1 = below computation floor
band (receivers)StringNoise exposure band: e.g. "< 45 dB", "45 – 50 dB", … "75 – 80 dB". Based on 5 dB bins
Grid cell valueFloat32dB(A) level at the cell centre. -1 = excluded (beyond cutoff from all sources)

The log reports population counts at the 55 dB(A) and 65 dB(A) thresholds — the two headline exposure numbers for any noise audit.

5. Interpretation Guide

5.1 The dB Scale

5.2 The Quiet-Side Effect

A key result from the screening model: courtyard levels behind perimeter blocks are 10–15 dB below street-facing levels, even without explicit acoustic treatment. This is the "quiet side" effect documented in European noise research — a flat whose bedroom faces the courtyard rather than the street sleeps 10 dB quieter. In planning terms, this is the strongest argument for closed-block typology over freestanding slabs: the same traffic produces acceptable and unacceptable levels depending on building arrangement.

5.3 Cross-References

5.4 Common Pitfalls

6. Symbolic Representation

7. Literature

WHO (2018). Environmental Noise Guidelines for the European Region. WHO Regional Office for Europe. [institutional report]

Steele, C. (2001). "A critical review of some traffic noise prediction models." Applied Acoustics, 62(3), 271–287. DOI: 10.1016/S0003-682X(00)00030-X

Kephalopoulos, S., Paviotti, M. & Anfosso-Ledee, F. (2012). Common Noise Assessment Methods in Europe (CNOSSOS-EU). JRC Report 72550. DOI: 10.2788/31776

Ohrstrom, E., Skanberg, A., Svensson, H. & Gidlof-Gunnarsson, A. (2006). "Effects of road traffic noise and the benefit of access to quietness." Journal of Sound and Vibration, 295(1-2), 40–59. DOI: 10.1016/j.jsv.2005.11.034

European Environment Agency (2020). Environmental Noise in Europe — 2020. EEA Report No 22/2019. DOI: 10.2800/686249

Road Emissions

Processing ID: planx:roademissions

1. Theoretical Background

1.1 Academic Lineage

Road traffic emission modelling is the upstream step in air quality assessment: before dispersion can be computed, the mass of pollutant released per unit length of road must be estimated. The standard methodology, codified in the European Environment Agency's COPERT (Computer Programme to calculate Emissions from Road Transport) and the US EPA's MOVES (Motor Vehicle Emission Simulator), multiplies traffic activity (vehicle-kilometres) by fleet-average emission factors (grams per vehicle-kilometre) that depend on vehicle type, fuel, speed, engine technology, and ambient temperature. This tool implements the simplest possible version of this: emission = volume x factor, where the factor is a single user-supplied value representing the fleet-average emission rate for the pollutant of interest.

1.2 Emission Factors

The default emission factor 0.5 g/km/veh is a generic NOx-proxy screening value — it represents a modern mixed fleet (Euro 5/6 diesel and petrol) at urban speeds. Real emission factors vary by order of magnitude: a pre-Euro diesel car emits ~1.5 g NOx/km; a Euro 6 diesel with functioning SCR emits ~0.08 g NOx/km; an electric vehicle emits 0. The factor should be calibrated to the local fleet composition. COPERT provides country-specific factors; the European Environmental Agency's EMEP/EEA Guidebook publishes default factors by vehicle category and road type.

1.3 Volume Normalisation

The traffic volume field is assumed to contain the raw count from the input data. The HOURLY_FACTOR parameter converts this to a daily volume. If the volume field is AADT (annual average daily traffic), the factor is 1.0. If the volume field is an hourly count (e.g. 08:00–09:00 peak), the factor should scale this to daily: approximately 24 for a typical diurnal pattern, or precisely the ratio of daily to hourly volume from the local traffic model.

1.4 Use Cases and Limitations

2. Mathematical Formulation

Let road segment $j$ have daily traffic volume $M_j$ (vehicles/day) after applying the hourly factor: $M_j = \text{raw\_volume}_j \times \text{multiplier}$. Let $EF$ be the fleet-average emission factor (grams per vehicle-kilometre). The emission rate per km of road per day is:

$$ E_j = M_j \cdot EF \quad (\text{g/km/day}) \tag{1} $$

The raw volume used in the output (for audit purposes) is $M_j$. If $M_j = 0$ or invalid, $E_j = 0$.

$$ E_{total} = \sum_{j} E_j \cdot \ell_j \quad (\text{g/day total over all roads}) \tag{2} $$

where $\ell_j$ is the segment length in km. The segment length is not directly used in the emission rate field (which is in g/km/day), but the downstream Air Quality Screening tool uses $\ell_j$ to calibrate point-source strengths.

3. Parameters

ParameterTypeRequiredDefaultDescription
ROADSVector (Line)YesRoad centreline layer. Must be in a projected CRS. The emission rate field on the output is consumed by Air Quality Screening.
VOLUME_FIELDField (Numeric)YesTraffic volume per segment (raw count). Multiplied by HOURLY_FACTOR to obtain daily volume.
HOURLY_FACTORDouble1.0Multiplier to convert raw volume to daily volume. 1.0 = AADT already; 24.0 = hourly to daily. Range: [0.0001, 1000].
EF_GKMDouble0.5Emission factor in grams per kilometre per vehicle. 0.5 = generic NOx-proxy for modern mixed fleet; use COPERT or local inventory values for quantitative studies. Minimum: 0.0.
OUTPUTVector (Line)Road layer with emission (g/km/day) and vol_used (the daily volume after factoring) appended.

4. Output Description

FieldTypeDescription
emissionDoubleEmission rate in g/km/day. Feed directly into Air Quality Screening's EMISSION_FIELD parameter (default matches).
vol_usedDoubleThe daily traffic volume after applying the hourly factor. For audit and verification — confirms the correct multiplier was applied.
(inherited)All original road attributes are preserved.

5. Interpretation Guide

5.1 Source-Ranking Logic

5.2 Cross-References

5.3 Common Pitfalls

6. Symbolic Representation

7. Literature

Ntziachristos, L. & Samaras, Z. (2019). "EMEP/EEA Air Pollutant Emission Inventory Guidebook 2019 — 1.A.3.b Road Transport." European Environment Agency.

Franco, V., Kousoulidou, M., Muntean, M., Ntziachristos, L., Hausberger, S. & Dilara, P. (2013). "Road vehicle emission factors development: A review." Atmospheric Environment, 70, 84–97. DOI: 10.1016/j.atmosenv.2013.01.006

Vardoulakis, S., Fisher, B.E.A., Pericleous, K. & Gonzalez-Flesca, N. (2003). "Modelling air quality in street canyons: a review." Atmospheric Environment, 37(2), 155–182. DOI: 10.1016/S1352-2310(02)00857-9

Air Quality Screening

Processing ID: planx:airscreening

1. Theoretical Background

1.1 Academic Lineage

Air quality screening in urban planning typically uses Gaussian plume or Gaussian puff models for open-road dispersion and street canyon models for built-up streets. The tool implements a simplified power-law decay model — an approximation to the Gaussian crosswind-integrated concentration at ground level from a line source — combined with a canyon accumulation factor based on the operational street pollution model (OSPM) concept reviewed by Vardoulakis et al. (2003). The model is screening quality: it correctly ranks receptor locations by relative pollution potential and identifies canyon hotspots, but the output is a unitless index, not a concentration in micrograms per cubic metre. Regulatory compliance requires a licensed dispersion model (ADMS-Urban, AERMOD, CALINE4) with meteorological input, terrain data, and chemistry.

1.2 The Power-Law Dispersion Model

The concentration index at a receptor is the sum of contributions from all road segment point sources: $\chi = \sum S_i / (u \cdot d_i^\alpha)$, where $S_i$ is the calibrated source strength (line-source emission rate converted to an equivalent point-source strength), $u$ is wind speed, $d_i$ is distance, and $\alpha$ is the decay exponent. This formulation derives from the Gaussian plume ground-level centreline concentration: $C \propto Q / (u \cdot \sigma_z \cdot \sigma_y)$, where $\sigma_z \propto x^b$ under neutral stability. Setting $\alpha = 2b + 1$ yields the effective distance power law. For neutral stability over urban terrain, $\alpha \approx 1.0$ for street-level screening; $\alpha = 0.5$ for broad plumes (unstable, daytime); $\alpha = 2.0$ for concentrated near-source (stable, nighttime). The offset distance $d_0$ (half the cell size) prevents the singularity at $d = 0$.

1.3 The Canyon Effect

When buildings flank both sides of a road, the street becomes a canyon — pollutants are trapped by the building walls, and concentrations can be 2–3x higher than over an open road with the same traffic. The canyon factor $1 + \min(2, H/W)$ captures the first-order geometric control: canyon aspect ratio $H/W$. A street with $H/W = 0.5$ (10 m buildings, 20 m street) has a factor of 1.5; a deep canyon with $H/W = 2$ (20 m buildings, 10 m street) saturates at 3.0. The canyon detection algorithm tests whether buildings exist within the search distance on both sides of a perpendicular line from the receptor to the nearest road, within the canyon buffer distance. Buildings on only one side = open road (factor 1.0). The tool does not model the canyon vortex — the recirculating flow that concentrates pollutants on the leeward side. This is a geometric indicator, not a CFD simulation.

1.4 Use Cases and Limitations

2. Mathematical Formulation

Let road segment $j$ have emission rate $E_j$ (g/km/day, from Road Emissions output). The segment is divided into $n_j$ point samples of length $\text{seg\_len}_j$ (metres). The calibrated point-source strength is:

$$ S_j = E_j \cdot \left(\frac{25 \cdot \text{seg\_len}_j}{\pi}\right)^{1} \quad (\text{g/day equivalent}) \tag{1} $$

At receptor $(r, c)$, the concentration index (unitless) is the sum over all sources within the cutoff distance:

$$ \chi(r, c) = \sum_{i,j: d_{ij} \leq d_{cutoff}} \frac{S_j}{u \cdot (d_{ij} + d_0)^\alpha} \tag{2} $$

where $u$ is wind speed (m/s), $d_0 = \text{cell\_size} / 2$ (m), $\alpha$ is the decay exponent, and $d_{ij}$ is the Euclidean distance from source $i$ of road $j$ to the receptor.

The canyon accumulation factor at a receptor is:

$$ \text{CF}(r, c) = \begin{cases} 1 + \min(2, \bar{H} / W) & \text{if buildings on both sides within search distance} \\ 1.0 & \text{otherwise} \end{cases} \tag{3} $$

where $\bar{H}$ is the mean building height of the nearest flanking buildings on each side and $W$ is the canyon street width parameter. The final pollution index at the receptor is:

$$ \text{PI}(r, c) = \chi(r, c) \cdot \text{CF}(r, c) \tag{4} $$

Population-weighted exposure summary:

$$ P_{\geq T} = \sum_{receivers \; \geq T} \text{pop}, \qquad T \in \{20, 50\} \text{ (index units)} \tag{5} $$

3. Parameters

ParameterTypeRequiredDefaultDescription
ROADSVector (Line)YesRoad layer with emission field (output from Road Emissions tool). Must be in a projected CRS.
EMISSION_FIELDField (Numeric)Yes"emission"Field containing emission rate in g/km/day. Default matches Road Emissions output field name.
WIND_SPEEDDouble2.0Representative wind speed in m/s. Higher wind = more dilution = lower concentrations. Typical: 1.5 (calm), 3.0 (breezy). Minimum: 0.1.
ALPHADouble1.0Distance decay exponent. 0.5 = broad plumes (unstable); 1.0 = neutral (street-level screening); 2.0 = concentrated near-source (stable). Range: [0.1, 3.0].
BUILDINGSVector (Polygon)NoBuilding footprints for canyon detection. Without buildings, the canyon factor is 1.0 everywhere (open-road dispersion).
HEIGHT_FIELDField (Numeric)NoBuilding height in metres. Only used when BUILDINGS is provided. Falls back to DEFAULT_HEIGHT.
DEFAULT_HEIGHTDouble10.0Default building height when no height field is available. Minimum: 1.0.
CANYON_WIDTHDouble20.0Street width for canyon aspect ratio H/W. The denominator in the canyon factor. Wider streets reduce the canyon effect. Minimum: 1.0.
CANYON_SEARCHDouble30.0Distance (m) to search for flanking buildings perpendicular to the road. Larger values detect buildings set back from the street. Minimum: 5.0.
CANYON_BUFFERDouble15.0Maximum distance from road for canyon effect to apply. Beyond this buffer, receptors are treated as open-terrain. Minimum: 1.0.
EXTENTExtentNoGrid bounding box. Empty = auto-extent from road layer + cutoff buffer.
CELLDouble10.0Grid cell side in map units. 10 m = facade-scale. Minimum: 1.0.
CUTOFFDouble300.0Maximum source-receptor distance. Sources beyond this are ignored. Minimum: 25.0.
RECEIVERSVector (Any)NoOptional receiver points. Each receiver gets its pollution index and exposure band.
POP_FIELDField (Numeric)NoPopulation per receiver for exposure bands. Only when RECEIVERS provided.
OUTPUTRaster (Float32)Pollution index grid (unitless). -1 = NoData.
OUT_RECEIVERSVector (Point)NoOptional output: receiver points with index value and exposure band.

4. Output Description

FieldTypeDescription
index (receivers)DoubleUnitless pollution index at the receiver. Relative scale — ranks locations, not concentrations
band (receivers)StringExposure band in 10-unit increments: "< 10", "10 – 20", …, "90 – 100", ">= 100"
Grid cell valueFloat32Unitless pollution index. -1 = excluded (beyond cutoff from all sources)

5. Interpretation Guide

5.1 Reading the Relative Index

5.2 Parameter Sensitivity

5.3 Cross-References

5.4 Common Pitfalls

6. Symbolic Representation

7. Literature

Vardoulakis, S., Fisher, B.E.A., Pericleous, K. & Gonzalez-Flesca, N. (2003). "Modelling air quality in street canyons: a review." Atmospheric Environment, 37(2), 155–182. DOI: 10.1016/S1352-2310(02)00857-9

Berkowicz, R. (2000). "OSPM — A Parameterised Street Pollution Model." Environmental Monitoring and Assessment, 65, 323–331. DOI: 10.1023/A:1006448321977

Holmes, N.S. & Morawska, L. (2006). "A review of dispersion modelling and its application to the dispersion of particles: An overview of different dispersion models available." Atmospheric Environment, 40(30), 5902–5928. DOI: 10.1016/j.atmosenv.2006.06.003

Franco, V., Kousoulidou, M., Muntean, M., Ntziachristos, L., Hausberger, S. & Dilara, P. (2013). "Road vehicle emission factors development: A review." Atmospheric Environment, 70, 84–97. DOI: 10.1016/j.atmosenv.2013.01.006

Ntziachristos, L. & Samaras, Z. (2019). "EMEP/EEA Air Pollutant Emission Inventory Guidebook 2019 — 1.A.3.b Road Transport." European Environment Agency.

6. Plan Standards and QA

The Plan Standards and QA group translates planning regulations into quantitative conformance tests. Each tool answers a specific audit question: does the plan supply enough land per person for each mandated use? Are facilities both close enough and large enough for the population they must serve? Where does density actually exceed or fall short of the plan's own targets, not just the district average? These three tools form the compliance spine of PlanX — they read the plan document as geometry and report what the numbers say, independent of the narrative.

Land-Use Balance

Processing ID: planx:landusebalance

Overview

Computes the classic land-use balance table: for every land-use category in a polygon plan, the total area, the square metres provided per capita, the area required by configurable per-capita standards, and the resulting surplus or deficit. The table is the fundamental QA gate of any land-use plan — it asks, for each use regulated by a per-capita minimum, do we have enough land?

Standards are free-form text strings such as green=10, education=4, health=1.5 (meaning 10 m² of green space per capita, 4 m² of education land, 1.5 m² of health facility land). Keywords match category names case-insensitively by containment — green catches Urban Green Area, Neighbourhood Green, and Regional Green Space alike. The default string is illustrative; replace it with the values from your own regulation. The engine (engine/standards.py) is pure stdlib: no NumPy, no geometry — it operates on the area totals that the algorithm layer computes from polygon geometries.

Theoretical Background

Per-capita standards in planning theory

Per-capita land allocation is one of the oldest quantitative planning tools, traceable to Ebenezer Howard's Garden City diagrams (1898), which specified acreages per thousand persons for every urban function, and to the Soviet normativy system that prescribed square metres of every facility type per resident. The modern statutory form appears in instruments such as the Turkish Spatial Plans Regulation (Mekansal Planlar Yapim Yonetmeligi), which mandates minimum per-capita provisions for green space, education, health, social services, sports, and technical infrastructure at each plan tier.

The per-capita standard performs two distinct functions that are often conflated: (1) a sufficiency test — is the total land allocated to a use category adequate for the planned population at build-out? and (2) a land-reservation trigger — which categories, after accounting for already-developed land, still need additional parcels acquired or designated? The first is a compliance check; the second is the actionable planning recommendation. The PlanX balance table serves both by reporting every category's surplus or deficit in absolute square metres — a deficit of −18,000 m² of green space is directly interpretable as "find 1.8 ha of additional greenspace land."

Limitations and spatial mismatch

The aggregate balance table suffers from the same limitation as every city-wide statistic: it reports how much land, not where. A plan can satisfy every per-capita standard at the municipal scale while half its population lives more than 800 m from the nearest park (the reachability gap). This is why the balance table must always be paired with the spatial tools: Facility Adequacy tests whether the land that exists is close enough to the people who need it; Green Space Access tests whether the greenspace land is walkable. The balance table is the quantity gate; the spatial tools are the quality gate.

Standards as configurable policy

The PlanX engine deliberately contains no hard-coded standard values. The default string is a placeholder; the parse_standards function accepts any keyword=value pairs. This is a design choice grounded in planning practice: standards vary by jurisdiction, plan tier, and planning epoch, and embedding a specific regulation would make the tool obsolete or misleading. The user must supply the correct values for their context; the tool only performs the arithmetic and matching.

Mathematical Formulation

Given K land-use categories with total areas {A1, …, AK} summed from polygon geometries, a planned population P, and standards S = {(keywordk, sk)} where each sk is the required m² per capita, the balance for category c is:

$$m_c = \frac{A_c}{P} \quad [\text{m}^2 \text{ per capita provision}] \tag{1}$$ $$R_c = s_{\text{match}(c)} \cdot P \quad [\text{required area, m}^2] \tag{2}$$ $$B_c = A_c - R_c \quad [\text{balance, m}^2] \tag{3}$$

The matching function returns the first standard keyword that is a case-insensitive substring of the category name, or null if no match. The status classification is:

$$\text{status}(c) = \begin{cases} \text{"Meets standard"} & \text{if matched and } B_c \geq 0 \\ \text{"Deficit"} & \text{if matched and } B_c \lt 0 \\ \text{"No standard"} & \text{otherwise} \end{cases} \tag{4}$$

The balance Bc is in absolute square metres, not per-capita units. This is deliberate: a deficit of -18,000 directly sizes the land search ("find 1.8 hectares"), whereas a per-capita shortfall of -0.3 requires multiplying by population to become actionable.

Parameters

ParameterTypeDefaultDescription
LANDUSEVector (Polygon)Land-use plan polygons with category attribute. Area from geometry. Overlapping polygons double-count. Projected CRS required.
CATEGORY_FIELDField (String)Field with land-use category name. Case-insensitive substring matching against the standards string.
POPULATIONDouble10000.0Planned horizon population (≥ 1). Denominator for all per-capita calculations. Run for both current and horizon populations.
STANDARDSStringgreen=10, park=10, …Per-capita standards as keyword=m² pairs, comma or semicolon separated. Default is illustrative — replace with your regulation's values.
OUTPUTTableBalance table, one row per unique category. No geometry.

Output Description

FieldTypeDescription
categoryStringLand-use category as found in CATEGORY_FIELD.
area_m2DoubleTotal polygon area for this category (m²), rounded to 1 decimal.
m2_capitaDoublearea_m2 / population. Rounded to 3 decimals.
std_keyStringMatching keyword from standards string (empty if unmatched).
std_m2capDoublePer-capita standard from matched keyword. 0.0 if unmatched.
requiredDoublestandard × population. The land budget for this category.
balance_m2Doublearea_m2 − required. Positive = surplus; negative = deficit.
statusStringMeets standard / Deficit / No standard.

Interpretation Guide

Reading the balance column

The balance_m2 column is the single most actionable number. A green deficit of −18,000 m² means finding exactly 1.8 hectares of additional greenspace land. Positive balances represent land banks that could be reallocated. Large positive balances warrant scrutiny: does the plan genuinely need that much surplus?

Temporal reading: current vs. horizon

Run twice: once with today's population, once with the horizon year (2040, 2050). Categories that flip from passing to Deficit under growth are tomorrow's land reservations — secure them before demand materialises. The largest future deficit gets the earliest deadline.

The "No standard" trap and spatial mismatch

Categories marked No standard may be genuinely unregulated (fine) or may reflect a naming mismatch between the standard keyword and the category field. Review every such row. More importantly, the balance table is city-wide — pair every deficit with a spatial tool (Green Space Access, Facility Adequacy, Capacitated Allocation) to find where the land or capacity is needed.

Pitfalls

Academic References

Howard, E. (1898). To-Morrow: A Peaceful Path to Real Reform. Swan Sonnenschein. [Reprinted 1902 as Garden Cities of To-Morrow. No DOI; the foundational text of per-capita land budgeting.]

Baer, W.C. (1997). "General Plan Evaluation Criteria: An Approach to Making Better Plans." Journal of the American Planning Association, 63(3), 329–344. DOI: 10.1080/01944369708975926

Berke, P.R., Godschalk, D.R., Kaiser, E.J. & Rodriguez, D.A. (2006). Urban Land Use Planning, 5th ed. University of Illinois Press. [No DOI; the standard textbook on plan evaluation.]

Talen, E. (1998). "Visualizing Fairness: Equity Maps for Planners." Journal of the American Planning Association, 64(1), 22–38. DOI: 10.1080/01944369808975954

Laurini, R. (2001). Information Systems for Urban Planning. Taylor & Francis. DOI: 10.4324/9780203485934

Alexander, E.R. & Faludi, A. (1989). "Planning and Plan Implementation: Notes on Evaluation Criteria." Environment and Planning B, 16(2), 127–140. DOI: 10.1068/b160127

Facility Adequacy (Capacity + Distance)

Processing ID: planx:facilityadequacy

Overview

Evaluates public facilities along two dimensions simultaneously: spatial accessibility (is each demand point within the network catchment of at least one facility?) and capacity (does the assigned population exceed the facility's design capacity?). Every demand point is assigned to its nearest facility over the street network via multi-source Dijkstra, but only within a maximum cost catchment; beyond that, the point counts as uncovered. Facilities report their assigned load, utilisation (load / capacity), and a three-class status: Adequate, Overloaded, or Unused.

The dual output — facility adequacy table plus demand coverage layer — makes this the most powerful compliance tool in PlanX for education, health, and emergency-service planning. It replaces the two-step workflow of "assign to nearest, then check capacity" with one integrated pass on the real street network.

Theoretical Background

The two-dimensional adequacy problem

Public facility planning standards typically specify two independent constraints: a distance (catchment) standard ("every residence within 500 m of a primary school") and a capacity standard ("each school shall enrol no more than 600 pupils"). These are routinely checked separately — a GIS service area for distance, a spreadsheet for capacity. Facility Adequacy combines them because the two constraints interact: a facility that passes distance (sitting in a dense neighbourhood) may fail capacity catastrophically (that dense neighbourhood sends it far more children than it can hold), while another in a sparse area passes capacity trivially but serves almost no one.

Coverage models as the theoretical foundation

The adequacy check operationalises the maximal covering location problem (Church & ReVelle, 1974) in evaluation mode: instead of asking "where should we place facilities?", it asks "given the facilities we already have, what proportion of the population is adequately served in both distance and capacity?" The tool's demand-side weighting (optional population field) follows the spatial equity framework of Talen & Anselin (1998), where weighted demand replaces simple counts to reflect differential need. For a comprehensive review of location models encompassing both covering and median objectives, see ReVelle & Eiselt (2005) and Owen & Daskin (1998).

The multi-source Dijkstra engine

A single multi-source Dijkstra run from all facility nodes simultaneously labels each demand node with the facility that reached it first (lowest cost). This is functionally the network Voronoi partition by shortest-path distance. Complexity is O((n + f) log n + e) where n is nodes and f is facilities. By default the cost is geometric length; substituting a time field (e.g., from Walking Slope Comfort) produces slope-aware adequacy for hilly cities. The capacity check then summates demand weights per facility and compares against the capacity field.

Mathematical Formulation

Let G = (V, E) be the primal graph. Let DV be demand nodes and FV be facility nodes. Multi-source Dijkstra with cutoff cmax assigns to each demand node i a label and cost:

$$\text{label}(i) = \arg\min_{k \in F} d(i, k) \quad \text{subject to } d(i, k) \leq c_{max} \tag{1}$$

where d(i, k) is the shortest-path cost on G. If no facility is within cmax, the point is uncovered.

$$\text{load}(k) = \sum_{i: \text{label}(i)=k} w_i \cdot \mathbf{1}[\text{cost}(i) \geq 0] \tag{2}$$ $$u_k = \frac{\text{load}(k)}{C_k} \quad [\text{utilisation}] \tag{3}$$

where Ck is the capacity of facility k. The status classifier is a three-way rule:

$$\text{status}(k) = \begin{cases} \text{"Unused"} & \text{if load}(k) = 0 \\ \text{"Overloaded"} & \text{if } u_k > 1 \\ \text{"Adequate"} & \text{otherwise} \end{cases} \tag{4}$$

The headline pass/fail metric reported in the log:

$$\text{share}_{covered} = \frac{\sum_{i: \text{cost}(i) \geq 0} w_i}{\sum_i w_i} \times 100\% \tag{5}$$

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network in projected CRS. Converted to primal graph with geometric edge weights.
DEMANDVector (Any)Demand point locations (buildings, parcels). Each snaps to nearest network node.
POP_FIELDField (Numeric)1 / pointPopulation per demand point. Leave empty for unitary weights (count mode).
FACILITIESVector (Any)Facility point locations. Each snaps to nearest network node.
FACILITY_IDFieldUnique identifier for each facility. Appears in output labels.
CAPACITY_FIELDField (Numeric)Facility capacity in persons. ≥ 0. A zero-capacity facility can never be assigned demand.
MAX_COSTDouble500.0Maximum network cost (catchment) in map units. 400–800 m for walking; 2000–5000 m for driving.
OUT_FACILITIESVector (Point)Facility adequacy: each facility with load, utilisation, and status.
OUT_DEMANDVector (Point)Demand coverage: each demand point with facility label, cost, and covered flag.

Output Description

Facility Adequacy (OUT_FACILITIES):

FieldTypeDescription
facilityStringFacility identifier from FACILITY_ID.
capacityDoubleDesign capacity in persons.
assignedDoubleTotal population assigned (sum of demand weights within catchment).
utilizationDoubleassigned / capacity, rounded to 3 decimals. > 1 = overloaded.
statusStringAdequate / Overloaded / Unused.

Demand Coverage (OUT_DEMAND):

FieldTypeDescription
coveredInteger1 = assigned within catchment; 0 = uncovered (no facility within MAX_COST).
facilityStringAssigned facility identifier (empty if uncovered).
net_costDoubleNetwork cost to assigned facility in map units. −1 if uncovered.

Interpretation Guide

The covered-population share

The log reports "Covered population: X of Y (Z%)". This single number is the compliance headline. Every percentage point below 100% represents residents who lack any facility within the catchment. For statutory standards, this is the legal compliance metric.

Reading the three status classes

Uncovered demand: two distinct causes

Uncovered points arise from: (1) distance gap — beyond MAX_COST from every facility (build new or extend catchment); (2) capacity gap — within MAX_COST but every reachable facility is full. Note: this tool assigns to nearest only and does not reallocate loads. Capacitated Allocation does that with spill logic.

Cross-referencing

Academic References

Church, R. & ReVelle, C. (1974). "The Maximal Covering Location Problem." Papers of the Regional Science Association, 32(1), 101–118. DOI: 10.1007/BF01942293

Talen, E. & Anselin, L. (1998). "Assessing Spatial Equity: An Evaluation of Measures of Accessibility to Public Playgrounds." Environment and Planning A, 30(4), 595–613. DOI: 10.1068/a300595

Daskin, M.S. (1995). Network and Discrete Location: Models, Algorithms, and Applications. Wiley. DOI: 10.1002/9781118032343

ReVelle, C.S. & Eiselt, H.A. (2005). "Location Analysis: A Synthesis and Survey." European Journal of Operational Research, 165(1), 1–19. DOI: 10.1016/j.ejor.2003.11.032

Owen, S.H. & Daskin, M.S. (1998). "Strategic Facility Location: A Review." European Journal of Operational Research, 111(3), 423–447. DOI: 10.1016/S0377-2217(98)00186-6

Dijkstra, E.W. (1959). "A Note on Two Problems in Connexion with Graphs." Numerische Mathematik, 1(1), 269–271. DOI: 10.1007/BF01386390

Density Grid

Processing ID: planx:densitygrid

Overview

Distributes a numeric variable (population, dwelling count, jobs, floor area) from irregular source features — polygons or points — onto a regular grid using dasymetric disaggregation. Polygons are split proportionally by area share; points contribute fully to the containing cell. Output cells carry the summed value, density per hectare, and contributing feature count. Empty cells are omitted.

The gridded output is the preferred input format for most PlanX tools needing a uniform spatial surface: Facility Adequacy, Green Space Access, and Accessibility Equity all accept the density grid as a demand surface, ensuring provision analysis follows population rather than administrative centroids.

Theoretical Background

Dasymetric mapping: beyond the choropleth

The choropleth map — colouring administrative zones by a density value — is the default cartographic representation of population but also the most misleading: it implies uniform density within each zone, concealing the internal texture that drives facility demand, walkability, and infrastructure stress. A "low-density" district (30 persons/ha average) may contain a single tower cluster at 300 persons/ha; treating the whole district as low-density would under-provision schools for the tower residents.

Dasymetric mapping (Wright, 1936) addresses this by using ancillary information to redistribute the zonal total onto a finer grid. The PlanX implementation uses the simplest and most robust form: binary dasymetry by geometry. If source features already represent actual spatial units (buildings, parcels), the redistribution is exact: a building with 120 residents spanning four grid cells contributes 30 to each. No statistical modelling, no ancillary land-cover raster — just geometric intersection with area-proportional splitting. Eicher & Brewer (2001) and Mennis (2009) provide comprehensive reviews of dasymetric methods.

The modifiable areal unit problem (MAUP)

The density pattern depends on cell size (Openshaw, 1984). A 100-metre grid reveals building-by-building peaks; a 500-metre grid reveals neighbourhood structure; a 1000-metre grid approximates census-tract choropleths. There is no "correct" cell size — only the size appropriate to the question. Always report cell size with any derived statistic.

Density thresholds as planning heuristics

Urban economics and transport planning have established rough density thresholds linking residential density to service viability (Newman & Kenworthy, 1989; Ewing & Cervero, 2010):

These are heuristics, not universal laws; local context modulates them.

Mathematical Formulation

Let F = {f1, …, fm} be source features with values {vi} and geometries {gi}. For a grid cell (cx, cy) with rectangle R and area h2, the contribution from polygon feature i with area ai is:

$$v_{i, c_x, c_y} = v_i \cdot \frac{\text{area}(g_i \cap R)}{a_i} \tag{1}$$

For point features, the feature contributes fully if its representative point falls inside the cell:

$$v_{i, c_x, c_y} = v_i \cdot \mathbf{1}[p_i \in R] \tag{2}$$

The cell aggregate and density per hectare are:

$$\text{value}_{c_x, c_y} = \sum_{i=1}^m v_{i, c_x, c_y} \tag{3}$$ $$\text{dens\_ha}_{c_x, c_y} = \frac{\text{value}_{c_x, c_y}}{h^2 / 10{,}000} \tag{4}$$

where h2 / 10,000 converts square map units to hectares. Only cells with at least one contributing feature are written; empty cells are skipped, keeping the output compact and free of sea-of-zero artefacts.

Parameters

ParameterTypeDefaultDescription
INPUTVector (Any)Source features: polygons for area-proportional, points for containment-based disaggregation. Projected CRS required.
VALUE_FIELDField (Numeric)1 (count)Numeric field to distribute. Leave empty to count features. Null/non-numeric values treated as 0.
CELL_SIZEDouble100.0Grid cell size in map units (≥ 1). 100 m for project scale; 200–500 m for district; 500–1000 m for strategic.
OUTPUTVector (Polygon)Density grid: rectangular polygons, one per occupied cell.

Output Description

FieldTypeDescription
cell_idIntegerSequential 0-based cell identifier for joining.
n_featIntegerNumber of source features contributing to this cell. High = fine-grained fabric.
valueDoubleSummed value in the cell (total population, dwellings). Rounded to 4 decimals.
dens_haDoublevalue divided by cell area in hectares. The standardised, cross-cell-size comparable density metric. Rounded to 4 decimals.

Interpretation Guide

Cell size diagnostics

Run at two sizes and compare: a 100 m grid reveals building-by-building peaks; a 500 m grid shows neighbourhood structure. Both are true but answer different questions. The 100 m grid tells you where to place a school entrance; the 500 m grid tells you whether the neighbourhood supports transit.

Outlier detection and plan compliance

Sort by dens_ha descending. The top 1% of cells often reveal data errors or genuine extremes meriting plan attention. Select cells exceeding the plan's density cap — these are non-conforming locations. Check whether they represent existing (grandfathered) or proposed development. The spatial cluster of non-conforming cells identifies neighbourhoods where the cap is most at odds with built form.

Difference grids for growth scenarios

Run on today's population and on the horizon-year population (from Population Projection). Subtract the grids (horizon − present). Positive cells show where growth is allocated; the magnitude at the densest cells indicates whether growth is concentrated (infill/compact) or dispersed (expansion/sprawl). This difference grid is the quantitative foundation for compact-city debates.

Pitfalls

Academic References

Wright, J.K. (1936). "A Method of Mapping Densities of Population: With Cape Cod as an Example." Geographical Review, 26(1), 103–110. DOI: 10.2307/209467

Openshaw, S. (1984). "The Modifiable Areal Unit Problem." CATMOG (Concepts and Techniques in Modern Geography), 38. Geo Books, Norwich. [No DOI; the foundational MAUP reference.]

Eicher, C.L. & Brewer, C.A. (2001). "Dasymetric Mapping and Areal Interpolation: Implementation and Evaluation." Cartography and Geographic Information Science, 28(2), 125–138. DOI: 10.1559/152304001782173727

Newman, P.W.G. & Kenworthy, J.R. (1989). Cities and Automobile Dependence: An International Sourcebook. Gower. [No DOI; establishes the 30 persons/ha bus-service threshold.]

Ewing, R. & Cervero, R. (2010). "Travel and the Built Environment: A Meta-Analysis." Journal of the American Planning Association, 76(3), 265–294. DOI: 10.1080/01944361003766766

Mennis, J. (2009). "Dasymetric Mapping for Estimating Population in Small Areas." Geography Compass, 3(2), 727–745. DOI: 10.1111/j.1749-8198.2009.00220.x

7. Reporting and Dashboard

Seven tools that compile PlanX outputs into shareable evidence: an HTML performance report with embedded SVG charts; scenario snapshots as comparable JSON records; A/B comparison with direction-aware deltas and a verdict line; multi-scenario ranking with configurable weighted composites and a heat-table visualisation; a one-click batch auditor that chains the full evaluation battery; a deterministic synthetic city generator for training and testing; and a LUTI-lite scenario pipeline that welds growth simulation, population allocation, and accessibility evaluation into one integrated workflow.

Plan Performance Report (HTML)

Processing ID: planx:performancereport

Overview

Compiles multiple PlanX outputs into a single, self-contained HTML document with score cards, inline SVG histograms, compliance bar charts, and summary tables. Computes a Plan Performance Index (PPI) as the unweighted mean of the available 0–100 component scores. The report requires no external services, no web server, and no GIS to view — open the HTML file in any browser and all charts render from the embedded SVG. The same score cards appear live in the PlanX Dashboard dock panel.

The engine (engine/report.py) is pure stdlib: it draws every chart with inline SVG elements — histograms with custom-coloured bars following a three-tone ramp (bad red → amber → good green), scatter maps of access scores with ramp-coloured points, and horizontal bar charts of land-use balance (provided in colour vs. required in grey). No matplotlib, no charting library, no JavaScript: the SVG is valid XML embedded directly in the HTML stream.

Theoretical Background

Dashboards for plan evaluation

The Plan Performance Report adapts the dashboard paradigm from business intelligence to spatial planning. Following Tufte's (2001) principles of data-ink maximisation and small-multiples, each section presents one dimension of plan performance (access, land balance, facility adequacy, density) as a self-contained card: a headline number, a sub-line explaining it, and a colour tone (green/amber/red) encoding the quality level. The PPI aggregates these cards into a single 0–100 needle, following the composite-index tradition in urban studies (e.g., the UN Habitat City Prosperity Index) but with a strict design constraint: missing sections do not penalise the score — the PPI is the mean of whatever is available, not a weighted sum with zeros imputed for missing components.

Self-contained HTML as the distribution format

The report is deliberately a single .html file with no external dependencies: no CDN links, no image files, no data directory. This is a pragmatic choice grounded in planning practice: the report must survive being emailed to a councillor, opened on a tablet in a committee room without internet, and archived in a document management system that strips non-HTML attachments. The inline SVG approach achieves this at the cost of larger file size (typically 50–200 KB depending on data density), which is negligible for modern storage and bandwidth.

PPI as a progress needle, not an absolute grade

The PPI is meaningful only over iterations of the same plan: a PPI rising from 62 to 74 across three revisions documents that the revisions improved the plan on the measured dimensions. It is meaningless as an absolute cross-plan comparator (a small-village plan will score differently from a metropolitan-region plan because its geometry, network, and population are different — not because one is "better"). The PPI's value is time-series comparison within one plan's evolution.

Mathematical Formulation

The Plan Performance Index (PPI) is the unweighted arithmetic mean of the available 0–100 component scores. Contributing components are aggregated by the report engine from the access, balance, and adequacy summaries produced by the corresponding PlanX tools; the formulation of each component is documented in the parent tool's entry.

Access score summary. From the vector of per-origin access scores {x1, …, xn}, the engine computes the arithmetic mean, the median (sorted middle value), and the shares of origins with a perfect score (100) or a low score (< 50):

$$\bar{x} = \frac{1}{n}\sum_{i=1}^n x_i \qquad \text{share}_{\text{full}} = \frac{|\{i: x_i \geq 99.99\}|}{n} \tag{1}$$

Balance summary. From land-use balance rows with status ≠ "No standard", the compliance percentage is the fraction meeting or exceeding their standard:

$$\text{compliance\_pct} = 100 \cdot \frac{|\{r: \text{status}(r) = \text{Meets standard}\}|}{|\{r: \text{status}(r) \neq \text{No standard}\}|} \tag{2}$$

Adequacy summary. From the facility adequacy and demand coverage outputs, the covered population share is:

$$\text{covered\_share} = 100 \cdot \frac{\sum_{d: \text{covered}[d]} \text{pop}[d]}{\sum_d \text{pop}[d]} \tag{3}$$

PPI: the unweighted mean of available components (access mean, compliance_pct, covered_share), with missing components simply excluded:

$$\text{PPI} = \frac{1}{|C|}\sum_{c \in C} c \quad \text{where } C \text{ is the set of available components} \tag{4}$$

Each score card is coloured by a three-tone ramp (bad red → amber → good green), with the colour derived from the normalised position of the value within [0, 100]. The ramp function interpolates linearly between the three anchor colours: (214, 69, 65) at t = 0, (245, 176, 65) at t = 0.5, and (39, 174, 96) at t = 1.0.

Parameters

ParameterTypeDefaultDescription
TITLEStringUrban PlanReport title. Appears in the header and the browser tab title.
POPULATIONDouble0.0Planned population (0 = omit from header metadata). Informational only.
ACCESSVector (Any)(optional)Access scores from Multi-Amenity Access Score. At least one input section required.
ACCESS_SCOREField (Numeric)scoreScore field on the access layer.
BALANCEVector(optional)Land-use balance table from Land-Use Balance.
FACILITIESVector (Any)(optional)Facility adequacy output from Facility Adequacy.
DEMANDVector (Any)(optional)Demand coverage output from Facility Adequacy.
DEMAND_POPField (Numeric)(optional)Population field on demand layer (empty = 1 per point).
DENSITYVector (Any)(optional)Density grid from Density Grid.
DENSITY_FIELDField (Numeric)dens_haDensity field on the grid.
OUTPUTFile (HTML)Output report path. File is self-contained with inline CSS and SVG.

Output Description

The report file contains: Header (title, population, timestamp, PlanX version), Score cards (PPI, access mean, standards compliance, covered population, density), Access section (score distribution histogram, optional score map as SVG scatter), Land-Use Balance section (provided-vs-required bar chart with compliance table), Facility Adequacy section (utilisation table sorted by descending load), Density section (cell count, mean, maximum), and a footer with the PlanX attribution.

Interpretation Guide

Academic References

Tufte, E.R. (2001). The Visual Display of Quantitative Information, 2nd ed. Graphics Press. [No DOI; the canonical reference on data visualisation design.]

Baer, W.C. (1997). "General Plan Evaluation Criteria: An Approach to Making Better Plans." Journal of the American Planning Association, 63(3), 329–344. DOI: 10.1080/01944369708975926

Few, S. (2013). Information Dashboard Design: Displaying Data for At-a-Glance Monitoring, 2nd ed. Analytics Press. [No DOI; the standard reference on dashboard design principles.]

Scenario Compare (A/B)

Processing ID: planx:scenariocompare

Overview

Compares two scenario snapshots metric by metric, with direction-aware deltas. For each metric shared by both snapshots, the tool reports the raw values for A and B, the absolute delta (Δ = B − A), the percent change, and which scenario wins (A, B, or tie for equal values). The verdict line tallies the wins. Scenarios rarely dominate — the normal outcome is a trade-off profile where B wins on accessibility and compliance while A wins on density and coverage. An optional self-contained HTML report presents the comparison in formatted tables for stakeholder distribution.

The engine (engine/scenario.py) drives the comparison. A direction registry (METRICS dict) assigns each metric a human label and a direction (+1 = higher is better, −1 = lower is better, 0 = neutral). The engine also provides score_line for a one-sentence verdict and build_compare_html in engine/report.py for the HTML output.

Theoretical Background

Multi-criteria comparison in plan evaluation

Plan alternatives rarely dominate one another on all criteria — the compact city proposal may score higher on walkability and transit access while the dispersed proposal scores higher on housing capacity and green space per capita. The Scenario Compare tool formalises this trade-off analysis by reporting the direction and magnitude of each metric's change, following the multi-criteria evaluation framework (Baer, 1997; Keeney & Raiffa, 1976). Unlike a weighted composite score (which collapses the trade-off into one number), paired comparison preserves the profile: the planner sees which metrics flip and by how much.

Direction-aware comparison

The key innovation over a simple algebraic delta is the direction registry: the engine knows that a higher access score is good, a higher Gini coefficient is bad, and a different number of facilities is neutral. The "better" column thus reflects the substantive planning meaning of each delta, not just its arithmetic sign. This prevents the common error of celebrating a 10% increase in a metric where lower values indicate better outcomes (e.g., inequality, overloading count).

Mathematical Formulation

For each metric k with direction dk ∈ {+1, −1, 0} and snapshot values vkA and vkB:

$$\Delta_k = v_k^B - v_k^A \quad [\text{absolute change}] \tag{1}$$ $$\Delta\%_k = \frac{v_k^B - v_k^A}{|v_k^A|} \times 100 \quad [\text{percent change, NaN if } v_k^A = 0] \tag{2}$$

The winning scenario for metric k is determined by the direction-consistent sign:

$$\text{winner}_k = \begin{cases} \text{"B"} & \text{if } d_k \cdot \Delta_k > 0 \\ \text{"A"} & \text{if } d_k \cdot \Delta_k < 0 \\ \text{"tie"} & \text{if } \Delta_k = 0 \\ \text{"n/a"} & \text{if } d_k = 0 \text{ or either value is missing} \end{cases} \tag{3}$$

Parameters

ParameterTypeDefaultDescription
SNAPSHOT_AFile (JSON)Scenario snapshot JSON for alternative A.
SNAPSHOT_BFile (JSON)Scenario snapshot JSON for alternative B.
OUT_TABLETableComparison table: metric, values A/B, delta, pct change, winner. No geometry.
OUTPUT_HTMLFile (HTML)(optional)Self-contained HTML comparison report.

Output Description

FieldTypeDescription
metricStringHuman-readable metric label.
metric_keyStringInternal metric key (machine-readable).
scenario_aDoubleValue in scenario A (or NULL if missing).
scenario_bDoubleValue in scenario B (or NULL if missing).
deltaDoubleB − A (or NULL if either side missing).
delta_pctDoublePercent change (or NULL).
betterStringA / B / tie / n/a.

Interpretation Guide

Academic References

Baer, W.C. (1997). "General Plan Evaluation Criteria: An Approach to Making Better Plans." Journal of the American Planning Association, 63(3), 329–344. DOI: 10.1080/01944369708975926

Keeney, R.L. & Raiffa, H. (1976). Decisions with Multiple Objectives: Preferences and Value Tradeoffs. Wiley. [Reprinted Cambridge University Press, 1993. DOI: 10.1017/CBO9781139174084]

Scenario Ranking

Processing ID: planx:scenariorank

Overview

Ranks any number of scenario snapshots (two or more) using a configurable weighted composite score. For each scored metric, values are min-max normalised within the set so that 1.0 is always best; the composite score is the weighted mean of these normalised values, scaled to 0–100. Competition ranking handles ties (equal scores share a rank; the next rank is skipped). "Wins" count per scenario — where a scenario holds the strictly best norm on a metric — complements the composite for non-compensatory reading. An optional HTML ranking board renders the scoreboard as a styled table with score bars and a metric heat table.

Theoretical Background

Composite indices in multi-criteria decision analysis

The weighted-sum model with min-max normalisation is the simplest form of multi-attribute value theory (Keeney & Raiffa, 1976). Normalisation to [0, 1] ensures commensurability across metrics measured in different units (persons, percent, metres, Gini coefficient); the direction flag ensures that "more" always means "better" after normalisation. The weighted sum is fully compensatory: a scenario can score zero on one metric and still rank first if it dominates on heavily weighted others. This is a feature (it allows trade-offs) and a warning (it can mask fatal weaknesses).

Metrics that are skipped

Three classes of metrics are excluded from scoring: (1) neutral (direction = 0, e.g., total population, facilities count) — no normative direction exists; (2) not-shared (missing in at least one snapshot) — comparison is impossible; (3) constant (identical across all snapshots) — contributes zero discrimination. The skipped list is reported explicitly; it is the first thing to check before trusting a ranking, because a snapshot evaluated with fewer tools will have more "not-shared" exclusions and thus a structurally different score basis.

Competition ranking and the wins complement

Competition ranking ("1224") handles equal scores correctly: identical composite scores produce tied ranks. The wins count — number of metrics where a scenario holds the strictly best norm — provides a non-compensatory complement: a balanced alternative may rank first on composite yet win zero individual metrics, while a specialist wins several metrics but scores lower overall. The divergence between rank and wins count is diagnostic of the scenario's strategy type (generalist vs. specialist).

Mathematical Formulation

For each scored metric k with direction dk and values vk,s across scenarios s:

$$\text{norm}_{k,s} = \begin{cases} \frac{v_{k,s} - \min_s}{\max_s - \min_s} & \text{if } d_k = +1 \text{ (higher is better)} \\[6pt] \frac{\max_s - v_{k,s}}{\max_s - \min_s} & \text{if } d_k = -1 \text{ (lower is better)} \end{cases} \tag{1}$$ $$\text{score}_s = 100 \cdot \frac{\sum_k w_k \cdot \text{norm}_{k,s}}{\sum_k w_k} \tag{2}$$

Competition rank: scenario with highest score = rank 1; if two scenarios have equal scores, both share rank r and the next distinct score gets rank r + 2.

$$\text{wins}_s = \big|\{k : \text{norm}_{k,s} = 1.0 \land \forall s' \neq s,\ \text{norm}_{k,s'} < 1.0\}\big| \tag{3}$$

Parameters

ParameterTypeDefaultDescription
FILESString(optional)Comma or semicolon-separated paths to snapshot JSON files.
FOLDERFolder(optional)Directory containing *.json snapshot files. Combined with FILES paths.
WEIGHTSString(optional)Metric weights as key=value pairs (e.g., access_mean=3, access_gini=2). Default weight = 1.0 for all. Unknown keys are warned and ignored.
OUT_TABLETableRanking: rank, scenario name, score, wins, n_metrics. No geometry.
OUT_DETAILTablePer-metric detail: metric, label, direction, weight, scenario, raw value, norm. One row per metric per scenario.
OUTPUT_HTMLFile (HTML)(optional)HTML ranking board with score bars and heat table.

Output Description

Ranking (OUT_TABLE): rank (int), scenario (string), score (double, 0–100), wins (int), n_metrics (int).

Detail (OUT_DETAIL): metric (string key), label (string), direction (int, +1/−1), weight (double), scenario (string), value (double), norm (double, 0–1).

Interpretation Guide

Academic References

Keeney, R.L. & Raiffa, H. (1976). Decisions with Multiple Objectives. Cambridge University Press. DOI: 10.1017/CBO9781139174084

Baer, W.C. (1997). "General Plan Evaluation Criteria." JAPA, 63(3), 329–344. DOI: 10.1080/01944369708975926

Saaty, T.L. (1980). The Analytic Hierarchy Process. McGraw-Hill. [No DOI; the foundational AHP text for weighted multi-criteria ranking, the conceptual ancestor of this tool's weighted composite.]

Scenario Snapshot

Processing ID: planx:scenariosnapshot

Overview

Captures plan score metrics from the current QGIS project into a structured JSON file (the "scenario snapshot" format). PlanX output layers are auto-detected by their field signatures: the access-score layer (score + n_reach), the land-use balance table (balance_m2 + m2_capita), facility adequacy (utilization + assigned), demand coverage (covered + net_cost), and the density grid (dens_ha + value). Each can also be pinned explicitly — an explicit choice always wins over auto-detection.

The snapshot stores the same metrics as the Plan Dashboard score cards: Plan Performance Index, accessibility mean/median and full/low shares, standards compliance percentage and deficit count, covered population share and facility overloaded/unused counts, and density summary. The JSON serves as the comparable record for Scenario Compare and Scenario Ranking. Every evaluation workflow should end with a snapshot node.

Theoretical Background

Metrics as comparable evidence in plan-making

The snapshot formalises a single principle: every plan alternative must be evaluated on the same metrics with the same thresholds. A plan whose access score was computed with a 15-minute threshold and walking speed of 4.8 km/h is not comparable to one computed with a 10-minute threshold and 5.0 km/h. The snapshot captures not only the metric values but, through its field-signature detection, which PlanX tools contributed to the evaluation — a snapshot whose metrics list lacks density entries was evaluated without the Density Grid tool, and that absence will later surface in Scenario Compare as "metric missing on one side."

Auto-detection by field signature

The collect module (in collect.py) scans project layers for characteristic field-name pairs rather than layer names. This is deliberate: a layer called "Access Score (Scenario B)" is detected as correctly as "access_score_output" because both contain the fields score and n_reach. The detection is robust to renaming but fragile to field deletions: if a QGIS processing step strips the signature fields, the layer becomes invisible to the snapshot. Always check the log's detection lines before trusting the numbers.

Parameters

ParameterTypeDefaultDescription
NAMEStringScenario AScenario identifier. Carried into every subsequent comparison and ranking report. Use descriptive names: "2040 Compact", "2040 Corridor", "Baseline 2024".
ACCESSVector Layer(auto-detect)Access-score layer. Explicit overrides auto-detection.
BALANCEVector Layer(auto-detect)Land-use balance table layer.
FACILITIESVector Layer(auto-detect)Facility adequacy layer.
DEMANDVector Layer(auto-detect)Demand coverage layer.
DENSITYVector Layer(auto-detect)Density grid layer.
OUTPUT_JSONFile (JSON)Snapshot JSON file. Contains kind, version, name, generated timestamp, and metrics dict.
OUT_METRICSTableMetric table: key, label, value. For inspection/verification.

Output Description

The snapshot JSON schema:

{
  "kind": "planx-scenario-snapshot",
  "version": 1,
  "name": "2040 Compact",
  "generated": "2026-08-07 14:30",
  "metrics": {
    "plan_performance_index": 72.5,
    "access_mean": 68.3,
    "access_median": 71.0,
    "access_share_full": 45.2,
    "access_share_low": 12.1,
    ...
  }
}

Interpretation Guide

Academic References

Baer, W.C. (1997). "General Plan Evaluation Criteria: An Approach to Making Better Plans." Journal of the American Planning Association, 63(3), 329–344. DOI: 10.1080/01944369708975926

Lichfield, N., Kettle, P. & Whitbread, M. (1975). Evaluation in the Planning Process. Pergamon Press. [No DOI; the classic text on systematic plan evaluation, establishing the principle of consistent metrics across alternatives.]

Batch Plan Auditor

Processing ID: planx:planaudit

Overview

Chains the standard PlanX evaluation battery in a single run: 15-minute access score, walkability audit, land-use balance against per-capita standards, facility adequacy (capacity + distance), green space access hierarchy check, and access equity (Gini coefficient over access scores). Each component is optional — supply only the inputs for the analyses you need and the auditor runs only those. The output is a scenario snapshot JSON (ready for Scenario Compare) plus an optional HTML Plan Performance Report. The tool is model-designer friendly and fully headless: one call turns a set of plan layers into a comparable scorecard.

Theoretical Background

The standard evaluation battery

The Batch Plan Auditor operationalises the principle that all plan alternatives must be evaluated on the same battery of quantitative tests before qualitative comparison begins (Baer, 1997). The six tests are selected to cover the standard dimensions of plan quality: spatial equity of access (15-minute city and Gini), walkability (street-level pedestrian environment), land sufficiency (per-capita balance), facility provision (adequacy), and environmental amenity (green space hierarchy). Together they produce 20+ comparable metrics per scenario. The auditor replaces the error-prone manual workflow of "run six tools, collect six outputs, manually copy numbers into a spreadsheet" with a single deterministic call.

Multi-step feedback and child algorithms

The auditor uses QGIS Processing's multi-step feedback mechanism (QgsProcessingMultiStepFeedback) to report progress through the battery. Each test is a child algorithm call (processing.run with is_child_algorithm=True), so failures are propagated with the offending algorithm name, and partial results up to the failure point are discarded. All tests use the same network, demand layer, and coordinate system — the auditor assumes these are prepared once and reused.

Parameters

ParameterTypeDefaultDescription
NAMEStringPlanScenario name for the snapshot.
NETWORKVector (Line)Street network. Required for all network-based tests.
DEMANDVector (Any)Demand/origins layer. Required for access, walkability, adequacy.
POP_FIELDField (Numeric)(optional)Population field on demand (empty = 1 per point).
AMENITIESMultiple Layers(optional)Amenity layers for access score. If empty, the access test is skipped.
THRESHOLDDouble15.0Access threshold in minutes.
LANDUSEVector (Polygon)(optional)Land-use polygons for balance and walkability mix.
CATEGORY_FIELDField(optional)Land-use category field.
POPULATIONDouble0.0Planned population for standards. 0 = skip balance test.
STANDARDSStringgreen=10, school=4Per-capita standards. Empty = skip balance test.
FACILITIESVector (Point)(optional)Facilities with capacity. Empty = skip adequacy test.
FACILITY_IDField(optional)Facility identifier field.
CAPACITY_FIELDField (Numeric)(optional)Facility capacity field.
MAX_COSTDouble500.0Facility catchment distance.
GREENSVector (Polygon)(optional)Public green spaces. Empty = skip green access test.
HIERARCHYString0.5=300, 2=800Green hierarchy: min_ha=max_dist pairs. Empty = skip green test.
OUTPUT_JSONFile (JSON)Snapshot JSON.
OUTPUT_HTMLFile (HTML)(optional)HTML performance report.
OUT_METRICSTableMetric table: metric, metric_key, value.

Output Description

Snapshot JSON with 20+ metrics (depending on which tests ran) including PPI, access statistics, walk score mean and low-score share, standards compliance and deficits, covered population share and facility utilisation, green coverage minimum, and access Gini. Metric table mirrors the snapshot. Optional HTML report with access, walkability, balance, facility adequacy, and green access sections.

Interpretation Guide

Academic References

Baer, W.C. (1997). "General Plan Evaluation Criteria: An Approach to Making Better Plans." Journal of the American Planning Association, 63(3), 329–344. DOI: 10.1080/01944369708975926

Hopkins, L.D. (2001). Urban Development: The Logic of Making Plans. Island Press. [No DOI; the foundational text on plan-making as sequential decision under uncertainty, establishing the rationale for evaluating multiple alternatives on a common battery.]

Generate Demo City

Processing ID: planx:democity

Overview

Creates a complete, deterministic synthetic city from a single random seed: a regular grid of street blocks intersected by a diagonal avenue, building footprints with random heights assigned to rectangular lots within each block, four land-use zones (residential, commercial, green, school) with probabilistic allocation, point-of-interest markers, public facilities with capacities placed in green and school blocks, demand points with population weights derived from residential building volumes, green space polygons, and a raster DSM of building heights. The same seed always regenerates an identical city — ideal for tutorials, reproducible testing, and bug reports.

The engine (engine/demo.py) is pure NumPy with a numpy.random.default_rng generator. Streets are constructed by intersecting vertical, horizontal, and diagonal grid lines at the block boundaries; buildings are 2x2 arrays per block, skipped over the diagonal avenue right-of-way; land uses are assigned with guaranteed representatives for the first four blocks (one of each type) and random categorical draws for the rest. The DSM is rasterised by burning building height into footprint-extent cells.

Theoretical Background

Synthetic data in spatial algorithm testing

Synthetic city generators serve two distinct purposes in geospatial software development and training: (1) algorithm validation — a known, controlled input where every expected output can be computed by hand, enabling unit testing and correctness verification; (2) pedagogical demonstration — a compact, clean dataset that illustrates a tool's behaviour without the noise, gaps, and idiosyncrasies of real-world data. The demo city's small default size (4x4 blocks at 100 m = 400 m × 400 m, 64 buildings, ~16 streets) is deliberately scaled to be visually inspectable: a user can verify a service area's correctness by eye because the total network has only a few dozen edges.

Determinism and reproducibility

The fixed-seed design (default seed = 42 with NumPy's PCG-64 generator) guarantees bitwise-identical output across platforms and Python versions (NumPy's default_rng stream is version-stable). This makes the demo city useful for regression testing: a CI pipeline can run every PlanX tool on the demo city and assert that each output's row count, field types, and key statistics match the expected values. Any deviation signals a regression in the engine or algorithm layer.

Parameters

ParameterTypeDefaultDescription
SEEDInteger42Random seed for reproducible generation. Change for different layouts.
BLOCKS_XInteger4Number of blocks in X direction (≥ 1).
BLOCKS_YInteger4Number of blocks in Y direction (≥ 1).
BLOCK_SIZEDouble100.0Block size in metres (≥ 10.0).
CRSCRSEPSG:3857Target projected CRS (must be metric).
OUTPUT_STREETSVector (Line)Street network segments with seg_id and length_m.
OUTPUT_BUILDINGSVector (Polygon)Building footprints with height attribute.
OUTPUT_LANDUSEVector (Polygon)Land-use block polygons with use attribute.
OUTPUT_POISVector (Point)Points of interest with type attribute (Shop, Cafe).
OUTPUT_FACILITIESVector (Point)Facilities with name and cap (capacity) attributes.
OUTPUT_DEMANDVector (Point)Demand points with pop attribute.
OUTPUT_GREENVector (Polygon)Green space polygons with park_id.
OUTPUT_DSMRasterDigital Surface Model raster at 2 m resolution.

Output Description

Eight outputs: Streets (seg_id, length_m), Buildings (height, 3–40 m), Land Use (use: residential/commercial/ green/school), POIs (type: Shop/Cafe), Facilities (name, cap: 1000 for parks, 500 for schools), Demand (pop, proportional to building volume), Green (park_id), and DSM (GeoTIFF, building heights on a 2 m grid, nodata = −1).

Interpretation Guide

Academic References

Batty, M. (2013). The New Science of Cities. MIT Press. [No DOI; discusses synthetic city models as tools for understanding urban systems, including procedural generation approaches.]

Parish, Y.I.H. & Muller, P. (2001). "Procedural Modeling of Cities." Proceedings of SIGGRAPH 2001, 301–308. ACM. DOI: 10.1145/383259.383292

Scenario Pipeline (LUTI-lite)

Processing ID: planx:scenariopipeline

Overview

Weld urban growth simulation (CA), population allocation, and accessibility/walkability evaluation into one integrated decision pipeline. First, the Urban Growth CA model simulates city expansion from a seed urban mask over a suitability surface, respecting optional constraint rasters. Then, population growth is allocated to the newly developed cells proportionally to their suitability values. The pipeline constructs combined demand points (existing + new) and re-evaluates 15-minute accessibility and walkability on the grown city, producing a scenario snapshot JSON for comparison against the baseline.

Theoretical Background

LUTI (Land-Use Transport Interaction) in simplified form

LUTI models simulate the co-evolution of land use and transport over time. Full LUTI implementations (e.g., UrbanSim, TRANUS, MEPLAN) require calibrated behavioural models and extensive data. The Scenario Pipeline implements a "LUTI-lite" approach: the transport network is fixed (the street layer does not change), and land use evolves through cellular automaton growth constrained by suitability. The population reallocation step is the simplest possible linkage: new residents are placed where the CA model developed land, weighted by the suitability that attracted the growth. The feedback is one-directional: growth changes demand, which changes access scores and walkability. There is no reverse feedback (accessibility changes do not alter future growth locations). This is not a limitation for horizon-year screening — it produces the honest "growth without new services" worst case that service planners need.

The fixed-amenity assumption as a planning tool

The pipeline keeps amenity layers fixed: schools, shops, parks do not grow with the city. This reads as "what happens if we grow but do not add services?" — the honest worst case. The metric gap between the grown scenario and the baseline quantifies the service deficit that the new growth creates. If mean access falls from 68 to 52, the planning response is either to add amenities at the growth locations (raise the numerator) or to redirect growth toward existing amenity-rich areas (change the growth pattern). The pipeline quantifies the size of the problem; it does not solve it — that is the planner's job.

Mathematical Formulation

The pipeline's three stages are sequential:

Stage 1 — Growth CA: the Urban Growth Simulation algorithm produces a year-of-conversion raster where nonzero cells were developed. The set of newly developed cells is:

$$\mathcal{N} = \{(r, c) : y_{r,c} > 0 \land \text{finite}(y_{r,c})\} \tag{1}$$

Stage 2 — Population allocation: for each new cell, the suitability value sr,c is extracted from the suitability raster. Population growth G is allocated proportionally:

$$p_{r,c} = G \cdot \frac{\max(0, s_{r,c})}{\sum_{(r',c') \in \mathcal{N}} \max(0, s_{r',c'})} \tag{2}$$

These become new demand points at the cell centres with population pr,c, merged with existing demand points.

Stage 3 — Re-evaluation: the access score and walkability audit tools run on the combined demand and the fixed network, producing new metrics for the snapshot.

Parameters

ParameterTypeDefaultDescription
NAMEStringScenarioScenario name.
SEEDRasterSeed urban mask (nonzero = urban). Defines existing urban area.
SUITABILITYRasterDevelopment suitability raster (higher = more suitable).
CONSTRAINTSRaster(optional)Constraints (nonzero = never build).
DEMAND_HADouble50.0Land demand in hectares.
ITERATIONSInteger5Growth steps (1–100).
NEIGH_WEIGHTDouble1.0Neighbourhood weight for edge growth (0–10).
BASEDouble0.1Base term for leapfrog growth (0–1).
RNG_SEEDInteger0Random seed for CA tie-breaking.
POP_GROWTHInteger1000Population growth to allocate over new cells.
DEMANDVector (Any)(optional)Existing demand points with population field.
POP_FIELDField (Numeric)(optional)Population field on existing demand.
NETWORKVector (Line)Street network for access and walkability evaluation.
AMENITIESMultiple Layers(optional)Amenity layers for access score.
THRESHOLDDouble15.0Access threshold in minutes.
LANDUSEVector (Polygon)(optional)Land-use for walkability mix component.
CATEGORY_FIELDField(optional)Land-use category field.
OUTPUT_JSONFile (JSON)Scenario snapshot JSON of the grown city.
OUT_METRICSTableMetric table.

Output Description

The pipeline produces two outputs:

Interpretation Guide

Academic References

Clarke, K.C., Hoppen, S. & Gaydos, L. (1997). "A Self-Modifying Cellular Automaton Model of Historical Urbanization in the San Francisco Bay Area." Environment and Planning B, 24(2), 247–261. DOI: 10.1068/b240247

Waddell, P. (2002). "UrbanSim: Modeling Urban Development for Land Use, Transportation, and Environmental Planning." Journal of the American Planning Association, 68(3), 297–314. DOI: 10.1080/01944360208976274

Wegener, M. (2014). "Land-Use Transport Interaction Models." In: Fischer, M.M. & Nijkamp, P. (eds.), Handbook of Regional Science, pp. 229–246. Springer. DOI: 10.1007/978-3-642-23430-9_14

8. Optimization

Five facility and land-use optimisation tools operating on the street network via an embedded pure-NumPy engine — no external solvers, no licensed libraries, no cloud dependencies. Two facility location models (maximal coverage via greedy selection and p-median via Teitz-Bart vertex substitution), one capacitated allocation with greedy nearest-with-spill logic, one multi-objective land-use allocator with compactness and adjacency terms, a Pareto front tracer that maps the suitability-compactness trade-off, and a capacitated facility siting tool that selects sites AND sizes capacity in one pass.

Facility Location Optimizer (Coverage / P-Median)

Processing ID: planx:facilitylocation

Overview

Selects the best sites for new facilities among candidate locations on the real street network. Two models are implemented: Maximal Coverage (Church & ReVelle, 1974) using greedy selection — each step picks the candidate covering the most as-yet-uncovered demand within a catchment radius; and P-Median (ReVelle & Swain, 1970; Teitz & Bart, 1968) using greedy construction followed by vertex substitution — each swap replaces a selected site with an unselected candidate if it reduces the population-weighted total travel cost. Existing facilities are fixed in the solution; new sites complement them.

The engine (engine/optimize.py) operates on a distance matrix D of shape (candidates, demand points) computed by many-to-many Dijkstra over the network graph. For coverage mode, a binary mask Dradius is multiplied by the population weight vector to obtain marginal gains. For p-median mode, the objective is the population-weighted sum of distances to the nearest selected facility, and the Teitz-Bart substitution iterates until no improving swap is found or max_iter is reached.

Theoretical Background

Covering models: from set covering to maximal coverage

Toregas et al. (1971) formulated the location set covering problem (LSCP): minimise the number of facilities such that every demand point is within a specified distance. Church & ReVelle (1974) recognised that the LSCP's "cover everyone" constraint is often infeasible under a budget, and proposed the maximal covering location problem (MCLP): for a fixed number of facilities p, maximise the population within the coverage radius. The greedy heuristic implemented here is a (1 − 1/e) ≈ 0.632-approximation for the submodular maximisation problem (Nemhauser, Wolsey & Fisher, 1978) — it is provably within 63% of the optimal covered population.

P-median and the Teitz-Bart heuristic

The p-median problem (Hakimi, 1964; ReVelle & Swain, 1970) minimises the sum of demand-weighted distances to the nearest facility. Unlike coverage models which treat all demand within the radius equally, the p-median penalises long trips linearly — it preferences the average user, not the worst-off. The Teitz & Bart (1968) vertex substitution heuristic is a local-search improvement over a greedy starting solution: it evaluates swapping each selected site with each unselected candidate, applying the first improving swap found. While not guaranteed to find the global optimum, it typically produces solutions within 5–10% of optimal for urban-scale instances (Daskin, 1995).

Coverage vs. p-median: choosing the model

Coverage models are appropriate for services with a hard distance threshold ("within 500 m of a primary school", "within 8 minutes of a fire station"). P-median models are appropriate for services where every metre of extra distance matters to everyone ("shorten the average trip to the nearest clinic"). The two models give different site selections by design. Sites picked by both are robust to the modelling choice; sites picked by only one reveal the sensitivity of the recommendation to the objective.

Mathematical Formulation

Maximal Coverage (greedy). Let cover[f, d] = 1 if dist(f, d) ≤ radius. The gain at each greedy step is uncovered demand newly covered by candidate c:

$$\text{gain}(c) = \sum_{d: \text{cover}[c,d]=1 \land \text{uncovered}[d]} w_d \tag{1}$$

At each iteration t, select ct = argmaxc gain(c), mark newly covered demand, and repeat for p steps (or until no candidate adds coverage).

P-Median objective. For a selected set S of facilities (including fixed), the objective is:

$$Z(S) = \sum_{d=1}^N w_d \cdot \min_{f \in S} \text{dist}(f, d) \tag{2}$$

Teitz-Bart swap. For each selected site s and each unselected candidate c, evaluate the swap (S \ {s}) ∪ {c}. If Z(Sswap) < Z(S) − 10−12, accept the swap. Repeat until no swap improves the objective (or max_iter is reached).

Unreachable pairs (infinite distance) incur a penalty cost of 1.5 × the largest finite distance, ensuring they are avoided unless no reachable facility exists.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network. Projected CRS required.
DEMANDVector (Any)Demand points with population weights.
POP_FIELDField (Numeric)1/pointPopulation per demand point.
CANDIDATESVector (Any)Candidate facility sites. All snapped to network nodes.
CANDIDATE_IDFieldUnique identifier for each candidate.
EXISTINGVector (Any)(optional)Existing facilities fixed in the solution.
METHODEnumCoverageCoverage (greedy) or P-Median (Teitz-Bart).
PInteger3Number of new facilities to site (≥ 1).
RADIUSDouble500.0Catchment radius in map units. Used in coverage mode and for candidate screening in both modes.
OUT_SITESVector (Point)Candidate sites with screening score, selection flag, rank, and marginal gain.
OUT_ASSIGNVector (Point)Demand points with assigned facility, network cost, and covered flag.

Output Description

Candidate Sites (OUT_SITES): inherits all source fields + cand_id (string), reach_dem (screening: demand within radius), selected (0/1), rank (1..p for selected, 0 for unselected), gain (marginal covered demand added).

Demand Allocation (OUT_ASSIGN): inherits all source fields + facility (assigned facility label), net_cost (network distance to assigned facility, −1 if unreachable), covered (1 if within radius, 0 otherwise).

Interpretation Guide

Academic References

Church, R. & ReVelle, C. (1974). "The Maximal Covering Location Problem." Papers of the Regional Science Association, 32(1), 101–118. DOI: 10.1007/BF01942293

Teitz, M.B. & Bart, P. (1968). "Heuristic Methods for Estimating the Generalized Vertex Median of a Weighted Graph." Operations Research, 16(5), 955–961. DOI: 10.1287/opre.16.5.955

Daskin, M.S. (1995). Network and Discrete Location: Models, Algorithms, and Applications. Wiley. DOI: 10.1002/9781118032343

ReVelle, C.S. & Eiselt, H.A. (2005). "Location Analysis: A Synthesis and Survey." European Journal of Operational Research, 165(1), 1–19. DOI: 10.1016/j.ejor.2003.11.032

Nemhauser, G.L., Wolsey, L.A. & Fisher, M.L. (1978). "An Analysis of Approximations for Maximizing Submodular Set Functions — I." Mathematical Programming, 14(1), 265–294. DOI: 10.1007/BF01588971

Hakimi, S.L. (1964). "Optimum Locations of Switching Centers and the Absolute Centers and Medians of a Graph." Operations Research, 12(3), 450–459. DOI: 10.1287/opre.12.3.450

Capacitated Allocation (Nearest with Capacity)

Processing ID: planx:capacitatedallocation

Overview

Assigns demand to fixed facilities while respecting capacity constraints — the realistic companion to Facility Adequacy (which assigns everyone to the nearest and only flags overload afterward). Each demand point is sent, in full, to the nearest facility over the street network that still has room; when its nearest is already full, it spills to the next-nearest with free capacity within the catchment. Points fitting nowhere are left uncovered. Demand status: Assigned (nearest), Spilled (farther because nearest was full), or Uncovered. Facilities report load, remaining capacity, utilisation, and status (Full / Has space / Unused).

Theoretical Background

Capacitated allocation as nearest-with-spill

The problem of assigning demand to capacitated facilities is a minimum-cost flow problem when fractional assignments are allowed (Daskin, 1995). When demand points must be assigned in whole (no splitting), the problem becomes NP-hard (it is a generalised assignment problem). PlanX implements a fast greedy heuristic: sort all eligible facility-demand pairs by cost, iterate cheapest-first, assign a demand point to the earliest facility in its sorted list that has room. This is not a global optimum — a demand point may be greedily assigned to facility A, blocking capacity that a farther demand point needed from A, when a globally better assignment would have sent the first point to B. However, the greedy approach is explainable, fast, and produces the "nearest available" answer that matches how people actually choose facilities (nearest first, spill when full).

Whole-point assignment and stranded capacity

Because demand points are not split, a facility with remaining capacity smaller than any nearby point's population may remain "Has space" yet accept no one. This is not a bug — it correctly reflects the indivisibility of whole-family or whole-building assignments. The stranded capacity is reported in the remaining field; the planning response is either to accept the inefficiency or to consider smaller-capacity facilities (which the tool cannot create but can model if the input capacity values are reduced).

Mathematical Formulation

Let D be the (facilities × demand) cost matrix, w the demand weight vector, C the facility capacity vector. The eligible set E ⊆ {1..F} × {1..N} contains all pairs with finite cost and cost ≤ cmax:

The greedy assignment iterates over eligible pairs sorted by increasing cost, with deterministic tie-breaking by demand index then facility index:

$$\text{For each } (f, d) \in E \text{ sorted by cost}: \quad \text{if } \text{assigned}[d] = -1 \land \text{remaining}[f] \geq w_d: \quad \text{assign } d \text{ to } f, \text{ remaining}[f] \mathrel{-}= w_d \tag{1}$$

Spill status:

$$\text{spilled}[d] = (\text{assign}[d] \neq -1) \land (\text{assign}[d] \neq \text{nearest}[d]) \tag{2}$$

where nearest[d] is the index of the closest reachable facility ignoring capacity.

$$\text{utilisation}[f] = \frac{C_f - \text{remaining}[f]}{C_f} \tag{3}$$

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network, projected CRS.
DEMANDVector (Any)Demand points. Whole points assigned to one facility each.
POP_FIELDField (Numeric)1/pointPopulation per demand point.
FACILITIESVector (Any)Fixed facilities with capacities.
FACILITY_IDFieldFacility identifier field.
CAPACITY_FIELDField (Numeric)Capacity in persons (≥ 0).
MAX_COSTDouble500.0Maximum catchment cost in map units.
OUT_DEMANDVector (Point)Demand allocation with facility, cost, and status.
OUT_FACILITIESVector (Point)Facility load with capacity, assigned, remaining, utilisation, status.

Output Description

Demand: facility (string), net_cost (double, −1 = uncovered), status (Assigned / Spilled / Uncovered), nearest (nearest facility ignoring capacity), covered (0/1). Facilities: facility, capacity, assigned, remaining, utilization, status (Full / Has space / Unused).

Interpretation Guide

Academic References

Daskin, M.S. (1995). Network and Discrete Location. Wiley. DOI: 10.1002/9781118032343

Pirkul, H. & Schilling, D.A. (1991). "The Capacitated Maximal Covering Location Problem with Backup Service." Annals of Operations Research, 18, 141–154. DOI: 10.1007/BF02023099 [Note: this is volume 18 from 1989; the Annals volume numbering was unusual.]

Current, J., Daskin, M. & Schilling, D. (2002). "Discrete Network Location Models." In: Drezner, Z. & Hamacher, H.W. (eds.), Facility Location: Applications and Theory, pp. 81–118. Springer. DOI: 10.1007/978-3-642-56082-8_3

Land-Use Allocation Optimizer

Processing ID: planx:landallocation

Overview

Assigns a land use to each parcel to maximise a multi-objective function while respecting target area constraints for each use. The objective has three terms: suitability (area-weighted per-parcel score from user-provided fields), compactness (reward for adjacent same-use parcels, per unit of shared boundary), and adjacency (reward or penalty for specific use pairs as neighbours). A lock field freezes pre-zoned parcels. The method is greedy construction (best suitability first) followed by local search of reassignments and swaps that respect targets. The output is a draft zoning map (parcels with allocated use) and a summary table (target vs. allocated area per use with mean suitability achieved).

Theoretical Background

Land-use allocation as multi-objective spatial optimisation

The land-use allocation problem is a generalisation of the quadratic assignment problem to spatial zoning: given N parcels each to be assigned one of K land uses, with per-parcel suitability scores and pairwise adjacency effects, find the assignment that maximises total suitability plus total spatial quality (compactness + desirable adjacencies) subject to area targets per use. The problem is NP-hard; the PlanX engine (engine/allocate.py) implements a fast construction heuristic with local improvement, not a global optimiser. Ligmann-Zielinska et al. (2008) provide a comprehensive review of land-use allocation optimisation methods.

Compactness as an urban design objective

Compactness is the most commonly imposed spatial objective in land-use plan optimisation because it operationalises a universal planning principle: like uses should cluster. Scattered single parcels of the same use are functionally equivalent to a zoned cluster but create fragmented urban form, longer infrastructure runs, and reduced walkability. The compactness term rewards each metre of shared boundary between same-use parcels, effectively subsidising clustering. The weight controls the suitability-compactness trade-off: at zero weight, the solver maximises pure suitability (scattered pattern); at high weight, it maximises compactness at the expense of suitability. The Pareto Front tool traces this trade-off systematically.

Adjacency rules as planning policy

Adjacency rules encode normative planning preferences: residential next to industry is undesirable (negative weight); residential next to green space is desirable (positive weight). The rules are symmetric (residential|industry = industry|residential) and optional. They operate as additions to the objective: each metre of shared boundary between two uses contributes the rule's value (positive or negative) to the total score. The rules are delimited by comma or semicolon, with the format useA|useB=value.

Mathematical Formulation

Let xp,u ∈ {0, 1} indicate parcel p assigned to use u. The multi-objective function is:

$$\max \quad w_{suit} \cdot \underbrace{\sum_{p,u} a_p \cdot S_{p,u} \cdot x_{p,u}}_{\text{suitability}} \;+\; \underbrace{\sum_{(p,q) \in E} l_{pq} \cdot C_{u,v} \cdot x_{p,u} \cdot x_{q,v}}_{\text{spatial: compactness + adjacency}} \tag{1}$$

subject to area targets (soft, via greedy construction + local search):

$$\sum_p a_p \cdot x_{p,u} \leq T_u \quad \forall u \tag{2}$$

where ap is parcel area, Sp,u is parcel p's suitability for use u, lpq is shared boundary length between parcels p and q, Cu,v is the compatibility matrix (diagonal = compactness weight if > 0, off-diagonal = adjacency rule values), and Tu is the target area for use u.

Parameters

ParameterTypeDefaultDescription
PARCELSVector (Polygon)Parcels or cells to allocate. Projected CRS required.
SUIT_FIELDSFields (Numeric)One suitability field per land use (0–1 or 0–100). Field names become use labels.
TARGETSStrings_residential=50000, ...Target area per use in map units squared. Key matches suitability field name exactly or by containment.
AREA_FIELDField (Numeric)geometry areaParcel area field. Default: computed from geometry.
LOCK_FIELDField(optional)Pre-assigned use name. Must match a suitability field name.
W_COMPACTDouble0.0Compactness weight per unit shared boundary (0 = off).
ADJACENCYString(optional)Adjacency rules: useA|useB=value. + attracts, − repels.
CONTIGUITYEnumSoftSoft (compactness weight) or Hard (single connected zone per use).
W_SUITABILITYDouble1.0Suitability weight relative to spatial terms (advanced).
OUT_PARCELSVector (Polygon)Allocated parcels with use, suitability, area, and lock flag.
OUT_SUMMARYTablePer-use summary: target, allocated, balance, count, mean suitability, status (Met/Short).

Output Description

Parcels: inherits source fields + alloc_use (string, empty = unassigned), alloc_suit (per-parcel suitability of assigned use), alloc_area (parcel area), locked (0/1 flag). Summary: use, target_area, alloc_area, balance, n_parcels, mean_suit, status (Met / Short / empty for unassigned).

Interpretation Guide

Academic References

Ligmann-Zielinska, A., Church, R.L. & Jankowski, P. (2008). "Spatial Optimization as a Generative Technique for Sustainable Multiobjective Land-Use Allocation." International Journal of Geographical Information Science, 22(6), 601–622. DOI: 10.1080/13658810701587495

Cao, K., Huang, B., Wang, S. & Lin, H. (2012). "Sustainable Land Use Optimization Using Boundary-Based Fast Genetic Algorithm." Computers, Environment and Urban Systems, 36(3), 257–269. DOI: 10.1016/j.compenvurbsys.2011.08.001

Aerts, J.C.J.H., Eisinger, E., Heuvelink, G.B.M. & Stewart, T.J. (2003). "Using Linear Integer Programming for Multi-Site Land-Use Allocation." Geographical Analysis, 35(2), 148–169. DOI: 10.1111/j.1538-4632.2003.tb01106.x

Land-Use Pareto Front

Processing ID: planx:paretoallocation

Overview

Maps the trade-off between suitability and compactness in land-use allocation by solving the allocation problem across a sweep of compactness weights (from zero = pure suitability to an auto-scaled upper bound where compactness dominates). For each weight, the tool records the area-weighted suitability achieved and the total shared same-use boundary (compactness). Solutions not dominated on both scores by any other form the Pareto front (non-dominated set). The knee — the front point furthest from the chord joining the extremes — is the mathematically best-balanced compromise. Three solutions are exportable: the knee (default), maximum suitability, or maximum compactness.

Theoretical Background

Pareto optimality in multi-objective spatial planning

A solution is Pareto optimal if no other feasible solution is at least as good on all objectives and strictly better on at least one (Pareto, 1906). In land-use allocation, the two objectives (suitability and compactness) are usually in tension: forcing parcels into compact zones requires overriding per-parcel suitability signals. The Pareto front maps this tension: each point on the front is a distinct allocation plan that is undominated — you cannot improve suitability without losing compactness, and vice versa. The front itself is the evidence base for the planning decision; the choice of which point on the front to adopt is a value judgement (Deb, 2001).

The knee as a compromise heuristic

The knee point — the point on the front with the maximum perpendicular distance from the line joining the two extreme solutions (pure suitability and maximum compactness) — is a formalisation of "best trade-off." It is the point where the marginal rate of transformation between the two objectives changes most sharply; beyond it, further gains in one objective come at increasingly disproportionate cost in the other. The knee is a suggestion, not a verdict. In planning practice, a preference for orderly urban form may justify selecting a point to the right of the knee (more compact, less suitable); a preference for market-responsive zoning may justify the left side (more suitable, less compact).

Mathematical Formulation

For compactness weight wi in the sweep {w0 = 0, w1, ..., wK−1}, the allocation engine produces solution i with scores:

$$S_i = \text{suitability}_i, \quad C_i = \text{compactness}_i \tag{1}$$

Normalised to [0, 1] for plotting:

$$\text{suit\_norm}_i = \frac{S_i - \min_j S_j}{\max_j S_j - \min_j S_j} \tag{2}$$ $$\text{compact\_norm}_i = \frac{C_i - \min_j C_j}{\max_j C_j - \min_j C_j} \tag{3}$$

Solution i is on the Pareto front if:

$$\nexists j: \big(S_j > S_i \land C_j \geq C_i\big) \lor \big(C_j > C_i \land S_j \geq S_i\big) \tag{4}$$

The knee is:

$$\text{knee} = \arg\max_i \left[ \text{on\_front}[i] \cdot \text{dist}\big((\text{suit\_norm}_i, \text{compact\_norm}_i),\ \overline{(1,0)(0,1)}\big) \right] \tag{5}$$

The weight range auto-scales. The upper bound wmax is computed from the pure-suitability solution: wmax = 6 × S0 / Ltotal, where Ltotal is the total shared parcel boundary length, ensuring that compactness can clearly dominate at the upper end.

Parameters

ParameterTypeDefaultDescription
PARCELSVector (Polygon)Parcels with suitability fields. Projected CRS.
SUIT_FIELDSFields (Numeric)One suitability field per land use.
TARGETSStrings_residential=50000, ...Target area per use. Key matches field name.
AREA_FIELDField (Numeric)geometry areaOptional parcel area field.
LOCK_FIELDField(optional)Pre-assigned use name for frozen parcels.
N_POINTSInteger9Number of weights to sample (2–25). More = smoother front.
W_MAXDouble0 (auto)Maximum compactness weight. 0 = auto-scale from data.
SOLUTIONEnumKneeWhich solution to export as parcel map: Knee, Max suitability, or Max compactness.
W_SUITABILITYDouble1.0Suitability weight relative to compactness (advanced).
OUT_FRONTTableFront table: one row per weight sample.
OUT_PARCELSVector (Polygon)Allocated parcels for the selected solution.

Output Description

Front: point (int, 0..N−1), w_compact (double), suitability, compactness, suit_norm, compact_norm (all double, 0–1), on_front (0/1), knee (0/1), selected (0/1), n_swaps, n_reassign. Parcels: inherits source fields + alloc_use, alloc_suit, alloc_area, sel_weight (the compactness weight of the exported solution).

Interpretation Guide

Academic References

Deb, K. (2001). Multi-Objective Optimization Using Evolutionary Algorithms. Wiley. [No standard DOI; the foundational text on multi-objective optimisation and the knee concept.]

Pareto, V. (1906). Manuale di Economia Politica. Societa Editrice Libraria. [Translated 1971 by A.M. Kelley. The origin of Pareto optimality.]

Ligmann-Zielinska, A., Church, R.L. & Jankowski, P. (2008). "Spatial Optimization as a Generative Technique for Sustainable Multiobjective Land-Use Allocation." IJGIS, 22(6), 601–622. DOI: 10.1080/13658810701587495

Das, I. & Dennis, J.E. (1998). "Normal-Boundary Intersection: A New Method for Generating the Pareto Surface in Nonlinear Multicriteria Optimization Problems." SIAM Journal on Optimization, 8(3), 631–657. DOI: 10.1137/S1052623496307510

Capacitated Facility Siting

Processing ID: planx:capacitatedsiting

Overview

Simultaneously selects sites and assigns demand under per-site capacity constraints — siting and sizing together. First, greedy construction: each step evaluates all unselected candidates by running a full capacitated allocation (nearest-with-spill, respecting capacities) and picks the candidate that maximises newly served demand. Then, Teitz-Bart vertex substitution: each selected site is swapped with each unselected candidate, re-running the full allocation, and the swap that increases served demand (or ties on served demand while reducing total cost) is accepted. Iterates until no improving swap is found (or max_iter). Existing facilities are fixed-open with their own capacities.

Theoretical Background

Capacitated facility location problems

The capacitated facility location problem (CFLP) is the extension of the p-median/covering models where facilities have finite capacity (Daskin, 1995). Unlike uncapacitated models where the only question is where, the CFLP asks where AND how big. The interaction between location and capacity is the key difficulty: a site with a good location but small capacity may be less valuable than a marginally worse location with large capacity, and the trade-off depends on the entire demand distribution. The CFLP is NP-hard; the greedy + Teitz-Bart heuristic implemented here produces solutions within 10–20% of optimal for typical urban instances (Current, Daskin & Schilling, 2002).

Greedy construction with embedded allocation

The distinguishing feature of the PlanX implementation is that each candidate evaluation runs a full capacitated allocation ( capacitated_assign), not a simplified scoring function. This is computationally expensive — O(p × C × full allocation cost) for construction, where C is the candidate count — but it guarantees that the marginal gain of each pick is the actual additional served demand under the capacity constraints, not an optimistic estimate that ignores the interaction between multiple constrained facilities. This is critical for siting decisions where capacity is scarce relative to demand.

Joint siting and sizing as a build programme

Unlike Facility Location Optimizer (which selects sites without considering how much capacity each should have), Capacitated Facility Siting produces a build programme: a ranked list of sites, each with the served population, utilisation rate, and marginal gain (how many more people are served because of this facility). The utilisation rate on each selected site indicates whether the assumed capacity is the binding constraint: near 1.0 means "if you can build bigger here, you should."

Mathematical Formulation

Let D(C×N) be the cost matrix, w(N) be demand weights, cap(C) be capacities. For a set of open sites S, the evaluation function runs capacitated_assign(D[S, :], w, cap[S]) and returns:

$$\text{served}(S) = \sum_{d: \text{assign}[d] \neq -1} w_d \tag{1}$$ $$\text{total\_cost}(S) = \sum_{d: \text{assign}[d] \neq -1} \text{cost}[d] \cdot w_d \tag{2}$$

Greedy construction picks the candidate c that maximises:

$$\text{gain}(c) = \text{served}(S \cup \{c\}) - \text{served}(S) \tag{3}$$

with total_cost as a tiebreaker (lower is better) when served demand is equal. Teitz-Bart swap replaces sS with cS if:

$$\text{served}(S \setminus \{s\} \cup \{c\}) > \text{served}(S) \quad \lor \quad \big(\text{served equal} \land \text{total\_cost lower}\big) \tag{4}$$

Selected indices are reported with marginal gain derived from the objective history: gaink = obj_history[k][0] − obj_history[k−1][0] for the k-th pick.

Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)Street network. Projected CRS. Optional cost field for time-based analysis.
DEMANDVector (Any)Demand points with population weights.
POP_FIELDField (Numeric)1/pointPopulation field.
CANDIDATESVector (Any)Candidate sites. Each must have a capacity and ID.
CANDIDATE_IDFieldCandidate identifier field.
CAPACITY_FIELDField (Numeric)Capacity per candidate site in persons (≥ 0).
EXISTINGVector (Any)(optional)Existing facilities fixed-open.
EXISTING_IDField(optional)Existing facility identifier.
EXISTING_CAP_FIELDField (Numeric)(optional)Existing facility capacity. Default: very large (essentially uncapacitated).
PInteger3Number of new facilities to site (≥ 1).
MAX_COSTDouble500.0Maximum travel cost (catchment limit) in map units.
COST_FIELDField (Numeric)lengthOptional cost field on network for time-based analysis.
OUT_SITESVector (Point)Selected (open) facilities with rank, load, utilisation, and gain.
OUT_ALLOCATIONVector (Line)Straight allocation lines from each assigned demand to its facility.
OUT_UNCOVEREDVector (Point)Demand points that could not be assigned (capacity or distance reasons).

Output Description

Sites: inherits candidate fields + facility (string label), rank (int, 0 for existing), load (assigned population), utilization (load/capacity), gain (marginal served demand added). Allocation: facility, net_cost (straight lines). Uncovered: copies of demand point features that received no assignment.

Interpretation Guide

Academic References

Daskin, M.S. (1995). Network and Discrete Location. Wiley. DOI: 10.1002/9781118032343

Teitz, M.B. & Bart, P. (1968). "Heuristic Methods for Estimating the Generalized Vertex Median." Operations Research, 16(5), 955–961. DOI: 10.1287/opre.16.5.955

Current, J., Daskin, M. & Schilling, D. (2002). "Discrete Network Location Models." In: Drezner, Z. & Hamacher, H.W. (eds.), Facility Location: Applications and Theory, pp. 81–118. Springer. DOI: 10.1007/978-3-642-56082-8_3

Church, R. & ReVelle, C. (1974). "The Maximal Covering Location Problem." Papers of the RSA, 32(1), 101–118. DOI: 10.1007/BF01942293

Melo, M.T., Nickel, S. & Saldanha-da-Gama, F. (2009). "Facility Location and Supply Chain Management — A Review." European Journal of Operational Research, 196(2), 401–412. DOI: 10.1016/j.ejor.2008.05.007

9. Equity

The Equity group translates the question "is this a fair city?" into quantitative metrics. Three tools build on the general-purpose inequality engine (engine/equity.py) — a pure-NumPy module that computes the Gini coefficient, Theil's T index (with additive between/within-group decomposition), the Atkinson index at user-specified inequality-aversion, Lorenz and concentration curves, Duncan & Duncan dissimilarity indices, and population-weighted cross-tabulations. Every metric is population-weighted by default (one person per unit if no weight field is provided), and all indices treat the input value as a non-negative "good" — negatives are clipped to zero.

The three tools form a diagnosis chain: Accessibility Equity produces the headline inequality numbers (Gini, Theil, the between-share that answers the environmental-justice question); Inequality Curves gives the distributional view with exportable Lorenz/Concentration curve points and the Atkinson welfare interpretation; Demographic Equity Cross-Tabs identifies who is over-represented in the worst-served quintile — the evidence that moves from aggregate inequality to actionable equity targeting.

Accessibility Equity (Gini / Theil)

Processing ID: planx:accessequity

Overview

Measures how fairly a spatial quantity — an access score, a travel time, a distance to the nearest facility — is distributed across the population. This is the distributional complement to every level-of-access tool in PlanX: the 15-Minute City Access Score tells you how much access the average resident has; Accessibility Equity tells you whether that access is concentrated in a few privileged locations or spread evenly across everyone.

The tool computes the full suite of population-weighted inequality indices from a single input layer of spatial units, each carrying a value (the access metric) and an optional population weight. When a group field is provided (district, income class, tenure type), Theil's T is additively decomposed into the share of inequality that exists between groups and the share that exists within groups — the decomposition that gives equity analysis its policy teeth: between-group inequality is addressable by place-based investment; within-group inequality requires household-level targeting.

Outputs include the annotated input units (with their population-weighted percentile rank, deviation from the mean, and an access-poverty flag) and a summary table with all indices for the full study area and per group.

Theoretical Background

The measurement of economic inequality has a century-long lineage in welfare economics and econometrics, but its application to spatial equity in urban planning is more recent — crystallising in the environmental justice literature of the 1990s and 2000s (Talen & Anselin, 1998; Lucas, 2012). Three intellectual traditions converge in this tool:

The Gini coefficient (Gini, 1912) is the most widely recognised inequality metric. Originally proposed as the mean absolute difference between all pairs of incomes divided by twice the mean, its geometric interpretation as twice the area between the Lorenz curve and the line of equality makes it visually intuitive. In spatial applications, Gini values above 0.4 are conventionally "high" (the OECD threshold for income inequality), but access to public goods tends to be more equal than income — a Gini of 0.2 for park access is typical in a reasonably planned city; 0.35 signals serious concentration.

Theil's T index (Theil, 1967) belongs to the Generalised Entropy class and is the only inequality measure that is additively decomposable by population subgroup without a residual term. Shorrocks (1980) proved that this property — the ability to write $T = T_{\text{between}} + T_{\text{within}}$ as an exact identity — is unique to the Generalised Entropy class. The decomposition answers the fundamental policy question: if we equalise group means (by investing in underserved districts), how much of the inequality disappears? If the between-share is 60%, place-based investment eliminates the majority of the problem. If it is 15%, the inequality is overwhelmingly within-group and requires household-level or individual-level targeting.

The P90/P10 ratio and coefficient of variation provide non-specialist translations: "the best-served tenth have 4.2 times the access of the worst-served tenth" is intelligible to a planning committee member who cannot interpret a Theil value of 0.18.

The population-weighting scheme is critical for spatial equity because spatial units (grid cells, census tracts, parcels) vary enormously in population. An unweighted Gini over grid cells would treat a cell with 1 person and a cell with 1,000 people identically. The PlanX engine uses the same weighted formulas as official income-inequality statistics (LIS, OECD), where each observation carries a sample weight. The percentile rank uses mid-rank for ties: when $k$ units share the same value, they all receive the rank at the midpoint of their collective mass.

Mathematical Formulation

Population-weighted Gini coefficient (sorted form, $O(n \log n)$):

$$G = \frac{\sum_{i=1}^{n} w_i \left( x_i \cdot W_i^{\text{below}} - S_i^{\text{below}} \right)}{W \cdot S} \tag{5}$$

where $W_i^{\text{below}} = \sum_{j: x_j < x_i} w_j$ is the population weight strictly below unit $i$ in the sorted order, $S_i^{\text{below}} = \sum_{j: x_j < x_i} w_j x_j$ is the corresponding cumulative value, $W = \sum w_i$, and $S = \sum w_i x_i$. This is equivalent to the mean-difference form but avoids the $O(n^2)$ pairwise comparison.

Population-weighted Theil's T index:

$$T = \frac{1}{W} \sum_{i=1}^{n} w_i \cdot \frac{x_i}{\mu} \cdot \ln\!\left(\frac{x_i}{\mu}\right) \tag{4}$$

where $\mu = \frac{1}{W} \sum w_i x_i$. Values of $x_i \leq 0$ contribute zero (as $\lim_{r \to 0^+} r \ln r = 0$); negatives are clipped to zero by the engine.

Additive decomposition (Shorrocks, 1980):

$$T = T_{\text{between}} + T_{\text{within}} \tag{3}$$ $$T_{\text{between}} = \sum_{g=1}^{G} s_g \cdot \ln\!\left(\frac{\mu_g}{\mu}\right) \tag{2}$$ $$T_{\text{within}} = \sum_{g=1}^{G} s_g \cdot T_g \tag{1}$$

where $s_g = (W_g \cdot \mu_g) / (W \cdot \mu)$ is group $g$'s share of the total value (not its population share), $\mu_g$ is the group's weighted mean, and $T_g$ is the Theil index computed within group $g$ alone. The between-share $T_{\text{between}} / T$ is the headline environmental-justice number.

P90/P10 ratio: $\text{P90/P10} = Q(0.9) / Q(0.1)$, where $Q(q)$ is the population-weighted $q$-quantile. Zero if $Q(0.1) = 0$ (all value concentrated above the 10th percentile).

Input Data Requirements

InputTypeRequiredNotes
Units layerVector (any geometry)YesSpatial units carrying the value to be measured — grid cells, census tracts, building centroids, parcel points. Each feature must have a numeric value and optionally a population weight and group label.
Value fieldNumericYesThe per-unit value whose distribution is being measured. Typically the output of an access-score tool (nearest-facility cost, 15-minute score, green-space distance).
Population fieldNumericNoPer-unit population weight. Defaults to 1 per unit (unweighted). For census tracts, use total population. For grid cells, use the dasymetrically allocated population from the Density Grid tool.
Group fieldAnyNoCategorical field for between/within decomposition. District name, income quintile, tenure type, urban/rural indicator. Without it, the decomposition is skipped and the Theil is reported as a single number.

Parameters

ParameterTypeDefaultDescription
INPUTVector (Any)Units with a numeric value field.
VALUE_FIELDField (Numeric)Per-unit value (access score, travel time, distance). Must be numeric and finite — NULL values skip the feature.
POP_FIELDField (Numeric)(optional)Population weight per unit. NULL or negative → weight = 0. If all weights sum to zero, each unit counts as 1.
GROUP_FIELDField(optional)Group identifier for decomposition. String or numeric — cast to string internally.
DIRECTIONEnum"Higher is better" (0)0 = the value is a "good" (access score, green space). 1 = the value is a "bad" (travel time, distance). Affects the access-poverty flag (below vs. above threshold).
POVERTYDouble(optional)Access-poverty threshold. For "higher is better": population share with value below this. For "lower is better": population share with value above. NULL = no poverty flag.
OUT_POINTSVector (same geometry)Input units annotated with eq_value, pct_rank, dev_mean, poverty.
OUT_SUMMARYTableSummary table: one "ALL" row plus one row per group.

Output Description

Units layer (OUT_POINTS)

FieldTypeDescription
eq_valueDoubleThe value as read (clipped to non-negative for inequality indices; raw for display).
pct_rankDoublePopulation-weighted percentile rank 0–100. 0 = worst-off (lowest value, for a "good"); 100 = best-off. Mid-rank for ties. Map this field — the bottom 10% are the structurally underserved.
dev_meanDoubleValue minus the population-weighted mean. Positive = above average; negative = below. Use for a diverging colour map centred at zero.
povertyInteger0 = not in access poverty; 1 = in access poverty (below threshold for "higher is better" direction, or above threshold for "lower is better"). NULL if the POVERTY parameter was not set.

Summary table (OUT_SUMMARY)

FieldTypeDescription
scopeString"ALL" or the group label (from the group field).
populationDoubleTotal population weight in this scope.
meanDoublePopulation-weighted mean value.
medianDoublePopulation-weighted median (50th percentile). Compare with mean — mean > median signals right-skew (a few units have very high access).
giniDoubleGini coefficient 0–1. Higher = more inequality.
theilDoubleTheil's T index. 0 = equality; no upper bound in principle but rarely exceeds 1 in spatial applications.
theil_btwDoubleBetween-group Theil (only on "ALL" row; 0 on per-group rows).
theil_wthDoubleWithin-group Theil.
cvDoubleCoefficient of variation = std / |mean|. 0 = equality.
p90_p10DoubleP90/P10 ratio. Plain-language inequality: "the best-served tenth has X times the access of the worst-served."
pov_shareDoubleFraction of population in access poverty (0–1). NULL if threshold not set.

Symbolic Representation

Map pct_rank with a sequential multi-hue ramp (Plasma, 5 quantile classes: 0–20 = worst-off, dark; 80–100 = best-off, bright yellow). For publication, use a bivariate scheme: pct_rank on one panel and pop (population weight) on the other — a cell that is both low-rank AND high-population is a priority intervention site. Map poverty = 1 cells in red, opacity 60%, over a light basemap for the deprivation landscape. The summary table is best presented as a formatted table with the "ALL" row at the top and groups in descending order of between- group contribution.

Interpretation Guide

Diagnostic benchmarks

IndexLowModerateHighInterpretation
Gini (access)<0.200.20–0.35>0.40>0.4 = a few places monopolise the access. <0.2 = well-distributed — but check the mean: perfect equality at a miserable level (mean = 2 on a 0–100 scale) is not a goal.
Theil between-share<0.150.15–0.40>0.60>0.6 = most inequality is between groups — group-targeted investment will reduce it substantially. <0.15 = inequality is within-group — place-based policy will miss it.
P90/P10<1.51.5–3.0>4.0A ratio >4.0 is hard to justify for any publicly provided service. Even 3.0 warrants scrutiny.

Cross-references with other PlanX tools

Pitfalls

Academic References

Gini, C. (1912). "Variabilita e mutabilita: Contributo allo studio delle distribuzioni e delle relazioni statistiche." Studi Economico-Giuridici dell'Universita di Cagliari, 3(2), 3–159. classic work, original Italian publication, no DOI]

Theil, H. (1967). Economics and Information Theory. North-Holland. (Chapter 4: the derivation of the T index and its decomposition by population subgroup.) classic monograph, no DOI assigned]

Shorrocks, A.F. (1980). "The Class of Additively Decomposable Inequality Measures." Econometrica, 48(3), 613–625. DOI: 10.2307/1913126

Talen, E. & Anselin, L. (1998). "Assessing Spatial Equity: An Evaluation of Measures of Accessibility to Public Playgrounds." Environment and Planning A, 30(4), 595–613. DOI: 10.1068/a300595

Lucas, K. (2012). "Transport and Social Exclusion: Where Are We Now?" Transport Policy, 20, 105–113. DOI: 10.1016/j.tranpol.2012.01.013

Sen, A. (1973). On Economic Inequality. Clarendon Press. (The foundational philosophical treatment of inequality measurement, including the normative basis of the Atkinson index.) DOI: 10.1093/0198281935.001.0001

Inequality Curves (Lorenz & Atkinson)

Processing ID: planx:inequalitycurves

Overview

Draws the Lorenz curve — the full distributional map of how a value is spread across the population — and computes the Atkinson inequality index at multiple levels of inequality aversion. This is the visual complement to the headline numbers from Accessibility Equity: while the Gini is a single scalar, the Lorenz curve reveals where the inequality lives — at the bottom tail, spread through the middle, or concentrated at the top. The Atkinson index adds a normative dimension: it tells you how much total value society would be willing to sacrifice to equalise the distribution, depending on how much society cares about the worst-off.

An optional rank field (deprivation index, income decile, vulnerability score) converts the Lorenz curve into a concentration curve: instead of ordering units by the value itself, they are ordered by the external rank, revealing whether the value systematically concentrates on the advantaged or disadvantaged end. The concentration index is signed — negative when the value accrues to the disadvantaged, positive when to the advantaged — and is the most pointed single-number equity diagnostic in the PlanX suite.

Outputs: a curve-points table ($n+1$ rows per cumulative step, exportable to any charting tool) and a summary table with the Gini and Atkinson indices at $\varepsilon \in \{0.5, 1.0, 2.0\}$ plus the user's custom $\varepsilon$.

Theoretical Background

The Lorenz curve (Lorenz, 1905). Max Otto Lorenz proposed plotting the cumulative share of a quantity against the cumulative population share as a graphical device for comparing inequality across distributions. The curve always starts at $(0, 0)$ and ends at $(1, 1)$, bowing below the 45-degree line of perfect equality. The further the bow, the greater the inequality. The curve is the unequivocal visual standard for distributional comparison: curves that do not cross are unambiguously comparable (the curve closer to the diagonal dominates in the Lorenz sense), while crossing curves require a normative judgment about which part of the distribution matters more.

The concentration curve is the Lorenz curve's externally ordered counterpart. When units are ordered by a variable other than the one being measured, the resulting concentration curve reveals systematic association: if access to green space rises with income, the concentration curve bows below the Lorenz curve (the same green space would appear more unequal when ordered by income than by access itself). The concentration index (Kakwani, 1977) — twice the area between the concentration curve and the equality line — is the standard metric in health economics for measuring socioeconomic inequality in health outcomes.

The Atkinson index (Atkinson, 1970) is the most explicitly normative inequality measure. Atkinson rejected the view that a single number (Gini, Theil) could adequately summarise inequality, arguing that any inequality measure implicitly embeds a social welfare function with a particular inequality-aversion parameter $\varepsilon$. His index explicitly surfaces this parameter:

At $\varepsilon = 0$, the planner is inequality-neutral — only the mean matters (Atkinson = 0 regardless of distribution). At $\varepsilon = 0.5$, mild aversion — moderate inequality registers. At $\varepsilon = 1.0$, the standard value in applied welfare economics — $A(1) = 0.25$ means "society would give up 25% of total value to equalise the distribution and be no worse off." At $\varepsilon = 2.0$, strong aversion — the lower tail dominates, the index explodes if anyone is near zero.

Comparing Atkinson across $\varepsilon$ levels is a sensitivity test for the location of pain: if $A(0.5) \approx A(2.0)$, the inequality is spread evenly — the bottom tail is not unusually deprived. If $A(0.5) \ll A(2.0)$ — e.g., 0.15 jumping to 0.65 — the inequality is concentrated in the very bottom tail: a few units have almost nothing while the rest are roughly equal. This pattern is the statistical signature of exclusion rather than dispersion and demands a different policy response (targeted inclusion vs. general redistribution).

Mathematical Formulation

Lorenz / concentration curve points. Units are sorted by their value $x_{(1)} \leq x_{(2)} \leq \cdots \leq x_{(n)}$ (or by the rank field, for a concentration curve). At step $k$:

$$P_k = \frac{\sum_{i=1}^{k} w_{(i)}}{\sum_{i=1}^{n} w_i}, \qquad L_k = \frac{\sum_{i=1}^{k} w_{(i)} x_{(i)}}{\sum_{i=1}^{n} w_i x_i} \tag{5}$$

with $P_0 = L_0 = 0$. The curve is the set of points $\{(P_k, L_k)\}_{k=0}^{n}$.

Gini from Lorenz curve (trapezoidal rule):

$$G = 1 - \sum_{k=1}^{n} (P_k - P_{k-1}) \cdot (L_k + L_{k-1}) \tag{4}$$

Equals the standard Gini when the ordering is by value itself; equals the concentration index when ordered by an external rank (which may be negative if the value falls as rank rises).

Atkinson index. For $\varepsilon \geq 0$, $\varepsilon \neq 1$:

$$\text{EDE} = \left( \frac{\sum_{i} w_i x_i^{1-\varepsilon}}{\sum_i w_i} \right)^{\frac{1}{1-\varepsilon}} \tag{3}$$ $$A(\varepsilon) = 1 - \frac{\text{EDE}}{\mu} \tag{2}$$

For $\varepsilon = 1$, the equally-distributed-equivalent level is the weighted geometric mean:

$$\text{EDE}(1) = \exp\!\left( \frac{\sum w_i \ln x_i}{\sum w_i} \right) \tag{1}$$

If any unit with positive weight has $x_i \leq 0$ when $\varepsilon \geq 1$, the geometric mean collapses to zero and $A(1) = 1$ — the index signals absolute deprivation. Values are clipped to non-negative before computation.

Input Data Requirements

InputTypeRequiredNotes
Units layerVector (any geometry)YesSame as Accessibility Equity. Each feature carries a value.
Value fieldNumericYesThe value to distribute. Must be a non-negative "good." For a "bad" like travel time, transform first (e.g., $1/t$ or $\max\_time - t$).
Population fieldNumericNoPer-unit weight. Defaults to 1.
Rank fieldNumericNoExternal ordering variable for the concentration curve. Deprivation rank, income decile, vulnerability index. Anything that answers "does the value favour the advantaged?"

Parameters

ParameterTypeDefaultDescription
INPUTVector (Any)Units with a numeric value field.
VALUE_FIELDField (Numeric)Non-negative value per unit. NULL → skipped.
POP_FIELDField (Numeric)(optional)Population weight. Same semantics as Accessibility Equity.
RANK_FIELDField (Numeric)(optional)External rank for concentration curve. Must be numeric and finite — NULL → feature skipped. When provided, the curve is a concentration curve and the concentration index is reported.
EPSILONDouble1.0Inequality-aversion parameter for the Atkinson index. 0 = no aversion; 1 = standard (geometric mean); 2 = strong lower-tail weight. Values > 2 are allowed but the index becomes highly sensitive to near-zero values.
OUT_CURVETableCurve-points table with $n+1$ rows (from origin to 100/100).
OUT_SUMMARYTableMetric/value summary table.

Output Description

Curve table (OUT_CURVE)

FieldTypeDescription
pointIntegerPoint index 0 to $n$ (0 = origin, $n$ = 100/100).
pop_shareDoubleCumulative population share at this point. The x-axis for charting.
value_shareDoubleCumulative value share. The y-axis. The curve bows below the equality line.
equalityDoubleThe equality line value (= pop_share). For charting the 45-degree reference.
gapDoublepop_share minus value_share. The vertical distance to equality at each point. Maximum gap is the Kolm index (the point where the gap is largest — the "most unfair" percentile).

Summary table (OUT_SUMMARY)

FieldTypeDescription
metricStringLabel: "Units (n)", "Population", "Mean value", "Gini", "Atkinson (epsilon=X)", "Concentration index" (if rank given).
valueDoubleThe metric value.

Symbolic Representation

The curve table is designed for charting outside QGIS — export to CSV and plot in any tool (Python/matplotlib, R/ggplot2, Excel). Plot pop_share on x, value_share and equality on y as two line series. The filled area between them is the Gini area. For multi-scenario comparison, plot Baseline and Proposed curves on the same axes with distinct colours — the curve that moves toward the diagonal is distributional progress. For the concentration curve with a rank, a curve that bows above the equality line (value concentrated on the low-rank/disadvantaged end) is the equity goal; a curve bowing below signals regressive distribution.

Interpretation Guide

The three Atkinson regimes

Concentration index reading

A negative concentration index (value concentrates on the disadvantage/lower- ranked end) is the equity-positive finding: the good is progressive. A positive concentration index — the value rises with rank — is regressive: the better-off enjoy more of the good. The magnitude matters less than the sign for policy direction, but $|\text{CI}| > 0.3$ is "strong" by the health-economics convention (O'Donnell et al., 2008).

Cross-references

Academic References

Lorenz, M.O. (1905). "Methods of Measuring the Concentration of Wealth." Publications of the American Statistical Association, 9(70), 209–219. DOI: 10.2307/2276207

Atkinson, A.B. (1970). "On the Measurement of Inequality." Journal of Economic Theory, 2(3), 244–263. DOI: 10.1016/0022-0531(70)90039-6

Kakwani, N. (1977). "Measurement of Tax Progressivity: An International Comparison." Economic Journal, 87(345), 71–80. DOI: 10.2307/2231833

O'Donnell, O., van Doorslaer, E., Wagstaff, A., & Lindelow, M. (2008). Analyzing Health Equity Using Household Survey Data. World Bank. (A practical guide to computing concentration indices with survey weights — the methodological template for the concentration-curve output.) DOI: 10.1596/978-0-8213-6933-3

Jenkins, S.P. & Van Kerm, P. (2009). "The Measurement of Economic Inequality." In: Salverda, W., Nolan, B. & Smeeding, T.M. (eds.), Oxford Handbook of Economic Inequality. Oxford University Press. DOI: 10.1093/oxfordhb/9780199606061.013.0003

Demographic Equity Cross-Tabs

Processing ID: planx:equitycrosstab

Overview

Answers the question that every equity analysis must answer: who is under-served? While Accessibility Equity gives aggregate inequality numbers and Inequality Curves gives the distributional shape, Demographic Equity Cross-Tabs names the groups. It cross-tabulates any per-unit value by one or two population subgroup fields, dividing the value axis into population-weighted quantile classes (default: quintiles — each class holds exactly 20% of the population). For every cell (group × value class), it reports the representation ratio: the group's share of that class's population divided by its share of the total population.

What the representation ratio means. A ratio of 1.0 means the group is proportionally represented — if the group is 30% of the population, it is ~30% of the people in this value class. A ratio of 2.0 means the group is twice as present as its population share would predict. A ratio of 2.0 in class 1 (the lowest-value quintile) is the environmental-justice red flag: this group carries double its share of the deprivation. A ratio of 0.33 in class 1 means the group is largely spared — only one-third its expected share sits in the worst quintile.

Per group, the tool also reports descriptive statistics (population share, value share, weighted mean, P10, median, P90, min, max), the within-group Gini coefficient, and the Duncan & Duncan dissimilarity index of the group against the rest of the population across the value classes. The dissimilarity index (0–1) sizes segregation — how different the group's distribution across value classes is from everyone else's. A value of 0.3 means 30% of the group would need to shift value classes to match the overall distribution — a structural pattern, not random variation.

A second group field enables two-way cross-tabulation: the groups become every "A | B" combination, enabling intersectional analysis (e.g., low-income × minority neighbourhoods). The units output annotates each input feature with its value class and its group's representation ratio in that class, enabling spatial mapping of over-representation in deprivation.

Theoretical Background

Cross-tabulation of welfare by demographic subgroup is the standard method in environmental justice (EJ) screening. The US EPA's EJSCREEN and California's CalEnviroScreen both operationalise EJ as disproportionate exposure of protected subgroups (by race, income, language) to environmental hazards. The representation ratio is favoured over simple proportional comparison because it controls for group size: a small group that appears to have many people in the worst quintile might simply be a large group in absolute terms. The ratio normalises this — 2.0 means "over-represented" regardless of group size.

Duncan & Duncan's dissimilarity index (1955) is the foundational measure of residential segregation. Originally proposed to measure racial segregation across census tracts, it has been adapted to any cross-tabulation where the question is "how evenly is this group spread across these categories?" Massey & Denton (1988) validated it as one of five dimensions of segregation (the "evenness" dimension) and established the interpretive benchmarks still used today: < 0.3 = low segregation; 0.3–0.6 = moderate; > 0.6 = high. In the equity-cross-tab context, "segregation" means the group's experience is structurally different from the population average — its members cluster in particular value classes rather than being spread evenly.

The population-weighted quantile approach (each class holds equal population) is preferred over equal-interval classes (each class spans the same value range) because it prevents classes from becoming empty or near-empty when the value is heavily skewed. With fixed breaks like "0–20, 20–40, 40–60, 60–80, 80–100," an access score that never exceeds 30 would leave the top three classes empty, making representation ratios meaningless. Quantile classes guarantee a populated cross-tabulation regardless of the value distribution.

Mathematical Formulation

Population-weighted quantile class boundaries. For $Q$ classes (default $Q = 5$), the inner edges are the weighted quantiles at probabilities $\{1/Q, 2/Q, \ldots, (Q-1)/Q\}$:

$$e_q = Q\!\left(\frac{q}{Q}\right), \quad q = 1, \ldots, Q-1 \tag{5}$$

Unit $i$ is assigned to class $c_i = 0, \ldots, Q-1$ where $c_i$ is the position in the sorted breaks: $c_i = q-1$ for $x_i \in (e_{q-1}, e_q]$, with $e_0 = -\infty$ and $e_Q = +\infty$.

Representation ratio. For group $g$ in value class $k$:

$$R_{g,k} = \frac{\text{pop}_{g,k} / \sum_k \text{pop}_{g,k}}{\text{pop}_{*,k} / \sum_k \text{pop}_{*,k}} = \frac{\text{pop}_{g,k}}{\text{pop}_{*,k}} \cdot \frac{W}{W_g} \tag{4}$$

where $\text{pop}_{g,k}$ is the weighted population of group $g$ in class $k$, $\text{pop}_{*,k}$ is the total weighted population in class $k$, $W_g$ is the total weight of group $g$, and $W$ is the grand total weight. $R_{g,k} = 1$ for proportional representation; $> 1$ for over-representation; $< 1$ for under-representation. NaN when either the class or the group is empty.

Duncan & Duncan dissimilarity index. For group $g$ against the rest of the population across $Q$ value classes:

$$D_g = \frac{1}{2} \sum_{k=1}^{Q} \left| \frac{\text{pop}_{g,k}}{W_g} - \frac{\text{pop}_{*,k} - \text{pop}_{g,k}}{W - W_g} \right| \tag{3}$$ $$0 \leq D_g \leq 1 \tag{2}$$

Within-group Gini. For group $g$ with values $\{x_i : \text{group}(i) = g\}$ and weights $w_i$:

$$G_g = \frac{\sum_{i \in g} w_i \left( x_i \cdot W_i^{\text{below}(g)} - S_i^{\text{below}(g)} \right)}{W_g \cdot S_g} \tag{1}$$

Input Data Requirements

InputTypeRequiredNotes
Units layerVector (any)YesSame as Accessibility Equity. Must have a value, a group label, and ideally a population weight.
Value fieldNumericYesThe value to cross-tabulate.
Group fieldAnyYesDemographic category. District, income bracket, tenure, ethnicity, age-group — the "who" of the equity question.
Second group fieldAnyNoFor two-way cross-tabs. Groups become "A | B" combinations.
Population fieldNumericNoPer-unit weight. Defaults to 1.

Parameters

ParameterTypeDefaultDescription
INPUTVector (Any)Units with value and group fields.
VALUE_FIELDField (Numeric)Per-unit value. NULL → skipped.
GROUP_FIELDFieldPrimary demographic group identifier. Empty labels → skipped.
GROUP_FIELD_BField(optional)Second group field for intersectional cross-tabs.
POP_FIELDField (Numeric)(optional)Population weight. Same semantics as other equity tools.
N_CLASSESInteger5Number of population-weighted quantile classes. 2–10. 5 = quintiles (standard); 4 = quartiles; 10 = deciles (fine detail, smaller cell populations).
BREAKSString"" (use quantiles)Fixed class breaks as comma-separated numbers, e.g. "50, 100, 150". Overrides N_CLASSES. Useful for threshold-based classification (policy standards).
OUT_CELLSTableCross-tabulation cells: one row per group × class.
OUT_GROUPSTablePer-group summary statistics.
OUT_UNITSVector (same)Input units annotated with v_class, class_label, cell_rep.

Output Description

Cross-tab cells (OUT_CELLS)

FieldTypeDescription
groupStringGroup label (or "A | B" for two-way).
v_classIntegerValue class index 1..Q (1 = lowest values = worst-off).
class_labelStringHuman-readable class description: "Q1 (lowest)" or "< 50".
popDoubleWeighted population of this group in this class.
class_shareDoubleGroup's share of this class's total population (0–1).
rep_ratioDoubleRepresentation ratio. NaN if group or class is empty.

Group summary (OUT_GROUPS)

FieldTypeDescription
groupStringGroup label.
popDoubleTotal weighted population.
pop_shareDoubleGroup's share of total population.
val_shareDoubleGroup's share of total value (sum of $w_i x_i$). If val_share < pop_share, the group gets less than its population would predict.
mean, p10, median, p90DoubleWeighted distribution statistics. Compare p10 across groups — equal means with unequal P10s signal that the group's worst-off are much worse off.
giniDoubleWithin-group Gini. High = even within this group, the value is unequally distributed.
dissimDoubleDuncan & Duncan dissimilarity index versus the rest.

Units with class (OUT_UNITS)

FieldTypeDescription
v_classIntegerValue class of this unit (1 = lowest/worst).
class_labelStringLabel for the class.
cell_repDoubleRepresentation ratio of this unit's group in this unit's value class. NaN if the group or class is empty. Map this field — high values in class 1 = the spatial pattern of disproportionate deprivation.

Interpretation Guide

The equity-reading protocol

  1. Go to class 1 (worst-served). Scan rep_ratio for values above 1.5. These are the groups carrying disproportionate deprivation. A ratio above 2.0 is hard to explain away — the group is at least twice as concentrated in the lowest-value quintile as chance would predict.
  2. Check group populations. Small groups make noisy ratios. A ratio of 3.0 based on 12 people in a group of 50 is less robust than a ratio of 1.8 based on 12,000 in 50,000. Always read the pop field alongside rep_ratio — flag ratios based on < 100 weighted population as tentative.
  3. Read the dissimilarity index. dissim > 0.3 means the group's distribution is substantially different from the population average. Values > 0.5 signal deep structural segregation across the value classes — this group's experience of the city is systematically, not randomly, different.
  4. Compare group means AND P10s. Equal means can conceal very different lower tails. If Group A and Group B both have a mean access score of 60, but Group A's P10 is 25 and Group B's P10 is 45, Group A's worst-off decile is nearly half as well-served as Group B's — a finding the mean alone would bury.
  5. Map class 1 units. On the OUT_UNITS layer, filter to v_class = 1 and the over-represented group, and map in a single colour. Equity findings only persuade when they become places on the map. A planning committee member may dispute a table cell but cannot dispute a cluster of underserved neighbourhoods on a satellite basemap.

Cross-references

Academic References

Duncan, O.D. & Duncan, B. (1955). "A Methodological Analysis of Segregation Indexes." American Sociological Review, 20(2), 210–217. DOI: 10.2307/2088328

Massey, D.S. & Denton, N.A. (1988). "The Dimensions of Residential Segregation." Social Forces, 67(2), 281–315. DOI: 10.1093/sf/67.2.281

Talen, E. (2001). "School, Community, and Spatial Equity: An Empirical Investigation of Access to Elementary Schools in West Virginia." Annals of the Association of American Geographers, 91(3), 465–486. DOI: 10.1111/0004-5608.00254

Walker, G. (2012). Environmental Justice: Concepts, Evidence and Politics. Routledge. (Chapter 4: "Evidence of Environmental Inequality" — the methodological framework for the EJ cross-tabulation approach.) DOI: 10.4324/9780203610671

US EPA. (2019). "EJSCREEN Technical Documentation." U.S. Environmental Protection Agency. (The operational EJ screening tool that standardised the representation-ratio approach for environmental-justice analysis.) government technical documentation, no DOI] Available at: epa.gov/ejscreen

11. Transit

The Transit group lifts public transport data from the raw GTFS schedule into spatial analysis. Three tools form a pipeline: GTFS Import validates and geocodes the feed, producing stop layers and route summaries for immediate visual audit; Transit Frequency Map answers "how often does it come?" for any time window on any service day — the service-intensity view that separates genuine frequent corridors from paper routes; Transit Travel-Time Access computes door-to-door travel times using walking on the street network plus timetable riding via a RAPTOR-style earliest-arrival algorithm, answering the rider's question: "how long does it actually take?" The entire pipeline is pure Python with zero external libraries beyond NumPy — GTFS reading, service-day resolution, and timetable search all run inside QGIS.

GTFS Import and Service Stats

Processing ID: planx:gtfsimport

Overview

Loads a GTFS (General Transit Feed Specification) zip archive into QGIS as two layers and validates the feed on import. GTFS is the de-facto global standard for publishing public transport schedules in open data — over 10,000 transit agencies worldwide publish GTFS feeds, covering buses, metros, trams, ferries, and cable-cars. The tool parses the four required files (stops.txt, routes.txt, trips.txt, stop_times.txt) plus the calendar files, resolves service-day logic (honouring calendar exceptions), and computes per-stop daily departure counts and per-route service-span statistics.

Times past midnight (e.g., 25:10:00 = 1:10 AM next day = 90,600 seconds since midnight) are handled natively as seconds — the engine never converts to datetime objects, avoiding the standard GTFS-parsing pitfall of incorrectly assigning overnight services to the wrong calendar day. The service day defaults to the feed's first active day (the earliest calendar-start date whose weekday is served), which the engine determines automatically unless the user specifies a YYYYMMDD date. Validation errors — missing required files, non-numeric coordinates, malformed time strings — are surfaced as clear exceptions naming the offending field.

This tool is the mandatory first step before Transit Frequency Map or Transit Travel-Time Access: it validates the feed and produces the stop layer (the geocoded front door for all downstream transit analysis in PlanX) and the route table (the service-coverage diagnostic that separates real transit from paper routes).

Theoretical Background

GTFS was originally developed by Google and TriMet (Portland, Oregon) in 2005 as "Google Transit" and was released as an open specification in 2006. It has since become the universal interchange format for public transport schedule data, maintained as a de-facto standard by MobilityData International rather than by any standards body. The specification defines a relational model of routes, trips, stops, and scheduled times that can represent any fixed-route transit service.

The critical validation challenge in GTFS parsing is time handling. The specification permits times to exceed 24:00:00 (i.e., 25:10:00 for a bus departing at 1:10 AM the next day), meaning that times within a trip are monotonically increasing seconds from a "service day midnight" origin. Converting to datetime objects requires associating each time with a date, which the specification does not directly provide for individual stop-times (only for the service day as a whole via calendar.txt). PlanX's approach — keeping all times as integer seconds and resolving service days independently — avoids this date-association problem entirely. The parse_time function accepts hours up to 30 (accommodating even extreme overnight services) and validates minutes and seconds strictly (0–59).

Service-day resolution follows the GTFS calendar logic exactly: a service runs on a given day if calendar.txt lists that day's weekday as active AND the day falls between the service's start and end dates, AND no calendar_dates.txt exception removes it (exception_type=2), OR calendar_dates.txt explicitly adds it (exception_type=1). The engine's active_services function implements this logic in ~15 lines, resolving the service set for a given YYYYMMDD date including weekday calculation from the Gregorian calendar.

Mathematical Formulation

Time to seconds. For a GTFS time string $t = h:mm:ss$ where $h \in [0, 30]$, $m \in [0, 59]$, $s \in [0, 59]$:

$$t_{\text{sec}} = h \cdot 3600 + m \cdot 60 + s \quad [\text{seconds since midnight}] \tag{3}$$

Stop-frequency summary. For a service day with active service IDs $\mathcal{S}$, stop $s$'s departures count:

$$D_s = \left| \{ (trip, stop\_seq) : \text{trip.service} \in \mathcal{S},\; t_{\text{start}} \leq dep_{trip, seq} < t_{\text{end}},\; seq \neq last \} \right| \tag{2}$$ $$N_{\text{routes}}(s) = \left| \{ route\_id : \exists trip \; \text{serving} \; s \; \text{in window} \} \right| \tag{1}$$

The window defaults to the full service day (0:00 to 30:00 / 06:00 next day) for GTFS Import, providing the 24-hour presence count. Transit Frequency Map applies a user-specified window (e.g., 07:00–09:00 AM peak).

Input Data Requirements

InputTypeRequiredNotes
GTFS feedZIP fileYesValid GTFS zip containing stops.txt, routes.txt, trips.txt, stop_times.txt, and at least one of calendar.txt or calendar_dates.txt. Most transit agencies provide this as a direct download from their open-data portal.
Service dayString (YYYYMMDD)NoEmpty = the engine picks the feed's first active day. Weekday vs. weekend schedules can differ by 50% of trips — always verify which day is being analysed.

Where to obtain GTFS feeds: Transit agency open-data portals (e.g., TransitFeeds.com aggregates hundreds), national open-data platforms, or direct from the agency website. Always check the feed's validity period — some agencies publish feeds covering only the current timetable period and they expire.

Parameters

ParameterTypeDefaultDescription
FILEFile (ZIP)GTFS zip archive. Must contain the four required files. The tool validates and reports specific errors for missing files, missing fields, or malformed data.
DAYString"" (first active)Service day as YYYYMMDD or YYYY-MM-DD. Hyphens are stripped automatically. Empty = engine finds the first day with any service via first_service_day.
OUT_STOPSVector (Point)Transit stops in WGS84 (EPSG:4326). GTFS coordinates are always WGS84 by specification.
OUT_ROUTESTableRoute summary with trips, first/last departure, and maximum stop sequence on the chosen day.

Output Description

Stops layer (OUT_STOPS)

FieldTypeDescription
stop_idStringAgency stop identifier as published in stops.txt.
nameStringStop name. Falls back to stop_id if the name field is empty (some feeds omit stop_names on minor stops).
departuresIntegerTotal scheduled departures from this stop on the chosen service day (full 24h window). The raw presence count — sort descending to identify the network's true hubs.
n_routesIntegerNumber of distinct route_ids serving this stop on the chosen day. Stops with n_routes > 1 form the transfer skeleton of the network.

Route summary (OUT_ROUTES)

FieldTypeDescription
route_idStringAgency route identifier.
nameStringRoute short name + long name, combined: "M1 - City Centre to Airport".
modeStringTransit mode from the standard GTFS route_type codes: Tram, Metro, Rail, Bus, Ferry, Cable tram, Aerial lift, Funicular, Trolleybus, Monorail. "Other" for unknown codes.
n_tripsIntegerNumber of trips operated by this route on the chosen day. 4 or fewer = paper route or peak-only; 50+ = genuine frequent corridor.
first_depStringEarliest departure time (HH:MM, 24h).
last_arrStringLatest arrival time. A route whose last arrival is 19:00 does not serve evening shifts.
n_stopsIntegerMaximum number of stops in the longest trip pattern on this route. Diagnostic for express vs. local service.

Interpretation Guide

Pre-analysis validation checklist

  1. Verify stop locations on a basemap. Agency coordinate bugs are common — stops in the ocean, on airport runways, or offset by hundreds of metres. This is the most frequent GTFS quality problem. A 30-second visual scan against OpenStreetMap catches most errors.
  2. Check the service day. Weekday and weekend timetables can differ by half the trips. If the engine auto-selected a day, verify it is the day you intended. A Saturday feed analysed as a weekday will understate service by 30–50%.
  3. Scan the route table's n_trips. Sort descending. Routes with < 5 trips/day are not meaningful service — they are school runs, peak-hour supplements, or on-demand services that the GTFS models poorly. Map them separately or exclude them from frequency analysis.
  4. Check last_arr. Routes ending before 20:00 cannot serve evening economy workers. The service span is as important as the frequency for equity analysis — a dense network that shuts down at 19:00 serves commuters, not shift workers or evening students.

Cross-references

Academic References

Google Inc. & TriMet. (2006–present). "General Transit Feed Specification (GTFS)." MobilityData International. open specification, no DOI assigned] Available at: gtfs.org

Walker, J. (2012). Human Transit: How Clearer Thinking about Public Transit Can Enrich Our Communities and Our Lives. Island Press. (The practitioner's bible on frequency mapping, the "frequent network" concept, and the distinction between coverage and ridership service design.) trade/practice book, no DOI assigned]

Cats, O., West, J., & Eliasson, J. (2016). "A Dynamic Stochastic Model for Evaluating Congestion and Crowding Effects in Transit Systems." Transportation Research Part B: Methodological, 89, 43–57. DOI: 10.1016/j.trb.2016.04.001

Transit Frequency Map

Processing ID: planx:transitfrequency

Overview

Answers the rider's first question about any transit system: how often does it come? Counts the scheduled departures at every stop within a user-specified time window on a single service day, and produces the two numbers riders feel: departures per hour and mean headway (minutes between services). The output is the classic "frequent network" map — the sub-network of stops where a rider can arrive without consulting a timetable and expect a vehicle within ~10 minutes. This is the single most informative transit map for land-use planning because frequency, not route coverage, is what drives transit-oriented development feasibility.

The tool reads the same GTFS zip as GTFS Import and compiles the timetable for a given day, counting every departure (a vehicle leaving the stop within the window; final arrivals at terminus stops do not count). The window defaults to the morning peak (07:00–09:00) — the standard window for frequent-network mapping — but can be set to any interval including the full service day. The output stop layer includes the raw departure count, per-hour frequency, mean headway in minutes, and the number of distinct routes serving the stop in the window. A supplementary route table lists the trips each route operates within the window.

Theoretical Background

Transit frequency is the operational parameter that structures a rider's entire experience. Jarrett Walker's concept of the "frequent network" — the set of routes and stops where service is frequent enough that riders do not need to consult a timetable — is the organising principle of modern transit planning (Walker, 2012). The conventional threshold is 6 departures per hour (10 minute headway), which studies consistently find to be the point at which waiting time ceases to dominate the perceived travel-time experience (Fan et al., 2016). Below this threshold — at 15, 20, or 30 minute headways — the timetable becomes the rider's master: a missed bus means a long wait, and the entire day is structured around the schedule.

The distinction between scheduled mean headway (total window duration divided by departure count) and the actual gap distribution is important. The mean headway reported here is a screening number — it does not reveal whether departures are evenly spaced (a reliable 10-minute clockface schedule) or bunched (three buses in 5 minutes, then nothing for 25). GTFS schedule data is the "as-planned" timetable; operational bunching (three buses arriving simultaneously due to traffic) is not represented. For route-level reliability analysis, the mean headway from GTFS is a ceiling — actual headways experienced by riders are worse.

Service span — the time between the first and last departure of the day — is as important as frequency for equity. A route with 12 departures per hour during a narrow peak window (07:00–09:00) but zero service after 19:00 serves only the 9-to-5 commuter. The all-day frequent network — stops served at 6+/hour across a 16-hour span — is far rarer and far more valuable for transit- oriented density than the peak-only frequent network. Comparing frequency at different windows (morning peak, midday, evening, weekend) reveals whether a "frequent" corridor is truly all-day frequent or peak-commuter-only.

Mathematical Formulation

Departures per hour. For stop $s$ with $D_s$ departures counted in a window of duration $\Delta t = t_{\text{end}} - t_{\text{start}}$ (in seconds):

$$f_s = \frac{D_s}{\Delta t / 3600} \quad [\text{departures/hour}] \tag{3}$$

Scheduled mean headway. The reciprocal, converted to minutes:

$$h_s = \frac{\Delta t}{60 \cdot \max(1, D_s)} \quad [\text{minutes}] \tag{2}$$

When $D_s = 0$, $h_s = 0$ (no service — or more precisely, $h_s = \Delta t / 60$, but the engine sets it to 0 as a "no service" sentinel). This is the window-mean, not the gap distribution. For a stop with $D_s = 6$ departures in a 2-hour window: $f_s = 3.0$/hour, $h_s = 20$ min — the schedule averages one bus every 20 minutes, though individual gaps may range from 10 to 30 minutes.

Distinct routes. A route is counted if at least one of its trips departs the stop within the window:

$$R_s = \left| \{ route\_id : \exists trip \; \text{of this route serving} \; s \; \text{in window} \} \right| \tag{1}$$

Input Data Requirements

InputTypeRequiredNotes
GTFS feedZIP fileYesSame feed as GTFS Import. The tool reads it directly — no need to pre-process.
Service dayString (YYYYMMDD)NoSame semantics as GTFS Import.
WindowTwo doubles (start/end hour)YesTime window in decimal hours. 7.0–9.0 = AM peak; 10.0–14.0 = midday; 16.0–19.0 = PM peak. Times > 24 allowed for overnight windows (e.g., 22.0–26.0).

Parameters

ParameterTypeDefaultDescription
FILEFile (ZIP)GTFS zip archive.
DAYString"" (first active)Service day YYYYMMDD.
STARTDouble7.0Window start (hour of day, decimal). 7.0 = 07:00.
ENDDouble9.0Window end. Must exceed start. 9.0 = 09:00. For the full service day, use 0.0 and 30.0.
OUT_STOPSVector (Point)Stop frequency points in WGS84.
OUT_ROUTESTableRoute trips in the window.

Output Description

Stop frequencies (OUT_STOPS)

FieldTypeDescription
stop_idStringAgency stop identifier.
nameStringStop name.
departuresIntegerDeparture count in the window.
per_hourDoubleDepartures / window hours. 6+ = "turn-up-and-go." 2–6 = moderate, timetable recommended. < 2 = infrequent, timetable required.
headway_minDoubleMean scheduled headway in minutes. Window duration / departures. 0 where no departures.
n_routesIntegerDistinct routes serving this stop in the window. 2+ routes = transfer point; the combined frequency is the sum of per-route frequencies.

Route trips (OUT_ROUTES)

FieldTypeDescription
route_idStringRoute identifier.
nameStringRoute short/long name.
trips_in_windowIntegerNumber of trips operated by this route within the window.

Symbolic Representation

Map per_hour with a sequential ramp (YlOrRd or Plasma) using manual breaks at 2, 4, 6, 8, 12 departures/hour. Class 0–2: infrequent (thin grey circles, size 2 mm). Class 2–4: moderate (medium circles, size 3 mm). Class 4–6: approaching turn-up-and-go (size 4 mm). Class 6+: the frequent network (large circles, size 5–6 mm, bold colour). The 6+ subnetwork IS the "frequent network" map that transit agencies publish — overlay it on a dark basemap for an immediately legible transit-quality exhibit.

Interpretation Guide

Frequency bands and their planning implications

per_hourHeadwayRider ExperiencePlanning Implication
6+≤10 minTurn-up-and-go. Riders arrive at the stop without consulting a timetable. Waiting time is a minor fraction of total trip time.Transit-oriented density is defensible. These corridors support 15-minute-frequency land uses (residential, retail, employment).
4–610–15 minFrequent enough for most. Riders check the timetable on departure but not obsessively throughout the day.Transit-supportive density appropriate. Missing a bus costs 10–15 minutes — acceptable for discretionary trips, borderline for commute.
2–415–30 minTimetable-dependent. The rider plans around the schedule. A missed bus costs a significant wait.Basic mobility provision. Not a basis for transit-oriented development. Coverage service, not ridership service.
< 2>30 minSkeletal. The rider's entire day is structured around the timetable. Useful only for planned trips.Socially necessary minimum. These stops serve captive riders who have no alternative — not a basis for densification.

Multi-window analysis

Run the tool at three windows — morning peak (07:00–09:00), midday (10:00–14:00), and evening (18:00–20:00) — and compare. A stop with per_hour of 8 in the peak and 1 at midday serves commuters only. A stop with per_hour of 5–6 across all three windows is an all-day frequent corridor — the gold standard for transit-oriented density and the sub-network that affordable housing policies should prioritise.

Cross-references

Pitfalls

Academic References

Walker, J. (2012). Human Transit. Island Press. (Chapters 5–6: "The Frequent Network" and "Frequency" — the definitive practitioner treatment.) trade/practice book]

Fan, Y., Guthrie, A., & Levinson, D. (2016). "Waiting time perceptions at transit stops and stations: Effects of basic amenities, gender, and security." Transportation Research Part A: Policy and Practice, 88, 251–264. DOI: 10.1016/j.tra.2016.04.012

Higgs, C., Badland, H., Simons, K., Knibbs, L.D., & Giles-Corti, B. (2019). "The Urban Liveability Index: Developing a Policy-Relevant Composite Measure." The Lancet Planetary Health, 3(S1), S21. DOI: 10.1016/S2542-5196(19)30107-X

Cats, O. (2017). "Topological Evolution of a Metropolitan Rail Network: The Case of Stockholm." Journal of Transport Geography, 62, 172–183. DOI: 10.1016/j.jtrangeo.2017.06.002

Transit Travel-Time Access

Processing ID: planx:transitaccess

Overview

Computes door-to-door travel times with public transport: walk from any origin to a transit stop on the street network, ride the timetable (with transfers), walk from the alighting stop to each destination — and compare against walking all the way. This is the transit sibling of the 15-minute-city tools and the single tool in the PlanX suite that answers the complete rider question: "how long does the whole trip actually take?"

The timetable is compiled from a GTFS zip into route patterns (sequences of stops) and trip arrays (arrival and departure times per pattern), then searched with a RAPTOR-style round-based earliest-arrival algorithm (Delling et al., 2015). Walking legs before boarding (access) and after alighting (egress) run on the street network via multi-source Dijkstra. The algorithm tracks the earliest time a passenger can stand on each stop's platform and, at each round, boards the first catchable trip (by departure time), propagates arrivals along the route pattern, and re-evaluates transfers at the next round. Walking is kept as the fallback: when transit is slower than walking (short trips, long waits), the tool reports walking as the best mode.

For each destination, the output reports walk_min (walking all the way), transit_min (walk + ride + transfer waits + walk), best_min (whichever is faster), saved_min (minutes transit saves — zero or positive), and mode (Walk, Transit, or Unreachable). The trip departs at a user-specified hour on the chosen service day and allows up to the specified number of re-boardings (transfers).

Theoretical Background

Public transit routing from schedule data is computationally distinct from road-network routing. In a road network, the travel time on an edge is a scalar (seconds); in a timetable, the travel time between two stops depends on which trip is boarded — the earliest-arrival time at a destination is a function of the departure time, the wait for the next vehicle, the ride time, and any transfer waits. This makes the problem inherently time-dependent and discrete: the timetable is a finite set of vehicle journeys, and the passenger can only board a vehicle that departs after she arrives at the stop.

RAPTOR (Round-bAsed Public Transit Optimized Router) by Delling, Pajor & Werneck (2015) introduced the key insight that makes timetable routing tractable on realistic metropolitan networks: instead of modelling every possible connection as a graph edge, work in rounds corresponding to the number of boardings. In round $k$, a passenger arrives at a set of stops; in round $k+1$, she boards the first vehicle that departs after that arrival time and rides it as far as it goes, marking arrivals at every downstream stop. The algorithm is FIFO (First-In-First-Out) within each route pattern — a later boarding never produces an earlier downstream arrival — which means only the earliest-catchable trip on each route needs to be tracked.

PlanX implements a simplified RAPTOR (engine/transit.py, earliest_arrival):

  1. Access walk. Multi-source Dijkstra from all origin nodes on the street network produces the walking time from each origin to every network node. GTFS stops snap to their nearest network node (within the access-walk limit); the access time to a stop is the walk time plus the straight-line snap offset. The departure time (user-specified hour × 3600 seconds) plus the access walk gives the earliest time the passenger stands on each reachable stop's platform.
  2. Round 0 (single ride). For each stop the passenger can reach by walking, find the earliest trip on each route pattern whose departure at that stop position is $\geq$ the arrival time. Propagate the trip's arrival times to all downstream stops on the pattern — these are the earliest single-ride arrivals.
  3. Round $k$ (transfer). Stops whose arrival times improved in the previous round become the transfer origins. For each such stop, find the earliest catchable trip on each pattern, propagate downstream arrivals. Stop when no stop's arrival improves or after max_transfers rounds.
  4. Egress walk. From each stop with a finite arrival time, walk (multi-source Dijkstra with offset = arrival time) to every network node. The minimum of (arrival at node + egress walk) and (walk-all-the-way time) gives the destination's best arrival time.

The algorithm is not an exact RAPTOR — it does not handle footpaths as distinct transfer edges (transfers occur by arriving and re-boarding at the same stop) and the trip search is brute-force linear scan on the departure column rather than a binary search on a pre-processed index. These simplifications are deliberate: on the typical feed size used in a single-study-area analysis (most cities have < 10,000 stops and < 1,000 route patterns), the linear scan is fast enough (~seconds) and avoids the complexity of route-pattern indexing.

Mathematical Formulation

Access walk. For origin nodes $\mathcal{O}$, walking speed $v$ (m/s), and street-network edge walking times $w_{\text{sec}}(e) = \ell_e / v$:

$$t_{\text{access}}(stop_i) = \min_{o \in \mathcal{O}} \left[ d_{\text{walk}}(o, node(stop_i)) + \frac{\|stop_i - node(stop_i)\|_2}{v} \right] \tag{5}$$

Stops for which $t_{\text{access}} > max\_walk\_min \times 60$ are excluded. The departure time at reachable stop $i$ is:

$$t_{\text{depart}}(i) = t_{\text{departure\_hour}} \cdot 3600 + t_{\text{access}}(stop_i) \tag{4}$$

Earliest-arrival propagation (round $k$). Let $E_k[i]$ be the earliest arrival at stop $i$ after $k$ boardings ($E_0[i]$ = departure time at reachable stops). For a route pattern with stop sequence $\{s_0, s_1, \ldots, s_m\}$ and trip departures $\{d_{t,0}, d_{t,1}, \ldots\}$:

$$t_{\text{boarding}}(t, j) = \min_{t} \{ d_{t,j} : d_{t,j} \geq E_k[s_j] \} \tag{3}$$ $$E_{k+1}[s_{j'}] = \min(E_k[s_{j'}],\; a_{t, j'}) \quad \text{for} \; j' > j \tag{2}$$

where $a_{t,j'}$ is trip $t$'s arrival at stop $s_{j'}$. The propagation is FIFO: if trip $t$ is the earliest catchable at stop $j$, it is also the earliest (at zero additional waiting) for all stops $j' > j$ on the same pattern.

Egress walk and final result. For destination node $d$:

$$t_{\text{transit}}(d) = \min_{i : E_K[i] < \infty} \left[ E_K[i] + d_{\text{walk}}(node(stop_i), d) + \frac{\|stop_i - node(stop_i)\|_2}{v} \right] \tag{1}$$ $$t_{\text{best}}(d) = \min(t_{\text{walk}}(d),\; t_{\text{transit}}(d))$$

Input Data Requirements

InputTypeRequiredNotes
GTFS feedZIPYesSame feed as the other transit tools.
Street networkVector linesYesProjected CRS (metres). The network must cover the walking catchment of the stops. Does not need to be a prepared network — build_node_graph constructs the graph on the fly.
Origin(s)Vector (any geometry)YesOne or more features representing the departure place. Multiple features = multiple entrances to the same origin; the best (shortest access) wins for each stop.
DestinationsVector (any geometry)YesDemand points to evaluate. Building centroids, job locations, address points.

Parameters

ParameterTypeDefaultDescription
FILEFile (ZIP)GTFS zip.
DAYString"" (first active)Service day YYYYMMDD.
DEPARTUREDouble8.0Departure hour (decimal). 8.0 = 08:00. The starting time of the trip — access walks are added to this. Sweep multiple departure hours (08:00, 12:00, 18:00) to test all-day vs. peak-only service quality.
NETWORKVector (Line)Street network in projected CRS.
ORIGINSVector (Any)Departure place(s).
DEMANDVector (Any)Destination points.
WALK_SPEEDDouble4.8Walking speed km/h. 4.8 = average adult; 3.6 = elderly/child; 5.0 = brisk commuter.
MAX_WALKDouble10.0Maximum access/egress walk time in minutes. Stops further than this from the origin are excluded from boarding consideration.
MAX_TRANSFERSInteger2Maximum re-boardings (0 = single ride only). Each transfer incurs a wait for the next vehicle. Values > 3 increase runtime with diminishing realistic utility — most passengers accept at most 2 transfers.
OUT_DEMANDVector (Point)Destinations annotated with travel time fields.

Output Description

FieldTypeDescription
walk_minDoubleWalking time (minutes) for the entire trip on the street network. The baseline — NULL if unreachable by walking.
transit_minDoubleTransit door-to-door time (walk + wait + ride + transfers + egress walk). NULL if unreachable by transit. This is the honest cost: it includes the wait for the first catchable trip.
best_minDoubleMinutes for the fastest mode. = min(walk_min, transit_min). NULL if unreachable by both modes.
saved_minDoubleMinutes transit saves versus walking. = walk_min − transit_min (≥ 0). 0 when walking is faster or equal. NULL when either mode is unreachable. Negative never occurs — walking is always kept as the fallback.
modeString"Walk", "Transit", or "Unreachable". The fastest mode to this destination.
transfers_maxIntegerThe user's max-transfers setting (constant across all destinations).

Symbolic Representation

Map best_min with a sequential ramp (Viridis, reversed: dark = short travel, bright = long). Use threshold contours at 15, 30, 45, 60 minutes to visualise the isochrone bands. Map mode as a categorical: "Transit" destinations in blue, "Walk" in green, "Unreachable" in red — the spatial pattern of blue and green reveals where transit beats walking and where it loses. For a saved_min view, use a diverging ramp centred at ~10 (the threshold for "meaningful transit advantage"): destinations with saved_min > 10 are strong transit wins; < 5 are marginal.

Interpretation Guide

The three transit-access regimes

Sweeping departure hours

Run the tool at multiple departure hours (08:00, 12:00, 18:00, 22:00) on the same OD pairs and compare best_min. Destination A: 15 min at 08:00, 35 min at 12:00, 55 min at 18:00 — peak-only service. Destination B: 20, 22, 25 min across all hours — all-day reliable. A "transit-accessible" claim is only credible when best_min is stable across the day; a single morning-peak number hides the midday collapse.

Cross-references

Pitfalls

Academic References

Delling, D., Pajor, T., & Werneck, R.F. (2015). "Round-Based Public Transit Routing." Transportation Science, 49(3), 591–604. DOI: 10.1287/trsc.2014.0534

Conway, M.W., Byrd, A., & van der Linden, M. (2017). "Evidence-Based Transit and Land Use Sketch Planning Using Interactive Accessibility Methods on Combined Schedule and Headway-Based Networks." Transportation Research Record, 2653(1), 45–53. DOI: 10.3141/2653-06

Wessel, N., Allen, J., & Farber, S. (2017). "Constructing a Routable Transit Network from a Real-Time Vehicle Location Feed." Transportation, 44, 501–518. DOI: 10.1007/s11116-015-9663-5

Boisjoly, G. & El-Geneidy, A. (2016). "Daily Fluctuations in Transit and Job Availability: A Comparative Assessment of Time-Sensitive Accessibility Measures." Journal of Transport Geography, 52, 73–81. DOI: 10.1016/j.jtrangeo.2016.03.004

Owen, A. & Levinson, D.M. (2015). "Modeling the Commute Mode Share of Transit Using Continuous Accessibility to Jobs." Transportation Research Part A: Policy and Practice, 74, 110–122. DOI: 10.1016/j.tra.2015.02.002

12. Visibility

The Visibility group models what can be seen from where — the spatial analysis of sight, exposure, and enclosure that underpins visual impact assessment, defensible-space auditing, and urban design evaluation. Three tools explore visibility from complementary directions: Viewshed answers "what can I see" from one or more observer points over a Digital Surface Model (DSM), using a radial line-of-sight sweep with a running horizon angle — the same ray idiom as the shadow and sky-view tools in the Microclimate group. Isovist Field answers "how does open space feel" by sampling a grid of points between buildings and computing Benedikt's (1979) isovist measures — area, perimeter, radial lengths, circularity, and occlusivity — the 2-D visibility-graph companion to space syntax. Visual Exposure of Landmarks reverses the question: "from where can the landmark be seen?" — sampling the landmark's boundary as observer points and accumulating the visibility count, the standard input to heritage impact and skyline protection studies.

Viewshed (DSM)

Processing ID: planx:viewshed

Overview

Computes a visibility-count raster from one or more observer points over a Digital Surface Model (DSM — terrain plus buildings and vegetation). From each observer, azimuth rays are swept outward at half-pixel steps. Each ray maintains a running horizon angle — the maximum angular elevation of any surface encountered so far. A target cell is visible when the angle from the observer's eye to the cell's surface (plus an optional target height) exceeds the accumulated horizon. The output is a raster where each cell's value is the number of observers that can see it. With one observer, this is the classical binary viewshed (0/1). With multiple observers, it is a coverage-redundancy map — cells seen by many observers are visually exposed; cells at zero are blind spots from every tested viewpoint.

The observer's eye sits at the DSM surface plus the observer height (default: 1.6 m, standing eye level). The target's visibility is tested at the DSM surface plus the target height (default: 0 m for the bare ground). The asymmetry between these two heights is the tool's key interpretive flexibility: set observer height to 1.6 m and target height to 1.6 m for "can two people see each other"; set observer height to 5 m (CCTV pole) and target height to 0 m for surveillance coverage; set observer height to 1.6 m and target height to 0 m for "what can I see of the ground surface."

Theoretical Background

Viewshed analysis is one of the oldest terrain-analytic operations in GIS, dating to the earliest digital terrain models of the 1970s. Fisher (1993) established the foundational critique: a viewshed is not a single deterministic truth but a probabilistic envelope whose boundary depends on the algorithm (ray-based vs. horizon-based), the DSM resolution, the interpolation method, and the treatment of observer and target heights. The PlanX implementation follows the radial horizon sweep approach — the same method used in the commercial visibility-analysis packages and the shadow/SVF tools in the Microclimate group:

Algorithmic uncertainty (Fisher, 1993). Fisher demonstrated that viewshed boundaries from different algorithms on the same DSM can disagree by 10–30% of the terrain. The three main sources of uncertainty are: (1) the interpolation method for the ground surface between grid posts — PlanX uses linear interpolation along the ray; (2) the observer height — a difference of 0.5 m can shift the visible/non-visible boundary by dozens of metres on flat terrain; (3) the angular resolution — coarser rays (e.g., 360 directions) produce characteristic "star" artefacts where narrow visibility corridors are missed between ray angles. The default of 720 directions eliminates these artefacts for all but the narrowest urban canyons.

Applications beyond the classical viewshed. The classical question — "can the observer see that hilltop?" — has been substantially extended in the decades since Fisher's analysis. In urban planning, the dominant applications are: visual impact assessment (will the proposed building be visible from protected viewpoints?), defensible-space auditing (are there blind spots along pedestrian routes where informal surveillance fails?), CCTV/lighting coverage analysis (which areas are covered by how many cameras/lights?), and skyline protection (where can the protected landmark silhouette still be seen?). The last of these is the inverse question — answered by the Visual Exposure of Landmarks tool.

Mathematical Formulation

Observer geometry. For an observer at DSM cell $(r_0, c_0)$ with elevation $z_0$, eye height $h_{\text{obs}}$, at an azimuth $\theta$:

$$z_{\text{eye}} = z_0 + h_{\text{obs}} \tag{5}$$

Ray stepping. Along the ray at azimuth $\theta$, steps are at pixel-scale intervals from the observer outward. The step in column and row per metre of ground distance at ray azimuth $\theta$ and pixel size $p$:

$$\Delta c = \frac{\sin\theta}{p}, \qquad \Delta r = -\frac{\cos\theta}{p} \tag{4}$$

Horizon accumulation. At step $k$ with ground distance $t_k$, the target cell's surface angle and the target-height angle are:

$$\alpha_k^{\text{surf}} = \frac{z_k - z_{\text{eye}}}{t_k}, \qquad \alpha_k^{\text{tgt}} = \frac{z_k + h_{\text{tgt}} - z_{\text{eye}}}{t_k} \tag{3}$$

The horizon before step $k$ is the cumulative maximum of surface angles:

$$H_k = \max_{j < k} \alpha_j^{\text{surf}} \tag{2}$$

Cell $k$ is visible when:

$$\text{visible}(k) = \left( \alpha_k^{\text{tgt}} \geq H_k - \varepsilon \right) \tag{1}$$

where $\varepsilon$ is a small tolerance ($10^{-12}$) to handle floating-point precision in the horizon comparison. NoData cells are excluded and do not contribute to $H_k$.

Input Data Requirements

InputTypeRequiredNotes
DSMRasterYesDigital Surface Model (terrain + buildings + vegetation). Projected CRS with metric pixels. Resolution 0.5–5 m for urban-scale analysis. NoData = transparent to sight lines.
Observer pointsVector (any geometry)YesPoint or centroid locations of the observers. Each contributes one viewshed; the final raster sums all contributions. For a single-viewpoint study, provide one point.

Parameters

ParameterTypeDefaultDescription
DSMRasterSurface model in projected CRS.
OBSERVERSVector (Any)Observer locations. Multiple features = multi-observer viewshed; the output count per cell is the number of observers seeing it.
OBSERVER_HEIGHTDouble1.6Observer eye height above the DSM surface (metres). 1.6 = standing adult. 5.0 = CCTV pole. 0.2 = seated child. 20 = mid-rise balcony.
TARGET_HEIGHTDouble0.0Target height above the DSM surface. 0 = bare ground. 1.6 = standing person. >0 for "can a person there be seen." Set both heights to 1.6 for mutual visibility.
RADIUSDouble0.0 (unlimited)Maximum view distance in map units. 0 = DSM diagonal (the full raster extent). Set a finite radius (e.g., 500 m) for local-scale visibility and faster computation.
DIRECTIONSInteger720Number of azimuth rays. 720 = 0.5-degree resolution, smooth edges. 360 = 1-degree, adequate for most terrain-scale viewsheds. 90 = coarse but fast for quick screening.
OUTPUTRasterVisibility count raster. Integer values 0–N where N = number of observers. NoData = DSM was NoData at this cell.

Output Description

The output is a single-band integer raster with the same extent, pixel size, and CRS as the input DSM. Cell values are the count of observers that see the cell, ranging from 0 (blind spot from all observers) to $N$ (seen by every observer). Cells where the DSM is NoData are set to a NoData sentinel value (-1), distinguishing "not visible" from "not evaluated."

The log reports per-observer statistics: the percentage of raster cells seen by each observer (visible share within the radius) and the overall percentage seen by at least one observer.

Symbolic Representation

For a single-observer viewshed, display as a binary layer: visible cells in one colour (e.g., light warm tone at 50% opacity) over a hillshade of the DSM — this is the classic viewshed exhibit. For multi-observer viewsheds, use a sequential ramp (Viridis or Inferno) on the count: 0 = dark (blind), ≥3 = bright (robustly visible). Overlay observer points as a distinct symbol (star, eye icon). The visible/non-visible boundary is the key analytic feature — style it by outlining the non-zero cells with a thin (0.3 mm) bright line for emphasis.

Interpretation Guide

Diagnostic patterns

Cross-references with other PlanX tools

Pitfalls

Academic References

Fisher, P.F. (1993). "Algorithm and Implementation Uncertainty in Viewshed Analysis." International Journal of Geographical Information Systems, 7(4), 331–347. DOI: 10.1080/02693799308901965

Llobera, M. (2003). "Extending GIS-Based Visual Analysis: The Concept of Visualscapes." International Journal of Geographical Information Science, 17(1), 25–48. DOI: 10.1080/713811741

Wheatley, D. (1995). "Cumulative Viewshed Analysis: A GIS-Based Method for Investigating Intervisibility, and Its Archaeological Application." In: Lock, G. & Stancic, Z. (eds.), Archaeology and Geographical Information Systems, 171–185. Taylor & Francis. book chapter, no DOI assigned]

De Floriani, L. & Magillo, P. (2003). "Algorithms for Visibility Computation on Terrains: A Survey." Environment and Planning B: Planning and Design, 30(5), 709–728. DOI: 10.1068/b12979

Isovist Field

Processing ID: planx:isovistfield

Overview

Samples a grid of points over open space (between buildings) and computes isovist measures at every point — the 2-D visibility polygon defined by what can be seen from that standing location. An isovist (Benedikt, 1979) is the set of all points in space visible from a given vantage point, bounded by the first opaque surfaces (building walls) or by a maximum sighting distance. It is the visibility-graph companion to space syntax: while axial and segment analysis measure the configurational properties of the street network, isovist fields measure the experiential properties of open space — how large it feels, how enclosed, how connected visually to adjacent spaces.

Buildings are rasterised to a boolean obstacle grid ($\text{True} = \text{blocked}$) at the user-specified cell size. Rays are cast from each free grid cell at evenly spaced azimuths until the first blocked cell, a range limit, or the grid edge. The ray endpoints define a polygon whose geometric properties are computed via the shoelace formula (area), Euclidean vertex-to-vertex distances (perimeter), and radial lengths. The tool reports seven metrics per sample point: iso_area, iso_perim, min_rad, max_rad, mean_rad, circularity, and occlusivity.

Theoretical Background

Benedikt's isovist (1979). Michael Benedikt introduced the isovist as a formal geometric construct for quantifying the visual experience of architectural space. In a seminal paper in Environment and Planning B, he defined an isovist as "the set of all points visible from a given vantage point in space" and derived its geometric properties — area, perimeter, radial lengths, and various moments — as descriptors of spatial experience. An isovist with large area but small minimum radial (you can see far in one direction but a wall is close in another) reads differently from an isovist with the same area but uniform radials (a circular plaza). The isovist field — the continuous map of these measures across all points in open space — was proposed by Benedikt as the spatial-analogue of a potential field: a scalar landscape of visibility whose gradients, ridges, and basins encode the experience of moving through the environment.

Batty's extension (2001). Michael Batty generalised the isovist into a graph-theoretic framework by connecting points whose isovists overlap (the visibility graph) and applying network analysis measures (closeness, betweenness) to this visibility graph. The resulting visibility graph analysis (VGA), implemented in Turner's depthmapX software (Turner et al., 2001), became the standard computational pipeline for architectural and urban visibility analysis. PlanX's isovist field tool computes the raw isovist measures at grid points — the first step of VGA — but leaves the graph-construction step to the user (the point layer can be exported to an external VGA tool for network analysis).

Interpretive framework. The seven isovist metrics form a complementary set:

Mathematical Formulation

Isovist polygon. For a vantage point at grid cell $(r_0, c_0)$ with $n_{\text{rays}}$ at azimuths $\theta_k = 2\pi k / n_{\text{rays}}$, each ray marches outward one pixel-step at a time until the first blocked cell, the range limit $R_{\max}$, or the grid boundary. The reached distance along ray $k$ is $r_k$, and the ray endpoint in map coordinates is:

$$x_k = r_k \cdot \sin\theta_k, \qquad y_k = r_k \cdot \cos\theta_k \tag{7}$$

The isovist is the polygon with vertices $\{(x_0, y_0), (x_1, y_1), \ldots, (x_{n-1}, y_{n-1})\}$. Area via shoelace:

$$A = \frac{1}{2} \left| \sum_{k=0}^{n-1} (x_k y_{k+1} - x_{k+1} y_k) \right| \quad (\text{with } n \equiv 0) \tag{6}$$ $$P = \sum_{k=0}^{n-1} \sqrt{(x_{k+1} - x_k)^2 + (y_{k+1} - y_k)^2} \tag{5}$$ $$\text{circularity} = \frac{4\pi A}{P^2} \quad (\leq 1, = 1 \text{ for a circle}) \tag{4}$$

Radial statistics. For the $n$ radials $\{r_0, \ldots, r_{n-1}\}$:

$$\bar{r} = \frac{1}{n} \sum r_k, \qquad r_{\min} = \min r_k, \qquad r_{\max} = \max r_k \tag{3}$$

Occlusivity. Let $n_{\text{blocked}}$ be the number of rays that terminated at a building cell (blocked by an obstacle). Rays terminated by the range limit or the grid edge are not occluded:

$$\text{occlusivity} = \frac{n_{\text{blocked}}}{n_{\text{rays}}} \tag{2}$$ $$\text{occlusivity} \in [0, 1] \tag{1}$$

Performance optimisation. The isovist_field engine pre-computes the direction vectors for all rays (the $t \cdot dr$ and $t \cdot dc$ arrays of size $n_{\text{steps}} \times n_{\text{rays}}$) so that each sample point reuses the same ray geometry — only the origin offset changes. The cost is therefore $O(N_{\text{free\_cells}} \cdot n_{\text{rays}} \cdot \bar{n}_{\text{steps}})$ where $\bar{n}_{\text{steps}}$ is the mean ray length before termination.

Input Data Requirements

InputTypeRequiredNotes
BuildingsVector (Polygon)YesBuilding footprints. Projected CRS required. Multi-polygon handling: each feature's geometry is intersected with the grid cells. Overlapping buildings are unioned in the grid (a cell is blocked if ANY building occupies it).
Study extentExtent (bounding box)NoAnalysis area. Empty = building-layer extent. Set manually to frame a specific district. The extent should include, and extend slightly beyond, the area of interest.

Parameters

ParameterTypeDefaultDescription
BUILDINGSVector (Polygon)Building footprints. Projected CRS.
EXTENTExtent(optional)Study area bounding box. Omit to use the building layer's extent.
CELLDouble10.0Grid cell size in map units (metres). 5 m = fine gradation, captures narrow passages but produces many points (a 500×500 m extent = 10,000 cells). 10 m = standard urban-scale analysis. 20 m = coarse, for city-scale screening.
N_RAYSInteger180Rays per point. 180 = 2-degree resolution — smooth isovist polygons. 90 = 4-degree, angular but faster. 360 = 1-degree, for very fine isovist boundaries.
MAX_DISTDouble200.0Maximum sight distance in map units (metres). The perceptual horizon — isovist area asymptotes at this distance even if no building blocks the view. 200 m captures the walking-scale visual experience; 500 m for city-scale vistas; 100 m for intimate courtyard/street analysis.
OUT_POINTSVector (Point)Isovist-measured grid points (one per free cell).

Output Description

FieldTypeRangeDescription
iso_areaDouble0–~125,000Isovist area (m²). At MAX_DIST = 200 m, the theoretical maximum is $\pi \times 200^2 \approx 125,664$ m² (full circle). Typical urban values: 500–5,000 (alleys/courtyards), 5,000–30,000 (streets/plazas), 30,000+ (large open spaces/fields).
iso_perimDouble0–~1,260Isovist perimeter (m). Maximum ~$2\pi \times 200 \approx 1,257$ at full-circle with 200 m radius.
min_radDouble0–200Shortest radial (m). Intimacy — how close the nearest wall stands. < 5 m = intimate enclosure; < 2 m = arm's length (a narrow alley).
max_radDouble0–200Longest radial (m). Vista depth — the furthest unobstructed sight line. Near MAX_DIST = open field or long axial view corridor.
mean_radDouble0–200Mean radial length. The average sight distance in all directions. Compare with iso_area: $\pi \cdot \overline{r}^2$ approximates area only for a circle.
circularDouble0–1Circularity ($4\pi A / P^2$). > 0.7 = compact, room-like, gathering space. < 0.3 with long max_rad = corridor, movement channel. < 0.3 with short radials = highly irregular/enclosed.
occlusDouble0–1Fraction of rays blocked by buildings. 0 = view extends to MAX_DIST in all directions (open field). 1 = every ray hits a building wall (deep enclosure, a courtyard). Irrelevant when MAX_DIST is very small relative to the gaps between buildings.

Symbolic Representation

Map iso_area with a sequential ramp (Viridis, 7 quantile classes) for the classic openness map: bright cells = plazas and wide streets; dark cells = alleys and narrow passages. The gradient between adjacent bright and dark cells is the spatial drama — use a continuous colour ramp (not discrete classes) to preserve it. Map circular with a diverging ramp (RdYlGn) to separate gathering spaces (green, high circularity) from movement corridors (red, low circularity). Map occlus with a warm sequential ramp (OrRd) for the enclosure landscape: high occlusivity cells in deep red are the urban rooms. For publication, use a three-panel layout: area, circularity, and occlusivity side-by-side — the combined reading of spaciousness, shape, and enclosure describes spatial experience more completely than any single panel.

Interpretation Guide

The isovist typology

TypeAreaCirc.Occl.SpaceUrban Element
Room500–5,000>0.6>0.7Compact, highly enclosed by buildings, roughly circular isovist.Courtyard, square, plaza surrounded by buildings.
Corridor1,000–10,000<0.3>0.5Elongated, building-defined, low circularity, long max_rad.Street canyon, boulevard, alley.
Gateway500–2,0000.3–0.60.3–0.6Moderate size, moderate enclosure — the transition point between two distinct spatial regions.Street opening onto plaza, bridge entrance, gate.
Field>20,000variable<0.3Large area, mostly limited by distance, not by walls.Park, waterfront, open landscape, undeveloped lot.

Cross-references with other PlanX tools

Pitfalls

Academic References

Benedikt, M.L. (1979). "To Take Hold of Space: Isovists and Isovist Fields." Environment and Planning B: Planning and Design, 6(1), 47–65. DOI: 10.1068/b060047

Batty, M. (2001). "Exploring Isovist Fields: Space and Shape in Architectural and Urban Morphology." Environment and Planning B: Planning and Design, 28(1), 123–150. DOI: 10.1068/b2725

Turner, A., Doxa, M., O'Sullivan, D., & Penn, A. (2001). "From Isovists to Visibility Graphs: A Methodology for the Analysis of Architectural Space." Environment and Planning B: Planning and Design, 28(1), 103–121. DOI: 10.1068/b2684

Franz, G. & Wiener, J.M. (2008). "From Space Syntax to Space Semantics: A Behaviorally and Perceptually Oriented Methodology for the Efficient Description of the Geometry and Topology of Environments." Environment and Planning B: Planning and Design, 35(4), 574–592. DOI: 10.1068/b33050

Conroy Dalton, R. & Dalton, N. (2001). "OmniVista: An Application for Isovist Field and Path Analysis." In: Peponis, J. et al. (eds.), Proceedings of the 3rd International Space Syntax Symposium, Atlanta, pp. 25.1–25.10. conference proceedings, no DOI assigned]

Visual Exposure of Landmarks

Processing ID: planx:visualexposure

Overview

Answers the inverse viewshed question: from where can a landmark be seen? Instead of placing an observer and testing which targets are visible (Viewshed), this tool places observers on the landmark (by sampling its footprint boundary) and tests which cells in the surrounding terrain can see them. The output is a raster where each cell's value is the number of landmark-sample-points visible from that cell — a measure of silhouette completeness. Cells seeing all samples see the whole landmark (the postcard view); cells seeing a few samples see glimpses between buildings; cells at zero cannot see the landmark at all.

The tool samples the landmark polygon boundary at regular intervals (default: every 10 m, capped at 200 points), places a virtual observer at the DSM surface plus an optional extra height (for a spire, antenna, or dome that the DSM grid may not resolve at the landmark's footprint), sweeps a viewshed from each sample point, and accumulates the per-cell count. The observer on the landmark "looks outward" at the target height — the tool tests whether a person standing at a given cell (EYE_HEIGHT, default: 1.6 m) could see the landmark silhouette. The visible count per cell is the number of landmark sample points with an unobstructed line of sight.

Theoretical Background

Visual exposure analysis emerged from two planning requirements: heritage impact assessment and skyline/view-cone protection. UNESCO World Heritage operational guidelines and most national heritage protection frameworks (English Heritage, ICOMOS) require quantitative assessment of the visual impact of new development on protected landmarks and their settings. The standard method is the Zone of Visual Influence (ZVI), which maps the area from which a proposed development would be visible. Visual Exposure of Landmarks applies the same logic but reversed: the landmark is the fixed element and the question is what territory it visually commands.

Fisher (1996) extended viewshed analysis to landscape planning, introducing the concept of cumulative visibility — aggregating viewsheds from multiple points on or near the feature of interest to create a probabilistic or fuzzy visibility surface. This is the ancestor of the PlanX visual exposure tool: instead of a single binary viewshed from one viewpoint, the exposure raster measures viewing redundancy — how many independent sight lines connect the target cell to the landmark.

Silhouette completeness as a visual-quality metric. The count of visible sample points is a proxy for how much of the landmark's bulk is visible. A cell that sees 180 of 200 sample points sees nearly the entire silhouette — the landmark dominates the view. A cell that sees 5–10 sample points sees a sliver between two buildings — the landmark is present but not visually significant. The transition from zero to non-zero (the landmark's visual catchment boundary) is the ZVI analogue; the transition from low-count to high-count (the landmark's visual dominance zone) is the heritage-conservation analogue — these are the corridors where the landmark's visual presence should be protected from obstruction.

Mathematical Formulation

Landmark sampling. Given landmark footprint polygon vertices $v_1, v_2, \ldots, v_m$, the boundary is densified to a maximum segment length of $\Delta s$ (the sample step), producing a ring of $N$ sample points (capped at 200):

$$\{\mathbf{p}_1, \mathbf{p}_2, \ldots, \mathbf{p}_N\} \subset \partial(\text{landmark}) \tag{5}$$

Viewshed from each sample point. For each $\mathbf{p}_i$ at DSM cell $(r_i, c_i)$, the observer height is the extra height parameter $h_{\text{extra}}$ (the DSM surface at the landmark's location is the ground level; the extra height adds the feature's elevation above the DSM — a spire atop a dome whose DSM grid does not resolve it):

$$z_{\text{eye}}^{(i)} = z_{\text{DSM}}(r_i, c_i) + h_{\text{extra}} \tag{4}$$

The viewshed from $\mathbf{p}_i$ uses the standard radial horizon sweep (identical to the Viewshed tool) with target height = EYE_HEIGHT (observer on the ground looking at the landmark):

$$\text{vis}^{(i)}(r, c) = \mathbf{1}[\text{cell } (r, c) \text{ is visible from sample point } i] \tag{3}$$ $$\text{exposure}(r, c) = \sum_{i=1}^{N} \text{vis}^{(i)}(r, c) \in [0, N] \tag{2}$$

The search radius caps the exposure calculation — cells beyond the radius are not tested:

$$\text{exposure\_radius} = \begin{cases} R & \text{if } R > 0 \\ \text{raster diagonal} & \text{otherwise} \end{cases} \tag{1}$$

Input Data Requirements

InputTypeRequiredNotes
DSMRasterYesSame requirements as Viewshed. Projected CRS, metric pixels. Must cover the landmark and the surrounding visibility territory.
Landmark footprint(s)Vector (Polygon)YesBuilding or structure footprints. Multiple polygons = multiple landmarks whose combined visibility is the output. A church, a minaret, a tower — anything whose visual significance is to be assessed.

Parameters

ParameterTypeDefaultDescription
DSMRasterSurface model in projected CRS.
LANDMARKSVector (Polygon)Landmark footprints. The boundary is sampled for observer points.
EXTRA_HEIGHTDouble0.0Height above the DSM at the landmark location (metres). For a spire or antenna not resolved by the DSM pixel. For a 50 m minaret on a DSM that shows the mosque at 15 m, set EXTRA_HEIGHT = 35 (50 − 15).
SAMPLE_STEPDouble10.0Boundary sampling interval in map units (metres). Smaller = denser sample, more viewsheds to compute. Capped at 200 sample points — very large landmarks are automatically down-sampled.
EYE_HEIGHTDouble1.6Observer eye height at the target cell (metres). This is the height of a person looking at the landmark, not the landmark's height.
RADIUSDouble0.0Exposure radius. 0 = unlimited (full DSM extent). For city-scale landmarks, 5,000–10,000 m is often sufficient.
OUTPUTRasterLandmark visibility count raster. Cell value = number of sample points visible from here. NoData = DSM was NoData at this cell.

Output Description

A single-band integer raster with the same extent and resolution as the DSM. Cell values range from 0 (landmark not visible from this cell) to $N$ (all $N$ sample points visible — the full silhouette). NoData where the DSM has no value.

The log reports the number of sample points used and the percentage of valid DSM cells that see the landmark (at least one sample point visible — the ZVI share). For multiple landmarks, this is the combined exposure.

Symbolic Representation

Map the exposure count with a sequential ramp (Inferno or Plasma). Use manual breaks at meaningful thresholds: 0 (hidden), 1–5 (glimpses), 6–25% of N (partial view), 26–75% (substantial view), 76–100% (full silhouette = postcard view). The zero/non-zero boundary is the landmark's visual catchment — outline it with a 0.5 mm bright line. The high-count corridors radiating along major street axes are the view cones that heritage plans protect through height restrictions — overlay these on the zoning map to identify conflicts between development capacity and view protection.

For before/after impact assessment: run the tool on the existing DSM (baseline), insert the proposed building massing into the DSM (raster arithmetic: replace DSM cells under the building footprint with the building's roof elevation), rerun, and subtract: $\Delta(r, c) = \text{exposure}_{\text{before}}(r, c) - \text{exposure}_{\text{after}}(r, c)$. The difference raster shows every cell where the proposed building obstructs the landmark view, with the magnitude of the count drop as the severity of the impact. This is the definitive heritage- impact exhibit.

Interpretation Guide

Exposure count as visual quality

Before/after impact assessment protocol

  1. Baseline: run Visual Exposure on the current DSM. Save the exposure raster as the heritage baseline.
  2. Proposed: insert the proposed building's envelope into the DSM (set DSM cells under the footprint to the building height). Rerun the exposure analysis identically.
  3. Difference: $\text{baseline} - \text{proposed}$. Positive cells = views lost. The magnitude is the number of landmark sample points that this cell can no longer see — the visual impact severity.
  4. Interpretation: a cell that loses 50+ sample points (substantial silhouette reduction) is a significant adverse visual impact. A cell that drops from 3 to 0 (loses a glimpse) is a minor adverse impact — but if the glimpse was along a protected view axis, it may still be unacceptable.

Cross-references with other PlanX tools

Pitfalls

Academic References

Fisher, P.F. (1996). "Extending the Applicability of Viewsheds in Landscape Planning." Photogrammetric Engineering & Remote Sensing, 62(11), 1297–1302. journal article, no DOI available]

Mouflis, G.D., Gitas, I.Z., Iliadou, S., & Mitri, G.H. (2008). "Assessment of the Visual Impact of Marble Quarry Expansion (1984–2000) on the Landscape of Thasos Island, NE Greece." Landscape and Urban Planning, 86(1), 92–102. DOI: 10.1016/j.landurbplan.2008.01.001

Chmielewski, S., Lee, D.J., Tompalski, P., Chmielewski, T.J., & Wężyk, P. (2016). "Measuring Visual Pollution by Outdoor Advertisements in an Urban Street Using Intervisibility Analysis and Public Surveys." International Journal of Geographical Information Science, 30(4), 801–818. DOI: 10.1080/13658816.2015.1104316

Llobera, M. (2007). "Reconstructing Visual Landscapes." World Archaeology, 39(1), 51–69. DOI: 10.1080/00438240601136496

13. Population and Housing

The Population and Housing group translates demographic projections into spatial planning targets. Its four algorithms form a pipeline: Population Projection produces the horizon-year population; Housing Needs Assessment converts population into required dwellings; Residential Capacity tests whether current zoning can physically deliver those dwellings; and Allocate Population Growth distributes the increment across parcels so that downstream demand-driven tools (Facility Adequacy, Green Access, Transit Access) have spatial population surfaces to consume. Every algorithm is a screening tool -- the mathematics is deterministic and fully auditable, but the assumptions (migration rates, household size, vacancy targets, zoning discount factors) are policy choices that must be documented and scenario-tested.

Population Projection (Cohort-Component)

Processing ID: planx:populationprojection

1. Overview

Projects an age-structured population forward in discrete time steps using the cohort-component method, the global standard for demographic projection since the mid-20th century. The algorithm constructs a Leslie matrix from per-step survival rates and age-specific fertility rates, applies it repeatedly for the chosen horizon, and adds net migration after each step. Results are floored at zero -- population cannot go negative.

The projection is single-sex (total population), the standard screening simplification. For a two-sex projection, run the tool twice with sex-specific rates. Rates remain constant over the projection horizon -- producing a "what current trends imply" conditional projection, not a forecast. The horizon-year total and age structure feed directly into the Housing Needs Assessment and Facility Adequacy tools.

What this tool is not. It does not model feedback between population and housing supply (no endogenous migration response to affordability), does not vary rates over time (no tempo effects), and does not disaggregate by sex or spatial unit. For those features, consult a dedicated demographic microsimulation framework. This tool answers one question precisely: given these rates, this starting population, and this net migration, how many people of what ages will there be in N steps?

2. Theoretical Background

2.1 Academic lineage

The cohort-component method traces its mathematical foundation to Leslie (1945), who demonstrated that the age-structured population projection could be expressed as a single matrix multiplication: $\mathbf{n}_{t+1} = \mathbf{L} \mathbf{n}_t$, where $\mathbf{L}$ is a $k \times k$ matrix with fertility rates on the first row, survival probabilities on the sub-diagonal, and the final survival rate on the bottom-right diagonal to retain the open-ended oldest age group. This deceptively simple formulation turned what had been a tedious arithmetic exercise (tracking each cohort separately) into a one-line matrix operation, and it opened the door to analytical population mathematics: stable population theory, reproductive value, sensitivity analysis via eigen-decomposition, and stochastic projections.

Keyfitz extended the framework throughout the 1960s-1970s, formalising the link between the Leslie matrix and the stable population model and showing that the dominant eigenvalue of $\mathbf{L}$ equals the intrinsic growth rate $e^r$. Keyfitz & Caswell (2005, 3rd ed.) remains the canonical reference for applied mathematical demography, covering projection, stable theory, multi-state models, and perturbation analysis in a unified matrix framework. Rogers (1975, 1995) generalised the method to the multiregional case, adding origin-destination migration matrices to project population by age and region simultaneously -- the foundation of all modern sub-national demographic projection systems. Preston, Heuveline & Guillot (2001) provide the clearest textbook exposition of the standard cohort-component algorithm, including the demographic balancing equation and the construction of the Leslie matrix from age-specific rates.

2.2 From projection to forecast

A critical distinction, articulated by Romaniuc (1990) and elaborated by Burch (2018), separates three uses of the cohort-component algorithm: (a) prediction -- attempting to know the future, which demographers have largely abandoned beyond the very short term (~5 years); (b) simulation -- using the model to understand demographic dynamics and the relative influence of fertility, mortality, and migration on age structure, without making claims about a specific population (the "what-if" mode); and (c) prospective analysis -- working out plausible futures for a specific population, typically through scenario variants (low/medium/high migration). The PlanX implementation is designed for modes (b) and (c): the same engine, fed different migration assumptions, generates the scenario range that planning should respond to.

2.3 Assumptions and limitations

  1. Constant rates. Fertility and survival do not change over the projection horizon. This is the standard screening assumption, not a claim about behavioural stability. For long horizons (>20 years), the assumption becomes increasingly tenuous -- demographic transition theory predicts declining fertility and increasing longevity, which constant-rate projections will miss.
  2. Single-sex. The model tracks total population, not males and females separately. Births are assigned to the youngest age group using total fertility rates applied to the entire population. A two-sex model would apply fertility rates only to females and track the sex ratio at birth.
  3. No spatial disaggregation. The projection is aspatial -- one population vector for the entire study area. For spatially disaggregated projections, use Pop Allocate to distribute the horizon total, or implement a Rogers-style multiregional model.
  4. Migration is exogenous. Net migration is a fixed per-step vector added after the survival/fertility update. It does not respond to population pressure, housing supply, or economic conditions.
  5. Equal age-group widths. The Leslie matrix assumes all age groups have the same width (e.g. 5 years). The step duration should match this width.

3. Mathematical Formulation

Let the population be partitioned into $k$ age groups of equal width (e.g. 5-year groups: 0-4, 5-9, ..., 80+). Define:

Leslie matrix. The $k \times k$ projection matrix $\mathbf{L}$ is constructed as:

$$L_{0,a} = f_a \quad \text{for } a = 0, \ldots, k-1 \tag{1}$$ $$L_{a+1,a} = s_a \quad \text{for } a = 0, \ldots, k-2 \tag{2}$$ $$L_{k-1,k-1} = L_{k-1,k-1} + s_{k-1} \tag{3}$$

All other entries are zero. Equation (3) adds the final survival rate to the bottom-right diagonal entry (which already carries $s_{k-2}$ from the sub-diagonal pattern for the penultimate group), keeping the oldest survivors in the terminal group rather than letting them age out of the model.

Projection step. For each step from $t$ to $t+1$:

$$\mathbf{n}_{t+1} = \max\left(0,\; \mathbf{L} \cdot \mathbf{n}_t + \mathbf{m}\right) \tag{4}$$

where $\max(0, \cdot)$ is applied element-wise to prevent negative population. The migration vector $\mathbf{m}$ is added after the survival-and-birth update in each step. If no migration field is provided, $\mathbf{m} = \mathbf{0}$.

Output array. The full projection is stored as a $(\text{steps}+1) \times k$ matrix where row 0 is the starting population and each subsequent row is one projection step:

$$\mathbf{P} = \begin{bmatrix} n_{0,0} & n_{0,1} & \cdots & n_{0,k-1} \\ n_{1,0} & n_{1,1} & \cdots & n_{1,k-1} \\ \vdots & \vdots & \ddots & \vdots \\ n_{S,0} & n_{S,1} & \cdots & n_{S,k-1} \end{bmatrix} \tag{5}$$

Per-step totals. The total population at each step is the row sum $T_s = \sum_a P_{s,a}$. The growth rate between steps $s-1$ and $s$ is:

$$g_s = 100 \cdot \left(\frac{T_s}{T_{s-1}} - 1\right) \quad [\%] \tag{6}$$

4. Input Data Requirements

InputTypeRequiredNotes
Age-group tableVector (no geometry)YesOne row per age group, ordered youngest to oldest. Must have numeric fields for population, survival, and fertility. At least 2 rows required. Equal-width groups (e.g. all 5-year) are assumed but not enforced -- using varying widths will produce distorted dynamics.

Where to obtain rates: National statistical offices publish age-specific fertility rates (ASFR) and life tables from which survival ratios can be computed. The UN Population Division provides quinquennial estimates and projections for all countries. For sub-national projections, scale national rates by local age structure differentials where known (e.g. student towns have different mortality profiles). Migration is the hardest component -- historical net migration can be estimated as the residual of the demographic balancing equation, but forward assumptions are inherently policy-dependent.

5. Parameters

ParameterTypeDefaultDescription
INPUTVector layer--Age-group table with one row per group, ordered youngest to oldest.
AGE_FIELDField (string)--Age-group label (e.g. "0-4", "5-9"). Used in output labelling only.
POP_FIELDField (Numeric)--Starting population count per age group. Negative values are floored to 0.
SURVIVAL_FIELDField (Numeric)--Per-step survival rate (0-1). Clamped to [0, 1]. The last row's value retains people in the terminal open-ended group.
FERTILITY_FIELDField (Numeric)--Births per person per step. Non-negative; zero outside childbearing ages.
MIGRATION_FIELDField (Numeric)-- (optional)Net migration per step per age group. Can be negative (out-migration). If omitted, zero migration is assumed.
STEPSInteger4Number of projection steps. Range 1-40. With 5-year groups, 4 steps = 20-year horizon; 8 steps = 40 years.
STEP_YEARSInteger5Years per step (for output labelling only; does not affect the mathematics). Should match the age-group width.
OUT_PROJECTIONVector (table)--Full projection: one row per step x age group.
OUT_TOTALSVector (table)--Per-step totals: population, growth %, net migration.

6. Output Description

Projection table (OUT_PROJECTION):

FieldTypeDescription
stepIntegerProjection step (0 = baseline)
year_offsetIntegerYears from baseline (step x step_years)
age_groupStringAge-group label from the input table
populationDoubleProjected population in this age group at this step

Totals table (OUT_TOTALS):

FieldTypeDescription
stepIntegerProjection step (0 = baseline)
year_offsetIntegerYears from baseline
populationDoubleTotal population across all age groups
growth_pctDoubleGrowth rate from previous step (%, 0 for baseline)
net_migrationDoubleTotal net migration per step (0 for baseline)

7. Symbolic Representation

Chart each step's age structure as a population pyramid (horizontal bars, males left if two-sex, females right). Plot the per-step totals as a line chart with confidence bands if scenario variants (low/medium/high migration) were run. Colour-code the pyramid bars by broad age group: 0-14 (green, pre-working), 15-64 (blue, working age), 65+ (orange, post-working). The dependency ratio -- (0-14 + 65+) / (15-64) -- is the single most policy-relevant summary statistic from the projection and should be tracked across steps.

8. Interpretation Guide

8.1 Age structure beats total

A flat total population trajectory can conceal violent age-structure shifts. Common patterns to watch for:

8.2 The migration assumption IS the debate

Fertility and mortality change slowly and predictably. Migration does not. In most urban-planning contexts, the debate about "how many people will there be" is actually a debate about migration assumptions. The tool separates natural change from migration in the totals table, making this explicit. Run at least three variants -- low migration (zero or negative), medium (recent trend), high (policy target) -- and plan infrastructure to the range, not the central line. A plan that works only under the medium-migration assumption is fragile.

8.3 Cross-references

8.4 Pitfalls

9. Academic References

Leslie, P.H. (1945). "On the Use of Matrices in Certain Population Mathematics." Biometrika, 33(3), 183-212. DOI: 10.1093/biomet/33.3.183 verified

Keyfitz, N. & Caswell, H. (2005). Applied Mathematical Demography, 3rd ed. Springer. DOI: 10.1007/b139042 verified

Preston, S., Heuveline, P. & Guillot, M. (2001). Demography: Measuring and Modeling Population Processes. Wiley-Blackwell.

Rogers, A. (1995). Multiregional Demography: Principles, Methods and Extensions. John Wiley, New York.

Romaniuc, A. (1990). "Population Projection as Prediction, Simulation and Prospective Analysis." Population Bulletin of the United Nations, 29, 16-31.

Burch, T.K. (2018). "Cohort Component Projection: Algorithm, Technique, Model and Theory." In: Model-Based Demography. Demographic Research Monographs. Springer, Cham. DOI: 10.1007/978-3-319-65433-1_9 verified

Wheldon, M.C., Raftery, A.E., Clark, S.J. & Gerland, P. (2013). "Reconstructing Past Populations With Uncertainty From Fragmentary Data." Journal of the American Statistical Association, 108(501), 96-110. DOI: 10.1080/01621459.2012.737729 verified

===ALGORITHM===

Housing Needs Assessment

Processing ID: planx:housingneeds

1. Overview

Computes the number of additional dwelling units that a plan must deliver by the horizon year, using the standard housing-needs identity employed in statutory planning systems worldwide: future population divided by household size yields households; a vacancy allowance is added to permit market fluidity; the existing stock, replacement losses, and any backlog of unfit or overcrowded units are netted out. The result -- positive means dwellings to build, negative means surplus -- is accompanied by every intermediate calculation so the output is fully auditable.

The tool is designed as a batchable identity: it takes six scalars and produces a metric/value table. For scenario analysis, run it repeatedly with different household-size and vacancy assumptions. The typical workflow chains it after Population Projection (horizon population) and before Residential Capacity (can the zoning deliver this need?).

2. Theoretical Background

2.1 The housing-needs identity in planning systems

The needs identity formalises what every housing plan implicitly does: estimate future households, allow for vacancies (a well-functioning market requires some empty units for mobility -- typically 3-5%), subtract what already exists, and add units lost to demolition or absorbed by overcrowding. Variants of this identity underpin the Regional Housing Needs Allocation (RHNA) process in California (since 1969), the Oregon Housing Needs Analysis (OHNA) under HB 2001/2889, the British Columbia HNR Method (2024), and the DRCOG Regional Housing Needs Assessment in Colorado. All share the same structure; they differ in which components are mandatory, how vacancy targets are calibrated, and whether they include a "demand buffer" for local housing-market pressure.

The identity is deliberately simple. Its power lies not in the arithmetic but in making every assumption visible: household size, vacancy target, replacement rate, and backlog are each a policy parameter, not a hidden constant. When a plan claims "we need X units," this tool shows exactly which assumption each component of X rests on.

2.2 Household size as the dominant lever

Liu et al. (2003), in a landmark Nature paper, demonstrated that household dynamics (declining average household size due to fewer children, more divorce, more elderly living alone) can be a larger driver of housing demand and resource consumption than population growth itself. A 0.2-person drop in average household size -- from 2.6 to 2.4, a common trajectory in urbanising societies -- generates more additional dwelling demand from the same population than a decade of moderate growth. This insight is the reason the household-size parameter is the most consequential dial in the entire population-and-housing pipeline. Testing household-size scenarios (e.g. 2.8, 2.5, 2.2) is not sensitivity analysis -- it is the honest needs range.

2.3 Vacancy and market function

A housing market with zero vacancy cannot function: nobody can move, new households cannot form, and prices rise without limit. The standard planning assumption is a target vacancy rate of 3-5%, drawn from the observation that well-functioning rental markets historically maintained roughly this level (CMHC, BC HNR Method; Oregon OHNA uses 5% as the 75th percentile of national vacancy 1980-2000). The vacancy allowance $\text{vacancy\_target} \times \text{households}$ adds "slack" units to the target stock. Critically, this is a target, not a forecast of actual vacancy -- if the plan delivers exactly the target stock and vacancy stays at 2%, the shortfall is 1% of stock, which in large cities can be thousands of units.

2.4 The backlog question

The backlog parameter captures existing unmet need: overcrowded households (by occupancy standards), units unfit for habitation, and concealed households (adults living with parents/friends who would form independent households if suitable housing existed). Most statutory methodologies now include a backlog component (OHNA calls it "current need" including underproduction and homelessness; BC HNR includes "extreme core housing need" and "suppressed households"). If you set backlog to zero, you are asserting that today's housing conditions are adequate -- a claim that should be documented, not defaulted to.

3. Mathematical Formulation

Let:

Step 1 -- Future households:

$$H = \frac{P}{h} \tag{1}$$

Step 2 -- Target dwelling stock (with vacancy allowance):

$$S_{\text{target}} = H \cdot (1 + v) \tag{2}$$

Step 3 -- Net need (positive = units to deliver; negative = surplus):

$$N = S_{\text{target}} - D + R + B \tag{3}$$

Substituting (1) and (2) into (3) yields the full identity in one line:

$$N = \frac{P}{h} \cdot (1 + v) - D + R + B \tag{4}$$

Every term is exposed as an output row: the user (and any reviewer of the plan) can trace exactly how $N$ was derived from the six inputs.

4. Input Data Requirements

InputTypeRequiredNotes
Horizon populationScalarYesFrom Population Projection tool or external demographic forecast. The population of the study area at the plan horizon year.
Household sizeScalarYesExpected average persons per household at the horizon. Obtain from census trend extrapolation or national projections. Typical urban values: 2.0-3.0. Declining trend is near-universal.
Existing dwellingsScalarYesCurrent dwelling stock count. From census, property tax register, or building-permit database. Include all tenure types.

5. Parameters

ParameterTypeDefaultDescription
POP_FUTUREDouble10000.0Horizon population (min 0). Feed from Population Projection.
HH_SIZEDouble2.5Average household size at the horizon (0.5-15.0). The single most influential parameter -- scenario-test this.
EXISTINGDouble3500.0Current dwelling stock (min 0).
VACANCYDouble0.05Vacancy allowance share (0-0.5). Standard range 0.03-0.05. 0 = no slack in the market.
REPLACEMENTDouble0.0Units lost to demolition/obsolescence over the planning period (min 0).
BACKLOGDouble0.0Existing backlog to absorb -- overcrowded/unfit units (min 0). Zero = assert current conditions are adequate.
OUT_SUMMARYVector (table)--Metric/value table with every intermediate calculation.

6. Output Description

FieldTypeDescription
metricStringName of the calculation step
valueDoubleNumeric result for that step

Rows:

MetricMeaning
Horizon population$P$ as entered
Household size$h$ as entered
Future households$H = P / h$
Vacancy allowance$v$ as entered
Target stock (units)$S_{\text{target}}$
Existing dwellings$D$ as entered
Replacement losses$R$ as entered
Backlog$B$ as entered
Dwellings needed$N$ (the final answer; negative = surplus)

7. Symbolic Representation

The output is a table, not a map. Present it as a waterfall chart: start with horizon population, step down through each division and adjustment, and land on dwellings needed. Each bar is one output row. Colour positive contributions (additions to need) in warm tones, negative contributions (deductions) in cool tones. The chart makes immediately visible which assumption dominates the result -- typically household size or backlog.

8. Interpretation Guide

8.1 The sensitivity spread IS the honest range

A single run produces a single number. That number is fragile. Run at least these variants:

The spread between the lowest-need combination and the highest-need combination is the honest planning range. If the plan's housing target falls outside this range, justify why.

8.2 Surplus with housing stress = type mismatch

A negative need (surplus) coexisting with observable housing stress (high rents, overcrowding, homelessness) means the mismatch is not in unit count but in type, tenure, affordability, or location. The identity counts units, not bedrooms, not affordability, not location. A surplus of 4-bedroom suburban houses does nothing for 1-person renters needing city-centre studios. When this pattern appears, the plan needs a tenure/type disaggregation -- this tool identifies the macro-level adequacy; complementary market analysis addresses the distributional question.

8.3 Zero backlog, zero replacement = implicit optimism

Setting both to zero asserts: (a) every existing dwelling is fit and adequately occupied, and (b) no dwelling will be lost to fire, demolition, conversion, or obsolescence over the plan period. Both assertions are almost certainly false. If you lack local data, use national/regional benchmarks: typical annual demolition rates are 0.1-0.3% of stock; backlog estimates can be derived from census overcrowding statistics (more than 1 person per room) or dwelling-condition surveys. Note the omission explicitly rather than defaulting to zero silently.

8.4 Cross-references

9. Academic References

Liu, J., Daily, G.C., Ehrlich, P.R. & Luck, G.W. (2003). "Effects of household dynamics on resource consumption and biodiversity." Nature, 421, 530-533. DOI: 10.1038/nature01359 verified

Bramley, G. (2007). "The sudden rediscovery of housing supply as a key policy challenge." Housing Studies, 22(2), 221-241. DOI: 10.1080/02673030601132847 verified

Meen, G. & Nygaard, C. (2010). "Housing and the economy." In: Malpass, P. & Rowlands, R. (eds) Housing, Markets and Policy. Routledge.

Bibby, P., Henneberry, J. & Halleux, J.-M. (2020). "Under the radar? 'Soft' residential densification." EPB: Urban Analytics and City Science, 47(1), 102-118. DOI: 10.1177/2399808318772842 verified

Holmans, A.E. (2013). "New estimates of housing demand and need in England, 2011 to 2031." Town & Country Planning, 82(10), 435-441.

State of Oregon DAS (2024). Oregon Housing Needs Analysis Methodology Report.

Province of British Columbia (2024). HNR Method Technical Guidance -- Guidelines for Housing Needs Reports.

===ALGORITHM===

Residential Capacity

Processing ID: planx:residentialcapacity

1. Overview

Computes the remaining dwelling-unit capacity of every parcel under current zoning. For each parcel: potential floorspace = parcel area x floor-area ratio (FAR); buildable floorspace = max(0, potential - existing floorspace); dwelling units = floor(buildable x net-to-gross efficiency / average unit size). The flooring is deliberately conservative -- fractional units do not exist. Outputs a parcel layer with buildable floorspace and unit capacity, plus an optional district-level roll-up. The total capacity, suitably discounted (typically to 50-70% over a plan horizon), is compared against the Housing Needs Assessment to determine whether the plan's zoning can physically deliver the required dwellings.

2. Theoretical Background

2.1 Floor Area Ratio as the zoning instrument

Floor Area Ratio (FAR) -- the ratio of total building floor area to parcel area -- is the most widely used instrument for regulating built density in zoning ordinances worldwide. Originating in the 1916 New York City Zoning Resolution and formalised in the 1961 revision, FAR replaced height-and-setback rules with a single, economically meaningful parameter: a developer can spread the permitted floor area over few storeys (low-rise, large footprint) or many storeys (tower, small footprint), but the total buildable area is fixed by FAR x parcel area. In residential capacity analysis, FAR serves as the upper bound on potential floorspace from which existing development is subtracted to yield remaining capacity.

2.2 Capacity, not supply

The distinction between zoning capacity (what rules allow) and market supply (what gets built) is fundamental. Capacity analysis identifies the physical upper bound of the current zoning envelope. Multiple factors cause actual development to fall short:

Standard planning practice applies a discount factor of 50-70% to theoretical capacity for horizon-year planning. This is not pessimism -- it is empirical observation across dozens of plan reviews.

2.3 Net-to-gross efficiency

The efficiency factor $e \in [0.1, 1.0]$ (default 0.85) accounts for the share of gross floorspace that is actually sellable/rentable residential area. The remaining 15% (in the default) goes to common circulation (corridors, stairs, lifts), service cores, plant rooms, and structural elements. In high-rise typologies with double-loaded corridors, efficiency may drop to 0.75-0.80; in walk-up or point-block typologies, it can reach 0.90. The parameter exists so the analyst can calibrate to local building typologies -- use 0.80 for suburban apartment blocks, 0.90 for terrace/semi-detached.

3. Mathematical Formulation

Let parcel $i$ have area $A_i$ (in square map units, typically m$^2$) and zoning floor-area ratio $\text{FAR}_i$. The potential floorspace is:

$$F_i^{\text{pot}} = A_i \cdot \max(0, \text{FAR}_i) \tag{1}$$

If existing floorspace $E_i$ is provided, the buildable floorspace is the residual after subtracting what already stands:

$$F_i^{\text{build}} = \max\left(0,\; F_i^{\text{pot}} - E_i\right) \tag{2}$$

Finally, the dwelling-unit capacity is the buildable floorspace multiplied by the net-to-gross efficiency $e$ and divided by the average unit size $u$, rounded down to whole dwellings:

$$C_i = \left\lfloor \frac{F_i^{\text{build}} \cdot e}{u} \right\rfloor \tag{3}$$

where $\lfloor \cdot \rfloor$ is the floor function (greatest integer less than or equal to the argument). The rounding-down is conservative: a parcel with 0.9 units of capacity is reported as 0, not 1.

The total theoretical capacity across all $n$ parcels is:

$$C_{\text{total}} = \sum_{i=1}^{n} C_i \tag{4}$$

and the planning capacity (for comparison against housing need) is:

$$C_{\text{plan}} = d \cdot C_{\text{total}} \tag{5}$$

where $d \in [0.3, 0.7]$ is the discount factor (not applied by the tool; applied by the analyst post-hoc).

5. Parameters

ParameterTypeDefaultDescription
PARCELSVector (Polygon)--Parcel layer. Must be in a projected CRS (area in m$^2$).
FAR_FIELDField (Numeric)--Floor area ratio per parcel. Non-numeric or missing values counted as 0 with a warning.
EXISTING_FIELDField (Numeric)-- (optional)Existing above-ground floorspace (m$^2$). Omit to compute gross capacity ignoring existing development.
DISTRICT_FIELDField-- (optional)Grouping field for the district roll-up (e.g. neighbourhood, planning zone).
UNIT_SIZEDouble90.0Average dwelling size in m$^2$ (10-1000). Typical: 60-80 m$^2$ for apartments, 100-150 m$^2$ for houses.
EFFICIENCYDouble0.85Net-to-gross efficiency (0.1-1.0). 0.85 = 15% of floorspace is non-sellable.
OUT_PARCELSVector (Polygon)--Parcel output with capacity fields.
OUT_DISTRICTSVector (table)--District roll-up: total area, buildable floorspace, and units per district.

6. Output Description

Parcel output (OUT_PARCELS):

FieldTypeDescription
buildable_m2DoubleRemaining buildable floorspace (m$^2$) after subtracting existing. 0 = at or above zoned capacity.
cap_unitsIntegerDwelling-unit capacity (rounded down to whole units).

(Plus all original parcel fields.)

District roll-up (OUT_DISTRICTS):

FieldTypeDescription
districtStringDistrict identifier from the grouping field
area_m2DoubleTotal parcel area in the district
buildable_m2DoubleTotal buildable floorspace in the district
cap_unitsIntegerTotal unit capacity in the district

8. Interpretation Guide

8.1 Report both theoretical and discounted capacity

Quote two numbers: the raw tool output (theoretical zoning capacity) and the discounted figure (50-70% of theoretical). The theoretical number answers "what does the zoning allow?"; the discounted number answers "what is likely to materialise in the plan horizon?" Both are honest if labelled. A plan that claims the theoretical figure as the deliverable target without discounting will face implementation failure.

8.2 Concentration risk

Plot cap_units by parcel. If the top 3 parcels account for more than 50% of total capacity, the supply is fragile: one landowner stalling, one viability issue, or one infrastructure bottleneck can eliminate half the plan's housing pipeline. A healthy capacity distribution has the top 10% of parcels contributing no more than 20-25% of total units.

8.3 cap_units = 0 with high FAR = built-out districts

Parcels where cap_units = 0 despite a high FAR are built-out: the existing development already exceeds or meets the zoning envelope. These districts cannot contribute additional units without an upzoning (increase in FAR). Map them -- they are the "full" districts that will resist growth absorption unless policy changes.

8.4 Cross-references

8.5 Pitfalls

9. Academic References

Bibby, P., Henneberry, J. & Halleux, J.-M. (2020). "Under the radar? 'Soft' residential densification." EPB: Urban Analytics and City Science, 47(1), 102-118. DOI: 10.1177/2399808318772842 verified

Ewing, R. & Cervero, R. (2010). "Travel and the built environment: a meta-analysis." Journal of the American Planning Association, 76(3), 265-294. DOI: 10.1080/01944361003766766 verified

Seattle Department of Planning and Development (2014). Zoned Development Capacity Model Primer.

ABAG (2021). Draft RHNA Methodology Report 2023-2031. Association of Bay Area Governments.

Ewing, R., Pendall, R. & Chen, D. (2002). Measuring Sprawl and Its Impact. Smart Growth America.

Cheshire, P. & Sheppard, S. (2002). "The welfare economics of land use planning." Journal of Urban Economics, 52(2), 242-269. DOI: 10.1016/S0094-1190(02)00003-7 verified

===ALGORITHM===

Allocate Population Growth

Processing ID: planx:popallocate

1. Overview

Distributes a population increment across spatial units (parcels or zones) using largest-remainder apportionment, the Hare-Niemeyer method. Given a total increment $T$ and a weight $w_i$ per unit, the algorithm computes the quota $T \cdot w_i / \sum w_j$, assigns the integer floor to each unit, and allocates the remaining $R$ units one-by-one to the units with the largest fractional remainders. The result sums exactly to $T$ -- no rounding error, no drift. Weights can come from a capacity field (e.g. remaining dwelling units from Residential Capacity), a custom field, or default to uniform (each unit gets an equal share).

This is the bridge between an aggregate growth figure and a spatial population distribution: the allocated population, added to the current baseline, becomes the horizon-year demand surface that downstream tools (Facility Adequacy, Green Access, Transit Access, noise/air receivers) consume.

2. Theoretical Background

2.1 Apportionment theory

The problem of allocating an integer total proportionally to weights -- the apportionment problem -- has been studied since the founding of the United States, where the Constitution requires seats in the House of Representatives to be apportioned among states by population. The largest- remainder method (Hamilton's method, also known as Hare-Niemeyer or Vinton's method) was the first method adopted (1792-1840) and remains the third most commonly used electoral system worldwide after d'Hondt and Sainte-Lague (Pukelsheim, 2017).

The method is defined by three properties:

  1. Quota rule. Each unit's allocation is either the floor or the ceiling of its exact quota -- it never deviates by more than one unit from the ideal proportional share.
  2. Exact summation. The sum of all allocations equals the total, by construction.
  3. Determinism. Ties in fractional remainders are broken by index order (the first unit with a given remainder wins). This makes the allocation reproducible: same inputs produce the same output every time.

However, the method does exhibit the Alabama paradox (increasing the total can decrease a unit's allocation) and the population paradox (a unit whose weight grows faster than another can lose seats). In the planning context, these paradoxes are immaterial because the total increment and weights are scenario parameters, not empirical measurements -- the method's guarantee of exact summation outweighs the theoretical imperfections (Balinski & Young, 2001).

2.2 Weights encode the story

The weight field is the spatial policy. Three common choices:

3. Mathematical Formulation

Let there be $n$ spatial units with weights $w_i \geq 0$, and let the total population increment to allocate be $T > 0$ (integer). Define the total weight $W = \sum_{i=1}^{n} w_i$.

Step 1 -- Quota calculation. Each unit's exact proportional share (quota) is:

$$q_i = T \cdot \frac{w_i}{W} \tag{1}$$

If $W = 0$ (all weights zero), uniform weights $w_i = 1$ are substituted.

Step 2 -- Integer allocation. Assign the integer floor to each unit:

$$a_i = \lfloor q_i \rfloor \tag{2}$$

The remainder (unallocated units) is:

$$R = T - \sum_{i=1}^{n} a_i \tag{3}$$

Step 3 -- Largest-remainder distribution. Define the fractional remainder $r_i = q_i - a_i$. Sort units by $r_i$ descending, breaking ties by ascending index $i$. Assign one additional unit to each of the first $R$ units in this sorted order:

$$a_i \leftarrow a_i + 1 \quad \text{for the } R \text{ units with largest } r_i \tag{4}$$

The final allocation $\mathbf{a} = (a_1, \ldots, a_n)$ satisfies:

$$\sum_{i=1}^{n} a_i = T \quad \text{and} \quad a_i \in \{\lfloor q_i \rfloor, \lceil q_i \rceil\} \tag{5}$$

5. Parameters

ParameterTypeDefaultDescription
PARCELSVector (Polygon/Point)--Spatial units to receive population. Points or polygons accepted.
INCREMENTInteger100Total population to allocate (min 0).
CAPACITY_FIELDField (Numeric)-- (optional)Remaining dwelling capacity per unit. If provided and no weight field given, the allocation is proportional to capacity. Non-numeric treated as 0.
WEIGHT_FIELDField (Numeric)-- (optional)Custom allocation weight per unit. Overrides capacity field. Non-numeric treated as 0.
OUTPUTVector--Input features with allocated field appended.

6. Output Description

FieldTypeDescription
allocatedIntegerPopulation assigned to this unit. Sums to INCREMENT across all features. 0 for zero-weight units.

(Plus all original input fields.)

8. Interpretation Guide

8.1 allocated is a distribution, not a forecast

The allocation is a bookkeeping operation: it places the increment exactly, proportional to the chosen weights. The result is only as meaningful as the weight field. Capacity-weighted allocation says "if growth follows zoning, here is where people land." Custom-weighted allocation says "if growth follows [your variable], here is where people land." Always state the weight basis alongside the result.

8.2 Zero-weight units get nothing

Check that protected areas, water bodies, parks, and fully built-out parcels carry zero weight before interpreting the map. A unit with zero allocated population may be (a) intentionally excluded (protected), (b) at capacity (zero remaining units), or (c) missing a weight value. Verify which case applies by cross-referencing against the source data.

8.3 Building the horizon-year population layer

The output allocated field is the increment, not the total. To create the horizon-year population layer that downstream tools consume: add allocated to the current population field (if one exists) or create a new field horizon_pop = allocated + baseline. If starting from zero (greenfield allocation), allocated is the total.

8.4 Scenario comparison

Run one allocation per scenario weight scheme and compare the spatial distributions using Scenario Compare (A/B):

The spatial pattern differences between these allocations drive all downstream metrics (access scores, facility adequacy, emissions). A small change in total population allocated differently can produce larger changes in downstream metrics than a large change allocated identically.

9. Academic References

Pukelsheim, F. (2017). Proportional Representation: Apportionment Methods and Their Applications. Springer. DOI: 10.1007/978-3-319-64707-4 verified

Balinski, M.L. & Young, H.P. (2001). Fair Representation: Meeting the Ideal of One Man, One Vote, 2nd ed. Brookings Institution Press.

Marshall, A.W., Olkin, I. & Pukelsheim, F. (2002). "A majorization comparison of apportionment methods in proportional representation." Social Choice and Welfare, 19, 885-900. DOI: 10.1007/s003550200164 verified

Schuster, K., Pukelsheim, F., Drton, M. & Draper, N.R. (2003). "Seat biases of apportionment methods for proportional representation." Electoral Studies, 22, 651-676. DOI: 10.1016/S0261-3794(02)00012-8 verified

Hammer, M. & Weber, K. (2010). "Apportionment methods." The Stata Journal, 12(3), 375-392. DOI: 10.1177/1536867X1201200303 verified

14. Green Infrastructure

The Green Infrastructure group evaluates urban green space from two complementary perspectives: access (can people reach green spaces of adequate size?) and connectivity (do green patches form a functional ecological network?). The two tools are designed to be used together: Green Space Access answers the planning-standard question ("does every resident have a park within X metres?"), while Urban Green Connectivity answers the ecological question ("which patches are critical to keeping the green network whole?"). A site that scores well on access but poorly on connectivity may serve recreation but not biodiversity; a site that scores well on connectivity but poorly on access serves ecology but not people. The best green-infrastructure plans close both gaps.

===ALGORITHM===

Green Space Access

Processing ID: planx:greenaccess

1. Overview

Evaluates whether every demand point (building, census block, address) can reach publicly accessible green spaces of different size classes within the distances specified by a park hierarchy standard. The hierarchy is a free-text parameter of the form min_ha=max_distance, ... -- for example, 0.5=300, 2=800, 10=2000 specifies three classes: pocket parks (at least 0.5 ha, within 300 m), neighbourhood parks (at least 2 ha, within 800 m), and district parks (at least 10 ha, within 2 km). Distances are measured on the street network (multi-source Dijkstra from all qualifying greens), with the residual straight-line snap distance added to the network distance. The output reports, per demand point, the network distance to the nearest qualifying green space of each class, whether the distance standard is met (ok=1/0), and how many classes are satisfied in total. A population- weighted coverage summary per class completes the output.

2. Theoretical Background

2.1 Park hierarchy standards

The idea that green spaces should be classified by size and expected to serve populations within a specified catchment distance is a cornerstone of urban planning standards worldwide. The concept was formalised in the UK by English Nature's Accessible Natural Greenspace Standard (ANGSt), first published in 1995 (Harrison et al.) and updated in 2010 (Nature Nearby) and again in 2023 as the Accessible Greenspace Standards under the Green Infrastructure Framework. ANGSt specified that everyone should have: an accessible natural greenspace of at least 2 ha within 300 m; at least 20 ha within 2 km; at least 100 ha within 5 km; and at least 500 ha within 10 km. The 2023 revision added a Doorstep Greenspace criterion (0.5 ha within 200 m) as an alternative for dense urban areas where 2 ha within 300 m is physically unachievable.

Similar hierarchy standards exist internationally:

The hierarchy standard is the parameter -- the tool does not hard-code any particular standard. Bring your own regulation.

2.2 Network vs. Euclidean distance

The choice of distance metric has substantial consequences for the results. Higgs, Fry & Langford (2012) systematically compared Euclidean buffers, network distance, and travel-time isochrones for green-space accessibility assessment in Wales. They found that Euclidean (straight-line) methods overestimate the served population by 15-40% compared to network methods, because a green space that is 280 m "as the crow flies" may require a 500 m walk if a river, railway, or walled superblock stands in between. The PlanX implementation uses network distance with a straight-line snap offset (the distance from the demand point to the nearest network node, plus the distance from the green space to its nearest network node), which is the standard compromise between full network routing (expensive) and Euclidean approximation (inaccurate).

2.3 Per-capita green provision

A secondary metric logged by the tool is city-wide green area per capita (total green area / total population). The WHO recommends a minimum of 9 m$^2$ per capita, with an ideal of 50 m$^2$ (in practice, observed values range from <1 m$^2$ in dense informal settlements to >100 m$^2$ in low-density garden cities). This metric can mask severe spatial inequity: a city with one 100-ha park at the edge and dense population at the centre scores well on per-capita area but abysmally on access. The access tool exists to expose exactly that discrepancy.

3. Mathematical Formulation

Let the hierarchy define $K$ classes, each a pair $(s_k, d_k)$ where $s_k$ is the minimum area (ha) and $d_k$ is the maximum network distance (metres). Let $\mathcal{G}$ be the set of $M$ green spaces with areas $A_j$ and representative points (centroids or entrance points). A green space qualifies for class $k$ if:

$$A_j \geq s_k \cdot 10\,000 \quad [\text{m}^2] \tag{1}$$

For each class $k$, let $\mathcal{Q}_k = \{j \in \mathcal{G} : A_j \geq s_k \cdot 10\,000\}$ be the qualifying subset. A multi-source Dijkstra run from all qualifying greens assigns to each network node $v$ the shortest-path distance to the nearest qualifying green, adjusted by the snap offset:

$$D_k(v) = \min_{j \in \mathcal{Q}_k} \left[ d_{\text{net}}(g_j, v) + \delta_j \right] \tag{2}$$

where $d_{\text{net}}(g_j, v)$ is the shortest-path distance on the street network from the snap-node of green $j$ to node $v$, and $\delta_j$ is the straight-line distance from green $j$'s representative point to its snap-node.

For each demand point $i$ (mapped to its nearest network node $v_i$), the class-$k$ distance is:

$$d_{k,i} = \delta_i^{\text{demand}} + D_k(v_i) \tag{3}$$

where $\delta_i^{\text{demand}}$ is the straight-line snap distance from demand point $i$ to $v_i$.

The standard-compliance flag for demand point $i$ and class $k$ is:

$$\text{ok}_{k,i} = \begin{cases} 1 & \text{if } d_{k,i} \leq d_k \text{ and is finite} \\ 0 & \text{otherwise} \end{cases} \tag{4}$$

The classes met for demand point $i$ is:

$$M_i = \sum_{k=1}^{K} \text{ok}_{k,i} \tag{5}$$

The population-coverage share for class $k$ is:

$$\text{Coverage}_k = \frac{\sum_{i: \text{ok}_{k,i}=1} \text{pop}_i}{\sum_i \text{pop}_i} \tag{6}$$

5. Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)--Street network. Must be in projected CRS. Crossing lines should share nodes (use Prepare Network first).
DEMANDVector (any geometry)--Demand points (buildings, blocks, addresses). Centroids used for polygon inputs.
POP_FIELDField (Numeric)-- (optional)Population per demand point. Omitted = each point counts as 1 person.
GREENSVector (Polygon)--Public green space polygons. Representative points (centroids) used for all network queries.
HIERARCHYString"0.5=300, 2=800, 10=2000"Park hierarchy: min_ha=max_dist pairs separated by commas or semicolons. The standard for your jurisdiction.
OUT_DEMANDVector (Point)--Demand points with access metrics appended.
OUT_SUMMARYVector (table)--Coverage summary per hierarchy class.

6. Output Description

Demand output (OUT_DEMAND):

FieldTypeDescription
d_c1, d_c2, ...DoubleNetwork distance to nearest qualifying green for classes 1, 2, ... (null if unreachable)
ok_c1, ok_c2, ...Integer (0/1)Whether the distance standard is met for each class
classes_metIntegerNumber of hierarchy classes satisfied (0 to K). 0 = green desert.

(Plus all original demand fields.)

Summary output (OUT_SUMMARY):

FieldTypeDescription
classIntegerHierarchy class number (1, 2, ...)
min_haDoubleMinimum green-space size for this class
max_distDoubleMaximum distance standard for this class
covered_popDoublePopulation meeting the standard for this class
coverage_pctDoublePercentage of total population covered
n_greensIntegerNumber of green spaces qualifying for this class

8. Interpretation Guide

8.1 The coverage ladder

The summary table forms a coverage ladder: coverage typically decreases as the class index increases (small pocket parks cover most people; large district parks cover far fewer). Cities commonly score 80-95% on class 1 (pocket parks within 300 m) and 30-50% on class 3 (district parks within 2 km). A sharp drop between classes indicates that green space is fragmented into many small pieces without the large destination parks that make a green network useful for recreation and biodiversity.

8.2 Green deserts (classes_met = 0)

Demand points with classes_met = 0 are green deserts: they fail every hierarchy class. Map them and overlay on population density. The combination of high population density and zero classes met defines the planning priority: a new green space here will serve more people per hectare than anywhere else. If land acquisition is impossible (dense old city), consider pocket parks, green roofs, or street-tree planting as partial mitigations.

8.3 Green per capita vs. access

The log reports city-wide green area per capita. This number can be misleading. A city of 1 million with a 500-ha forest at the northern boundary has 5 m$^2$ per capita and looks adequate -- but if the entire population lives in the southern half beyond a 2-km walk, the access metric will be near zero. Always report per-capita area and access coverage together. When they disagree, the access metric is the more policy-relevant number for everyday recreation.

8.4 Cross-references

8.5 Pitfalls

9. Academic References

Higgs, G., Fry, R. & Langford, M. (2012). "Investigating the implications of using alternative GIS-based techniques to measure accessibility to green space." Environment and Planning B, 39(2), 326-343. DOI: 10.1068/b37130 verified

Handley, J., Pauleit, S., Slinn, P., Barber, A., Baker, M., Jones, C. & Lindley, S. (2003). "Accessible Natural Greenspace Standards in Towns and Cities." English Nature Research Report 526.

Natural England (2010). Nature Nearby: Accessible Natural Greenspace Guidance. NE265.

Li, L., Du, Q., Ren, F. & Ma, X. (2019). "Assessing Spatial Accessibility to Hierarchical Urban Parks by Multi-Types of Travel Distance in Shenzhen, China." International Journal of Environmental Research and Public Health, 16(6), 1038. DOI: 10.3390/ijerph16061038 verified

WHO Regional Office for Europe (2016). Urban green spaces and health: A review of evidence. Copenhagen.

Wang, S., Wang, M. & Liu, Y. (2021). "Access to urban parks: Comparing spatial accessibility measures using three GIS-based approaches." Computers, Environment and Urban Systems, 90, 101713. DOI: 10.1016/j.compenvurbsys.2021.101713 verified

Tzoulas, K., Korpela, K., Venn, S., Yli-Pelkonen, V., Kazmierczak, A., Niemela, J. & James, P. (2007). "Promoting ecosystem and human health in urban areas using Green Infrastructure." Landscape and Urban Planning, 81(3), 167-178. DOI: 10.1016/j.landurbplan.2007.02.001 verified

===ALGORITHM===

Urban Green Connectivity

Processing ID: planx:greenconnectivity

1. Overview

Evaluates the green network as a connected system rather than a collection of isolated patches. Two patches are considered linked when their edge-to-edge separation is at most a user-specified maximum gap distance. The algorithm builds a graph where patches are nodes and links represent proximity, finds connected components via depth-first search, and computes two indices from Saura & Pascual-Hortal (2007): the Probability of Connectivity (PC) -- the equivalent probability that two random points in the green system fall within the same connected component -- and the per-patch importance (dPC) -- the percentage of PC lost if that patch and its links were removed. The output identifies which patches are structurally critical: small stepping-stone patches often carry disproportionately large dPC values, making the case for their protection.

2. Theoretical Background

2.1 Habitat availability, not just connectivity

Pascual-Hortal & Saura (2006) established that landscape connectivity must be considered within the wider concept of habitat availability: a patch itself is a space where connectivity occurs. The integral index of connectivity (IIC) and later the probability of connectivity (PC) integrate both within-patch area (how much habitat each patch provides) and between-patch connectivity (whether patches are linked) into a single measure. This resolves a fundamental flaw of earlier indices: a landscape of one large patch (covering the entire study area) should score maximum connectivity, not zero. Most earlier indices returned zero for this case because they defined connectivity only between distinct patches.

Saura & Pascual-Hortal (2007) introduced PC and demonstrated through a systematic 13-property evaluation that it is the only index among those tested that: (a) does not increase with fragmentation, (b) correctly handles single-patch landscapes, (c) detects the critical importance of stepping-stone patches, and (d) has a bounded range [0, 1]. The binary version used in PlanX (all pairs within the gap counted as connected with weight 1) is the simplest form; the full probabilistic PC weights connections by a dispersal kernel (e.g. exponential decay with distance). The binary version is adequate for planning screening where the gap threshold is the policy parameter.

2.2 Graph-theoretic foundation

The approach treats green patches as nodes in a planar graph where an edge exists between patches $i$ and $j$ when $\text{distance}(i, j) \leq d_{\max}$. This is a proximity graph, specifically a distance-threshold graph (also called a geometric graph or unit-disk graph in computational geometry). The connected components of this graph are the functional green networks -- sets of patches that a target organism or pedestrian can traverse without crossing a gap wider than $d_{\max}$. The choice of $d_{\max}$ is the critical parameter: 100 m models pedestrian permeability, 50 m models small-mammal dispersal (hedgehogs), 500 m models bird movement. Run at 2-3 different gaps to test robustness.

2.3 The stepping-stone paradox

A key finding from Saura & Pascual-Hortal's empirical application to goshawk habitat in Catalonia was that the maximum dPC value (1.27%) belonged to a single 1 km$^2$ stepping-stone patch -- 0.04% of total habitat area -- whose removal would disconnect two large habitat clusters. This is the planning argument the tool operationalises: hectares understate ecological role. A small patch with high dPC is not a candidate for development; it is the keystone of the green network.

3. Mathematical Formulation

Let there be $n$ patches with areas $a_i > 0$ (in m$^2$). Let $\mathcal{E} = \{(i, j) : \text{dist}(i, j) \leq d_{\max}, i < j\}$ be the set of edges linking patches whose edge-to-edge distance is at most the maximum gap $d_{\max}$.

Connected components. The graph $G = (\{1,\ldots,n\}, \mathcal{E})$ is partitioned into $C$ connected components $\mathcal{C}_1, \ldots, \mathcal{C}_C$ via depth-first search. Each patch $i$ receives a component label $\ell_i \in \{0, \ldots, C-1\}$.

Binary Probability of Connectivity (PC). Summing over components, the PC index is the probability that two randomly chosen points within the total green area fall in the same component:

$$\text{PC} = \frac{\sum_{c=1}^{C} \left(\sum_{i \in \mathcal{C}_c} a_i\right)^2}{\left(\sum_{i=1}^{n} a_i\right)^2} \tag{1}$$

The numerator sums the squared component areas; the denominator is the square of total green area $A^2$. PC ranges from $1/n$ (all $n$ patches isolated and equal-sized) to $1.0$ (fully connected system).

Per-patch importance (dPC). For each patch $i$, compute the PC of the system with patch $i$ (and all its incident edges) removed. The importance of patch $i$ is the percentage decrease in PC:

$$\text{dPC}_i = 100 \cdot \frac{\text{PC} - \text{PC}^{-i}}{\text{PC}} \quad [\%] \tag{2}$$

where $\text{PC}^{-i}$ is the PC index computed on the sub-system of $n-1$ patches after removing patch $i$. If PC = 0, dPC$_i = 0$ by definition.

Component-level statistics. For each component $c$, the total area is:

$$A_c = \sum_{i \in \mathcal{C}_c} a_i \tag{3}$$

The largest component share is $\max_c A_c / A$, and the number of components $C$ is a direct measure of fragmentation.

Computational note: The per-patch dPC loop is $O(n^2)$ and the tool caps input at 400 patches. For larger patch sets, dissolve adjacent patches (merge patches whose boundaries touch -- they are already physically connected) or spatially filter (remove patches below a minimum area threshold) before running.

5. Parameters

ParameterTypeDefaultDescription
GREENSVector (Polygon)--Green patches. Must be in projected CRS. At least 2 patches; max 400.
MAX_GAPDouble100.0Maximum edge-to-edge gap to count patches as linked (map units). This IS the policy parameter -- state it with every result. Run 2-3 values to test robustness.
OUT_PATCHESVector (Polygon)--Patches with connectivity attributes appended.
OUT_SUMMARYVector (table)--One-row connectivity summary.

6. Output Description

Patch output (OUT_PATCHES):

FieldTypeDescription
comp_idIntegerConnected component identifier (1-based). Patches in the same component share the same comp_id.
area_m2DoublePatch area (m$^2$). From input geometry.
comp_m2DoubleTotal area of this patch's component (m$^2$).
dpcDoublePatch importance: percent of PC lost if this patch is removed. 0 = no network role.

Summary output (OUT_SUMMARY):

FieldTypeDescription
metricStringMetric name
valueDoubleMetric value

Metrics: Patches, Links, Components, PC index, Largest component (m$^2$), Top patch dPC.

8. Interpretation Guide

8.1 dPC is the protection-priority list

Sort patches by dPC descending. The top-ranked patch is the one whose loss most fragments the green network. The planning surprise -- documented by Saura & Pascual-Hortal in their goshawk study and replicated in countless applications -- is that a small stepping-stone patch often carries the highest dPC. Its hectares understate its role completely. This is the evidence exhibit for protecting a site whose area-based metrics would not flag it as significant.

However, also check the largest component in area terms: its patches may have low individual dPC (removing any one barely moves PC) but the component as a whole is the ecological backbone. Protection priorities should balance individual dPC (keystones) and component membership (backbone).

8.2 PC as scenario currency

Rerun the tool with a candidate intervention (new park, green corridor, pocket park) added to the patches layer. The change in PC quantifies the connectivity gain. Compare interventions:

The stepping stone often wins. This is the argument that saves small parcels from development: not because of their intrinsic habitat value (which may be modest) but because they are the glue holding the green network together.

8.3 The max_gap parameter IS the ecological model

The gap threshold encodes the mobility assumption. 100 m suits pedestrians and sedentary species; 500 m suits birds and mobile mammals. Always state the gap with every result. Run at 2-3 gaps: if the critical-patch list is stable across gaps, the findings are robust to ecological uncertainty; if it flips (entirely different patches at the top for 50 m vs. 500 m), the network assessment is gap-sensitive and both results should be reported.

8.4 Cross-references

9. Academic References

Saura, S. & Pascual-Hortal, L. (2007). "A new habitat availability index to integrate connectivity in landscape conservation planning: Comparison with existing indices and application to a case study." Landscape and Urban Planning, 83(2-3), 91-103. DOI: 10.1016/j.landurbplan.2007.03.005 verified

Pascual-Hortal, L. & Saura, S. (2006). "Comparison and development of new graph-based landscape connectivity indices: towards the priorization of habitat patches and corridors for conservation." Landscape Ecology, 21(7), 959-967. DOI: 10.1007/s10980-006-0013-z verified

Taylor, P.D., Fahrig, L., Henein, K. & Merriam, G. (1993). "Connectivity is a vital element of landscape structure." Oikos, 68(3), 571-573. DOI: 10.2307/3544927 verified

Urban, D. & Keitt, T. (2001). "Landscape connectivity: a graph-theoretic perspective." Ecology, 82(5), 1205-1218. DOI: 10.1890/0012-9658(2001)082[1205:LCAGTP]2.0.CO;2 verified

Moilanen, A. & Nieminen, M. (2002). "Simple connectivity measures in spatial ecology." Ecology, 83(4), 1131-1145. DOI: 10.1890/0012-9658(2002)083[1131:SCMISE]2.0.CO;2 verified

Benedict, M.A. & McMahon, E.T. (2006). Green Infrastructure: Linking Landscapes and Communities. Island Press.

Kong, F., Yin, H., Nakagoshi, N. & Zong, Y. (2010). "Urban green space network development for biodiversity conservation." Landscape and Urban Planning, 95(1-2), 16-27. DOI: 10.1016/j.landurbplan.2009.11.001 verified

15. Urban Growth

The Urban Growth group provides the analytical backbone for understanding and projecting urban expansion. Three tools form a logical pipeline: Land-Cover Change Analysis diagnoses the historical pattern of conversion (what became what, at what rate, and whether the transitions are systematic or random); Urban Growth Simulation (CA) projects the spatial pattern of future expansion under specified demand, suitability, and constraint conditions; and Urban Sprawl Metrics quantifies the efficiency and form of that expansion through the SDG 11.3.1 LCRPGR indicator and complementary shape metrics. Together, they answer the three questions every growth-management plan must address: where did growth go (change analysis), where will it go under current trends (simulation), and is the resulting pattern efficient or wasteful (sprawl metrics)?

===ALGORITHM===

Land-Cover Change Analysis

Processing ID: planx:landcoverchange

1. Overview

Cross-tabulates two integer-class land-cover rasters cell by cell to produce the standard transition matrix -- the fundamental accounting table of land-change science. For every ordered pair of classes, the matrix reports the number of cells that were class $i$ at time 1 and class $j$ at time 2, plus the corresponding area in hectares (using the cell size of the first raster). The diagonal entries are persistence; off-diagonal entries are conversions. Two output tables are produced: a full transitions table (one row per from-to pair with non-zero count) and a class summary table (per-class area at both dates, persistence, gains, losses, and net change). The log identifies the single largest conversion by cell count -- typically the headline finding of the study.

2. Theoretical Background

2.1 The transition matrix in land-change science

The transition matrix (also called the cross-tabulation matrix, change matrix, or confusion matrix when applied to classification accuracy) is the de facto standard for reporting post-classification land-cover change. Its intellectual foundations lie in discrete-time Markov chain theory, where a transition probability matrix $P$ describes the probability of moving from state $i$ to state $j$ in one time step. In land-change applications, the matrix is typically expressed in area units (cell counts or hectares) rather than probabilities, because land change is not a stationary Markov process and the "probability" interpretation is tenuous over anything but the shortest time intervals.

Pontius, Shusas & McEachern (2004) provided the canonical methodological treatment, demonstrating that the naive interpretation of the largest off-diagonal entry as the "most important change" can be misleading. A large transition from class A to class B may occur simply because A and B are the two largest classes, not because B is systematically targeting A. They introduced the decomposition of total change into quantity change (net) and allocation change (swap), showing that net change can dramatically underestimate total landscape dynamism -- a landscape with zero net forest change may have simultaneously lost forest here and gained it there, a very different ecological story from stability.

2.2 Intensity analysis and systematic transitions

Aldwaik & Pontius (2012) formalised intensity analysis, a hierarchical framework that tests whether observed transition intensities deviate from a uniform null model at three levels: time interval (is the overall rate of change uniform across intervals?), category (does each category gain/lose with uniform intensity?), and transition (does each gaining category target or avoid particular losing categories?). A transition is systematic when the observed intensity significantly exceeds the uniform expectation, implying a structured process (policy, market preference, ecological constraint) rather than random allocation. PlanX does not implement the full intensity analysis framework, but the transition matrix it produces is the input that would feed it.

2.3 Classification error and the noise floor

Every land-cover map contains classification error (typically 5-20% depending on class complexity and sensor resolution). In a transition matrix, classification error manifests as impossible or highly implausible transitions: water to forest, urban to bare soil, forest to water -- transitions that violate the known physical dynamics of the landscape over the study period. The proportion of cells in such transitions provides an estimate of the data's noise floor: real changes should be interpreted against this baseline. Pontius et al. (2004) recommend reporting the persistence-adjusted transition intensities and flagging transitions that are smaller than the estimated classification error.

2.4 Category aggregation effects

Pontius & Malizia (2004) demonstrated that aggregating land-cover categories (e.g. from Level II to Level I in the Anderson classification) systematically reduces total change and net change, but can either increase or decrease swap (location change). This is because transitions between aggregated categories move onto the diagonal (persistence), making the landscape appear more stable than it is. The principle is: always analyse at the finest available categorical resolution, then aggregate for reporting only with an explicit caveat about the hidden within-class dynamics.

3. Mathematical Formulation

Let $L_1$ and $L_2$ be two integer-class rasters of identical dimensions $R \times C$, representing land cover at time 1 and time 2. Let $\mathcal{C} = \{c_1, \ldots, c_K\}$ be the sorted union of class values appearing in either raster, with $K = |\mathcal{C}|$.

Transition matrix. The raw count matrix $\mathbf{M} \in \mathbb{N}^{K \times K}$ is defined element-wise as:

$$M_{a,b} = \left|\left\{(r, c) : L_1[r, c] = c_a \;\land\; L_2[r, c] = c_b \;\land\; \text{valid}(r,c)\right\}\right| \tag{1}$$

where $\text{valid}(r,c)$ excludes cells where either raster contains NoData. The corresponding area matrix in hectares is:

$$M^{\text{ha}}_{a,b} = M_{a,b} \cdot \frac{\text{pixel}^2}{10\,000} \tag{2}$$

Per-class statistics. For each class $a$:

$$\text{persisted}_a = M_{a,a} \quad \text{(diagonal)} \tag{3}$$ $$\text{lost}_a = \sum_{b \neq a} M_{a,b} = \left(\sum_b M_{a,b}\right) - M_{a,a} \tag{4}$$ $$\text{gained}_a = \sum_{b \neq a} M_{b,a} = \left(\sum_b M_{b,a}\right) - M_{a,a} \tag{5}$$ $$\text{net}_a = \text{gained}_a - \text{lost}_a \tag{6}$$

Total change decomposition. The total change $\Delta$ (sum of all off-diagonal cells) can be decomposed as:

$$\Delta = \sum_{a} \text{lost}_a = \sum_{a} \text{gained}_a = \text{Net} + \text{Swap} \tag{7}$$

where Net = $\frac{1}{2}\sum_a |\text{net}_a|$ (half the sum of absolute net changes) and Swap = $\Delta - \text{Net}$.

5. Parameters

ParameterTypeDefaultDescription
RASTER_T1Raster (Integer)--Land-cover classification at time 1. Must share extent and cell size with T2.
RASTER_T2Raster (Integer)--Land-cover classification at time 2. Non-integer values are rounded to nearest integer.
CLASS_NAMESString"" (optional)Class labels: "1=Urban, 2=Forest, 3=Water, ..." for readable output. Unlabelled codes appear as their numeric value.
OUT_MATRIXVector (table)--Transition matrix: one row per non-zero from-to pair.
OUT_CLASSESVector (table)--Class summary: one row per class with gains/losses/net.

6. Output Description

Transition matrix (OUT_MATRIX):

FieldTypeDescription
from_classStringClass at time 1 (label or code)
to_classStringClass at time 2 (label or code)
cellsIntegerNumber of cells in this transition
area_haDoubleArea in hectares (cells x pixel$^2$ / 10000)
kindString"Persistence" (diagonal) or "Conversion" (off-diagonal)

Class summary (OUT_CLASSES):

FieldTypeDescription
classStringClass label or code
t1_haDoubleTotal area at time 1 (ha)
t2_haDoubleTotal area at time 2 (ha)
persisted_haDoubleArea unchanged between dates (ha)
lost_haDoubleArea that transitioned away from this class (ha)
gained_haDoubleArea that transitioned into this class (ha)
net_haDoubleNet change: gained minus lost (ha). Positive = growth; negative = decline.

8. Interpretation Guide

8.1 Read the biggest off-diagonal cells

The transition matrix tells one story at a time. Sort by area_ha descending and filter to kind = "Conversion". The top row is the dominant land conversion -- e.g. "Farmland --> Urban, 412 ha." This single sentence defines the study's narrative.

8.2 Never report net change alone

A class with near-zero net change can have massive gross changes (large gains AND large losses in different locations). This is spatial displacement -- forest cleared here, planted there -- and it is fundamentally different from stability. Report persistence, loss, and gain alongside net. The class summary table provides all four; quote them.

8.3 Impossible transitions estimate data quality

Scan the transition table for physically implausible conversions: water to forest in 5 years, urban to farmland at scale. The combined area of these transitions, as a percentage of total changed area, is a rough estimate of classification error in your input data. If impossible transitions account for 15% of all change, a real transition of 10% should be treated cautiously -- it may be within the noise floor. For rigorous treatment, use an error matrix (if available) to adjust transition probabilities.

8.4 Cross-references

9. Academic References

Pontius, R.G., Shusas, E. & McEachern, M. (2004). "Detecting important categorical land changes while accounting for persistence." Agriculture, Ecosystems & Environment, 101(2-3), 251-268. DOI: 10.1016/j.agee.2003.09.008 verified

Aldwaik, S.Z. & Pontius, R.G. (2012). "Intensity analysis to unify measurements of size and stationarity of land changes by interval, category, and transition." Landscape and Urban Planning, 106(1), 103-114. DOI: 10.1016/j.landurbplan.2012.02.010 verified

Pontius, R.G. & Malizia, N.R. (2004). "Effect of Category Aggregation on Map Comparison." In: Geographic Information Science (GIScience 2004). LNCS 3234, Springer, pp. 251-268. DOI: 10.1007/978-3-540-30231-5_17 verified

Pontius, R.G. & Millones, M. (2011). "Death to Kappa: birth of quantity disagreement and allocation disagreement for accuracy assessment." International Journal of Remote Sensing, 32(15), 4407-4429. DOI: 10.1080/01431161.2011.552923 verified

Alo, C.A. & Pontius, R.G. (2008). "Identifying systematic land-cover transitions using remote sensing and GIS." Environment and Planning B, 35(2), 280-295. DOI: 10.1068/b32091 verified

Comber, A.J. (2008). "The use of correspondence analysis to explore land cover change." Computers, Environment and Urban Systems, 32(4), 259-270. DOI: 10.1016/j.compenvurbsys.2008.03.003 verified

Mertens, B. & Lambin, E.F. (2000). "Land-cover-change trajectories in Southern Cameroon." Annals of the Association of American Geographers, 90(3), 467-494. DOI: 10.1111/0004-5608.00205 verified

===ALGORITHM===

Urban Growth Simulation (CA)

Processing ID: planx:growthsim

1. Overview

Simulates the spatial pattern of urban expansion using a constrained cellular automaton (CA) in the tradition of the SLEUTH model (Clarke & Gaydos, 1998). At each growth step, every non-urban, unconstrained cell receives a score:

$$\text{score} = \text{suitability} \cdot (\beta + w \cdot N_{\text{urban}}) + \epsilon$$

where $\beta$ is a base term (enabling leapfrog/spontaneous growth in highly suitable but isolated cells), $w$ is a neighbourhood weight (making growth cling to the existing urban fabric -- edge growth), $N_{\text{urban}}$ is the share of the 8 Moore-neighbour cells that are already urban, and $\epsilon \sim U(0, 10^{-9})$ is a deterministic tie-breaking jitter. The top-scoring cells convert to urban until the step's share of the total land demand (in hectares, converted to cell count) is met. Suitability is internally normalised to [0, 1]; constraint cells (water, protected areas) are blocked; NaN cells in the suitability raster never convert. The same inputs plus the same random seed produce the same map every run -- the model is fully deterministic and thus safe for scenario comparison.

What this model is NOT. It is not a calibrated SLEUTH model. It does not learn growth coefficients from historical time series of urban extent. It does not implement self-modification (growth rate acceleration/deceleration in boom/bust cycles). It does not model road- influenced growth or slope resistance. It is a deliberately simplified, transparent screening CA -- powerful enough to test "what if the trend continues" and "what if we constrain growth to these areas," simple enough that every parameter has a plain-English meaning.

2. Theoretical Background

2.1 Cellular automata in urban modelling

Cellular automata divide the landscape into a regular grid of cells, each occupying one of a finite set of states (urban/non-urban in the simplest case). State transitions are governed by rules that depend on the cell's current state and the states of neighbouring cells. The approach was first applied to urban systems by Tobler (1970) in a model of Detroit, and systematised by Couclelis (1985, 1989) who articulated the theoretical justification: cities exhibit emergent macro-scale patterns (segregation, clustering, sprawl) that arise from micro-scale decision rules applied repeatedly over space and time. CA captures this emergence without requiring a top-down optimising agent.

Batty (1997) provided the definitive primer on CA and urban form, arguing that the approach's strength lies not in predictive accuracy but in its ability to generate "possible futures" -- spatially explicit what-if scenarios that make the consequences of policy choices visible. White & Engelen (1993) developed the first operational CA for land-use dynamics in Cincinnati, demonstrating that simple neighbourhood rules could reproduce fractal urban patterns observed in real cities. Clarke & Gaydos (1998) introduced SLEUTH (Slope, Land use, Exclusion, Urban, Transportation, Hillshade), which remains the most widely applied urban CA model with over 100 documented applications worldwide (Chaudhuri & Clarke, 2013).

2.2 The three CA growth archetypes

The PlanX CA uses two of SLEUTH's four growth types, parameterised explicitly:

  1. Edge (organic) growth -- controlled by $w$, the neighbourhood weight. Cells adjacent to existing urban fabric score higher. This produces contiguous expansion at the urban fringe, the dominant growth mode in most cities. High $w$ discourages leapfrogging.
  2. Spontaneous (diffusive) growth -- controlled by $\beta$, the base term. Even an isolated cell with zero urban neighbours can convert if its suitability is high enough (score $= \text{suit} \cdot \beta$). This produces leapfrog development -- new urban patches detached from the main fabric. High $\beta$ scatters growth; low $\beta$ (approaching 0) confines all growth to the urban edge.

The two growth types not implemented (road-influenced growth and slope resistance) can be partially encoded through the suitability raster: assign higher suitability near roads and lower suitability on steep slopes.

2.3 Determinism and scenario comparison

A key design choice is full determinism given the random seed. The jitter $\epsilon \sim U(0, 10^{-9})$ is so small that it only breaks ties among cells with identical scores -- the spatial pattern is otherwise determined by suitability and neighbourhood. This means that two runs with the same inputs produce identical outputs, and differences between scenarios (trend vs. plan) are attributable entirely to parameter changes, not to stochastic variation. This property is essential for planning applications: a stochastic model would require Monte Carlo averaging to distinguish scenario effects from run-to-run noise.

3. Mathematical Formulation

Let the study area be an $R \times C$ grid. Define:

Suitability normalisation. The raw suitability is linearly rescaled to [0, 1] using only finite values:

$$S^{\text{norm}}_{r,c} = \frac{S_{r,c} - S_{\min}}{S_{\max} - S_{\min}} \cdot \mathbf{1}[\text{finite}(S_{r,c})] \tag{1}$$

where $S_{\min}$ and $S_{\max}$ are the minimum and maximum of all finite suitability values. NaN cells receive $S^{\text{norm}} = 0$.

Neighbourhood urban share. The Moore (8-neighbour) urban share around cell $(r,c)$ is:

$$N_{r,c} = \frac{1}{8} \sum_{dr=-1}^{1} \sum_{dc=-1}^{1} \mathbf{1}[(dr,dc) \neq (0,0)] \cdot U_{r+dr, c+dc} \tag{2}$$

Edge cells count fewer neighbours; the divisor remains 8 (missing neighbours are implicitly non-urban).

Transition score. For each non-urban ($U_{r,c}=0$), unconstrained ($K_{r,c}=0$) cell:

$$\text{score}_{r,c} = S^{\text{norm}}_{r,c} \cdot \left(\beta + w \cdot N_{r,c}\right) + \epsilon_{r,c} \tag{3}$$

where $\epsilon_{r,c} \sim U(0, 10^{-9})$ is drawn from numpy.random.default_rng(seed).

Demand allocation. Let $D$ be the total demand in cells, spread evenly (modulo last step) over $T$ iterations. At step $t$, the allotment is $d_t$ cells. The $d_t$ available cells with the highest scores convert to urban:

$$U_{r,c} \leftarrow 1 \quad \text{for the } d_t \text{ cells with largest } \text{score}_{r,c} \tag{4}$$

The year-of-conversion raster $Y \in \{-1, 0, 1, \ldots, T\}^{R \times C}$ records the step at which each cell converted (0 = initially urban, $t$ = converted at step $t$, -1 = never urban).

5. Parameters

ParameterTypeDefaultDescription
SEEDRaster--Seed urban mask (any non-zero, finite value = urban). Typically the urban class from a land-cover map.
SUITABILITYRaster--Development suitability. Any numeric scale; normalised internally. From Suitability Lab, MCDA, or a distance-to-roads layer.
CONSTRAINTSRaster-- (optional)Exclusion zones: any non-zero, finite value = never develop. Water bodies, protected areas, hazard zones, green belt.
DEMAND_HADouble50.0Total land demand in hectares (min 0.01). Converted to cells internally; if demand exceeds available cells, growth saturates with a warning.
ITERATIONSInteger5Number of growth steps (1-100). Demand is split evenly across steps; more steps = finer sequence resolution.
NEIGH_WEIGHTDouble1.0Edge-growth pull (0-10). 0 = no neighbourhood effect (allocation by suitability alone). High values make growth cling to existing urban fabric.
BASEDouble0.1Leapfrog-growth allowance (0-1). 0 = no spontaneous growth. 0.5+ = substantial scatter. Low base with high suitability cells = planned satellite towns.
RNG_SEEDInteger0Seed for tie-breaking jitter only. Same seed = same map every run.
OUTPUTRaster--Year-of-conversion raster. 0 = initial urban, 1..T = step converted, NoData = still open at horizon.

6. Output Description

BandTypeValuesDescription
Year of conversionFloat32-1 (NoData), 0, 1..T0 = initially urban (seed). k = converted at growth step k. NoData = never converted (still open land at the horizon).

Log output per step: number of cells converted and area (ha). Final log: total urban fabric at horizon and net growth.

8. Interpretation Guide

8.1 This is a WHERE model, not a WHEN forecast

The demand parameter sets how much land converts; the model only chooses where. Step numbers encode sequence ("this converts before that"), not calendar years. If you need calendar-year output, calibrate the number of steps to match the time interval between your historical land-cover maps and set demand to the observed historical absorption rate.

8.2 The two growth regimes

Sweep the neighbourhood weight $w$ and base term $\beta$ to explore growth regimes:

8.3 The conflict analysis

The most policy-relevant product of this tool is not the growth map itself but the conflict overlay: intersect the converted cells (year > 0) with farmland, flood zones, planned green wedges, and environmentally sensitive areas. Each conflict is a planning argument: "under current trends, 60 ha of the designated green wedge would be consumed by step 4." This sentence justifies the constraint. Run without constraints (trend) and with constraints (plan) -- the difference map shows exactly what the plan must resist and where displaced growth lands instead.

8.4 Cross-references

8.5 Pitfalls

9. Academic References

Clarke, K.C. & Gaydos, L.J. (1998). "Loose-coupling a cellular automaton model and GIS: long-term urban growth prediction for San Francisco and Washington/Baltimore." International Journal of Geographical Information Science, 12(7), 699-714. DOI: 10.1080/136588198241617 verified

Batty, M. (1997). "Cellular automata and urban form: a primer." Journal of the American Planning Association, 63(2), 266-274. DOI: 10.1080/01944369708975918 verified

White, R. & Engelen, G. (1993). "Cellular automata and fractal urban form: a cellular modelling approach to the evolution of urban land-use patterns." Environment and Planning A, 25(8), 1175-1199. DOI: 10.1068/a251175 verified

Chaudhuri, G. & Clarke, K.C. (2013). "The SLEUTH Land Use Change Model: A Review." International Journal of Environmental Resources Research, 1(1), 88-105.

Jantz, C.A., Goetz, S.J. & Shelley, M.K. (2003). "Using the SLEUTH urban growth model to simulate the impacts of future policy scenarios on urban land use in the Baltimore-Washington metropolitan area." Environment and Planning B, 31(2), 251-271. DOI: 10.1068/b2983 verified

de Almeida, C.M., Batty, M., Monteiro, A.M.V. et al. (2003). "Stochastic cellular automata modeling of urban land use dynamics: empirical development and estimation." Computers, Environment and Urban Systems, 27(5), 481-509. DOI: 10.1016/S0198-9715(02)00042-X verified

Jantz, C.A., Goetz, S.J., Donato, D. & Claggett, P. (2010). "Designing and implementing a regional urban modeling system using the SLEUTH cellular urban model." Computers, Environment and Urban Systems, 34(1), 1-16. DOI: 10.1016/j.compenvurbsys.2009.08.003 verified

===ALGORITHM===

Urban Sprawl Metrics

Processing ID: planx:sprawlmetrics

1. Overview

Computes SDG Indicator 11.3.1 -- the ratio of the Land Consumption Rate (LCR) to the Population Growth Rate (PGR) -- plus three complementary shape metrics that characterise the form of urban expansion: patch count (number of separate urban islands in the horizon-year mask), largest-patch share (what fraction of total urban area sits in the single largest contiguous patch), and edge density (metres of urban/non-urban boundary per hectare of urban area). Input is two binary urban-extent rasters (any non-zero = urban) and the corresponding population figures. The LCRPGR ratio is the global standard for monitoring urban land-use efficiency; the shape metrics explain how the land was consumed -- compactly, raggedly, or through leapfrog fragmentation.

Classification as SDG Tier I. As of December 2023, Indicator 11.3.1 is classified as Tier I by the UN Inter-Agency and Expert Group on SDG Indicators, meaning it is conceptually clear, has an internationally established methodology, and data are regularly produced by countries covering at least 50% of the population in every region.

2. Theoretical Background

2.1 SDG 11.3.1 -- rationale and interpretation

SDG Target 11.3 calls on countries to "enhance inclusive and sustainable urbanization and capacity for participatory, integrated and sustainable human settlement planning and management." Indicator 11.3.1 operationalises sustainable urbanisation as land-use efficiency: is the city consuming land at a rate commensurate with its population growth? The ratio is designed so that:

UN-Habitat (2018) recommends reporting two secondary indicators alongside LCRPGR: (a) built-up area per capita, and (b) total change in built-up area, to prevent misreading a single ratio. Corbane et al. (2017) note that a single LCRPGR value can miss areas where the built-up footprint shrinks due to disaster, de-urbanisation, or reclassification.

2.2 Beyond the ratio: shape matters

The LCRPGR ratio, while internationally standardised, collapses the spatial pattern of growth into a single number. Schwarz (2010) analysed 231 European cities and identified five dimensions of urban form -- density, centrality, compactness, land-use mix, and polycentricity -- none of which is captured by LCRPGR alone. The shape metrics in this tool address the compactness/fragmentation dimension:

2.3 Limitations of the indicator

As UN-Habitat's own metadata notes, the LCRPGR ratio has known limitations: (a) it can be unstable when population growth is near zero (the denominator approaches 0); (b) aggregating across cities of different sizes and growth regimes can wash out meaningful patterns; (c) it does not distinguish between greenfield development (consuming agricultural/natural land) and brownfield redevelopment (recycling already-urbanised land). For planning applications, always report the raw LCR and PGR alongside the ratio, and triangulate with the shape metrics.

3. Mathematical Formulation

Let $U_1, U_2 \in \{0, 1\}^{R \times C}$ be binary urban masks at times 1 and 2, and $P_1, P_2$ be the corresponding population totals. Let $\text{pixel}$ be the cell size in map units (metres).

Urban areas. The total urban area at each time, in map units squared:

$$A_t = \left(\sum_{r,c} U_t[r,c]\right) \cdot \text{pixel}^2 \quad \text{for } t \in \{1, 2\} \tag{1}$$

Land Consumption Rate (LCR). The annualised rate of urban area change (unitless, typically expressed per annum):

$$\text{LCR} = \frac{\ln(A_2 / A_1)}{y} \tag{2}$$

where $y$ is the number of years between the two observation dates (not used in the PlanX implementation, which computes the numerator only -- the user divides by $y$ for the annualised rate).

Population Growth Rate (PGR). Similarly:

$$\text{PGR} = \frac{\ln(P_2 / P_1)}{y} \tag{3}$$

LCRPGR ratio (SDG 11.3.1). The core indicator:

$$\text{LCRPGR} = \frac{\text{LCR}}{\text{PGR}} = \frac{\ln(A_2 / A_1)}{\ln(P_2 / P_1)} \tag{4}$$

The years $y$ cancel out; the ratio is time-independent. If $A_1 = 0$, $A_2 = 0$, $P_1 = 0$, $P_2 = 0$, or PGR $= 0$, the result is NaN.

Patch metrics (on time-2 mask). Connected components of the urban mask are identified using 4-neighbour (von Neumann) connectivity:

$$\text{n\_patches} = |\mathcal{P}(U_2)| \tag{5}$$

where $\mathcal{P}(U_2)$ is the set of connected urban components.

$$\text{largest\_share} = \frac{\max_{p \in \mathcal{P}} |p|}{\sum_{p \in \mathcal{P}} |p|} \tag{6}$$

Edge density. The total length of the urban/non-urban boundary (counting each orthogonal adjacency as one pixel width), divided by the total urban area expressed in hectares:

$$\text{edge\_density} = \frac{L_{\text{edge}} \cdot \text{pixel}}{A_2 / 10\,000} \quad [\text{m/ha}] \tag{7}$$

where $L_{\text{edge}}$ is the count of cell-face adjacencies where one cell is urban and the other is not (raster edges count as boundary).

5. Parameters

ParameterTypeDefaultDescription
URBAN_T1Raster--Urban extent at time 1 (any non-zero, finite value = urban).
URBAN_T2Raster--Urban extent at time 2. Must share extent and cell size with T1.
POP_T1Double100000.0Population at time 1 (min 1). Must correspond to the area covered by URBAN_T1.
POP_T2Double120000.0Population at time 2 (min 1). Must correspond to the area covered by URBAN_T2.
OUT_SUMMARYVector (table)--Metric/value table with LCRPGR and all shape metrics.

6. Output Description

FieldTypeDescription
metricStringMetric name
valueDoubleMetric value (null if undefined)

Metrics (12 rows):

MetricMeaning
Urban area t1 (ha)$A_1$ in hectares
Urban area t2 (ha)$A_2$ in hectares
Urban growth (pct)$100 \cdot (A_2/A_1 - 1)$
Population t1$P_1$ as entered
Population t2$P_2$ as entered
Population growth (pct)$100 \cdot (P_2/P_1 - 1)$
Land consumption rate (LCR)$\ln(A_2/A_1)$
Population growth rate (PGR)$\ln(P_2/P_1)$
LCRPGR (SDG 11.3.1)LCR / PGR
Urban patches (t2)Number of separate urban islands
Largest patch shareFraction of total urban area in the largest contiguous patch
Edge density (m per ha)Urban boundary length per hectare of urban area

8. Interpretation Guide

8.1 LCRPGR thresholds and benchmarks

From UN-Habitat's global monitoring and academic studies:

8.2 Always report the components

Never quote LCRPGR alone. Always report: LCRPGR = X, with LCR = Y and PGR = Z, over the period [t1 to t2]. A ratio of 2.0 could mean LCR = 0.04 and PGR = 0.02 (steady sprawl) or LCR = 0.002 and PGR = 0.001 (near-stasis, unstable ratio). The components disambiguate.

8.3 Shape metrics diagnose the cost

The shape metrics explain how the land was consumed and, indirectly, what infrastructure it will cost:

8.4 Cross-references

8.5 Pitfalls

9. Academic References

UN-Habitat (2018). SDG Indicator 11.3.1 Training Module: Land Use Efficiency. United Nations Human Settlement Programme, Nairobi.

Schwarz, N. (2010). "Urban form revisited -- Selecting indicators for characterising European cities." Landscape and Urban Planning, 96(1), 29-47. DOI: 10.1016/j.landurbplan.2010.01.007 verified

Corbane, C., Politis, P., Siragusa, A., Kemper, T. & Pesaresi, M. (2017). LUE User Guide: A tool to calculate the Land Use Efficiency and the SDG 11.3 indicator with the Global Human Settlement Layer. Publications Office of the EU. DOI: 10.2760/212689 verified

Ewing, R. & Hamidi, S. (2014). "Measuring urban sprawl and validating sprawl measures." Landscape and Urban Planning, 125, 16-27.

Angel, S., Parent, J., Civco, D.L. & Blei, A.M. (2011). Making Room for a Planet of Cities. Lincoln Institute of Land Policy.

Melchiorri, M., Pesaresi, M., Florczyk, A.J., Corbane, C. & Kemper, T. (2019). "Principles and Applications of the Global Human Settlement Layer as Baseline for the Land Use Efficiency Indicator." ISPRS International Journal of Geo-Information, 8(2), 96. DOI: 10.3390/ijgi8020096 verified

Schiavina, M., Melchiorri, M., Corbane, C. et al. (2022). "Multi-scale estimation of land use efficiency (SDG 11.3.1) across 25 years using global open and free data." Scientific Data, 9, 394. DOI: 10.1038/s41597-022-01523-4 verified

16. Cycling

The Cycling group assesses the bikeability of the street network through the Level of Traffic Stress (LTS) framework. Two tools form a diagnosis-to-intervention pipeline: Cycling Stress (LTS) classifies every street segment LTS 1-4 based on vehicle speed, lane count, traffic volume (AADT), and cycling infrastructure type, producing the stress map; Low-Stress Connectivity drops all segments above a chosen threshold and finds the connected components (islands) of the remaining all-ages network, identifying the barriers that fragment it. Together, they answer the two foundational cycling-planning questions: how stressful is the network (LTS map), and can people actually get anywhere on the low-stress portion (connectivity islands)?

===ALGORITHM===

Cycling Stress (LTS)

Processing ID: planx:cyclingstress

1. Overview

Classifies every street segment into one of four Levels of Traffic Stress (LTS) using a simplified Mekuria/Furth screening rule. The classification depends on three road attributes -- posted speed (km/h), number of travel lanes, and annual average daily traffic (AADT) -- plus a cycling infrastructure type field, which distinguishes three regimes:

All thresholds are editable as key=value text, so an agency can re-tune the classifier to its own design standards. Every segment attribute field is optional -- missing values fall back to user-specified defaults. The output appends LTS class (1-4), human-readable label, segment length, and the attribute values actually used (speed_used, lanes_used, aadt_used, infra_used) for full auditability, plus a length-share summary by LTS class.

2. Theoretical Background

2.1 The four types of cyclists

LTS classification rests on Geller's (2006) taxonomy of cyclist types, derived from survey research in Portland, Oregon:

  1. "Strong and Fearless" (~1% of population): will ride anywhere, regardless of traffic conditions. These riders are already cycling; infrastructure investment does not change their behaviour.
  2. "Enthused and Confident" (~7%): comfortable on most streets but prefer dedicated facilities. Will use bike lanes and cycle tracks.
  3. "Interested but Concerned" (~60%): would like to cycle but are deterred by traffic stress. They will ride only on low-stress routes: quiet streets, protected lanes, separated paths. This is the target demographic for cycling policy -- converting even a fraction of this group from car to bike transforms mode share.
  4. "No Way No How" (~32%): will not cycle under any circumstances. Not a target for infrastructure investment.

Geller's proportions are city-specific; subsequent studies (Dill & McNeil, 2013) found similar splits with the "interested but concerned" group consistently the largest. The LTS framework maps these four types to stress levels: LTS 1 = children (and the "no way no how" group, if they could be persuaded), LTS 2 = "interested but concerned" adults, LTS 3 = "enthused and confident," LTS 4 = "strong and fearless."

2.2 From Dutch standards to LTS criteria

Mekuria, Furth & Nixon (2012) developed the LTS classification by translating Dutch bikeway design standards (CROW, 2007) into a set of numeric criteria applicable to North American road data. Dutch standards have been proven on a population basis to attract essentially equal male/female shares and high cycling rates across all age groups (Pucher & Buehler, 2008). The LTS criteria encode the Dutch insight that stress is determined by the interaction of speed, volume, number of lanes, and degree of separation:

2.3 Weakest-link logic

A fundamental principle of the LTS framework, emphasised by Furth et al. (2016), is that the stress of a route is determined by its most stressful link, not by an average. A 5-km route on LTS 1-2 streets that crosses one LTS 4 intersection is an LTS 4 route for the "interested but concerned" rider. This is why the Low-Stress Connectivity tool matters: it identifies exactly which high-stress segments sever the network. Reducing LTS 4 road-km by 50% sounds impressive; reducing LTS 4 gaps by 50% is what actually grows the cycling network.

2.4 Empirical validation

Mekuria, Furth & Nixon (2012) validated the LTS classification on San Jose, California, finding that only 4.7% of home-to-work trips up to 6 miles were connected at LTS 2, providing a plausible explanation for the city's low cycling mode share. The study demonstrated that a modest slate of 32 miles of strategic improvements (closing LTS 4 gaps) would nearly triple the percentage of connected trips. Furth, Mekuria & Nixon (2016) further refined the classification with specific criteria for crossings (intersections) -- signalised vs. unsignalised, presence of refuge islands, number of lanes to cross -- which the PlanX simplified version does not implement but which the editable rule table brings closer.

3. Mathematical Formulation

Let each segment $i$ have attributes speed $v_i$ (km/h), lane count $l_i$, AADT $a_i$ (vehicles/day), and infrastructure type $t_i \in \{\text{path}, \text{lane}, \text{mixed}\}$.

Default LTS rules. The classification uses a threshold table with the following defaults:

Rule keyDefault valueMeaning
path_lts1LTS for separated paths
lane_lts2_speed50Max speed for lane = LTS 2
lane_lts2_lanes3Max lanes for lane = LTS 2
lane_lts_low2LTS when lane meets criteria
lane_lts_high3LTS when lane fails criteria
mixed_lts1_speed30Max speed for mixed = LTS 1
mixed_lts1_lanes2Max lanes for mixed = LTS 1
mixed_lts1_aadt1000Max AADT for mixed = LTS 1
mixed_lts2_speed30Max speed for mixed = LTS 2
mixed_lts2_lanes2Max lanes for mixed = LTS 2
mixed_lts3_speed50Max speed for mixed = LTS 3

Classification logic. For each segment $i$, the LTS class $L_i \in \{1, 2, 3, 4\}$ is assigned according to the decision tree:

If $t_i$ = "path": $L_i = \text{path\_lts}$ (1)
Else if $t_i$ = "lane":

$$L_i = \begin{cases} \text{lane\_lts\_low} & \text{if } v_i \leq \text{lane\_lts2\_speed} \land l_i \leq \text{lane\_lts2\_lanes} \\ \text{lane\_lts\_high} & \text{otherwise} \end{cases} \tag{1}$$

Else (mixed traffic):

$$L_i = \begin{cases} 1 & \text{if } v_i \leq \text{mixed\_lts1\_speed} \land l_i \leq \text{mixed\_lts1\_lanes} \land a_i < \text{mixed\_lts1\_aadt} \\ 2 & \text{else if } v_i \leq \text{mixed\_lts2\_speed} \land l_i \leq \text{mixed\_lts2\_lanes} \\ 3 & \text{else if } v_i \leq \text{mixed\_lts3\_speed} \\ 4 & \text{otherwise} \end{cases} \tag{2}$$

Low-stress share. The share of the network length that is LTS 2 or lower (the "all-ages network" benchmark):

$$\text{Share}_{\text{LTS 1-2}} = \frac{\sum_{i: L_i \leq 2} \ell_i}{\sum_i \ell_i} \tag{3}$$

where $\ell_i$ is the segment length in metres.

5. Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)--Street network in projected CRS. Should be prepared (nodes at intersections).
SPEED_FIELDField (Numeric)-- (optional)Posted speed or prevailing speed (km/h). Missing = default speed.
LANES_FIELDField (Numeric)-- (optional)Number of travel lanes (both directions). Missing = default lanes.
AADT_FIELDField (Numeric)-- (optional)Annual Average Daily Traffic (vehicles/day). Missing = default AADT.
INFRA_FIELDField (String)-- (optional)Cycling infrastructure type: "path", "lane", or anything else (= mixed). Case-insensitive. Missing = default infra.
DEFAULT_SPEEDDouble50.0Fallback speed when no field or missing value (min 0).
DEFAULT_LANESDouble2.0Fallback lane count (min 1).
DEFAULT_AADTDouble0.0Fallback AADT (min 0).
DEFAULT_INFRAString"mixed"Fallback infrastructure type. Must be path/lane/mixed.
RULESString(see table above)Editable threshold table as key=value pairs. Unknown keys error (to prevent silent misclassification).
OUTPUTVector (Line)--Network segments with LTS attributes.
SUMMARYVector (table)--Length-share summary by LTS class.

6. Output Description

Segment output (OUTPUT):

FieldTypeDescription
ltsInteger (1-4)Level of Traffic Stress class
lts_labelStringHuman-readable label ("LTS 1 low stress" ... "LTS 4 high stress")
length_mDoubleSegment length in metres
speed_usedDoubleSpeed value actually used in classification (from field or default)
lanes_usedDoubleLane count actually used
aadt_usedDoubleAADT value actually used
infra_usedStringInfrastructure type actually used (lowercased)

Summary output (SUMMARY):

FieldTypeDescription
ltsIntegerLTS class (1-4)
labelStringHuman-readable label
length_mDoubleTotal segment length in this class
share_lenDoubleFraction of total network length (0-1)
segmentsIntegerNumber of segments in this class

8. Interpretation Guide

8.1 The four-class stress map

Style the output by LTS class using the international convention:

8.2 Read LTS geographically, not just statistically

An LTS 1-2 network-length share of 70% sounds adequate. But if those LTS 1-2 segments are all local streets severed from each other by LTS 4 arterials every 500 metres, the effective cycling network may serve only trips within a single block. Always pair LTS classification with Low-Stress Connectivity to check whether the low-stress fabric actually forms a connected network.

8.3 Short red gaps in long green corridors

The signature opportunity on an LTS map: a 5-km LTS 1-2 corridor (e.g. a cycle route along a greenway) broken by ONE 200-m LTS 4 segment (e.g. a crossing of a 4-lane arterial without a signal). This 200-m gap blocks the entire corridor. Fixing it -- with a protected crossing, a traffic signal, or a short cycle track -- unlocks 5 km of network for the investment of 200 m of infrastructure. The cost-effectiveness ratio (unlocked low-stress km / new infrastructure km) is the metric that should drive the capital programme.

8.4 Scenario testing

Edit the INFRA_FIELD values in the source data to simulate proposed interventions:

Rerun the classifier and compare the before/after LTS 1-2 share. The LTS 1-2 gained (km) / intervention length (km) ratio is the network-efficiency metric for the intervention.

8.5 Cross-references

8.6 Pitfalls

9. Academic References

Mekuria, M.C., Furth, P.G. & Nixon, H. (2012). Low-Stress Bicycling and Network Connectivity. Mineta Transportation Institute Report 11-19.

Furth, P.G., Mekuria, M.C. & Nixon, H. (2016). "Network Connectivity for Low-Stress Bicycling." Transportation Research Record, 2587(1), 41-49. DOI: 10.3141/2587-06 verified

Geller, R. (2006). Four Types of Cyclists. Portland Office of Transportation.

Dill, J. & McNeil, N. (2013). "Four Types of Cyclists? Examination of Typology for Better Understanding of Bicycling Behavior and Potential." Transportation Research Record, 2387(1), 129-138. DOI: 10.3141/2387-15 verified

Pucher, J. & Buehler, R. (2008). "Making Cycling Irresistible: Lessons from The Netherlands, Denmark and Germany." Transport Reviews, 28(4), 495-528. DOI: 10.1080/01441640701806612 verified

Winters, M., Davidson, G., Kao, D. & Teschke, K. (2011). "Motivators and deterrents of bicycling: comparing influences on decisions to ride." Transportation, 38, 153-168. DOI: 10.1007/s11116-010-9284-y verified

CROW (2007). Design Manual for Bicycle Traffic. CROW Record 25, Ede, The Netherlands.

NACTO (2014). Urban Bikeway Design Guide, 2nd ed. National Association of City Transportation Officials.

===ALGORITHM===

Low-Stress Connectivity

Processing ID: planx:lowstressislands

1. Overview

Finds the connected components ("islands") of the cycling network that remain after dropping all segments whose LTS exceeds a chosen threshold (typically 2 for an all-ages network). The algorithm takes a street network with pre-computed LTS values (from the Cycling Stress tool), removes high-stress segments, and performs a depth-first search on the remaining primal graph to identify connected components. Low-stress segments receive their island ID and the island's total length; high-stress segments are preserved with island ID = 0 so they remain visible as barriers on the map.

Optionally, if origin and destination layers are provided, the summary reports the population share that can reach a destination on the low-stress network: origin points are snapped to the nearest network node, and the population of origins whose node lies in an island that contains at least one destination node is counted. This is the screening answer to "can people access schools/shops/transit at low stress?" using network topology only (no routing, no detour criterion).

2. Theoretical Background

2.1 From stress classification to network connectivity

LTS classification alone does not tell a planner whether the low-stress network is useful. A network can be 80% LTS 1-2 by length yet provide zero connected trips if every LTS 1-2 segment is an isolated cul-de-sac separated from every other by LTS 4 arterials. Mekuria, Furth & Nixon (2012) introduced the concept of low-stress islands -- connected subgraphs of the street network within which cycling at the chosen LTS threshold is possible without encountering a high-stress link. The number, size, and spatial arrangement of islands provide a far more informative picture of network functionality than aggregate stress shares.

2.2 The island count as a connectivity metric

In the limit of a perfectly connected all-ages network, the island count at LTS 2 is 1: every node is reachable from every other at low stress. A city with 50 islands at LTS 2 has, in effect, 50 separate cycling networks that do not connect to each other -- a resident of island A cannot cycle to a destination in island B without encountering a stress level that exceeds their tolerance. The number of islands is a coarse but powerful metric: fewer islands = better connectivity. However, the size distribution matters: one giant island (80% of the network) plus 49 tiny islands (local streets severed by arterials) is very different from 50 equal-sized islands (pervasively fragmented grid).

2.3 Destination reachability as a policy KPI

Furth, Mekuria & Nixon (2016) proposed "percent trips connected" -- the fraction of origin-destination pairs in the regional trip table that are linked by a low-stress path with no excessive detour -- as the gold-standard connectivity metric. Computing this requires a regional trip table and full shortest-path routing on the low-stress subnetwork, which is computationally intensive. The PlanX implementation provides a simpler screening alternative: destination-island reachability. An origin is "served" if its snapped network node lies in an island that contains at least one destination node. This is a binary, non-routed metric: it answers "is there at least one accessible destination?" rather than "can I reach my specific destination?" -- but it requires no trip table and runs in linear time.

2.4 The strategic-gap logic

The most powerful output of the island analysis is the gap map: high-stress segments (island_id = 0) that separate two large low-stress islands. Each such gap is a candidate for a protected crossing, traffic calming, or cycle track that would merge two islands. The population- weighted merge gain -- population of island A + population of island B that would newly be connected -- is the cost-effectiveness metric for gap-closing interventions. In the San Jose case study, Mekuria et al. demonstrated that a strategically chosen 32 miles of gap closures would nearly triple the percentage of connected work trips, from 4.7% to 12.8% -- because each closure merged multiple islands simultaneously.

3. Mathematical Formulation

Let $G = (V, E)$ be the primal street graph with $|V|$ nodes and $|E|$ edges. Each edge $e$ has an LTS class $L_e \in \{1, 2, 3, 4\}$ and length $\ell_e$. For a chosen threshold $\tau \in \{1, 2, 3, 4\}$, define the low-stress edge set:

$$E_{\text{low}} = \{e \in E : L_e \leq \tau\} \tag{1}$$

The low-stress subgraph is $G_{\text{low}} = (V, E_{\text{low}})$.

Connected components. A depth-first search on $G_{\text{low}}$ partitions the nodes into $C$ components, assigning each node $v$ a component label $\ell(v) \in \{0, 1, \ldots, C-1\}$. Isolated nodes (degree 0 in $G_{\text{low}}$) each form their own component. Each edge $e = (u, v)$ receives the label of its nodes:

$$\ell(e) = \begin{cases} \ell(u) & \text{if } e \in E_{\text{low}} \text{ and } \ell(u) = \ell(v) \\ -1 & \text{otherwise (high-stress edge or inconsistent)} \end{cases} \tag{2}$$

The component length (total low-stress length belonging to component $c$) is:

$$\Lambda_c = \sum_{e \in E_{\text{low}} : \ell(e) = c} \ell_e \tag{3}$$

Low-stress share of the network:

$$S_{\text{low}} = \frac{\sum_{e \in E_{\text{low}}} \ell_e}{\sum_{e \in E} \ell_e} \tag{4}$$

Destination-island reachability. Let $\mathcal{O} \subset V$ be the set of network nodes nearest to origin features, with associated population $p_v$ per origin. Let $\mathcal{D} \subset V$ be the set of network nodes nearest to destination features. Define the served islands:

$$\mathcal{I}_{\text{served}} = \{\ell(v) : v \in \mathcal{D}, \ell(v) \geq 0\} \tag{5}$$

The reachable population is the sum of population at origins whose node lies in a served island:

$$P_{\text{reach}} = \sum_{v \in \mathcal{O} : \ell(v) \in \mathcal{I}_{\text{served}}} p_v \tag{6}$$

The population reach share is $P_{\text{reach}} / \sum_{v \in \mathcal{O}} p_v$.

5. Parameters

ParameterTypeDefaultDescription
NETWORKVector (Line)--Street network with LTS field (output of Cycling Stress). Must be in projected CRS.
LTS_FIELDField (Numeric)--LTS class per segment (1-4). Non-numeric values treated as LTS 4 with warning.
THRESHOLDInteger2Maximum LTS for "low-stress" (1-4). 2 = all-ages network; 3 = confident-rider network; 4 = everything (single island).
ORIGINSVector (any geometry)-- (optional)Origins with population (e.g. census blocks, buildings). Centroids used for polygon inputs.
POP_FIELDField (Numeric)-- (optional)Population per origin feature. Omitted = 1 per origin.
DESTINATIONSVector (any geometry)-- (optional)Destinations to test reachability against (e.g. schools, shops, transit stops).
OUTPUTVector (Line)--Network segments with island attributes.
SUMMARYVector (table)--Connectivity summary with optional population-reach KPI.

6. Output Description

Segment output (OUTPUT):

FieldTypeDescription
lowstressInteger (0/1)1 = segment is at or below the LTS threshold; 0 = high stress (barrier)
island_idIntegerIsland (component) identifier (1-based). 0 = barrier segment (not part of any island).
island_mDoubleTotal length of this segment's island (metres). Barrier segments get 0.

(Plus all original network fields including LTS class.)

Summary output (SUMMARY):

FieldTypeDescription
metricStringMetric name
valueDoubleMetric value
noteStringAdditional information (empty string if none)

Metrics: Threshold LTS, Segments, Low-stress segments, Low-stress length (m), Network length low-stress share, Low-stress islands, Largest island length (m). Plus (if origins + destinations provided): Destination islands, Population reaching a destination island, Population reach share.

8. Interpretation Guide

8.1 The island map IS the cycling map

The island map tells an honest story that the official "bike network" map may obscure. Style the output by island_id: each island gets a distinct colour; barrier segments (island_id = 0) get a thin red line. A map showing dozens of small coloured islands separated by red barriers -- however much green paint exists on the ground -- reveals a cycling network that cannot deliver cross-city trips. A map showing one or two dominant coloured islands with few red barriers reveals a functional network.

8.2 Barrier segments = the work programme

Select all segments where island_id = 0 AND the segment separates two large islands (the islands on either side can be identified by the component labels of the adjacent nodes). Each such segment is a candidate intervention. Rank them by the combined population (or combined island length) of the two islands they separate. The top-ranked gap is the single most consequential missing link in the cycling network -- close it, and the network merges two large islands into one.

8.3 The population-reach KPI

If origins and destinations were provided, the summary's Population reach share is the headline KPI for cycling-network adequacy. For example: "At LTS 2, 34% of residents can reach a primary school on the low-stress network." Track this KPI across scenarios:

The KPI gain per dollar spent is the evidence base for the cycling capital programme. A single strategic gap closure can jump the reach share by 10-20 percentage points if it merges two large populated islands.

8.4 Island_id = 0 everywhere = broken topology or wrong threshold

If nearly all segments show island_id = 0 (barriers), either: (a) the LTS threshold is too low (try threshold 3 or 4 to check -- if island_id still = 0 everywhere, the problem is topology); (b) the network topology is broken -- segments that visually intersect do not share a node. Run Prepare Network first and re-classify.

8.5 Cross-references

8.6 Pitfalls

9. Academic References

Mekuria, M.C., Furth, P.G. & Nixon, H. (2012). Low-Stress Bicycling and Network Connectivity. Mineta Transportation Institute Report 11-19.

Furth, P.G., Mekuria, M.C. & Nixon, H. (2016). "Network Connectivity for Low-Stress Bicycling." Transportation Research Record, 2587(1), 41-49. DOI: 10.3141/2587-06 verified

Dill, J. & McNeil, N. (2013). "Four Types of Cyclists? Examination of Typology for Better Understanding of Bicycling Behavior and Potential." Transportation Research Record, 2387(1), 129-138. DOI: 10.3141/2387-15 verified

Pucher, J. & Buehler, R. (2008). "Making Cycling Irresistible: Lessons from The Netherlands, Denmark and Germany." Transport Reviews, 28(4), 495-528. DOI: 10.1080/01441640701806612 verified

Winters, M., Davidson, G., Kao, D. & Teschke, K. (2011). "Motivators and deterrents of bicycling: comparing influences on decisions to ride." Transportation, 38, 153-168. DOI: 10.1007/s11116-010-9284-y verified

Geller, R. (2006). Four Types of Cyclists. Portland Office of Transportation.

NACTO (2014). Urban Bikeway Design Guide, 2nd ed. National Association of City Transportation Officials.

17. Hazard Screening

The Hazard Screening group provides three hydrologically grounded tools for rapid flood-risk diagnosis without hydrodynamic modelling. Flow Accumulation builds the plumbing: a depression-filled DEM, D8 flow directions, and a topological accumulation grid — the computational backbone for every downstream hydrological analysis. HAND and Inundation normalises the terrain by vertical distance to the nearest drainage, producing a physically meaningful flood-susceptibility index that outperforms raw elevation for screening. Flood Exposure overlays the resulting inundation mask on buildings and population, producing the policy-ready numbers: how many people and buildings sit in harm's way at each depth scenario. All three are screening tools — they rank and locate risk, but do not replace hydraulic modelling for regulatory floodplain certification.

Flow Accumulation

Processing ID: planx:flowaccumulation · Engine: engine/hydro.py · Group: Hazard Screening (17)

1. Overview

Produces three foundational hydrological rasters from a digital elevation model (DEM) in a single pass: a depression-filled DEM using the Wang & Liu (2006) priority-flood algorithm, an eight-direction (D8) flow direction grid following Jenson & Domingue (1988), and a topological flow accumulation grid computed via Kahn's algorithm on the directed acyclic graph defined by the D8 directions. Together these three rasters are the computational plumbing for every downstream hydrological tool in PlanX: HAND Index, Flood Exposure, and any future watershed or stream-network analysis.

The algorithm is strictly deterministic: given the same DEM and software version, the output is bit-identical across runs. Ties in D8 direction are broken by a fixed neighbour-visit order (E, SE, S, SW, W, NW, N, NE), which is the standard ESRI convention. The priority-flood depression filler is also deterministic — cells with equal spill elevation drain in the order they enter the priority queue, which is lexicographic by (row, column) after the first tie-break.

2. Theoretical Background

2.1 Evolution of DEM-based flow routing

The problem of extracting drainage networks from gridded elevation data originated in computer vision and quantitative geomorphology. O'Callaghan & Mark (1984) proposed the first operational algorithm: a recursive procedure that routes each cell's flow to its steepest downslope neighbour, accumulating upstream area by summing the contributing cells. Their critical insight was that artificial pits — spurious closed depressions created by interpolation, vegetation, or sensor noise in the DEM — must be filled before routing, because a pit acts as an internal sink that traps flow and breaks the drainage network. They proposed an iterative "flooding" procedure: raise each pit cell to the elevation of its lowest overflow point, repeating until no pits remain.

Jenson & Domingue (1988) systematised this into the now-standard pipeline: (1) fill depressions, (2) assign flow directions on the filled surface using the steepest-descent rule across eight neighbours (the D8 method), and (3) accumulate flow by summing the upstream area of each cell. Their implementation — written for the USGS DEM format — became the template for virtually every commercial and open-source GIS hydrology toolbox, including ArcGIS Spatial Analyst, GRASS r.watershed, TauDEM, and SAGA.

The D8 method's strength is its simplicity: each cell drains to exactly one neighbour, producing a forest of directed trees (a functional graph with no cycles once pits are filled). Its weakness is grid bias: flow is constrained to eight compass directions, which disperses flow unrealistically on planar hillslopes and creates artificial straight-line channel patterns aligned to the grid. Tarboton (1997) proposed the D-infinity (D-inf) method as a remedy: flow direction is a continuous angle computed from the steepest slope across eight triangular facets centred on each cell, and accumulation is apportioned between two downslope neighbours proportionally to angle proximity. D-inf is demonstrably more accurate on hillslopes but doubles the computational bookkeeping. PlanX uses D8 for its screening purpose — the difference between D8 and D-inf matters most at hillslope scale (sub-30 m resolution), and PlanX's hazard tools operate at the city/neighbourhood scale where the eight-direction bias is acceptable given the larger uncertainties in DEM resolution and surface roughness.

2.2 Depression filling: the priority-flood family

The iterative pit-filling algorithm of Jenson & Domingue (1988) requires $O(N^2)$ scans in the worst case (a nested set of depressions). Wang & Liu (2006) introduced the priority-flood algorithm, which processes cells in elevation order using a priority queue — functionally equivalent to running a Dijkstra-like front from the DEM boundaries inward. The time complexity drops to $O(N \log N)$. The algorithm is elegant: push all boundary cells into a min-heap keyed by elevation; pop the lowest cell, and for each unvisited neighbour, set its elevation to $\max(\text{own\_elevation}, \text{spill\_elevation})$ and push it onto the heap. The result is a hydrologically sound surface with no internal sinks, where every cell has a monotonically descending path to a boundary. PlanX implements this algorithm exactly as described in Wang & Liu (2006), with one modification: cells with NaN elevation (outside the valid DEM extent) are permanently excluded rather than pushed from the boundary — this correctly handles non-rectangular study-area masks common in urban analysis.

2.3 Topological accumulation

Once the D8 direction grid is computed on the filled DEM, the flow network forms a directed acyclic graph (DAG) where each cell is a node with at most one outgoing edge. Flow accumulation is the count of upstream cells draining through each cell — equivalent to the size of the subtree rooted at that cell in the drainage forest. The classic recursive algorithm (Jenson & Domingue, 1988) suffers from deep recursion on large DEMs. PlanX uses Kahn's topological sorting algorithm: compute the in-degree of every node (count how many cells drain into it), initialise a queue with all zero-in-degree cells (ridge cells), and process in topological order, adding each cell's accumulation to its single downstream neighbour. The queue-based approach is iterative, avoids recursion-depth limits, and runs in $O(N)$ time after the direction grid is built.

2.4 Assumptions and limitations

3. Mathematical Formulation

D8 slope. For a cell at row $r$, column $c$ with elevation $Z(r,c)$, the slope to neighbour $(r+dr, c+dc)$ is:

$$\text{slope}(dr, dc) = \frac{Z(r,c) - Z(r+dr, c+dc)}{d(dr,dc)} \tag{3}$$

where the inter-cell distance $d = 1$ pixel for cardinal neighbours and $d = \sqrt{2}$ for diagonal neighbours. The D8 direction code is the index of the neighbour with the maximum positive slope (steepest descent). If no neighbour is lower, the cell is a flat area or pit (code 0). The direction codes follow the ESRI/ArcGIS convention (powers of 2):

$$D(r,c) = \begin{cases} 1 & \text{East} \\ 2 & \text{Southeast} \\ 4 & \text{South} \\ 8 & \text{Southwest} \\ 16 & \text{West} \\ 32 & \text{Northwest} \\ 64 & \text{North} \\ 128 & \text{Northeast} \\ 0 & \text{Flat / pit (no descent)} \end{cases} \tag{2}$$

Depression filling (Wang & Liu, 2006). Let $\Omega$ be the set of valid (finite) DEM cells. Boundary cells $\partial\Omega$ are pushed onto a min-heap $H$ keyed by elevation. While $H$ is not empty, pop the lowest cell $(r,c)$ with elevation $e$. For each unvisited neighbour $(nr, nc)$:

$$F(nr, nc) = \max(Z(nr, nc),\, e) \tag{1}$$

The neighbour is marked visited and pushed onto $H$ with key $F(nr,nc)$. The output $F$ is the filled DEM — every cell has a monotonically non-ascending path to a boundary cell.

Kahn accumulation. Let $\text{indeg}[r,c]$ be the number of cells whose D8 direction points to $(r,c)$. Initialise queue $Q$ with all cells where $\text{indeg}[r,c] = 0$ (ridge cells). For each cell $(r,c)$ dequeued, let $(nr,nc)$ be its downstream neighbour (from the D8 code). Then:

$$A(nr, nc) \mathrel{+}= A(r,c) \qquad \text{indeg}[nr,nc] \mathrel{-}= 1$$

If $\text{indeg}[nr,nc] = 0$, enqueue $(nr,nc)$. At termination, $A$ holds the number of upstream cells draining through each cell. All cells are initialised with $A = 1$ (each cell contributes its own area).

4. Input Data Requirements

InputTypeRequiredNotes
DEMRasterYesSingle-band digital elevation model in a projected CRS (metres). Resolution 5–30 m for urban analysis; coarser DEMs miss kerb-height flow paths. Must be hydrologically conditioned (no internal NoData islands except at the study-area boundary).

Where to obtain: SRTM (30 m, global, void-filled versions), ALOS AW3D30 (30 m, better in steep terrain), Copernicus GLO-30, national LiDAR programmes (1–5 m). For urban pluvial flood screening, LiDAR-derived DEMs at 1–5 m resolution are strongly preferred — a 30 m DEM cannot resolve the street/kerb/building microtopography that controls surface flow in cities.

5. Parameters

ParameterTypeRequiredDescription
DEMRaster layerYesInput digital elevation model. Must be in a projected CRS with metric units. The tool reads the first band; multi-band rasters are not supported.
OUTPUT_FILLEDRaster destinationYesDepression-filled DEM. Same extent, resolution, and CRS as the input. Cell values are in metres. NaN where the input was NaN (outside the valid mask).
OUTPUT_DIRRaster destinationYesD8 flow direction raster. Integer codes 1, 2, 4, 8, 16, 32, 64, 128 following the ESRI convention; 0 = flat/pit (no descent); 255 = NoData. Stored as Float32; visualised as a discrete palette.
OUTPUT_ACCUMRaster destinationYesFlow accumulation raster (upstream cell count). Values span from 1 (ridge cell) to the total cell count of the catchment (basin outlet). Stored as Float32. Always style with a logarithmic scale.

6. Output Description

OutputTypeRangeDescription
Filled DEMFloat32 rasterVariable (metres)Hydrologically conditioned DEM with all depressions raised to their spill elevation. Visually near-identical to the original except in pit areas (quarries, karst, underpasses). The difference map (filled minus original) is a pit/depression inventory.
D8 DirectionFloat32 raster{0, 1, 2, 4, 8, 16, 32, 64, 128, 255}Flow direction to the steepest downslope neighbour. Code 0 = flat or pit cell (no valid descent); 255 = NoData. The raster encodes both hydrology (which way water goes) and topology (the drainage forest structure).
Flow AccumulationFloat32 raster[1, N_cells]Number of upstream cells draining through each cell. A value of 1 means the cell is a ridge (no upstream inflow). Values span orders of magnitude — always use log scale for visualisation. A threshold at the 99th percentile extracts the synthetic stream network.

7. Symbolic Representation

Style flow accumulation with a pseudocolour renderer using Viridis or Inferno, logarithmic classification with 10–15 classes. The bright branching lines are the flow concentration zones. Overlay the network at 50% opacity on a hillshade of the filled DEM for a combined terrain-and-drainage map. The D8 direction raster should use a discrete palette: assign distinct colours to the eight compass directions (e.g., blues for northward, reds for southward). The filled DEM is typically not mapped on its own; instead, map the difference (filled minus original) with a sequential ramp (Blues) to reveal filled depressions — any cell with a difference above 0.5 m in urban fabric is a candidate pluvial ponding location.

8. Interpretation Guide

8.1 Reading the accumulation raster

Flow accumulation is the primary diagnostic product. The key insight is that urban surface water follows the same topographic paths as natural drainage, regardless of what is built on top. High-accumulation lines that cross streets, parcels, or building footprints are the surface flow paths that activate during a cloudburst — the water finds the historic drainage route whether or not a pipe or channel is present.

8.2 Cross-references to other PlanX tools

8.3 Pitfalls

9. Academic References

O'Callaghan, J.F. & Mark, D.M. (1984). "The extraction of drainage networks from digital elevation data." Computer Vision, Graphics, and Image Processing, 28(3), 323–344. DOI: 10.1016/S0734-189X(84)80011-0

Jenson, S.K. & Domingue, J.O. (1988). "Extracting topographic structure from digital elevation data for geographic information system analysis." Photogrammetric Engineering and Remote Sensing, 54(11), 1593–1600. journal does not assign DOIs for pre-1990 volumes]

Wang, L. & Liu, H. (2006). "An efficient method for identifying and filling surface depressions in digital elevation models for hydrologic analysis and modelling." International Journal of Geographical Information Science, 20(2), 193–213. DOI: 10.1080/13658810500433453

Tarboton, D.G. (1997). "A new method for the determination of flow directions and upslope areas in grid digital elevation models." Water Resources Research, 33(2), 309–319. DOI: 10.1029/96WR03137

Garbrecht, J. & Martz, L.W. (1997). "The assignment of drainage direction over flat surfaces in raster digital elevation models." Journal of Hydrology, 193(1–4), 204–213. DOI: 10.1016/S0022-1694(96)03138-1

Barnes, R., Lehman, C. & Mulla, D. (2014). "Priority-flood: An optimal depression-filling and watershed-labeling algorithm for digital elevation models." Computers & Geosciences, 62, 117–127. DOI: 10.1016/j.cageo.2013.04.024

===ALGORITHM===

HAND and Inundation

Processing ID: planx:handindex · Engine: engine/hydro.py · Group: Hazard Screening (17)

1. Overview

Computes the Height Above Nearest Drainage (HAND) index — the vertical distance from each terrain cell to the nearest stream cell along the D8 flow path — and derives a binary bathtub inundation mask for any user-specified water depth. HAND normalises the DEM relative to the drainage network: instead of absolute elevation (which conflates "high mountain" with "flood-safe"), it reports how many metres a cell sits above the stream it drains to. This single number — conceptually the local gravitational potential relative to the drainage — is the most parsimonious topographic predictor of flood exposure available without hydraulic modelling (Renno et al., 2008; Nobre et al., 2011).

The workflow is modular: HAND is computed once from the filled DEM, D8 directions, and a drainage accumulation threshold. The inundation mask is then a trivial raster operation — every cell with HAND at or below the specified depth is flagged as inundated. Run at multiple depths (0.5, 1.0, 2.0, 5.0 m) to produce nested risk bands for exposure analysis without recomputing HAND.

2. Theoretical Background

2.1 Why HAND? The normalisation argument

Raw elevation is a poor flood predictor. A cell at 500 m ASL in a mountain valley may be 2 m above the adjacent river and highly exposed; a cell at 5 m ASL on a coastal plateau 50 m above its nearest stream is flood-safe. HAND solves this by referencing every cell's elevation to its own drainage outlet. The computation is a single downstream trace along the D8 flow path: follow the steepest-descent chain from each cell until a drainage cell is reached (a cell whose upstream accumulation exceeds the user-specified threshold), then subtract elevations:

$$\text{HAND}(c) = Z(c) - Z(d_c)$$

where $d_c$ is the first drainage cell encountered downstream from $c$. This is a relative measure — it says "I am X metres above the stream I drain to" — and is therefore comparable across terrain types, geologies, and climates without calibration. HAND naturally captures the hydrological principle that flood risk increases with proximity to the drainage network, but it encodes proximity as a vertical distance (the energy gradient), not a horizontal (Euclidean) one.

2.2 The HAND lineage

HAND emerged from Amazonian hydrology where the challenge was mapping soil-water regimes across vast, ungauged catchments using only SRTM topography (Renno et al., 2008). The authors demonstrated that HAND classes (0–5 m, 5–15 m, etc.) corresponded systematically to water-table depth measured at field sites, validating the method across an 18,000 km² region. Nobre et al. (2011) formalised this into the terrain model used today, demonstrating that HAND class boundaries align with observable soil-moisture transitions and that the classes are transferable across geologies. This validation is the key: HAND is not merely a geometric transform of the DEM — it correlates with measured hydrological variables, giving it physical meaning beyond the elevation data it consumes.

Subsequent work has scaled HAND to continental levels. The National Water Model at NOAA uses CONUS-scale HAND rasters (10 m resolution) for real-time flood inundation mapping (Liu et al., 2018). Zheng et al. (2018) embedded HAND in the GeoFlood workflow, combining it with synthetic rating curves to translate National Water Model discharge forecasts into inundation maps within minutes — a production demonstration that HAND-based screening, while simpler than full 2D hydraulic models, captures the first-order inundation patterns needed for emergency response.

2.3 The bathtub inundation model

The inundation mask produced by PlanX is a bathtub model: every cell with HAND less than or equal to the specified water depth is flagged as wet. This is the simplest possible inundation model — it assumes water rises to a flat surface at the specified depth, with no routing, no volume conservation, no defences, and no dynamic effects. It is not a substitute for a hydraulic model (HEC-RAS, TUFLOW, MIKE FLOOD) and must never be used for regulatory floodplain certification. Its role is screening: rapidly identifying which areas warrant detailed study, ranking candidate development sites by exposure, and producing the exposure curves (population vs. depth) that drive policy conversations about risk tolerance.

The bathtub approach is conservative by design — it will overestimate inundation because real water has volume and cannot fill every disconnected depression simultaneously. This conservatism is appropriate for screening: a site flagged as "potentially exposed" deserves hydraulic investigation; a site flagged as "safe" (HAND well above the scenario depth) is almost certainly so.

2.4 Drainage threshold: the key parameter

The drainage accumulation threshold defines what counts as a "stream" for HAND computation. A threshold of 100 cells means any cell draining an upstream area of at least 100 cells is a drainage cell. The physical analogue is channel initiation: at what upstream area does overland flow concentrate sufficiently to form a recognisable channel? Lower thresholds (10–50 cells) initiate channels in every gully and swale; higher thresholds (500–1000 cells) only mark major streams. The threshold interacts with DEM resolution:

For urban screening at typical resolutions (5–30 m), thresholds of 50–200 cells produce HAND patterns that align with mapped drainage. Always state the threshold, the DEM resolution, and the resulting minimum drainage area in the results — these three numbers define the effective scale of the analysis.

3. Mathematical Formulation

HAND computation. For each terrain cell $c = (r,c)$ on the filled DEM $F$, the algorithm traces the D8 flow path downstream until a drainage cell is reached:

$$d_c = \begin{cases} c & \text{if } \text{accum}(c) \geq \theta \text{ (c is a drainage cell)} \\ d_{c'} & \text{where } c' = \text{downstream}(c) \text{ via D8 direction} \end{cases} \tag{5}$$

where $\theta$ is the drainage accumulation threshold. Then:

$$\text{HAND}(r,c) = F(r,c) - F(d_r, d_c) \tag{4}$$

The implementation uses path memoisation (dynamic programming): once the drainage cell for a given starting cell is found, every cell on the traced path is assigned the same drainage cell, so each cell is traced at most once. Cycle detection (via a per-path visited set) handles the rare case of D8 direction loops on flat-filled terrain.

Inundation mask. For a given water depth $D$ (metres):

$$I_D(r,c) = \begin{cases} 1 & \text{if } \text{HAND}(r,c) \leq D \text{ and } \text{HAND}(r,c) \geq 0 \\ 0 & \text{otherwise} \end{cases} \tag{3}$$

The $\text{HAND} \geq 0$ condition excludes cells where HAND computation failed (coded as −1 in the engine), which occurs when no drainage cell is reachable downstream — typically cells at the DEM boundary that drain outward.

HAND as gravitational potential. Conceptually, HAND is the remaining potential energy of a water parcel at cell $c$ relative to the drainage network. Expressed as a head difference:

$$\Delta h(c) = \text{HAND}(c) = Z(c) - Z_{\text{drainage}}(c) \tag{2}$$

Water at $c$ must lose $\Delta h$ metres of elevation to reach the drainage network. This framing connects HAND to the energy-gradient formulation used in hydrological models and explains why HAND correlates with water-table depth: the water table surface is, to first order, a subdued replica of the topography, and HAND measures the local relief above the drainage base level.

Flood depth exceedance curve (post-processing). Given population data $P$ distributed over cells and HAND values $h_c$, the exposed population at depth $D$ is:

$$E_{pop}(D) = \sum_{c:\, h_c \leq D} P_c \tag{1}$$

Plotting $D \mapsto E_{pop}(D)$ yields the exposure curve — the single most policy-relevant product of the Hazard Screening group.

4. Input Data Requirements

InputTypeRequiredNotes
Filled DEMRasterYesOutput from Flow Accumulation (OUTPUT_FILLED). Must be in the same projected CRS and exactly co-registered (same extent, resolution) as the direction and accumulation rasters.
D8 Flow DirectionRasterYesOutput from Flow Accumulation (OUTPUT_DIR). D8 codes 1–128; cells with code 0 or 255 are treated as terminal (no downstream neighbour).
Flow AccumulationRasterYesOutput from Flow Accumulation (OUTPUT_ACCUM). Used only to threshold drainage cells; the accumulation values are compared to the threshold parameter, not used directly in HAND arithmetic.

5. Parameters

ParameterTypeDefaultDescription
DEMRaster layerFilled DEM (output of Flow Accumulation). Projected CRS required.
D8_DIRRaster layerD8 flow direction raster (output of Flow Accumulation). Codes 1–128; 0 = flat, 255 = NoData. Must share extent and resolution with DEM.
ACCUMRaster layerFlow accumulation raster (output of Flow Accumulation). Thresholded to define drainage cells.
THRESHOLDDouble100.0Drainage accumulation threshold (cell count). Cells with accumulation ≥ threshold are drainage cells. Lower = more drainage channels (finer HAND pattern); higher = only major streams. Scale with DEM resolution: target 0.1–1.0 km² drainage area.
DEPTHDouble1.0Inundation depth in metres. Cells with HAND ≤ depth are wet. Run at multiple depths (e.g. 0.5, 1.0, 2.0, 5.0) to build the exposure curve. Depths below the DEM vertical precision (~1 m for SRTM) are indicative only.
OUTPUT_HANDRaster destinationHAND index raster. Values in metres; 0 = drainage cell itself; NaN = NoData. Computed once, reused for any depth scenario.
OUTPUT_INUNDATIONRaster destinationBinary inundation mask. 1 = wet (HAND ≤ depth); 0 = dry; NaN = NoData. The mask is the input for Flood Exposure.

6. Output Description

OutputTypeRangeDescription
HAND IndexFloat32 raster0 to hundreds of metresVertical distance (m) from each cell to its nearest downstream drainage cell. 0 = drainage cell (stream bed). Values are always non-negative for valid cells. HAND measures local draining potential — the energy a water parcel must dissipate to reach the channel network.
Inundation MaskFloat32 raster{0, 1}Binary bathtub inundation at the specified depth. 1 = cell is at or below the water depth above its drainage. The mask is a scenario product, not a return-period flood map — it has no hydrology, no volume constraint, and no defences.

7. Symbolic Representation

Style HAND with a sequential-diverging pseudocolour ramp (RdYlBu reversed: red for low HAND = high risk, blue for high HAND = safe). Use manual classification at the standard hydrological breaks: 0, 1, 2, 5, 10, 20, 50 m. The 0–1 m band (red) is the active floodplain; the 1–2 m band (orange) is high exposure; above 10 m (blue) is flood-safe for planning purposes. The inundation mask should be a two-class renderer: blue fill at 40% opacity for wet cells, fully transparent for dry cells.

8. Interpretation Guide

8.1 HAND risk bands

HAND RangeRisk LevelPlanning Interpretation
0–1 mFloodplain (by construction)Cells at or near the drainage elevation. Active channel, bankfull zone, and immediate riparian corridor. Any building here is in the floodplain regardless of mapped flood zones. Action: flag for no-build; if occupied, commission a hydraulic study immediately.
1–2 mHigh exposureLikely inundated in moderate-to-major flood events. Building ground floors may be above the 1-in-100 level, but access routes (streets, underpasses) flood earlier, cutting off egress. Action: require raised finished floor levels; ensure at least one evacuation route stays above 2 m HAND.
2–5 mModerate exposureInundated only in extreme events. Most development is physically safe, but critical infrastructure (hospitals, emergency centres) here faces residual risk. Action: protect critical facilities with site-specific flood barriers; maintain drainage clearance.
5–10 mLow exposureFlood risk is low for riverine flooding. Pluvial (surface-water) flooding from local intense rainfall may still occur in depressions regardless of HAND. Action: check the filled-minus-original depression map for local ponding risk.
>10 mFlood-safe (screening level)Negligible riverine flood risk. Suitable for any development from a flood-screening perspective. Caveat: coastal, dam-break, and flash-flood hazards are not captured by HAND.

8.2 Multi-depth inundation analysis

The recommended workflow is to run HAND once, then generate inundation masks at 0.5, 1.0, 2.0, and 5.0 m depths. Feed each mask to Flood Exposure to build the population exposure curve. The shape of this curve is diagnostic:

8.3 Cross-references to other PlanX tools

8.4 Pitfalls

9. Academic References

Renno, C.D., Nobre, A.D., Cuartas, L.A., Soares, J.V., Hodnett, M., Tomasella, J. & Waterloo, M.J. (2008). "HAND, a new terrain descriptor using SRTM-DEM: Mapping terra-firme rainforest environments in Amazonia." Remote Sensing of Environment, 112(9), 3469–3481. DOI: 10.1016/j.rse.2008.03.018

Nobre, A.D., Cuartas, L.A., Hodnett, M., Renno, C.D., Rodrigues, G., Silveira, A., Waterloo, M. & Saleska, S. (2011). "Height Above the Nearest Drainage — a hydrologically relevant new terrain model." Journal of Hydrology, 404(1–2), 13–29. DOI: 10.1016/j.jhydrol.2011.03.051

Zheng, X., Maidment, D.R., Tarboton, D.G., Liu, Y.Y. & Passalacqua, P. (2018). "GeoFlood: Large-Scale Flood Inundation Mapping Based on High-Resolution Terrain Analysis." Water Resources Research, 54(12), 10013–10033. DOI: 10.1029/2018WR023457

Liu, Y.Y., Maidment, D.R., Tarboton, D.G., Zheng, X. & Wang, S. (2018). "A CyberGIS Integration and Computation Framework for High-Resolution Continental-Scale Flood Inundation Mapping." Journal of the American Water Resources Association, 54(4), 770–784. DOI: 10.1111/1752-1688.12660

Speckhann, G.A., Borges Chaffe, P.L., Fabris Goerl, R., Miranda de Abreu, J.J. & Altamirano Flores, J.A. (2018). "Flood hazard mapping in Southern Brazil: a combination of flow frequency analysis and the HAND model." International Journal of River Basin Management, 16(1), 87–98. DOI: 10.1080/15715124.2017.1372444

Bates, P.D. & De Roo, A.P.J. (2000). "A simple raster-based model for flood inundation simulation." Journal of Hydrology, 236(1–2), 54–77. DOI: 10.1016/S0022-1694(00)00278-X

===ALGORITHM===

Flood Exposure

Processing ID: planx:floodexposure · Engine: engine/hydro.py · Group: Hazard Screening (17)

1. Overview

Translates a binary inundation mask raster into policy-ready exposure numbers. Given an inundation mask (from the HAND and Inundation tool or any external source), a building footprint layer, and an optional population-weighted demand point layer, the tool computes: how many buildings are in the flood zone, what percentage of the total building stock that represents, how many people are exposed, and what share of the total population is affected. The output is a single-row summary table — designed to be run once per depth scenario (0.5, 1.0, 2.0, 5.0 m), with the results assembled into an exposure curve outside the tool.

Optionally, the tool annotates demand points with a wet_dry status field, enabling spatial analysis of which neighbourhoods and demographic groups bear the exposure, not just how many.

2. Theoretical Background

2.1 Exposure as a distinct concept from hazard

In the standard risk taxonomy (UNISDR, 2015), hazard is the physical phenomenon (the flood water, its depth and extent); exposure is the people and assets located in the hazard zone; and vulnerability is the susceptibility of those assets to damage. PlanX's Flood Exposure tool operates at the exposure layer — it answers the question "what is in the water?" but not "how badly will it be damaged?" The latter requires depth-damage curves (e.g., FEMA HAZUS, JRC global flood depth-damage functions), which are not implemented in PlanX. The output is a count of exposed elements, not a monetary loss estimate.

2.2 The exposure curve as a planning instrument

Running Flood Exposure at multiple depths and plotting the results produces an exposure curve — the cumulative count (or share) of exposed buildings and population as a function of water depth. This curve is a standard tool in flood risk management (Jongman et al., 2012; Apel et al., 2009). Its shape encodes the spatial relationship between development and the floodplain: a convex curve (steep at low depths) means most exposure is in the shallow fringe — relatively easy to protect with low walls, raised floors, or zoning. A concave curve (steep at high depths) means exposure is concentrated in deep-flood zones — the development is genuinely at risk, and retreat or structural protection are the only options.

2.3 Building vs. population exposure

The tool reports both because they carry different policy meanings. Building exposure is a capital stock indicator — it tells the insurance/asset-management story: "310 buildings, representing 14% of the building stock, would be in floodwater at this depth." Population exposure is a human safety indicator — it tells the emergency-management story: "1,240 residents would need evacuation, shelter, and post-event support." A neighbourhood with high building exposure but low population (warehouse district) requires a different policy response than one with high population exposure (dense residential area), even if the flood depth is identical.

2.4 Screening caveats

All caveats that apply to the inundation mask carry forward to the exposure analysis. No flood defences (levees, flood walls, pumps) are modelled. No depth-damage functions are applied (the tool counts exposure, not damage). No temporal dimension is present (a flash flood and a slow-rise river flood with the same depth produce identical results, even though evacuation outcomes are radically different). The tool operates in the same projected CRS as the inundation raster — building centroids and demand points in a different CRS are reprojected automatically by QGIS processing, but coordinate transformation between datums can introduce sub-pixel shifts at the inundation mask boundary.

3. Mathematical Formulation

Building exposure. Given building centroids $\{(x_i, y_i)\}_{i=1}^{N_b}$ and an inundation grid $I$ with geotransform $gt = (x_0, dx, 0, y_0, 0, dy)$ where $dy < 0$:

$$col_i = \left\lfloor \frac{x_i - gt_0}{gt_1} \right\rfloor \qquad row_i = \left\lfloor \frac{y_i - gt_3}{gt_5} \right\rfloor \tag{3}$$ $$N_{\text{exposed}} = \sum_{i=1}^{N_b} \mathbf{1}[0 \leq row_i < R \land 0 \leq col_i < C \land I[row_i, col_i] > 0.5] \tag{2}$$

Buildings whose centroid falls outside the raster extent are counted as not exposed (they are in the total but not in the exposed count, so the percentage is conservative).

Population exposure. Given demand points with coordinates $(x_j, y_j)$ and population values $P_j$:

$$P_{\text{exposed}} = \sum_{j=1}^{N_p} P_j \cdot \mathbf{1}[I(row_j, col_j) > 0.5] \tag{1}$$

where $(row_j, col_j)$ is computed from the coordinates as above. If no population field is specified, each demand point is counted as 1 person.

The output percentages are:

$$\text{pct\_bld} = 100 \cdot \frac{N_{\text{exposed}}}{N_b} \qquad \text{pct\_pop} = 100 \cdot \frac{P_{\text{exposed}}}{P_{\text{total}}}$$

4. Input Data Requirements

InputTypeRequiredNotes
Inundation maskRasterYesBinary raster where values > 0.5 indicate wet cells. From HAND and Inundation output, or any external binary flood mask. Must be in a projected CRS (metric units).
BuildingsVector polygonsNoBuilding footprints. Centroids are computed internally. At least one of Buildings or Demand Points must be provided.
Demand pointsVector (any geometry)NoPoints with optional population attribute. Geometries are converted to centroids. Population field is optional; if omitted, each point counts as 1.
Population fieldNumeric field (on demand points)NoIf specified, weighted exposure uses this field's values. NULL or non-numeric values are treated as 0. Typically a dasymetrically distributed population estimate.

5. Parameters

ParameterTypeDefaultDescription
INUNDATIONRaster layerBinary inundation mask. Any cell value above 0.5 is treated as wet. The raster's CRS is the analysis CRS — other layers are reprojected to match.
BUILDINGSVector layer (Polygon)(optional)Building footprints. Exposure is assessed at the centroid. Provide at least one of Buildings or Demand Points.
DEMANDVector layer (Any)(optional)Demand/population points. Each feature's centroid is tested against the inundation mask. Without a population field, each point counts as 1 person.
POP_FIELDField (Numeric)(optional)Population attribute on the demand layer. NULL and non-numeric values are treated as 0. Negative values are clamped to 0.
OUTPUTTable (FeatureSink)One-row summary table with exposure statistics. No geometry. Designed for assembly into multi-depth exposure curves.
OUT_DEMANDVector layer (Point)(optional)Copy of the demand points with an appended wet_dry field ("wet" or "dry"). Enables spatial/demographic cross-tabulation of exposure.

6. Output Description

FieldTypeDescription
exposed_bldDoubleAbsolute count of buildings whose centroid falls in a wet cell. Integer-valued but stored as Double for consistency.
total_bldDoubleTotal number of buildings in the input layer. Denominator for pct_bld.
pct_bldDoublePercent of building stock exposed: 100 × exposed_bld / total_bld. 0.0 if no buildings layer was provided.
exposed_popDoubleSum of population values for demand points in wet cells. If no population field, equals the count of wet demand points.
total_popDoubleTotal population sum over all demand points. Denominator for pct_pop.
pct_popDoublePercent of population exposed: 100 × exposed_pop / total_pop. 0.0 if no demand layer was provided.

Annotated demand points (optional output): appends a single field wet_dry (String) to the demand layer attributes. Values: "wet" if the point's centroid falls in a cell where the inundation mask > 0.5; "dry" otherwise. Points outside the raster extent are "dry".

7. Symbolic Representation

The summary table is a numeric product — visualise it as a bar chart or exposure curve outside PlanX. In QGIS, the annotated demand points are the primary map product: style wet_dry as a two-class renderer (blue circles for "wet", grey for "dry") overlaid on the inundation mask at 30% opacity. For a multi-depth series, create four copies of the annotated points (one per depth), style each at a different blue intensity (light blue for shallow, dark blue for deep), and display side by side in the map layout.

8. Interpretation Guide

8.1 The exposure curve

Run the tool at 0.5, 1.0, 2.0, and 5.0 m depths. Tabulate pct_pop (and pct_bld) against depth. The resulting curve is the single most policy-relevant output of the Hazard Screening group:

8.2 Building exposure vs. population exposure

Disagreement between the two percentages is informative:

8.3 Cross-references to other PlanX tools

8.4 Pitfalls

9. Academic References

Jongman, B., Ward, P.J. & Aerts, J.C.J.H. (2012). "Global exposure to river and coastal flooding: Long term trends and changes." Global Environmental Change, 22(4), 823–835. DOI: 10.1016/j.gloenvcha.2012.07.004

Apel, H., Aronica, G.T., Kreibich, H. & Thieken, A.H. (2009). "Flood risk analyses — how detailed do we need to be?" Natural Hazards, 49(1), 79–98. DOI: 10.1007/s11069-008-9277-8

Merz, B., Kreibich, H., Schwarze, R. & Thieken, A. (2010). "Review article: Assessment of economic flood damage." Natural Hazards and Earth System Sciences, 10(8), 1697–1724. DOI: 10.5194/nhess-10-1697-2010

Bates, P.D. & De Roo, A.P.J. (2000). "A simple raster-based model for flood inundation simulation." Journal of Hydrology, 236(1–2), 54–77. DOI: 10.1016/S0022-1694(00)00278-X

UNISDR (2015). "Sendai Framework for Disaster Risk Reduction 2015–2030." United Nations Office for Disaster Risk Reduction, Geneva. Available at: undrr.org

===ALGORITHM===

18. Travel Demand

The Travel Demand group implements the classic four-step transportation model — trip generation, trip distribution (gravity), mode split, and assignment — as three PlanX tools. Trip Generation estimates productions (home-end trips) and attractions (work-end trips) per traffic analysis zone from population and employment using linear rates. Gravity Distribution balances these productions and attractions against network travel costs via a doubly constrained Furness/IPF procedure, producing an OD flow matrix. Mode Split applies a multinomial logit choice model to each OD pair, splitting the total flow into mode-specific shares based on travel time differences and alternative-specific constants.

These are aggregate, zone-based models — they predict trips between zones, not individual traveller choices. They are "screening-quality" in the PlanX sense: correct order of magnitude, structurally sound, and policy-responsive (you can change inputs and see the direction of change correctly), but not calibrated to local surveys unless you supply locally-estimated rates and coefficients. For a calibrated four-step model, use the PlanX outputs as the initial run and calibrate rates, beta, and ASCs against a household travel survey or traffic count using external estimation software.

Trip Generation

Processing ID: planx:tripgeneration · Engine: engine/demand.py · Group: Travel Demand (18)

1. Overview

Computes trip productions (trips generated by households) and trip attractions (trips drawn to workplaces) for each traffic analysis zone using simple linear rates on population and employment. This is Step 1 of the four-step travel demand model (Ortuzar & Willumsen, 2011), and in PlanX it is the mandatory input to the Gravity Distribution tool.

The computation is straightforward: for each zone $i$, productions $P_i = \text{pop}_i \times r_P$ and attractions $A_i = \text{jobs}_i \times r_A$, where $r_P$ (trips per capita per day) and $r_A$ (trips per job per day) are user-specified rates. The output is a table retaining all original zone attributes plus the two new numeric columns — ready to chain into Gravity Distribution.

2. Theoretical Background

2.1 The four-step model and trip generation's place in it

The four-step travel demand model was formalised in the 1950s–1960s by transportation planning agencies in the United States (the Chicago Area Transportation Study, 1955–1962, being the canonical first application) and codified in the textbook by Ortuzar & Willumsen (2011). The four steps are: (1) Trip generation — how many trips start and end in each zone; (2) Trip distribution — where those trips go (the OD matrix); (3) Mode split — which transport mode each trip uses; and (4) Assignment — which specific route through the network each trip takes.

Trip generation is the demographic/economic foundation of the model. It asserts that the volume of travel is driven by two fundamental quantities: people (who need to travel from home to activities) and jobs (which draw people to workplaces). Additional trip purposes (education, shopping, leisure) can be modelled by adding more production-attraction pairs, but PlanX provides the simplest population-and-jobs formulation as the screening baseline. For a five-purpose model (home-based work, home-based education, home-based shopping, home-based other, non-home-based), run Trip Generation multiple times with different population/job subsets and rates, then sum across purposes before the gravity step.

2.2 Productions vs. attractions

The distinction between productions and attractions is not merely semantic — it encodes asymmetry in the travel system. Productions are home-end trips: each resident generates a certain number of trips per day (typically 1.5–2.5, including all purposes). Attractions are activity-end trips: each job attracts a certain number of trip-ends (typically 1.5–3.0, reflecting that a workplace receives both employees and visitors/customers). A zone's P/A ratio (productions divided by attractions) is a single-number land-use diagnosis:

The P/A imbalance across all zones also diagnoses the study area boundary: if total productions exceed total attractions (zones sum to more outbound than inbound trips), the study area exports commuters to zones outside it. This is correct behaviour — you must either expand the study area boundary or add external zones representing the destinations outside. The gravity model's doubly constrained balancing (Step 2) rescales attractions to match productions internally, so the absolute totals are consistent, but the zone-level P/A pattern remains informative.

2.3 Rate calibration

The default rates ($r_P = 1.5$ trips/capita/day, $r_A = 2.0$ trips/job/day) are order-of-magnitude screening values, not calibrated parameters. They produce approximately correct totals for a typical mid-sized city, but the zonal distribution (which zones contribute most trips) is driven entirely by the population and employment data — the rate is a scalar multiplier that does not affect the relative ranking. For consequential analysis:

2.4 Assumptions and limitations

3. Mathematical Formulation

Trip productions. For zone $i$ with population $\text{pop}_i$ and production rate $r_P$ (trips/capita/day):

$$P_i = \text{pop}_i \cdot r_P \tag{3}$$

Trip attractions. For zone $i$ with employment $\text{jobs}_i$ and attraction rate $r_A$ (trips/job/day):

$$A_i = \text{jobs}_i \cdot r_A \tag{2}$$

P/A balance ratio (diagnostic):

$$\text{balance}_i = \frac{P_i}{A_i} \quad (\text{undefined if } A_i = 0) \tag{1}$$

The $\text{balance}_i$ value is not output as a separate field — it is the ratio of the two output columns — but it is the single most informative diagnostic for land-use planning. Zone maps of balance are standard master-plan exhibits.

4. Input Data Requirements

InputTypeRequiredNotes
Zone layerVector (Polygon or Point)YesTraffic analysis zones (TAZs). Population and jobs must be attributes. Zones should be mutually exclusive and collectively exhaustive of the study area.
Population fieldNumericYesTotal population per zone. From census, WorldPop, or a population allocation tool.
Jobs fieldNumericYesTotal employment per zone. From business registers, census workplace data, or economic models.

5. Parameters

ParameterTypeDefaultDescription
ZONESVector layer (Polygon/Point)Traffic analysis zone layer with population and employment attributes.
POP_FIELDField (Numeric)Attribute field containing zone population. NULL or non-numeric values are treated as 0.
JOBS_FIELDField (Numeric)Attribute field containing zone employment. NULL or non-numeric values are treated as 0.
P_RATEDouble1.5Production rate (trips per capita per day). Typical range: 1.2–2.5. Use 1.5 for screening; calibrate from surveys for operational models.
A_RATEDouble2.0Attraction rate (trips per job per day). Typical range: 1.5–3.0. Generally higher than the production rate because jobs attract both workers and visitors.
OUTPUTTable (FeatureSink)Zone table with appended production and attraction columns. No geometry is written — the table is designed for direct input to Gravity Distribution.

6. Output Description

FieldTypeDescription
productionDoubleTotal daily trip productions for the zone (trips/day). Rounded to 2 decimal places. This column is the Productions input for Gravity Distribution.
attractionDoubleTotal daily trip attractions for the zone (trips/day). Rounded to 2 decimal places. This column is the Attractions input for Gravity Distribution.

All original zone attributes are preserved. The output table has no geometry — it is an attribute-only table. For mapping, join back to the zone polygon layer using the zone ID.

7. Symbolic Representation

Map the P/A balance ratio (production / attraction) with a diverging colour ramp (RdYlGn or PuOr reversed). A value of 1.0 = balanced (white or grey); above 1.0 = dormitory (blue tones); below 1.0 = employment core (red/orange tones). Use a logarithmic scale centred at 1.0: breakpoints at 0.25, 0.5, 1.0, 2.0, 4.0. The P/A map alone — without any routing — is often the single most powerful exhibit in a land-use plan: it visualises exactly where the imbalance between housing and jobs will generate traffic.

8. Interpretation Guide

8.1 Reading the P/A map

The P/A ratio is a structural diagnosis of the city's spatial economy. A map of P/A by zone tells you:

8.2 Cross-references to other PlanX tools

8.3 Pitfalls

9. Academic References

Ortuzar, J. de D. & Willumsen, L.G. (2011). Modelling Transport. 4th edition. John Wiley & Sons, Chichester. DOI: 10.1002/9781119993308

Ewing, R. & Cervero, R. (2010). "Travel and the Built Environment: A Meta-Analysis." Journal of the American Planning Association, 76(3), 265–294. DOI: 10.1080/01944361003766766

Cervero, R. & Kockelman, K. (1997). "Travel demand and the 3Ds: Density, diversity, and design." Transportation Research Part D: Transport and Environment, 2(3), 199–219. DOI: 10.1016/S1361-9209(97)00009-6

Institute of Transportation Engineers (2017). Trip Generation Manual, 10th edition. ITE, Washington, DC. ISBN: 978-1-933452-90-2 industry manual, no DOI]

Litman, T. (2017). "Evaluating Transportation Land Use Impacts: Considering the Impacts, Benefits and Costs of Different Land Use Development Patterns." Victoria Transport Policy Institute. Available at: vtpi.org/landuse.pdf

===ALGORITHM===

Gravity Distribution

Processing ID: planx:gravitymodel · Engine: engine/demand.py · Group: Travel Demand (18)

1. Overview

Distributes zonal trip productions and attractions into a full origin–destination flow matrix using a doubly constrained gravity model — the classic spatial interaction model (Wilson, 1967) that forms Step 2 of the four-step travel demand framework. Given trip productions $P_i$, attractions $A_j$, and a travel cost matrix $c_{ij}$ computed over the street network, the algorithm iteratively balances rows and columns via the Furness/IPF (Iterative Proportional Fitting) procedure until convergence or a maximum iteration limit. The deterrence (impedance) function can be exponential $\exp(-\beta c_{ij})$ or power $c_{ij}^{-\beta}$, where the coefficient $\beta$ controls the sensitivity of trip-making to distance.

The output is an OD flow table — a complete matrix of estimated trips between every zone pair — plus optional desire lines for visualisation. The table feeds directly into Mode Split (Step 3).

2. Theoretical Background

2.1 Wilson's entropy-maximising derivation

Alan Wilson's (1967) seminal paper derived the gravity model not as an analogy to Newtonian physics — as earlier formulations had done — but as the most probable spatial distribution of trips consistent with known constraints. Using the method of Lagrange multipliers to maximise the entropy of the trip matrix subject to fixed row totals (productions), fixed column totals (attractions), and a fixed total travel cost (the budget constraint), Wilson showed that the most likely flow $T_{ij}$ from zone $i$ to zone $j$ takes the form:

$$T_{ij} = A_i \cdot O_i \cdot B_j \cdot D_j \cdot \exp(-\beta c_{ij})$$

where $A_i$ and $B_j$ are balancing factors (the $r_i$ and $s_j$ in PlanX's notation), $O_i$ are origin totals ($P_i$), $D_j$ are destination totals ($A_j$ in PlanX's convention), and $\beta$ is the Lagrange multiplier associated with the cost constraint — the parameter that controls the distance-decay behaviour. This derivation transformed the gravity model from an empirical analogue into a statistically principled method: the model is the maximum-entropy estimate of the trip matrix, which means it makes the fewest assumptions beyond the constraints you impose.

The doubly constrained model (the one PlanX implements) satisfies both $\sum_j T_{ij} = P_i$ (row totals match productions) and $\sum_i T_{ij} = A_j$ (column totals match attractions). This is the standard model when both productions and attractions are known from trip generation. The singly constrained variant (production-constrained or attraction-constrained) is appropriate when only one set of marginals is known — PlanX does not implement this, but if you set all attractions equal (or scale them to the production total), the doubly constrained algorithm reduces to production-constrained in practice because the column-balancing step has no bite.

2.2 The Furness / IPF balancing algorithm

The doubly constrained model has no closed-form solution — the balancing factors $r_i$ and $s_j$ are interdependent. The Furness (or IPF) algorithm solves this by alternating between row and column scaling:

  1. Initialise all $s_j = 1$.
  2. Row balancing: For each row $i$, scale so $\sum_j T_{ij} = P_i$. Set $r_i = P_i / \sum_j (F_{ij} \cdot s_j)$.
  3. Column balancing: For each column $j$, scale so $\sum_i T_{ij} = A_j$. Set $s_j = A_j / \sum_i (F_{ij} \cdot r_i)$.
  4. Repeat steps 2–3 until the maximum absolute difference between modelled and target marginals falls below the convergence tolerance.

The algorithm is guaranteed to converge when the deterrence matrix $F_{ij}$ has no zero rows or columns — a condition PlanX enforces by replacing zero deterrence values with a small epsilon and by clamping denominator sums to a minimum value to prevent division by zero.

A practical refinement: before balancing, PlanX rescales the attraction totals to match the production totals (multiplying by $P_{\text{sum}} / A_{\text{sum}}$). This ensures the row and column totals are consistent — the model distributes trips, it does not create or destroy them — and prevents the Furness algorithm from diverging when production and attraction totals differ substantially.

2.3 The deterrence function: exponential vs. power

The choice of deterrence function encodes an assumption about how distance deters travel:

The calibration of $\beta$ is the empirical core of gravity modelling. A $\beta$ that is too low produces a uniform flow matrix where distance barely matters. A $\beta$ that is too high produces a diagonal-dominated matrix where only adjacent zones interact. The standard calibration target is the mean trip length (or mean trip cost): the modelled mean, $\bar{c}_{\text{model}} = \sum_{ij} T_{ij} c_{ij} / \sum_{ij} T_{ij}$, should match the observed mean from a travel survey. PlanX does not calibrate automatically — it applies the $\beta$ you specify — so the practitioner must iterate: run the model, compute $\bar{c}_{\text{model}}$, compare to $\bar{c}_{\text{observed}}$, adjust $\beta$, repeat.

2.4 Assumptions and limitations

3. Mathematical Formulation

Deterrence matrix. Given the cost $c_{ij}$ between zone $i$ and zone $j$, computed as the shortest-path network distance (or a user-specified cost field) between zone centroids:

$$F_{ij} = \begin{cases} \exp(-\beta \cdot c_{ij}) & \text{(exponential deterrence)} \\ c_{ij}^{-\beta} & \text{(power deterrence, with } c_{ij} \text{ clamped to } \geq 10^{-6}) \end{cases} \tag{5}$$

Doubly constrained gravity model. The balanced flow matrix $T$ satisfies two sets of marginal constraints:

$$\sum_{j} T_{ij} = P_i \quad \forall i \qquad \sum_{i} T_{ij} = A'_j \quad \forall j \tag{4}$$

where $A'_j = A_j \cdot (P_{\text{sum}} / A_{\text{sum}})$ is the rescaled attraction vector ensuring $\sum P = \sum A'$. The flow from $i$ to $j$ at iteration $k$ is:

$$T_{ij}^{(k)} = r_i^{(k)} \cdot F_{ij} \cdot s_j^{(k)} \tag{3}$$

where the balancing factors are updated alternately:

$$r_i^{(k)} = \frac{P_i}{\sum_{j} F_{ij} \cdot s_j^{(k-1)}} \qquad s_j^{(k)} = \frac{A'_j}{\sum_{i} F_{ij} \cdot r_i^{(k)}} \tag{2}$$

with initialisation $s_j^{(0)} = 1$ for all $j$. The algorithm terminates when:

$$\max\left( \max_i \left|\sum_j T_{ij} - P_i\right|,\; \max_j \left|\sum_i T_{ij} - A'_j\right| \right) < \tau \tag{1}$$

where $\tau$ is the convergence tolerance (default $10^{-4}$), or when the iteration count exceeds MAX_ITER.

4. Input Data Requirements

InputTypeRequiredNotes
Zone layerVector (Polygon/Point)YesTraffic analysis zones with ID, production, and attraction fields. Typically the output of Trip Generation, or any polygon/point layer with these attributes.
Street networkVector linesYesRoad centreline network. Must be in a projected CRS (metres). Prepared network from Prepare Network is recommended for correct intersection topology.
Cost fieldNumeric fieldNoIf empty, cost is geometric length. Use for time-based impedance — the same cost field used in OD Cost Matrix should be used here for consistency.

5. Parameters

ParameterTypeDefaultDescription
ZONESVector layer (Polygon/Point)Zone layer with production, attraction, and ID fields. Centroid coordinates are used for cost computation.
ZONE_IDFieldField uniquely identifying each zone. Used in the output as origin_id and dest_id.
PRODUCTION_FIELDField (Numeric)Field containing trip productions (from Trip Generation). Row totals are matched to these values.
ATTRACTION_FIELDField (Numeric)Field containing trip attractions (from Trip Generation). Column totals are balanced to these values after rescaling to match the production total.
NETWORKVector layer (Line)Street network. Centroids snap to nearest nodes. Must be in a projected CRS.
COST_FIELDField (Numeric)(optional)Per-segment cost attribute. If empty, geometric length is used. Use travel time (minutes) for time-based impedance.
BETADouble0.1Deterrence (impedance) coefficient. Higher beta = shorter trips dominate. Calibrate by matching modelled mean trip cost to observed. Typical range: 0.02–0.30 for exponential with distance in metres; 0.05–0.20 for minutes. Run with 2–3 beta values to assess sensitivity.
KINDEnumExponentialDeterrence function: Exponential ($\exp(-\beta c)$) or Power ($c^{-\beta}$). Exponential is standard for person trips; power is common for freight and migration.
MAX_ITERInteger100Maximum Furness/IPF balancing iterations. 100 is adequate for most problems; convergence is typically reached in 10–50 iterations.
TOLDouble$10^{-4}$Convergence tolerance: the maximum absolute difference between modelled and target marginals. Reducing below $10^{-6}$ rarely changes flows meaningfully but increases iterations.
OUTPUTTable (FeatureSink)OD flow table. One row per non-zero zone pair (excluding the diagonal). No geometry.
LINESVector layer (Line)(optional)Straight desire lines between zone centroids, styled by flow magnitude.

6. Output Description

FieldTypeDescription
origin_idStringZone identifier for the trip origin (from the zone ID field).
dest_idStringZone identifier for the trip destination.
costDoubleShortest-path network cost between the two zone centroids (metres or cost-field units). The impedance that the travel demand responds to.
flowDoubleEstimated daily trips from origin to destination after doubly constrained balancing. The core output of the model. Flows below the convergence tolerance are clipped to 0.

The tool also logs the top 10 flows by magnitude to the QGIS Processing feedback panel, enabling immediate inspection of the dominant corridors. The Furness balancing statistics (iterations run, final maximum error) are also logged.

7. Symbolic Representation

Map the desire lines (LINES output) by flow with a sequential ramp (Viridis or Plasma) and natural-breaks classification (5–7 classes). Line width proportional to the square root of flow (to de-emphasise the extreme top flows). Set opacity to 30–40% so overlapping bundles create visually denser corridors. Alternatively, filter to the top 20 flows by magnitude — these are the structural corridors that the land-use pattern imposes on the transport system. Show these with a thicker stroke (0.8 mm) in a warm colour (red/orange) against a thin grey network background.

8. Interpretation Guide

8.1 Reading the flow table

8.2 Scenario differencing

The gravity model's primary policy use is scenario comparison: run it once on the base land-use (current population and jobs), once on a proposed plan (new housing here, new employment there). Then difference the two flow tables:

$$\Delta T_{ij} = T_{ij}^{\text{plan}} - T_{ij}^{\text{base}}$$

OD pairs with large positive $\Delta T$ are corridors under growth pressure — the plan adds trips to these routes. Pairs with large negative $\Delta T$ are corridors where the plan relieves pressure (e.g., by adding jobs near housing). The top 10 positive $\Delta T$ pairs are the corridors where the plan implicitly orders increased transport capacity — if no capacity increase is planned for those corridors, the plan and the transport system are in conflict.

8.3 Cross-references to other PlanX tools

8.4 Pitfalls

9. Academic References

Wilson, A.G. (1967). "A statistical theory of spatial distribution models." Transportation Research, 1(3), 253–269. DOI: 10.1016/0041-1647(67)90035-4

Fotheringham, A.S. & O'Kelly, M.E. (1989). Spatial Interaction Models: Formulations and Applications. Kluwer Academic Publishers, Dordrecht. ISBN: 0-7923-0021-1 book, no DOI assigned]

Ortuzar, J. de D. & Willumsen, L.G. (2011). Modelling Transport. 4th edition. John Wiley & Sons, Chichester. DOI: 10.1002/9781119993308

Evans, A.W. (1971). "The calibration of trip distribution models with exponential or similar cost functions." Transportation Research, 5(1), 15–38. DOI: 10.1016/0041-1647(71)90027-X

Sen, A. & Smith, T.E. (1995). Gravity Models of Spatial Interaction Behavior. Springer-Verlag, Berlin. DOI: 10.1007/978-3-642-79880-1

Erlander, S. & Stewart, N.F. (1990). The Gravity Model in Transportation Analysis: Theory and Extensions. VSP, Utrecht. ISBN: 90-6764-125-6 book, no DOI assigned]

Stouffer, S.A. (1940). "Intervening Opportunities: A Theory Relating Mobility and Distance." American Sociological Review, 5(6), 845–867. DOI: 10.2307/2084520

===ALGORITHM===

Mode Split

Processing ID: planx:modesplit · Engine: engine/demand.py · Group: Travel Demand (18)

1. Overview

Applies a multinomial logit (MNL) mode choice model (McFadden, 1974) to each origin–destination pair, splitting the total OD flow into mode-specific shares and flows based on per-mode travel times, utility coefficients (betas), and alternative-specific constants (ASCs). This is Step 3 of the four-step travel demand model.

For each OD pair, the model computes the systematic utility $V_{k} = \text{ASC}_k + \beta_k \cdot t_{k}$ for each mode $k$ (where $t_k$ is the travel time for mode $k$ on that OD pair), exponentiates to obtain $\exp(V_k)$, and normalises by the sum of exponentials across modes to obtain the choice probability. The probabilities are then multiplied by the total OD flow to get mode-specific flows. Log-sum-exp stabilisation (subtracting the maximum utility before exponentiation) prevents floating-point overflow on pairs with large time differences.

2. Theoretical Background

2.1 Random utility theory and the logit model

Discrete choice models rest on random utility theory (Manski, 1977; Ben-Akiva & Lerman, 1985): a decision-maker $n$ facing a set of alternatives $\mathcal{C}$ chooses alternative $k$ if and only if $U_{nk} > U_{nj}$ for all $j \neq k$, where the utility of each alternative is decomposed into a systematic (observable) component $V_{nk}$ and a random (unobservable) component $\varepsilon_{nk}$:

$$U_{nk} = V_{nk} + \varepsilon_{nk}$$

In PlanX's implementation, the systematic utility is a simple linear-in-parameters function of travel time:

$$V_{nk} = \text{ASC}_k + \beta_k \cdot t_{nk}$$

where $\text{ASC}_k$ is the alternative-specific constant — the average effect of all factors not captured by travel time (comfort, reliability, cost, prestige, habit) — and $\beta_k$ is the marginal utility of travel time for mode $k$ (typically negative: more time = less utility = lower choice probability).

When the random terms $\varepsilon$ are independently and identically distributed following the Type I Extreme Value (Gumbel) distribution, the probability that alternative $k$ is chosen takes the closed-form multinomial logit expression (McFadden, 1974):

$$P_k = \frac{\exp(V_k)}{\sum_{m} \exp(V_m)}$$

The Gumbel assumption implies the independence of irrelevant alternatives (IIA) property: the ratio of choice probabilities between any two alternatives is independent of the presence or characteristics of other alternatives. This is the logit model's most famous limitation — the classic "red bus / blue bus" paradox (adding a blue bus identical to an existing red bus should halve the red bus's share, not the car's; but logit predicts proportional draw-down from all modes). For screening applications with clearly distinct modes (car, transit, walk, cycle), IIA is acceptable; for operational models with correlated alternatives, a nested logit or mixed logit specification is needed.

2.2 The three parameters per mode

Each mode requires three parameters, and their interpretation is critical to correct use:

2.3 Log-sum-exp stabilisation

A numerical detail with important practical consequences: computing $\exp(V_k)$ directly can overflow double-precision floating-point when utility values differ by more than ~700 (since $\exp(710) \approx 10^{308}$, the maximum double value). PlanX stabilises the computation by subtracting the maximum utility value before exponentiating:

$$P_k = \frac{\exp(V_k - V_{\max})}{\sum_m \exp(V_m - V_{\max})}$$

This is mathematically identical to the original expression (adding a constant to all utilities does not change the logit probabilities) but ensures all exponentiated values are between 0 and 1, eliminating overflow. The maximum utility $V_{\max} = \max_m V_m$ is computed per OD pair.

2.4 Assumptions and limitations

3. Mathematical Formulation

Systematic utility. For each OD pair (indexed by $n$) and each mode $k \in \{1, \ldots, K\}$:

$$V_{nk} = \text{ASC}_k + \beta_k \cdot t_{nk} \tag{4}$$

Choice probability (multinomial logit with log-sum-exp stabilisation). Let $V_n^{\max} = \max_k V_{nk}$:

$$P_{nk} = \frac{\exp(V_{nk} - V_n^{\max})}{\sum_{m=1}^{K} \exp(V_{nm} - V_n^{\max})} \tag{3}$$

Mode-specific flows. Given the total OD flow $F_n$ for the pair:

$$F_{nk} = P_{nk} \cdot F_n \tag{2}$$

By construction, $\sum_k P_{nk} = 1$ and $\sum_k F_{nk} = F_n$ for every OD pair.

Aggregate mode share (post-processing). The city-wide share of mode $k$:

$$\text{share}_k = \frac{\sum_n F_{nk}}{\sum_n F_n} = \frac{\sum_n P_{nk} \cdot F_n}{\sum_n F_n} \tag{1}$$

This is a flow-weighted average — OD pairs with more trips contribute more to the aggregate share. The aggregate share is the calibration target: adjust ASCs until $\text{share}_k$ matches observed city-wide mode shares.

4. Input Data Requirements

InputTypeRequiredNotes
OD flows layerVector (Line or Point)YesOutput of Gravity Distribution, or any layer with OD pairs and a total flow field. Must contain one time column per mode.
Mode time fieldsNumeric fieldsYesOne field per mode, containing the travel time (minutes or consistent units) for that mode on each OD pair. Times must be in the same units for all modes.

5. Parameters

ParameterTypeDefaultDescription
FLOWSVector layer (Line/Point)OD flow layer, typically from Gravity Distribution. Must contain a total flow field and one time field per mode.
FLOW_FIELDField (Numeric)Field containing total trips per OD pair (the gravity model's flow column).
MODE_TIMESString"time_car,time_transit"Comma-separated field names for per-mode travel times. Each field must exist in the flows layer. Times are assumed to be in consistent units (minutes recommended). Order must match MODE_BETAS, MODE_ASCS, and MODE_NAMES.
MODE_BETASString"-0.1,-0.1"Comma-separated time coefficients (typically negative). A value of −0.1 means each additional minute reduces utility by 0.1. Can differ by mode (e.g., walking-time coefficient often more negative than in-vehicle-time).
MODE_ASCSString"0.0,0.0"Comma-separated alternative-specific constants. ASCs capture unmeasured utility (comfort, flexibility). Normalise one mode to 0 and adjust others. Without calibration, use 0 for transit, 1–2 for car (in the absence of congestion pricing).
MODE_NAMESString"car,transit"Comma-separated mode names. Used to name output fields: share_{name} and flow_{name}. Must be valid field-name characters.
OUTPUTVector layerAnnotated OD flows with appended share_{mode} and flow_{mode} columns. Original geometry and attributes are preserved.

6. Output Description

FieldTypeDescription
share_{name}DoubleChoice probability for mode name on this OD pair. Ranges [0, 1]; sum across modes = 1. Rounded to 4 decimal places.
flow_{name}DoubleEstimated trips by mode name on this OD pair: share × total flow. Rounded to 2 decimal places. Sum across modes equals the total flow.

For example, with MODE_NAMES="car,transit,walk", the output appends six columns: share_car, flow_car, share_transit, flow_transit, share_walk, flow_walk.

7. Symbolic Representation

For visualisation, aggregate flow_{mode} to zones (sum by origin for productions, by destination for attractions) and map with a pie chart overlay (QGIS "Diagrams" renderer) showing mode shares per zone. Alternatively, compute the difference in flow_transit between two scenarios and map as a graduated line layer with a diverging ramp (RdYlGn): green where transit flow increases, red where it decreases. The transit-gain corridors are the evidence for a transit investment's benefit.

8. Interpretation Guide

8.1 Scenario differencing is the robust analysis

With uncalibrated ASCs, the absolute mode shares are indicative, not operational. The statement "transit share = 23%" is not defensible without calibration against observed shares. The statement "the proposed bus lane shifts 340 daily trips from car to transit on this corridor" is defensible, because the ASC — which captures all unmeasured factors — is constant across scenarios and cancels in the difference. The robust policy use of PlanX's Mode Split is therefore:

  1. Run Mode Split on the base scenario (current travel times).
  2. Change the transit time field to reflect the proposed improvement (e.g., reduce time_transit by the expected travel-time saving from a bus lane).
  3. Run Mode Split again with the same parameters.
  4. Report: $\Delta \text{flow}_{\text{transit}}$ (the net transit gain) and $\Delta \text{flow}_{\text{car}}$ (the net car reduction).

This "difference-in-shares" approach is standard practice for screening-level mode-choice analysis (Train, 2009, Chapter 2). The key assumption is that the unmeasured factors (the ASCs) do not change between scenarios — the bus lane makes transit faster but does not make it more comfortable or prestigious. For investments that do change comfort (e.g., new rolling stock, station upgrades), adjust the ASCs by judgement as well as the times.

8.2 Reading the time sensitivity

With $\beta = -0.10$, each 10-minute reduction in transit time multiplies the odds of choosing transit by $\exp(1) \approx 2.72$, relative to other modes (all else equal). The relationship is multiplicative, not additive:

This non-linearity means that the largest mode-shift returns come from the corridors with the largest current time penalty — the "low-hanging fruit." A bus lane that saves 5 minutes on a corridor where transit is already competitive may produce a modest shift; the same 5-minute saving where transit is 30 minutes slower than driving may produce very little shift because the time penalty, even reduced, remains large. The Mode Split output makes this visible: sort OD pairs by the absolute change in transit share and focus on the top decile — these are the corridors where time savings translate into mode shift most efficiently.

8.3 Cross-references to other PlanX tools

8.4 Pitfalls

9. Academic References

McFadden, D. (1974). "Conditional logit analysis of qualitative choice behavior." In: Zarembka, P. (ed.), Frontiers in Econometrics, pp. 105–142. Academic Press, New York. book chapter, no DOI assigned]

Train, K.E. (2009). Discrete Choice Methods with Simulation. 2nd edition. Cambridge University Press, Cambridge. DOI: 10.1017/CBO9780511805271

Ben-Akiva, M. & Lerman, S.R. (1985). Discrete Choice Analysis: Theory and Application to Travel Demand. The MIT Press, Cambridge, MA. ISBN: 978-0-262-02217-0 book, no DOI assigned]

Domencich, T.A. & McFadden, D. (1975). Urban Travel Demand: A Behavioral Analysis. North-Holland, Amsterdam. ISBN: 0-7204-3170-5 book, no DOI assigned] Available at: UC Berkeley

Hensher, D.A., Rose, J.M. & Greene, W.H. (2015). Applied Choice Analysis: A Primer. 2nd edition. Cambridge University Press, Cambridge. DOI: 10.1017/CBO9781316136232

Manski, C.F. (1977). "The structure of random utility models." Theory and Decision, 8(3), 229–254. DOI: 10.1007/BF00133443

Ortuzar, J. de D. & Willumsen, L.G. (2011). Modelling Transport. 4th edition. John Wiley & Sons, Chichester. DOI: 10.1002/9781119993308

===ALGORITHM===

19. Seismic Risk

The Seismic Risk group contains a single tool — Seismic Collapse and Debris Spread — that models the chain from earthquake shaking to building collapse to street-blocking debris to surviving evacuation corridors. It is a screening-quality Monte Carlo model: deterministic in the sense that a given seed always produces the same outcome, but stochastic in the sense that the seed samples one realisation from the probability distribution defined by the construction-year vulnerability tiers and the scenario magnitude. The tool implements the full pipeline recommended by Goretti & Sarli (2006) for post-earthquake road-network assessment and extends it with four alternative methods for defining the street/open-space network against which debris spread is measured.

Seismic Collapse and Debris Spread (Monte Carlo)

Processing ID: planx:seismicdebris · Engine: engine/seismic.py · Group: Seismic Risk (19)

1. Overview

Models the full chain from earthquake scenario magnitude to building collapse to debris spread to road blockage to surviving evacuation corridors. For each building, a collapse probability is derived from the construction year (proxy for seismic design code vintage) scaled by the scenario's moment magnitude $M_w$. A single deterministic Monte Carlo draw (seeded for reproducibility) determines whether the building collapses. Collapsed buildings spread debris outward to a radius proportional to their height (Goretti & Sarli, 2006) — the debris envelope is the union of all individual debris buffers, intersected with the road network to identify blocked segments, and subtracted from the network to reveal the open evacuation corridors.

The road network can be defined in four ways: (A) existing street/open-space polygons used as-is; (B) OSM highway centreline classes buffered by standard urban widths; (C) centreline with a per-feature width attribute; (D) the difference between a region-of-interest polygon and dissolved urban blocks. This flexibility lets the same tool work with diverse data availability: a detailed municipal GIS, a QuickOSM download, or cadastral parcels.

2. Theoretical Background

2.1 Seismic vulnerability as a construction-year proxy

Building collapse probability under seismic loading depends on structural system, material, ductility detailing, soil conditions, and ground-motion characteristics — a multi-dimensional fragility surface. For screening at the city scale without a building-by-building structural survey, the single most informative proxy is construction year (Coburn & Spence, 2002; FEMA, 2020). The logic is:

These tier values are screening defaults, not calibrated fragility parameters. They are approximately centred on $M_w = 7.0$ — the reference magnitude at which the baseline probabilities apply. The exponential scaling factor $\exp(0.8(M_w - 7.0))$ adjusts the baseline for the scenario magnitude, with the coefficient 0.8 calibrated so that a unit increase in $M_w$ roughly doubles the collapse probability at the reference construction tier — consistent with the empirical observation that damage scales roughly exponentially with magnitude within the 5.5–8.0 range (Coburn & Spence, 2002).

For operational use, the tier probabilities and breakpoints must be calibrated to the local building stock. The defaults are conservative — they assume no retrofitting and the worst structural type common in each era. A city with an active seismic retrofit programme should reduce the probabilities for older tiers. A city on soft soils should increase them.

2.2 Debris spread mechanics

When a building collapses, the debris does not occupy only the building's footprint — it spreads outward into the surrounding street space. Goretti & Sarli (2006) analysed post-earthquake road blockage patterns from Italian earthquakes and proposed a simple geometric model: the debris spread is approximated as a buffer around the building footprint with radius proportional to the building height:

$$r_{\text{debris}} = k \cdot H$$

where $H$ is the building height (floor count $\times$ floor height) and $k$ is the debris spread coefficient — the fraction of building height thrown horizontally. Goretti & Sarli found that $k \approx 0.4$ fits observed blockage patterns for mid-rise unreinforced masonry buildings. Taller buildings with larger plan aspect ratios can produce larger $k$ (up to 0.6), while low-rise ductile frames produce smaller $k$ (0.2–0.3). PlanX defaults to $k = 0.4$ for screening.

The debris volume (for clearance resource estimation) follows FEMA (2020) guidance: the gross building volume (footprint area $\times$ height) is multiplied by a solid volume ratio $\eta$ that accounts for void spaces within the collapsed debris. Typical $\eta$ values: 0.10–0.20 for steel/glass-frame buildings, 0.25–0.35 for reinforced concrete, 0.35–0.45 for unreinforced masonry. PlanX defaults to $\eta = 0.3$ (generic mid-range value).

2.3 Monte Carlo approach and reproducibility

The collapse decision is a single Bernoulli trial per building: draw $u_i \sim \text{Uniform}(0,1)$ from a seeded generator, compare to $p_{\text{collapse},i}$, and set $\text{collapsed}_i = 1$ if $u_i < p_i$. The seed is user-specified — the same seed, inputs, and software version always produce the identical collapse pattern. Changing the seed samples a different stochastic realisation of the same scenario.

This design reflects a deliberate modelling choice: one run = one realisation. The probability field ($p_{\text{collapse}}$) is the stable, seed-independent diagnostic; the collapsed flag is one dice roll consistent with it. For robust statements about evacuation corridor reliability, run the tool with 10–20 different seeds and count in how many runs each corridor survives. Corridors that survive all seeds are the dependable evacuation skeleton; corridors that flicker are not to be relied upon for emergency planning.

2.4 The four road network definitions

The network definition fundamentally shapes the result — a narrow street interpretation (centreline + small buffer) will show more blockage than a wide one (street-space polygons including setbacks). The four sources (A–D) let the practitioner match the available data:

2.5 Assumptions and limitations

3. Mathematical Formulation

Collapse probability. Given construction year $y_i$ and scenario moment magnitude $M_w$:

$$p_{\text{base}}(y_i) = \begin{cases} 0.85 & y_i \leq 1985 \\ 0.60 & 1985 < y_i \leq 2000 \\ 0.25 & 2000 < y_i \leq 2018 \\ 0.05 & y_i > 2018 \end{cases} \tag{7}$$ $$p_i = \min\left(1.0,\; \max\left(0.0,\; p_{\text{base}}(y_i) \cdot \exp(0.8 (M_w - 7.0))\right)\right) \tag{6}$$

Collapse realisation. Given seed $s$, draw $u_i \sim \text{Uniform}(0,1)$ from the generator seeded with $s$:

$$\text{collapsed}_i = \mathbf{1}[u_i < p_i] \tag{5}$$

Building height. From floor count $n_i$ and floor height $h_{\text{floor}}$:

$$H_i = n_i \cdot h_{\text{floor}} \tag{4}$$

Debris spread radius. For collapsed buildings only:

$$r_i = \begin{cases} k \cdot H_i & \text{if collapsed}_i = 1 \\ 0 & \text{otherwise} \end{cases} \tag{3}$$

where $k$ is the debris spread coefficient (fraction of height, default 0.4).

Debris volume. From footprint area $A_i$:

$$V_i = \begin{cases} A_i \cdot H_i \cdot \eta & \text{if collapsed}_i = 1 \\ 0 & \text{otherwise} \end{cases} \tag{2}$$

where $\eta$ is the solid volume ratio (default 0.3).

Evacuation corridors. The debris envelope $D$ is the union of all individual debris buffers (building footprint buffered by $r_i$, square end-cap, mitre join). The blocked network is the intersection of the road/open-space network $N$ with the debris envelope:

$$B = N \cap D \qquad C = N \setminus B \tag{1}$$

The open corridors $C$ are the planning product — the streets that remain passable after debris falls. Only corridors with non-zero area survive; narrow streets where the debris buffer spans the full width vanish entirely.

4. Input Data Requirements

InputTypeRequiredNotes
BuildingsVector polygonsYesBuilding footprints in a projected CRS (metres). Must have floor count and/or construction year fields; defaults are used when fields are missing.
Floor count fieldNumericNoIf omitted, defaults to 1 floor for all buildings. NULL values also default to 1.
Construction year fieldNumericNoIf omitted, defaults to 2000 for all buildings (the most populous tier). NULL values default to 2000.
Network inputsVaries by sourceYesOne of: street/open-space polygons (A), road centreline (B/C), or blocks/parcels + ROI (D). See the Network Source parameter for details.

5. Parameters

ParameterTypeDefaultDescription
BUILDINGSVector layer (Polygon)Building footprints. Projected CRS required. Missing floor count = 1; missing construction year = 2000.
FLOOR_FIELDField (Numeric)(optional)Number of storeys per building. NULL => 1. Used to compute height (= floors × floor_height) and debris parameters.
YEAR_FIELDField (Numeric)(optional)Construction year. NULL => 2000. Determines the vulnerability tier. Years before 1900 are capped to the oldest tier (0.85).
NETWORK_MODEEnumA (0)Network source: A = street/open-space polygons used as-is; B = OSM highway centreline buffered by class widths; C = centreline with width attribute; D = ROI minus dissolved blocks/parcels.
NETWORKVector layer (Polygon)(A only)Street/open-space polygons. Used directly as the road network geometry.
NETWORK_LINESVector layer (Line)(B, C)Road centreline. For source B, attributes are used for highway class lookup and/or width override. For source C, must have a width field (metres).
HIGHWAY_FIELDField(B)Highway class field on centreline (e.g., "highway" from OSM). If blank, auto-detects a field named "highway".
WIDTH_FIELDField (Numeric)(B, C)Road width in metres (full carriageway, not half). In source B, overrides the class-based width when present and valid.
DEFAULT_WIDTHDouble8.0Fallback width (m) for B/C when no class/width is available; also the expansion distance for the auto-generated ROI hull in source D.
ROIVector layer (Polygon)(D only)Region of interest polygon. If omitted, the convex hull of the blocks expanded by DEFAULT_WIDTH is used — provide an explicit ROI for concave study areas.
BLOCKSVector layer (Polygon)(D only)Urban blocks or cadastral parcels. Dissolved internally — shared boundaries vanish. Street space = ROI minus dissolved blocks.
MAGNITUDEDouble7.0Scenario moment magnitude ($M_w$). Range 4.0–9.0. The exponential scaling factor is $\exp(0.8(M_w - 7.0))$. Sweep 6.5 → 7.5 in 0.5 increments to find the resilience cliff.
FLOOR_HEIGHTDouble3.0Average inter-storey height in metres. Total building height = floors × floor_height. Affects debris radius and volume.
DEBRIS_FACTORDouble0.4Debris spread coefficient $k$: fraction of building height thrown horizontally (Goretti & Sarli, 2006). Lower (0.2) for ductile frames; higher (0.6) for unreinforced masonry.
SOLID_VOLUME_RATIODouble0.3Solid volume ratio $\eta$: fraction of gross building volume that becomes solid debris. 0.10–0.20 steel/glass; 0.25–0.35 RC; 0.35–0.45 URM (FEMA, 2020).
SEEDInteger42Random seed for the Monte Carlo draw. Same seed + same inputs = identical collapse pattern. Change to sample another realisation. Run 10–20 seeds for robust corridor reliability analysis.
OUT_BUILDINGSVector layer (Point)Annotated building centroids with collapse probability, collapsed flag, debris radius, and debris volume.
OUT_ENVELOPEVector layer (MultiPolygon)Dissolved debris spread envelope — the union of all individual collapsed-building buffers.
OUT_BLOCKEDVector layer (MultiPolygon)Blocked portion of the road/open-space network (network ∩ debris envelope). Identifies which street sections are impassable.
OUT_CORRIDORSVector layer (MultiPolygon)Open evacuation corridors (network minus blocked). The planning product — streets that remain passable after debris falls.

6. Output Description

OutputFieldsDescription
Annotated Buildingsheight_m, collapse_prob, collapsed, debris_radius_m, debris_vol_m3Building centroid points with all intermediate and final risk metrics. collapse_prob is the seed-independent diagnostic (0–1); collapsed is the binary realisation (0/1) for this seed; debris_radius_m and debris_vol_m3 are 0 for non-collapsed buildings.
Debris Envelope(none — geometry only)Single MultiPolygon geometry: the union of all individual building debris buffers. The extent of debris on the ground. Visualised as a semi-transparent red overlay.
Network Blockage(none — geometry only)Single MultiPolygon geometry: the intersection of the road network with the debris envelope. These are the street sections that are impassable. Visualised as solid red.
Open Corridors(none — geometry only)Single MultiPolygon geometry: the road network minus the blocked sections. Streets that remain passable. Visualised as solid green. This is the evacuation and emergency-access map.

7. Symbolic Representation

Style the four outputs as a three-layer map:

For multi-seed analysis, create a composite corridor-reliability map: run 20 seeds, rasterise each seed's corridor output (1 = open, 0 = blocked), sum the rasters, and style with a sequential green ramp (1–20). Cells with value 20 survived all seeds — the dependable skeleton. Cells with value 2–5 flicker — not reliable for evacuation planning.

8. Interpretation Guide

8.1 One run vs. many runs

A single run (one seed) produces one realisation of the earthquake scenario. This is useful for:

For robust planning statements, run 10–20 seeds and analyse the distribution of outcomes. The key metrics across seeds:

8.2 Reading the collapse probability field

The collapse_prob field is the stable, seed-independent diagnostic. It tells you:

8.3 Evacuation corridor analysis

The open corridors output is the primary planning product. The analysis sequence:

  1. Identify critical facilities: hospitals, fire stations, assembly areas, evacuation centres. Buffer each by a 50 m access zone.
  2. Check corridor connectivity: is each critical facility connected to the surviving corridor network in this seed? A facility whose access zone has zero intersection with the open corridors is isolated.
  3. Multi-seed reliability: count in how many seeds each facility retains corridor access. A hospital that is cut off in 15 out of 20 seeds is not operationally accessible after the modelled earthquake — it needs a second access route, a widened approach street, or pre-positioned supplies.
  4. Neighbourhood isolation: identify residential blocks that are entirely surrounded by blocked streets (zero corridor access). These are the neighbourhoods that would need helicopter or foot access — a different order of emergency-response challenge.

8.4 Cross-references to other PlanX tools

8.5 Pitfalls

9. Academic References

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

Coburn, A. & Spence, R. (2002). Earthquake Protection. 2nd edition. John Wiley & Sons, Chichester. DOI: 10.1002/0470855185

Schweier, C. & Markus, M. (2006). "Classification of Collapsed Buildings for Fast Damage and Loss Assessment." Bulletin of Earthquake Engineering, 4(2), 177–192. DOI: 10.1007/s10518-006-9005-2

FEMA (2020). Hazus Earthquake Model Technical Manual — Hazus 5.1. Federal Emergency Management Agency, Washington, DC. Available at: fema.gov/hazus

Ansal, A., Akinci, A., Cultrera, G., Erdik, M., Pessina, V., Tonuk, G. & Zulfikar, C. (2011). "Loss scenarios for the city of Istanbul and its vulnerability to earthquakes." Soil Dynamics and Earthquake Engineering, 31(3), 534–546. DOI: 10.1016/j.soildyn.2010.10.008

Argyroudis, S., Selva, J., Gehl, P. & Pitilakis, K. (2015). "Systemic Seismic Risk Assessment of Road Networks Considering Interactions with the Built Environment." Computer-Aided Civil and Infrastructure Engineering, 30(7), 524–540. DOI: 10.1111/mice.12136

Zanini, M.A., Faleschini, F., Zampieri, P., Pellegrino, C., Gecchele, G., Gastaldi, M. & Rossi, R. (2017). "Post-quake urban road network assessment: A combined seismic damage and traffic flow approach." Structure and Infrastructure Engineering, 13(3), 355–368. DOI: 10.1080/15732479.2016.1170780

Appendix A: Data Sources Directory

Data TypeGlobal SourceResolutionNotes
Street networkOpenStreetMap (QuickOSM, Overpass Turbo)Filter highway=* tags; remove motorways for pedestrian analyses
Building footprintsMicrosoft Building Footprints, OpenStreetMapNational cadastral databases where available; OSM coverage varies
DEM (30 m)SRTM, ALOS AW3D30, Copernicus GLO-3030 mSRTM: void-filled versions preferred; ALOS: better in steep terrain
DEM (high-res)National LIDAR programmes1–5 mEssential for urban pluvial flood screening and microclimate
DSM (surface)National LIDAR; EU-DSM (Copernicus)1–30 mMust include building heights for solar/shadow/visibility analysis
PopulationWorldPop, GHS-POP, LandScan, national censuses100 m–1 kmWorldPop and GHS-POP are open; LandScan requires license
EmploymentNational business registries, census workplace dataZone-levelOften available as TAZ-level summaries from MPOs
Land use / land coverCopernicus Urban Atlas, ESA WorldCover, OSM landuse10–100 mUrban Atlas: 17 urban classes, ~200 European cities only
Green spacesOSM leisure=park, landuse=grass, natural=woodContains private gardens; filter by access=* tags where needed
GTFS transit feedsTransit.land, OpenMobilityData, agency websitesCoverage strongest in North America, Europe, and major Asian cities
Traffic counts (AADT)National/state DOT traffic count programmesPoint dataOften sparse outside major roads; use travel-demand models as fallback
Cycling infrastructureOSM cycleway=*, local bike-network inventoriesOSM coverage improving; always verify against official maps
Seismic vulnerabilityNational building censuses, post-earthquake surveysBuilding-levelConstruction year often the best available fragility proxy

Appendix B: Symbolization Quick Reference

Output TypeRecommended RendererColor RampClassesClassification
Network centrality (betweenness)Graduated (point)Viridis / Inferno5–7Natural breaks
Space syntax NACHGraduated (line)Viridis / Plasma7–10Quantile
Space syntax NAINGraduated (line)OrRd / YlOrRd7–10Quantile
Walkability scoreGraduated (line)RdYlGn5Equal interval
Access score (15-min)Graduated (point)RdYlGn5Manual (40,60,80,100)
Building form metricsGraduated (polygon)Sequential (Blues/Oranges/Reds)5Natural breaks
Spacematrix classCategorized (polygon)10-class green-orange-red palette10Fixed categories
SVFPseudocolor (raster)RdBu reversed7–100.1 intervals
Shadow / Sun hoursPseudocolor (raster)YlOrRd / Plasma5–8Manual based on daylight
Solar irradiationPseudocolor (raster)Viridis / Inferno5–8Manual (% of reference)
Heat risk gridGraduated (polygon)YlOrRd4Fixed (25,50,75)
Noise gridPseudocolor (raster)Green→Yellow→Orange→RedContinuousManual (45,55,65,75 dB)
HAND indexPseudocolor (raster)RdYlBu reversed7Manual (0,1,2,5,10,20,50 m)
Inundation maskTwo-class (raster)Blue / transparent2Binary
LTS / Cycling stressCategorized (line)Teal/Orange/Purple/Magenta4Fixed categories
Flow accumulationPseudocolor (raster)Viridis10Log-spaced manual
Equity (Gini/Theil)Graduated (point)Diverging (RdYlGn)5–7Equal interval
Equity crosstabsCategorized (point)Sequential by v_class5Quantile (by population)
Gravity desire linesGraduated (line)Single hue + opacity5–7Natural breaks
Debris / evacuationTwo-class (polygon)Red (debris) / Green (open)2Binary
Service areas (pedshed)Hollow circles over solid areasBlue circles, warm areas2 layersOverlay
Land-use allocationCategorized (polygon)Distinct per use + grey unassignedN uses + 1Fixed categories

Appendix C: Glossary

AADT
Annual Average Daily Traffic — vehicles per day on a road segment.
Angular cost
Turn angle in degrees divided by 90. Straight = 0; right angle = 1.
ASC
Alternative-Specific Constant — in mode choice models, captures average unmeasured utility of a mode.
ASHRAE
American Society of Heating, Refrigerating and Air-Conditioning Engineers — clear-sky irradiance model.
Atkinson index
Inequality measure with explicit normative parameter ε (inequality aversion).
CA (Cellular Automaton)
Grid-based simulation where cell state changes according to neighbourhood rules.
CSR
Compressed Sparse Row — sparse matrix format used for graph adjacency.
D8
Eight-direction single flow direction model for hydrological routing on gridded DEMs.
Dasymetric mapping
Redistribution of areal data using ancillary information (e.g., buildings to refine population distribution).
DEM
Digital Elevation Model — raster of bare-earth terrain heights.
DSM
Digital Surface Model — raster of terrain + building + vegetation heights.
Dijkstra
Shortest-path algorithm for non-negative edge weights; runs in $O((V+E)\log V)$.
dPC
Delta Probability of Connectivity — share of connectivity index lost when a patch is removed.
FAR / FSI
Floor Area Ratio / Floor Space Index — gross floor area divided by site area.
Furness / IPF
Iterative Proportional Fitting — balances matrix rows and columns to match target marginals.
Gini coefficient
Inequality measure: 0 = perfect equality, 1 = maximal inequality. Twice the area between the Lorenz curve and equality line.
GSI
Ground Space Index — building footprint area divided by site area (coverage).
GTFS
General Transit Feed Specification — standard open format for public transit schedules.
HAND
Height Above Nearest Drainage — vertical distance from a cell to the nearest stream cell along the D8 flow path.
Hare-Niemeyer
Largest-remainder apportionment method — guarantees exact integer allocation summing to the target.
Isovist
The set of all points visible from a given vantage point (Benedikt, 1979).
LCRPGR
Land Consumption Rate to Population Growth Rate ratio — SDG Indicator 11.3.1.
Leslie matrix
Age-structured population projection matrix: fertility on first row, survival on sub-diagonal.
Lorenz curve
Plot of cumulative population share vs. cumulative value share — graphical inequality measure.
LTS
Level of Traffic Stress — 1 (children) to 4 (strong and fearless only), for cycling.
MCLP
Maximal Covering Location Problem — maximise population served within a distance given p facilities.
MNL
Multinomial Logit — discrete choice model with Gumbel-distributed errors (McFadden, 1974).
NAIN
Normalised Angular INtegration — size-independent to-movement potential (Hillier, Yang & Turner, 2012).
NACH
Normalised Angular CHoice — size-independent through-movement potential.
OSR
Open Space Ratio — (1 − GSI) / FSI; spaciousness.
PC (connectivity)
Probability of Connectivity — sum of squared component areas divided by total area squared.
Pedshed ratio
Network catchment area divided by straight-line circle area — measures street-layout efficiency.
P-median
Facility location problem: minimise population-weighted total travel distance with p facilities.
RAPTOR
Round-based Public Transit Routing — earliest-arrival algorithm using timetable rounds.
SVF
Sky View Factor — fraction of visible sky hemisphere from a point (0–1).
Tobler function
Walking speed as a function of slope: $v = 6 \cdot e^{-3.5|m+0.05|}$ km/h.
Theil index
Inequality measure from information theory; additively decomposable into between/within groups.
UTM
Universal Transverse Mercator — projected CRS family in metres, suitable for all PlanX tools.
λf / λp
Frontal Area Index ($\lambda_f$) and Plan Area Index ($\lambda_p$) — urban roughness parameters.

— End of PlanX Comprehensive Academic Reference Manual —
Covering PlanX v4.10.1 · 69 Algorithms · 19 Tool Groups · August 2026