Skip to content

Network

Road/rail-network-constrained distance: build a routable graph from Overture Maps transportation data, snap trajectory points to it, and query network (not straight-line) distance via a Rust contraction-hierarchy router.

API Description
RoadNetwork A road/rail network prepared once (contraction hierarchy), reused for many distance queries.
fetch_road_network Fetch and build a car-routable graph from Overture road segments.
build_road_graph Load a cached road graph from disk, or fetch and cache it.
fetch_rail_network Fetch and build a bidirectional rail graph from Overture segments.
build_rail_graph Load a cached rail graph from disk, or fetch and cache it.
snap_locations_to_graph Snap each row to its nearest road/rail graph node.
RoadNetwork.batch_routes Route geometry (waypoint coordinates) for (from_node, to_node) queries.
od_desire_lines Aggregate OD-pair flows onto graph edges (desire lines).
haversine_m_batch Vectorized Haversine distance (metres) between two arrays of points.

fetch_road_network and fetch_rail_network require DuckDB: install it with pip install duckdb. Snapping uses Fastmob's native Rust spatial index and has no additional Python dependency.

Network-aware distance measures

fastmob.measures.individual.jump_lengths_road/radius_of_gyration_road mirror jump_lengths/radius_of_gyration but measure distance along a prepared RoadNetwork instead of straight-line, falling back to Haversine per-pair wherever a point is unsnapped or the graph is disconnected between the two points:

import pandas as pd
from fastmob.network import RoadNetwork, fetch_road_network
from fastmob.measures.individual import jump_lengths_road, radius_of_gyration_road

nodes_df, edges_df = fetch_road_network(2.34, 48.85, 2.36, 48.86, "2026-05-20.0")
network = RoadNetwork.build(edges_df, nodes_df)

traj = pd.DataFrame(...)  # uid, datetime, lat, lng columns
jumps_km = jump_lengths_road(traj, network=network)
rg_km = radius_of_gyration_road(traj, network=network)

Route geometry and OD desire lines

RoadNetwork.batch_routes returns the actual waypoint path (not just total distance) for a batch of (from_node, to_node) queries, decimated to at most max_waypoints points per route (always keeping the first and last). od_desire_lines is the Overture-native analogue of stplanr's overline/overline2: it aggregates many origin-destination flows onto the road/rail graph's edges, so overlapping trips accumulate onto shared segments instead of remaining one separate desire line per pair.

import numpy as np
from fastmob.network import RoadNetwork, od_desire_lines

network = RoadNetwork.build(edges_df, nodes_df)

routes = network.batch_routes(np.array([0, 5]), np.array([12, 3]), max_waypoints=50)
# columns: query_id, lat, lng, cum_weight_ds

edges_with_flow, dropped_flow = od_desire_lines(
    network, np.array([0, 1]), np.array([12, 12]), np.array([5.0, 3.0])
)
# columns: edge_from, edge_to, from_lat, from_lng, to_lat, to_lng, total_flow

Both fall back gracefully for unsnapped (negative node id) or graph-disconnected queries: batch_routes contributes zero rows for that query, and od_desire_lines adds that query's flow to dropped_flow instead of any edge.


fastmob.network.RoadNetwork

A road (or rail) network prepared once (contraction hierarchy) and reused for many point-to-point physical-distance queries.

Bundles the snap-target nodes together with the routing handle (unlike a split handle + separate nodes_df pair), since a caller always needs both to go from raw lat/lng to a routed distance.

batch_distances(from_nodes, to_nodes)

Batch physical-distance (metres) query for (from_node, to_node) pairs.

Returns (distances_m, connected), connected as a bool array; False for negative/unsnapped node ids or a disconnected graph component (fall back to straight-line Haversine in that case).

batch_routes(from_nodes, to_nodes, max_waypoints=50)

Batch route-geometry query for (from_node, to_node) pairs.

