City2Graph: Geospatial Graphs for Network Analysis and GeoAI¶
City2Graph is a Python library that turns geospatial data into analysis-ready graphs for spatial network analysis, urban analytics, and GeoAI. Build networks from buildings, streets, public transport feeds, origin–destination matrices, zones, and points of interest; analyse them with NetworkX or convert them to PyTorch Geometric for Graph Neural Networks (GNNs).
City2Graph provides one interface across GeoPandas, NetworkX, PyTorch Geometric, and rustworkx. It preserves geospatial geometries and attributes while converting between graph representations, so the same graph can support mapping, spatial network analysis, and machine-learning workflows.
Paper and citation¶
Any use of City2Graph in research must cite the paper below:
Sato, Y., Pietrostefani, E., Mahabir, R., & Arribas-Bel, D. (2026). City2Graph: A Python library for Heterogeneous Graph Neural Networks and spatial analysis in urban systems. Computers, Environment and Urban Systems, 130, 102492. https://doi.org/10.1016/j.compenvurbsys.2026.102492
@article{sato2026city2graph,
title = {City2Graph: A Python library for Heterogeneous Graph Neural Networks and spatial analysis in urban systems},
author = {Sato, Yuta and Pietrostefani, Elisabetta and Mahabir, Ron and Arribas-Bel, Daniel},
journal = {Computers, Environment and Urban Systems},
volume = {130},
pages = {102492},
year = {2026},
issn = {0198-9715},
doi = {10.1016/j.compenvurbsys.2026.102492},
url = {https://www.sciencedirect.com/science/article/pii/S0198971526000943},
}
Quickstart¶
This installs the graph construction and spatial network analysis features. Install city2graph[cpu] or a CUDA extra when you also need PyTorch Geometric. See the installation guide for a package comparison, supported Python and CUDA versions, and conda-forge instructions.
import city2graph as c2g
# Buildings + street segments -> heterogeneous morphological graph
nodes, edges = c2g.morphological_graph(
buildings_gdf, segments_gdf, center_point, distance=500
)

import city2graph as c2g
gtfs = c2g.load_gtfs("itm_london_gtfs.zip")
# Stop-to-stop travel-time graph from a GTFS feed
nodes, edges = c2g.travel_summary_graph(
gtfs, calendar_start="20250601", calendar_end="20250601"
)

import city2graph as c2g
# OD matrix + zone geometries -> weighted spatial graph
nodes, edges = c2g.od_matrix_to_graph(
od_df, zones_gdf,
source_col="origin", target_col="destination",
weight_cols=["flow"], zone_id_col="zone_id",
)

import city2graph as c2g
# Proximity graphs over points of interest
knn_nodes, knn_edges = c2g.knn_graph(poi_gdf, k=5)
wax_nodes, wax_edges = c2g.waxman_graph(poi_gdf, r0=100, beta=0.5)
# Queen contiguity between polygonal zones
w_nodes, w_edges = c2g.contiguity_graph(wards_gdf, contiguity="queen")

import city2graph as c2g
# Compose relations: amenity -> segment -> segment -> amenity
metapaths = [[("amenity", "is_nearby", "segment"),
("segment", "connects", "segment"),
("segment", "is_nearby", "amenity")]]
nodes, edges = c2g.add_metapaths(
(nodes, edges), metapaths, edge_attr="distance_m", edge_attr_agg="sum"
)

import city2graph as c2g
# GeoPandas -> NetworkX
G = c2g.gdf_to_nx(nodes, edges)
# GeoPandas -> PyTorch Geometric
data = c2g.gdf_to_pyg(nodes, edges)
# PyTorch Geometric -> GeoPandas
nodes, edges = c2g.pyg_to_gdf(data)
# PyTorch Geometric -> NetworkX
G = c2g.pyg_to_nx(data)
# NetworkX -> rustworkx
G_rx = c2g.nx_to_rx(G)
# rustworkx -> NetworkX
G_nx = c2g.rx_to_nx(G_rx)
Examples¶
Applied projects¶
-

A reproducible research pipeline for Liverpool: open data become heterogeneous graphs, graph autoencoders learn embeddings, and clusters characterise urban structure.
-

Workshop: From Geospatial Data to GNNs
GeoAI in Practice, a hands-on FOSS4G 2026 workshop: construct spatial networks from open data, then build a graph autoencoder pipeline for spatial clustering.
Tutorials¶
-

No GTFS? Create It from OpenStreetMap
Generate a synthetic GTFS feed from OpenStreetMap for any city, build a public transport graph, and map accessibility along walking streets.
-

How to Use Overture Maps Like OSMnx
Bring the OSMnx-like experience to Overture Maps: fetch buildings, streets, and POIs for any place and turn them into analysis-ready graphs.
-

Morphological Graphs from Overture Maps & OpenStreetMap
Tessellate Liverpool's urban fabric, link it to the street network, and export a heterogeneous graph to NetworkX and PyTorch Geometric.
-

Metapath Construction for Heterogeneous GNNs
Materialise metapath edges between amenities reachable within a few street hops — the composite relations used by heterogeneous GNNs.
-

Convert a raw GTFS feed for London into a travel-time graph, rank stops by betweenness centrality, and draw walk-plus-transit isochrones.
-

OD Matrices to Mobility Graphs
Turn origin–destination data into spatial graphs, up to the 2021 census migration flows between all MSOAs of England and Wales.
-

Generate KNN, Delaunay, Gilbert, and Waxman graphs over Tokyo POIs under Euclidean, Manhattan, and network distances.
Why City2Graph?¶
Turning a city into a graph is rarely the interesting part of the work. It usually means writing throwaway code to parse a feed, deciding what counts as a node, flattening geometries into indices, and then writing it all again in a different shape as soon as the analysis moves from mapping to centrality to a GNN. City2Graph removes that step.
- A city is more than one network. Buildings, streets, transit stops, zones, and amenities can live in the same graph as distinct node and relation types rather than being collapsed into a single homogeneous network. That is the structure heterogeneous GNNs expect, and it keeps the question "which kind of thing is connected to which" answerable.
- Build the graph once, use it everywhere. The same graph moves between GeoDataFrames, NetworkX, PyTorch Geometric
DataandHeteroData, and rustworkx. Explore it in NetworkX, run heavy algorithms in rustworkx, train on it in PyTorch Geometric, and map the results, without rebuilding it for each tool or maintaining conversion code of your own. - Start from the data you already have. Overture Maps, OpenStreetMap-derived data, GTFS and GBFS feeds, origin–destination matrices, points of interest, and ordinary GIS layers are read directly, so a working graph is a few lines away rather than a preprocessing project.
What you can do with the graphs¶
A graph built with City2Graph is an ordinary GeoDataFrame, NetworkX, rustworkx, or PyTorch Geometric object, so one dataset supports several kinds of work.
- Spatial network analysis: centrality, shortest paths, and accessibility isochrones on street and public transport networks.
- Urban and transport research: urban morphology, walkability, multimodal accessibility, and origin–destination mobility flows.
- Spatial statistics and mapping: proximity and contiguity graphs over points of interest and zones, returned to GeoDataFrames for plotting.
- Graph Neural Networks and GeoAI:
DataandHeteroDatafor PyTorch Geometric. The Liverpool case study clusters urban functions with graph autoencoders, and the FOSS4G workshop walks through the pipeline.
The paper describes the design.
FAQ¶
What data can City2Graph convert into graphs?¶
City2Graph supports buildings, street segments, tessellations, Overture Maps features, GTFS and GBFS feeds, origin-destination matrices, points of interest, polygonal zones, and existing GeoDataFrame or NetworkX graphs. Each input has a builder function and a tutorial.
What can I use City2Graph for?¶
City2Graph graphs support spatial network analysis such as centrality, shortest paths and accessibility isochrones; urban morphology, walkability and public transport research; mobility flow analysis; spatial statistics with proximity and contiguity graphs; and Graph Neural Network workflows including GeoAI. The same graph converts between GeoDataFrames, NetworkX, rustworkx and PyTorch Geometric.
Is PyTorch required?¶
No. The core installation builds and analyses geospatial graphs with GeoPandas, NetworkX, and rustworkx without installing PyTorch. PyTorch and PyTorch Geometric are optional dependencies for GNN-ready Data and HeteroData tensors.
Can graphs be converted between GeoPandas, NetworkX, and PyTorch Geometric?¶
Yes. City2Graph supports round-trip conversion between GeoDataFrames, NetworkX, and PyTorch Geometric, including heterogeneous graphs.
How do I analyse a city network with NetworkX?¶
Build a graph, for example a morphological or transit graph, then convert it with gdf_to_nx to a NetworkX graph. Run centrality, shortest-path or connectivity analysis there, and return the results to GeoDataFrames with nx_to_gdf for mapping.
How do I build a graph for a GNN from Overture Maps or OpenStreetMap data?¶
Load buildings and street segments, for example with load_overture_data for Overture Maps, build a heterogeneous morphological graph with morphological_graph, then convert it with gdf_to_pyg to a PyTorch Geometric HeteroData object.
Can I analyse public transport without a GTFS feed?¶
Yes. load_osm_transport reads public transport route relations from OpenStreetMap and generates a synthetic GTFS feed in DuckDB. The same feed supports transit graphs, accessibility isochrones, and export as a GTFS ZIP.
How does City2Graph relate to OSMnx?¶
OSMnx is a focused toolkit for downloading and analysing OpenStreetMap street networks. City2Graph can use street data from OSMnx and combines it with buildings, Overture Maps, transit, mobility, proximity, and heterogeneous graph workflows. The two libraries are complementary.
Sample workflow¶
| Input or task | Graph produced | Main API | Typical use | Guide |
|---|---|---|---|---|
| Buildings, streets, and tessellations | Heterogeneous urban morphology graph | morphological_graph | Urban form, walkability, GNN embeddings | Morphology tutorial |
| GTFS public transport feed | Stop-to-stop travel-time graph | travel_summary_graph | Accessibility, centrality, multimodal routing | GTFS tutorial |
| OpenStreetMap public transport routes (no GTFS feed needed) | Synthetic GTFS feed, then a stop-to-stop travel-time graph | load_osm_transport | Transit accessibility in cities without an open feed | Create GTFS from OpenStreetMap |
| GBFS shared-mobility JSON feeds | DuckDB tables with station or vehicle point geometry | load_gbfs | Bike-share and shared-mobility preprocessing | Transportation API |
| OD matrix or flow edge list | Weighted mobility graph | od_matrix_to_graph | Migration, commuting, bike-sharing flows | OD matrix tutorial |
| Points, polygons, or graph layers | Proximity, contiguity, bridge, or containment graph | knn_graph, contiguity_graph | POI access, spatial interaction, zonal adjacency | Proximity tutorial |
| Typed relations in a heterogeneous graph | Metapath-derived edges | add_metapaths | Heterogeneous GNNs and composite relations | Metapath tutorial |
| GeoDataFrames, NetworkX, PyG, or rustworkx | Converted graph representation | Graph conversion API | Spatial analysis, fast graph algorithms, GNN training | API reference |