Network Analysis
Whitebox Next Gen R brings the same deep network-analysis engine as the Python API: topology auditing, point-to-point routing, service areas, closest facility, OD cost matrices, location-allocation, accessibility metrics, sensitivity analysis, multimodal transit modelling, map matching, and fleet dispatch optimization. This chapter walks through those capabilities in order, mirroring the prepare-then-query-then-scale-up workflow used in practice.
Capability Note (Open Tier)
The workflows in this chapter target the open-tier engine and include advanced
network controls such as turn/u-turn penalties, node-entry costs, and optional
time-dependent edge profiles (temporal_cost_profile + departure_time) for
scenario and peak-period analysis.
Session Setup
library(whiteboxworkflows)
s <- wbw_session()
setwd('/data/network')
Core Concepts You Should Know First
Before running tools, it helps to align on a few core terms used throughout this chapter.
- Network: A graph made of edges (road or transit segments) and nodes (intersections, stops, junctions).
- Cost / impedance: The value minimized during routing. This can be distance, travel time, generalized cost, or another friction metric.
- Origin / destination (OD): Origins are trip start points; destinations are trip end points.
- OD matrix: A table of costs from many origins to many destinations. This is the standard structure for accessibility, market access, and assignment analyses.
- Shortest path: The minimum-cost path between one origin and one destination.
- K-shortest paths: The best k distinct alternatives between the same OD pair, useful for resilience and choice modelling.
- Service area (isochrone): The portion of the network reachable from an origin within a cost threshold (for example 10 minutes).
- Closest facility: For each incident or demand point, the least-cost route to the nearest candidate facility on the network.
- Location-allocation: Selecting facility sites that optimize a demand objective, such as minimizing total travel cost or maximizing coverage.
- Connectivity: Whether all required origins and destinations are in the same connected component. Disconnected components cause failed routes.
- Node degree: The number of edges touching a node. Degree supports basic network centrality interpretation and QA for odd junction structure.
- Multimodal routing: Pathfinding across multiple transport modes (walk/bus/rail) with transfer penalties and mode constraints.
- Map matching: Snapping GPS trajectories to the most plausible sequence of network edges.
If you keep these definitions in mind, each workflow step below becomes easier to interpret and validate.
Modeling Intersection Delay With Node Costs
Network tools in this chapter support optional node-entry cost modeling for intersections, gates, crossings, or turn-heavy junctions:
node_cost_points: point layer containing node-cost observations.node_cost_field: numeric field innode_cost_pointswith non-negative entry cost values.node_cost_snap_distance: optional max assignment distance from each node-cost point to the nearest network node.
Use node costs when edge impedance alone underestimates urban delay at intersections.
Step 1 — Prepare and Audit the Network
Every routing workflow should begin with a topology check.
Topology Audit
network_topology_audit scans a line network for dead ends, pseudo-nodes,
overshoots, and isolated islands, writing each flagged location as a point
feature and optionally producing a text report.
wbw_network_topology_audit(i = 'roads.shp',
output = 'topology_errors.shp',
snap_tolerance = 0.5,
one_way_field = 'ONEWAY',
report = 'topology_report.txt')
Review topology_report.txt to understand the error count and class
distribution before continuing.
Connected Components
network_connected_components assigns a component identifier to every edge so
you can identify and resolve disconnected islands before running OD queries.
wbw_network_connected_components(i = 'roads.shp',
output = 'roads_components.shp',
snap_tolerance = 0.5)
# Edges not in the dominant component are candidates for removal or bridging.
Node Degree
network_node_degree writes the degree of every node as a point layer.
Degree-1 nodes are dead ends; unusually high-degree nodes may be duplicates.
wbw_network_node_degree(i = 'roads.shp',
output = 'node_degree.shp',
snap_tolerance = 0.5)
Building Network Topology
If your raw network lacks proper topology (node points, edge connectivity structure), use build_network_topology() to construct nodes and validate edges. This is essential before running advanced analysis like service areas or facility location.
result <- wbw_build_network_topology(i=roads, snap_tolerance=0.5)
edges <- result$edges
nodes <- result$nodes
wbw_write_vector(edges, 'roads_noded.shp')
wbw_write_vector(nodes, 'network_nodes.shp')
Snapping Facilities and Demand Points
Before routing from facilities or demand points, snap them to the nearest network location. This ensures routing queries don't fail on "off-network" origins.
facilities <- wbw_read_vector('fire_stations.shp')
snapped <- wbw_snap_points_to_network(
network=edges,
points=facilities,
snap_distance=50.0 # meters
)
wbw_write_vector(snapped, 'fire_stations_snapped.shp')
# Output includes SNAP_DIST (offset to network) and snapped geometry.
Step 2 — Shortest Path and Alternatives
Single Shortest Path
shortest_path_network finds the minimum-cost path between two coordinates
using Dijkstra's algorithm. Supply edge_cost_field for travel-time routing;
omit it to route by Euclidean arc length.
wbw_shortest_path_network(i = 'roads.shp',
output = 'route_shortest.shp',
start_x = 454230.0,
start_y = 4823150.0,
end_x = 458900.0,
end_y = 4819700.0,
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY')
Turn penalties model the real cost of left, right, and U-turns in dense urban networks.
wbw_shortest_path_network(i = 'roads.shp',
output = 'route_with_turns.shp',
start_x = 454230.0,
start_y = 4823150.0,
end_x = 458900.0,
end_y = 4819700.0,
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY',
node_cost_points = 'intersection_delay_points.shp',
node_cost_field = 'DELAY_MIN',
node_cost_snap_distance = 25.0,
turn_penalty = 0.5,
u_turn_penalty = 3.0,
forbid_u_turns = TRUE)
K-Shortest Alternative Paths
k_shortest_paths_network returns the k least-cost distinct paths for the
same endpoints — useful for resilience analysis and presenting alternatives to
planners.
wbw_k_shortest_paths_network(i = 'roads.shp',
output = 'routes_k3.shp',
start_x = 454230.0,
start_y = 4823150.0,
end_x = 458900.0,
end_y = 4819700.0,
k = 3L,
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY')
# Each feature carries a PATH_RANK attribute (1 = shortest).
Step 3 — Service Areas
network_service_area delineates every part of the network reachable within a
cost threshold from one or more origins. Typical uses include drive-time
catchments for emergency services, walking isochrones around transit stops, and
delivery zones.
wbw_network_service_area(i = 'roads.shp',
origins = 'fire_stations.shp',
output = 'fire_catchment_5min.shp',
max_cost = 5.0,
snap_tolerance = 20.0,
output_mode = 'polygon',
polygon_merge_origins = TRUE,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY')
To model rush-hour conditions, pass a temporal speed profile and apply turn penalties.
wbw_network_service_area(i = 'roads.shp',
origins = 'fire_stations.shp',
output = 'fire_catchment_5min_am_peak.shp',
max_cost = 5.0,
snap_tolerance = 20.0,
output_mode = 'polygon',
polygon_merge_origins = TRUE,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY',
node_cost_points = 'intersection_delay_points.shp',
node_cost_field = 'DELAY_MIN',
node_cost_snap_distance = 25.0,
turn_penalty = 0.3,
u_turn_penalty = 2.0,
forbid_u_turns = TRUE,
temporal_cost_profile = 'rush_hour_profiles.csv',
temporal_edge_id_field = 'EDGE_ID',
departure_time = '2024-06-15T08:00:00Z',
temporal_mode = 'multiplier',
temporal_fallback = 'static_cost')
Use output_mode = 'edges' to retain actual road arcs inside the catchment
rather than fill a polygon — more appropriate when the network is sparse.
Step 4 — Closest Facility
closest_facility_network routes each incident point to its nearest facility,
measuring cost along the network rather than in straight-line distance. This
is the core tool for emergency-response siting, healthcare access studies, and
school-catchment delineation.
wbw_closest_facility_network(i = 'roads.shp',
incidents = 'accidents.shp',
facilities = 'hospitals.shp',
output = 'routes_to_hospital.shp',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY')
# Output carries INCIDENT_FID, FACILITY_FID, and COST fields per route.
For peak-hour response-time analysis, combine turn penalties with a temporal speed profile.
wbw_closest_facility_network(i = 'roads.shp',
incidents = 'accidents.shp',
facilities = 'hospitals.shp',
output = 'routes_to_hospital_am_peak.shp',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY',
node_cost_points = 'intersection_delay_points.shp',
node_cost_field = 'DELAY_MIN',
node_cost_snap_distance = 25.0,
turn_penalty = 0.5,
u_turn_penalty = 3.0,
forbid_u_turns = TRUE,
temporal_cost_profile = 'rush_hour_profiles.csv',
temporal_edge_id_field = 'EDGE_ID',
departure_time = '2024-06-15T08:00:00Z',
temporal_mode = 'multiplier',
temporal_fallback = 'static_cost')
Step 5 — OD Cost Matrix and Batch Route Export
OD Cost Matrix
network_od_cost_matrix solves all pairwise paths and writes results to a CSV.
Each row contains an origin identifier, a destination identifier, and the
network cost between them.
wbw_network_od_cost_matrix(i = 'roads.shp',
origins = 'schools.shp',
destinations = 'libraries.shp',
output = 'od_costs.csv',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY')
od <- read.csv('od_costs.csv')
# Minimum travel time from each school to any library:
print(aggregate(COST ~ ORIGIN_FID, data = od, FUN = min))
For a time-of-day comparison, run a second matrix at AM-peak departure.
wbw_network_od_cost_matrix(i = 'roads.shp',
origins = 'schools.shp',
destinations = 'libraries.shp',
output = 'od_costs_am_peak.csv',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY',
node_cost_points = 'intersection_delay_points.shp',
node_cost_field = 'DELAY_MIN',
node_cost_snap_distance = 25.0,
turn_penalty = 0.5,
temporal_cost_profile = 'am_peak_profiles.csv',
temporal_edge_id_field = 'EDGE_ID',
departure_time = '2024-06-15T08:00:00Z',
temporal_mode = 'multiplier',
temporal_fallback = 'static_cost')
Materializing OD Routes as Geometry
network_routes_from_od creates the actual path lines between OD pairs.
wbw_network_routes_from_od(i = 'roads.shp',
origins = 'schools.shp',
destinations = 'libraries.shp',
output = 'od_routes.shp',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY')
Step 6 — Location-Allocation
location_allocation_network solves the p-median problem: given candidate
facility locations and weighted demand points, which p facilities minimise
total travel cost? Directly supports clinic siting, school consolidation, and
warehouse network design.
wbw_location_allocation_network(i = 'roads.shp',
demand_points = 'demand_points.shp',
facilities = 'candidate_sites.shp',
output = 'selected_facilities.shp',
facility_count = 4L,
solver_mode = 'minimize_impedance',
demand_weight_field = 'POP',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES')
# SELECTED == 1 on the four chosen candidate sites.
# Demand points carry ASSIGNED_FID linking each to its nearest selected site.
Supported solver modes: minimize_impedance (p-median), maximize_coverage,
and maximize_attendance.
To optimise under peak-hour travel times, pass a temporal profile.
wbw_location_allocation_network(i = 'roads.shp',
demand_points = 'demand_points.shp',
facilities = 'candidate_sites.shp',
output = 'selected_facilities_am_peak.shp',
facility_count = 4L,
solver_mode = 'minimize_impedance',
demand_weight_field = 'POP',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES',
node_cost_points = 'intersection_delay_points.shp',
node_cost_field = 'DELAY_MIN',
node_cost_snap_distance = 25.0,
temporal_cost_profile = 'am_peak_profiles.csv',
temporal_edge_id_field = 'EDGE_ID',
departure_time = '2024-06-15T08:00:00Z',
temporal_mode = 'multiplier',
temporal_fallback = 'static_cost')
Step 7 — Network Accessibility Metrics
compute_network_accessibility() is exposed as a typed facade wrapper. It
computes a gravity-model or cumulative-opportunity accessibility score per
origin — a standard indicator in transport equity analysis.
residents <- wbw_read_vector('resident_centroids.shp', session = s)
supermarkets <- wbw_read_vector('supermarkets.shp', session = s)
result <- s$compute_network_accessibility(
input = 'roads.shp',
origins = residents$file_path(),
destinations = supermarkets$file_path(),
output = 'food_accessibility.shp',
edge_cost_field = 'MINUTES',
node_cost_points = 'intersection_delay_points.shp',
node_cost_field = 'DELAY_MIN',
node_cost_snap_distance = 25.0,
impedance_cutoff = 30.0,
decay_function = 'negative_exponential',
decay_parameter = 0.1
)
# Each origin point carries an ACCESS_SCORE field.
Step 8 — OD Sensitivity Analysis
analyze_od_cost_sensitivity() quantifies how stable OD costs are under
stochastic perturbation of edge weights — useful for stress-testing a routing
model against travel-time uncertainty or hypothetical road-closure scenarios.
result <- s$analyze_od_cost_sensitivity(
input = 'roads.shp',
origins = 'schools.shp',
destinations = 'libraries.shp',
output = 'od_sensitivity.shp',
edge_cost_field = 'MINUTES',
node_cost_points = 'intersection_delay_points.shp',
node_cost_field = 'DELAY_MIN',
node_cost_snap_distance = 25.0,
impedance_disturbance_range = 0.2, # ±20 % perturbation
monte_carlo_samples = 500L
)
Step 9 — Multimodal Analysis
Whitebox Next Gen supports networks with a MODE field on each edge (e.g.
walk, cycle, bus, rail). Multimodal tools honour mode-specific speeds,
transfer penalties, and time-of-day profiles.
Multimodal OD Scenarios
analyze_multimodal_od_scenarios() runs a batch of named scenarios from a CSV,
each with different mode allowances, speed overrides, or departure times.
transit_net <- wbw_read_vector('transit_network.shp', session = s)
bus_stops <- wbw_read_vector('bus_stops.shp', session = s)
destinations <- wbw_read_vector('key_destinations.shp', session = s)
result <- s$analyze_multimodal_od_scenarios(
input = transit_net$file_path(),
origins = bus_stops$file_path(),
destinations = destinations$file_path(),
output = 'multimodal_od_scenarios.shp',
mode_field = 'MODE',
allowed_modes = 'walk,bus,rail',
transfer_penalty = 3.0,
edge_cost_field = 'MINUTES',
scenario_bundle_csv = 'scenarios.csv',
departure_time = '08:00',
temporal_mode = 'scheduled'
)
Exporting Multimodal Route Geometry
export_multimodal_routes_for_od_pairs() materializes the optimal multimodal
route for each OD pair with per-segment mode attributes.
result <- s$export_multimodal_routes_for_od_pairs(
input = transit_net$file_path(),
origins = bus_stops$file_path(),
destinations = destinations$file_path(),
output = 'multimodal_routes.shp',
mode_field = 'MODE',
allowed_modes = 'walk,bus,rail',
transfer_penalty = 3.0,
edge_cost_field = 'MINUTES'
)
Step 10 — Map Matching
map_matching_v1 snaps a raw GPS trajectory to the most plausible sequence of
network edges using a hidden Markov model with candidate expansion. It is the
first step in any floating-vehicle data or probe-data workflow.
wbw_map_matching_v1(i = 'roads.shp',
trajectory_points = 'gps_probe_points.shp',
timestamp_field = 'TIMESTAMP',
output = 'matched_route.shp',
search_radius = 30.0,
candidate_k = 5L,
snap_tolerance = 10.0,
edge_cost_field = 'MINUTES',
matched_points_output = 'matched_points.shp',
match_report = 'match_report.txt')
Step 11 — Fleet and Vehicle Routing (Pro)
fleet_routing_and_dispatch_optimizer solves CVRP and VRPTW problems: given
a fleet of vehicles and a set of service or delivery stops, it assigns and
sequences routes to minimise total cost subject to capacity and time-window
constraints.
result <- s$run_tool(
'fleet_routing_and_dispatch_optimizer',
list(
network = 'roads.shp',
depots = 'depots.shp',
stops = 'delivery_stops.shp',
vehicles_csv = 'fleet.csv',
route_output = 'fleet_routes.shp',
route_kpis_csv_output = 'fleet_kpis.csv',
edge_cost_field = 'MINUTES',
one_way_field = 'ONEWAY',
vrp_mode = 'VRPTW'
)
)
kpis <- read.csv('fleet_kpis.csv')
print(kpis)
Note: This tool requires a session initialised with a valid Pro licence.
Complete Workflow: Emergency Response Planning
library(whiteboxworkflows)
s <- wbw_session()
setwd('/data/emergency_planning')
# 1. Audit topology.
wbw_network_topology_audit(i = 'roads.shp', output = 'topology_errors.shp',
snap_tolerance = 0.5, report = 'topology_report.txt')
# 2. Five-minute drive catchments from existing stations.
wbw_network_service_area(i = 'roads.shp',
origins = 'fire_stations.shp',
output = 'existing_catchment_5min.shp',
max_cost = 5.0,
output_mode = 'polygon',
polygon_merge_origins = TRUE,
edge_cost_field = 'MINUTES',
snap_tolerance = 20.0)
# 3. Route historical incidents to nearest existing station.
wbw_closest_facility_network(i = 'roads.shp',
incidents = 'historical_incidents.shp',
facilities = 'fire_stations.shp',
output = 'incident_routes.shp',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES')
# 4. Find two new station sites that maximise coverage.
wbw_location_allocation_network(i = 'roads.shp',
demand_points = 'historical_incidents.shp',
facilities = 'candidate_stations.shp',
output = 'new_station_sites.shp',
facility_count = 2L,
solver_mode = 'maximize_coverage',
snap_tolerance = 20.0,
edge_cost_field = 'MINUTES')
Tips
- Always run
network_topology_auditfirst — even one disconnected segment can cause a path query to return no result without an explicit error. - Use
network_connected_componentsto confirm all origins and destinations belong to the same component before running OD queries. - Supply
edge_cost_fieldpointing to a travel-time column for realistic routing; omit it only for pure geometric distance problems. - For scheduled transit, pass
temporal_cost_profileanddeparture_timeto load time-of-day speeds. - The
fleet_routing_and_dispatch_optimizerPro tool requires a session initialised with a valid Pro licence.