Returns a flat pyarrow.Table with one row per waypoint: columns query_id (0-based index into from_nodes/to_nodes), lat, lng, cum_weight_ds (cumulative travel-time weight from the route's start), following the same flat-output + boundary convention used by other variable-rows-per-group measures in this library (rather than one Python object per query). A query with connected == False (unsnapped/disconnected) contributes zero rows; join back on query_id against a connected array from batch_distances if you need to distinguish "no route" from "route with no waypoints" (the latter cannot happen: every connected route has at least its two endpoints).

Waypoints are decimated to at most max_waypoints per query (always keeping the first and last), so a caller wanting the full, undecimated node path should pass a large max_waypoints.

build(edges_df, nodes_df) classmethod

Prepare a contraction hierarchy from a road/rail graph's edges.

Parameters:

Name Type Description Default
edges_df

Columns from_node, to_node, weight_ds, length_m (as returned by :func:fastmob.network.builder.fetch_road_network / fetch_rail_network); any Narwhals-compatible backend.

required
nodes_df

Columns node_idx, lat, lng -- used later for snapping via :func:fastmob.network.snap.snap_locations_to_graph (stored as a pyarrow.Table here regardless of input backend, sorted by node_idx); any Narwhals-compatible backend.

required

fastmob.network.fetch_road_network(min_lon, min_lat, max_lon, max_lat, overture_release)

Fetch and build a car-routable graph from Overture road segments.

Returns:

Type Description
nodes_df, edges_df:

nodes_df: node_idx (dense, 0-based), connector_id, lat, lng. edges_df: from_node, to_node, length_m, speed_kmh, weight_ds, class.


fastmob.network.build_road_graph(min_lon, min_lat, max_lon, max_lat, overture_release, nodes_output, edges_output)

Load a cached road graph from disk, or fetch and cache it.


fastmob.network.fetch_rail_network(min_lon, min_lat, max_lon, max_lat, overture_release, classes=None, speed_kmh_by_class=None, default_speed_kmh=35.0)

Fetch and build a simple bidirectional rail graph from Overture segments.


fastmob.network.build_rail_graph(min_lon, min_lat, max_lon, max_lat, overture_release, nodes_output, edges_output, classes=None, speed_kmh_by_class=None, default_speed_kmh=35.0)

Load a cached rail graph from disk, or fetch and cache it.


fastmob.network.snap_locations_to_graph(tessellation_df, nodes_df, max_distance_m, lat_col='lat', lng_col='lng')

Snap each tessellation row to its nearest road/rail graph node.

Parameters:

Name Type Description Default
tessellation_df

Rows with lat/lng columns to snap.

required
nodes_df

Graph nodes with columns node_idx, lat, lng (as returned by :func:fastmob.network.builder.fetch_road_network / fetch_rail_network).

required
max_distance_m float

Maximum snap distance; farther rows are reported unsnapped.

required
lat_col str

Column names on tessellation_df.

'lat'
lng_col str

Column names on tessellation_df.

'lat'

Returns:

Type Description
Int64Array

int64 array aligned 1:1 with tessellation_df rows; -1 when the nearest node is farther than max_distance_m (unsnapped).


fastmob.network.haversine_m_batch(lat1, lng1, lat2, lng2) builtin

Elementwise Haversine distance (metres) between two same-length arrays of points, computed in parallel. See [fastmob_core::utils::haversine::haversine_m_batch].


fastmob.network.od_desire_lines(road_network, from_nodes, to_nodes, flows)

Aggregate OD-pair flows onto the road/rail graph's edges (desire lines).

The Overture-native analogue of stplanr's overline/overline2: for each (from_node, to_node, flow) triple, walks the time-optimal route between the two nodes and adds flow to every edge it crosses, so many overlapping OD pairs accumulate onto shared road segments instead of remaining one separate desire line per pair.

Parameters:

Name Type Description Default
road_network

A prepared network (see RoadNetwork.build); from_nodes/ to_nodes are node ids from that network's nodes_df (e.g. via :func:fastmob.network.snap.snap_locations_to_graph).

required
from_nodes

Node ids per OD pair; a negative value marks an unsnapped origin or destination.

required
to_nodes

Node ids per OD pair; a negative value marks an unsnapped origin or destination.

required
flows

Flow volume (e.g. trip count) per OD pair.

required

Returns:

Type Description
(edges_df, dropped_flow)

edges_df has columns edge_from, edge_to, from_lat, from_lng, to_lat, to_lng, total_flow, one row per edge touched by at least one route. dropped_flow is the summed flow of OD pairs that were unsnapped or whose endpoints sit in disconnected graph components.


fastmob.measures.individual.network_distance.jump_lengths_road(traj, *, network, uid_col=None, lat_col=None, lng_col=None, datetime_col=None, snap_max_distance_m=750.0)

Road-network jump lengths (km): distance between consecutive stops for the same user, sorted by datetime -- mirrors :func:~fastmob.measures.individual.jump_lengths.jump_lengths's sort key and its inclusion of zero-length jumps, but measures along the network instead of straight-line, falling back to Haversine per-pair when unsnapped or disconnected.

Parameters:

Name Type Description Default
traj Any

Trajectory dataframe; any Narwhals-compatible eager backend.

required
network RoadNetwork

A prepared :class:fastmob.network.RoadNetwork.

required
uid_col str | None

Explicit column name overrides; auto-detected when None.

None
lat_col str | None

Explicit column name overrides; auto-detected when None.

None
lng_col str | None

Explicit column name overrides; auto-detected when None.

None
datetime_col str | None

Explicit column name overrides; auto-detected when None.

None
snap_max_distance_m float

Maximum distance (metres) to snap a stop to the network; farther stops fall back to Haversine entirely for any jump touching them.

750.0

Returns:

Type Description
ndarray

One value per consecutive same-user pair (length: len(traj) - n_users).


fastmob.measures.individual.network_distance.radius_of_gyration_road(traj, *, network, uid_col=None, lat_col=None, lng_col=None, snap_max_distance_m=750.0)

Road-network radius of gyration (km) per user: RMS network distance from each of a user's stops to the arithmetic-mean centroid of their stops -- mirrors the unweighted-centroid formula r_g(u) = sqrt(mean(d(r_i, r_cm)^2)) used by :func:~fastmob.measures.individual.radius_of_gyration.radius_of_gyration, but measures d along the network instead of straight-line.

Parameters:

Name Type Description Default
traj Any

Trajectory dataframe; any Narwhals-compatible eager backend.

required
network RoadNetwork

A prepared :class:fastmob.network.RoadNetwork.

required
uid_col str | None

Explicit column name overrides; auto-detected when None.

None
lat_col str | None

Explicit column name overrides; auto-detected when None.

None
lng_col str | None

Explicit column name overrides; auto-detected when None.

None
snap_max_distance_m float

Maximum distance (metres) to snap a stop to the network.

750.0

Returns:

Type Description
DataFrame

[uid_col, "radius_of_gyration"], one row per user, in the same backend as input.