Network & Accessibility Analysis
Purpose: replace as-the-crow-flies guesswork with network-true travel
costs, at the right scale and with honest assumptions about speeds and
modes. First decision on every task: Euclidean distance is only acceptable
as a declared approximation — flag it whenever you see it standing in for
access.
Tool selection by scale
| Scale |
Tool |
| Neighborhood-city, research flexibility |
OSMnx + NetworkX |
| City-region, many-to-many OD (>10⁴×10⁴) |
r5py (multimodal + transit w/ GTFS) or pandana (contraction-hierarchy speed) |
| Production routing service |
Valhalla / OSRM / OpenRouteService API |
| Proprietary stacks |
ArcGIS Network Analyst (script it headlessly) |
NetworkX chokes on metro-scale many-to-many — don't loop shortest_path
over thousands of origins; switch tools instead.
Graph construction (OSMnx)
import osmnx as ox
G = ox.graph_from_place("City, Country", network_type="drive") # walk/bike/all
G = ox.add_edge_speeds(G) # imputes from highway tags where maxspeed missing
G = ox.add_edge_travel_times(G) # edge attr: travel_time (s)
G = ox.project_graph(G) # metric CRS before any distance work
- network_type matters: pedestrian analysis on a
drive graph misses
paths, stairs, plazas; driving on all uses footpaths. Match mode.
- Imputed speeds are averages by road class — a systematic bias, not
noise. State it; calibrate against known trips when stakes are high.
- Keep the strongly connected component for routing
(
ox.truncate.largest_component(G, strongly=True)); orphan islands
cause spurious infinities.
- Snapping: origins/destinations map to nearest nodes/edges
(
ox.distance.nearest_nodes). Report the snap-distance distribution;
a facility snapped 2 km away (riverside, gated area) silently corrupts
results.
Core products
- Isochrones / service areas: ego-graph by travel_time cutoff → alpha
shape or buffered edge union around reached edges. Node-based convex
hulls overstate coverage across rivers/highways — prefer edge-based
polygons. Always label the assumptions: mode, speed model, cutoff.
- OD matrix: many-to-many travel costs; the substrate for
accessibility and location-allocation. For big matrices use
pandana/r5py; store as Parquet with origin/destination IDs.
- Closest facility: k-nearest by network cost (not Euclidean); report
both the assigned facility and the cost.
- Centrality: betweenness on travel_time (sampled
k for big
graphs — exact is O(nm)); edge betweenness ≈ through-traffic potential.
Interpret as network structure, not observed traffic.
Accessibility metrics — pick deliberately
| Metric |
Question it answers |
Weakness |
| Cumulative opportunities (# jobs/POIs within T min) |
Simple, communicable |
Cliff at T; all-or-nothing |
| Gravity-based (distance-decayed sum) |
Smooth access |
Decay parameter must be justified |
| 2SFCA / E2SFCA |
Supply-demand ratio access (health care standard) |
Catchment size choice drives results |
| Closest-facility time |
Worst-case need |
Ignores capacity/congestion |
For equity analyses, join metrics to population/demographic polygons
(area-weighted or dasymetric — see geo-data-engineering) and report
distributions per group, not just city means. Route statistical testing of
disparities to spatial-statistics.
Location-allocation
Optimal siting (p-median, max-coverage) on the OD matrix: formulate with
PuLP/OR-Tools; inputs are the OD matrix + demand weights + candidate
sites. State the objective explicitly — minimize mean travel time
(p-median) vs maximize covered demand within T (max-coverage) give
different answers, and stakeholders rarely know which they asked for.
Feed results back to mcda-suitability-analysis when siting mixes network
access with other criteria.
Transit (GTFS)
Use r5py with OSM + GTFS feeds; results are departure-time sensitive —
compute over a time window (e.g., 07:00-09:00 percentiles), never a single
departure. Validate the feed (calendar coverage on your analysis date!) —
an expired GTFS calendar yields walking-only times that look plausible.
Verification protocol
- Spot-check 3 routes against an external router (Google/OSRM) — within
~20% or explain why.
- Map unreachable/infinite-cost pairs — usually snapping or connectivity
artifacts, not real inaccessibility.
- Isochrone eyeball: does it respect rivers, highways, one-ways?
Pitfalls checklist
- Euclidean buffers presented as "service areas".
- Wrong network_type for the mode.
- Convex-hull isochrones bridging barriers.
- Snap distances unchecked.
- One departure time for transit accessibility.
- Betweenness sold as traffic volume.
- OD matrix in degrees-CRS travel "distances".
Execution contract
- Workflow: define mode, time, impedance, origins, destinations, and equity question; build and validate the network; snap inputs; compute routes or matrices; summarize access; verify.
- Decision rules: use network costs for constrained travel, movement analytics for observed tracks, and MCDA only when accessibility becomes one criterion in a broader preference model.
- Verification protocol: audit connectivity and snapping, spot-check routes, map unreachable pairs, test departure-time or impedance sensitivity, and reconcile OD dimensions and units.
- Failure modes: withhold access claims for disconnected graphs, wrong mode or turn rules, expired GTFS service, excessive snapping, Euclidean substitution, or unstable departure-time results.
- Deliverables: network provenance, assumptions and cost function, routes or OD matrix, isochrones or access metrics, unreachable-case report, validation evidence, and equity caveats.
- Source freshness: consult the authoritative source registry before using network, GTFS, or routing APIs and archive source dates.
1---2name: network-accessibility-analysis3description: Always invoke for access to facilities or opportunities by walking, driving, cycling, or public transport, even for a conceptual question with no routing terms or data yet. Covers hospital and service access, transit/GTFS, routes, isochrones, OD matrices, closest facility, 2SFCA, walkability, coverage, and equity. Invoke when Euclidean buffers proxy for network access. Use movement-trajectory for observed tracks and MCDA for suitability without network costs.4license: MIT5---67# Network & Accessibility Analysis89Purpose: replace as-the-crow-flies guesswork with network-true travel10costs, at the right scale and with honest assumptions about speeds and11modes. First decision on every task: Euclidean distance is only acceptable12as a declared approximation — flag it whenever you see it standing in for13access.1415## Tool selection by scale1617| Scale | Tool |18|---|---|19| Neighborhood-city, research flexibility | **OSMnx + NetworkX** |20| City-region, many-to-many OD (>10⁴×10⁴) | **r5py** (multimodal + transit w/ GTFS) or **pandana** (contraction-hierarchy speed) |21| Production routing service | Valhalla / OSRM / OpenRouteService API |22| Proprietary stacks | ArcGIS Network Analyst (script it headlessly) |2324NetworkX chokes on metro-scale many-to-many — don't loop `shortest_path`25over thousands of origins; switch tools instead.2627## Graph construction (OSMnx)2829```python30import osmnx as ox3132G = ox.graph_from_place("City, Country", network_type="drive") # walk/bike/all33G = ox.add_edge_speeds(G) # imputes from highway tags where maxspeed missing34G = ox.add_edge_travel_times(G) # edge attr: travel_time (s)35G = ox.project_graph(G) # metric CRS before any distance work36```3738- **network_type matters**: pedestrian analysis on a `drive` graph misses39 paths, stairs, plazas; driving on `all` uses footpaths. Match mode.40- Imputed speeds are averages by road class — a systematic bias, not41 noise. State it; calibrate against known trips when stakes are high.42- Keep the strongly connected component for routing43 (`ox.truncate.largest_component(G, strongly=True)`); orphan islands44 cause spurious infinities.45- **Snapping**: origins/destinations map to nearest nodes/edges46 (`ox.distance.nearest_nodes`). Report the snap-distance distribution;47 a facility snapped 2 km away (riverside, gated area) silently corrupts48 results.4950## Core products5152- **Isochrones / service areas**: ego-graph by travel_time cutoff → alpha53 shape or buffered edge union around reached edges. Node-based convex54 hulls overstate coverage across rivers/highways — prefer edge-based55 polygons. Always label the assumptions: mode, speed model, cutoff.56- **OD matrix**: many-to-many travel costs; the substrate for57 accessibility and location-allocation. For big matrices use58 pandana/r5py; store as Parquet with origin/destination IDs.59- **Closest facility**: k-nearest by network cost (not Euclidean); report60 both the assigned facility and the cost.61- **Centrality**: betweenness on travel_time (sampled `k` for big62 graphs — exact is O(nm)); edge betweenness ≈ through-traffic potential.63 Interpret as network structure, not observed traffic.6465## Accessibility metrics — pick deliberately6667| Metric | Question it answers | Weakness |68|---|---|---|69| Cumulative opportunities (# jobs/POIs within T min) | Simple, communicable | Cliff at T; all-or-nothing |70| Gravity-based (distance-decayed sum) | Smooth access | Decay parameter must be justified |71| **2SFCA / E2SFCA** | Supply-demand ratio access (health care standard) | Catchment size choice drives results |72| Closest-facility time | Worst-case need | Ignores capacity/congestion |7374For equity analyses, join metrics to population/demographic polygons75(area-weighted or dasymetric — see `geo-data-engineering`) and report76distributions per group, not just city means. Route statistical testing of77disparities to `spatial-statistics`.7879## Location-allocation8081Optimal siting (p-median, max-coverage) on the OD matrix: formulate with82PuLP/OR-Tools; inputs are the OD matrix + demand weights + candidate83sites. State the objective explicitly — minimize mean travel time84(p-median) vs maximize covered demand within T (max-coverage) give85different answers, and stakeholders rarely know which they asked for.86Feed results back to `mcda-suitability-analysis` when siting mixes network87access with other criteria.8889## Transit (GTFS)9091Use r5py with OSM + GTFS feeds; results are departure-time sensitive —92compute over a time window (e.g., 07:00-09:00 percentiles), never a single93departure. Validate the feed (calendar coverage on your analysis date!) —94an expired GTFS calendar yields walking-only times that look plausible.9596## Verification protocol97981. Spot-check 3 routes against an external router (Google/OSRM) — within99 ~20% or explain why.1002. Map unreachable/infinite-cost pairs — usually snapping or connectivity101 artifacts, not real inaccessibility.1023. Isochrone eyeball: does it respect rivers, highways, one-ways?103104## Pitfalls checklist105106- Euclidean buffers presented as "service areas".107- Wrong network_type for the mode.108- Convex-hull isochrones bridging barriers.109- Snap distances unchecked.110- One departure time for transit accessibility.111- Betweenness sold as traffic volume.112- OD matrix in degrees-CRS travel "distances".113114## Execution contract115116- **Workflow:** define mode, time, impedance, origins, destinations, and equity question; build and validate the network; snap inputs; compute routes or matrices; summarize access; verify.117- **Decision rules:** use network costs for constrained travel, movement analytics for observed tracks, and MCDA only when accessibility becomes one criterion in a broader preference model.118- **Verification protocol:** audit connectivity and snapping, spot-check routes, map unreachable pairs, test departure-time or impedance sensitivity, and reconcile OD dimensions and units.119- **Failure modes:** withhold access claims for disconnected graphs, wrong mode or turn rules, expired GTFS service, excessive snapping, Euclidean substitution, or unstable departure-time results.120- **Deliverables:** network provenance, assumptions and cost function, routes or OD matrix, isochrones or access metrics, unreachable-case report, validation evidence, and equity caveats.121- **Source freshness:** consult [the authoritative source registry](references/authoritative-sources.md) before using network, GTFS, or routing APIs and archive source dates.