UrbanGraph basics

UrbanGraph is the core IduEdu data model. It stores topology and geometry in two tables and builds SciPy CSR adjacency matrices for routing.

# To install IduEdu in a clean environment:
# !pip install IduEdu

OSM_ID = 1114252
from iduedu import UrbanGraph, get_walk_graph

G = get_walk_graph(osm_id=OSM_ID, simplify=True, keep_largest_subgraph=True)
G
2026-07-07 15:19:37.764 | WARNING  | Removing 332 nodes outside the largest connected component. Retaining 19588 of 19920 nodes.
UrbanGraph(nodes=19588, edges=26384, is_multigraph=True, is_directed=False, edge_direction_column=None, crs='EPSG:32636', type='walk')

Nodes and edges

The node index is the graph node id. Edges reference node ids through u and v; multigraph edges also use k.

G.nodes_gdf.head()
geometry
0 POINT (342445.98 6648580.728)
1 POINT (342446.941 6648576.639)
2 POINT (342462.306 6648574.49)
3 POINT (342478.126 6648582.111)
4 POINT (342485.496 6648566.956)
G.edges_gdf.head()
geometry highway name u v length_meter time_min type k
0 LINESTRING (342445.98 6648580.728, 342451.109 ... service NaN 0 17080 26.935 0.323 walk 0
1 LINESTRING (342445.98 6648580.728, 342419.381 ... service NaN 0 17081 28.181 0.338 walk 0
2 LINESTRING (342446.941 6648576.639, 342418.099... footway NaN 1 17082 30.602 0.367 walk 0
3 LINESTRING (342462.306 6648574.49, 342445.98 6... service NaN 2 0 17.477 0.210 walk 0
4 LINESTRING (342462.306 6648574.49, 342462.084 ... service NaN 2 4 37.423 0.449 walk 0
print("crs:", G.crs)
print("graph type:", G.type)
print("directed:", G.is_directed)
print("multigraph:", G.is_multigraph)
print("edge direction column:", G.edge_direction_column)
crs: EPSG:32636
graph type: walk
directed: False
multigraph: True
edge direction column: None

Validation and copying

validate checks table contracts. copy returns an independent graph object and preserves adjacency cache state when it exists.

G.validate()
G_copy = G.copy()
print(G_copy)
UrbanGraph(nodes=19588, edges=26384, is_multigraph=True, is_directed=False, edge_direction_column=None, crs='EPSG:32636', type='walk')

Adjacency matrix cache

to_csr builds a matrix without changing the graph cache. update_adjacency_matrix stores the matrix on the graph and records the node order.

csr = G.to_csr(weight="time_min")
print(csr.shape, csr.nnz)
print(G.adjacency_matrix is None)

cached = G.update_adjacency_matrix(weight="time_min")
print(cached.shape, cached.nnz)
print(G.adjacency_weight)
print(G.adjacency_nodelist[:5])
(19588, 19588) 52372
True
(19588, 19588) 52372
time_min
[0, 1, 2, 3, 4]

Empty graph

Use UrbanGraph.empty when a workflow needs to return a graph object even when no edges were found.

empty_graph = UrbanGraph.empty(crs=G.crs, is_multigraph=True, is_directed=False, graph_type="walk")
empty_graph
UrbanGraph(nodes=0, edges=0, is_multigraph=True, is_directed=False, edge_direction_column=None, crs='EPSG:32636', type='walk')

Read and write .urbangraph archives

The archive stores node and edge tables. It can also store the adjacency cache if it has been built.

from pathlib import Path
from tempfile import TemporaryDirectory
from iduedu import read_urban_graph, write_urban_graph

with TemporaryDirectory() as tmp_dir:
    path = Path(tmp_dir) / "walk.urbangraph"
    write_urban_graph(G, path, include_adjacency=True)
    restored = read_urban_graph(path)

restored
UrbanGraph(nodes=19588, edges=26384, is_multigraph=True, is_directed=False, edge_direction_column=None, crs='EPSG:32636', type='walk')