network
High-level CityNetwork API for urban network analysis. The CityNetwork class wraps network construction, centrality computation, land-use analysis, and export into a single interface. It builds dual graphs (where street segments become nodes rather than intersections) directly from LineString geometries, enabling both shortest-path (metric distance) and simplest-path (angular change) analysis. See the Guide for concepts, conventions, and worked examples.
CityNetwork
High-level interface for urban network analysis. Wraps network construction, centrality computation, and land-use analysis into a single object that manages graph topology, node attributes, and coordinate reference systems. The network is built as a dual graph where street segments become nodes and intersections become edges, enabling both shortest-path (metric) and simplest-path (angular) centrality analysis.
Construct instances via the class methods rather than calling __init__ directly:
from_geopandas– from a GeoDataFrame of LineString geometriesfrom_wkts– from a dictionary of WKT strings or Shapely geometriesfrom_nx– from a cityseer-compatible NetworkX MultiGraphfrom_osm– from OpenStreetMap via a bounding polygonload– from a previously saved parquet/pickle pair
Most methods return self to support method chaining:
cn = (
CityNetwork.from_geopandas(edges_gdf, crs=32632)
.set_boundary(boundary_polygon)
.centrality_shortest(distances=[500, 1000, 2000])
)
Notes
short self-loops are removed, chains of segments meeting at filler (degree-2) endpoints are welded into single segments, short danglers are removed, and near-duplicate parallel edges are merged. Each step is controlled by a constructor parameter (remove_fillers, remove_danglers, merge_parallel_dist) and can be disabled to pass a network through as-is. Use the feature_status property to inspect which input features were filtered or merged and why.
- Shortest-path analysis uses metric distances along street segments.
- Simplest-path analysis uses cumulative angular change along streets and at intersections as the routing cost.
Because the dual is built automatically, there is no need to call nx_to_dual when using CityNetwork. Although the topology is dual internally, results are visualised and exported as the original street segment geometries via to_geopandas, so each row in the output corresponds to one input street.
Working with results
All computed metrics are written to the internal nodes_gdf GeoDataFrame. Since CityNetwork uses a dual graph, each row in nodes_gdf represents a street segment, with a Point geometry at the segment midpoint.
To retrieve results with the original LineString geometries, use to_geopandas:
cn = CityNetwork.from_geopandas(edges_gdf, crs=32632)
cn.centrality_shortest(distances=[800])
# Midpoint geometries (internal representation)
cn.nodes_gdf["cc_harmonic_800"]
# Original LineString geometries with the same computed columns
result_gdf = cn.to_geopandas()
result_gdf["cc_harmonic_800"]
Column names follow the cc_{metric}_{distance} convention described in the Column Naming Conventions section.
Feature cleaning
Input geometries are automatically cleaned during construction, on the primal edge set before dual conversion, in this order:
- Short self-loops (under 1 m) are removed as corrupt geometry. 2. Filler welding (
remove_fillers=True): chains of segments meeting at degree-2 endpoints are welded into single segments, so a street subdivided during digitisation is treated as one segment. The welded feature keeps the id of the longest constituent; absorbed features are marked"merged". 3. Danglers (remove_danglers=10.0): dead-end stubs up to this length are removed iteratively, judged on the full welded length.0disables. 4. Parallel merging (merge_parallel_dist=2.0): edges whose endpoints fall within this distance of another edge’s endpoints, with near-identical lengths, are the same street drawn twice; the longest is kept.0disables.
Directed networks skip filler welding and parallel merging, which do not preserve one-way semantics. To pass a network through with minimal intervention, disable the steps:
cn = CityNetwork.from_geopandas(edges_gdf, remove_fillers=False, remove_danglers=0, merge_parallel_dist=0)
The feature_status property returns a Series indicating the status of each input feature:
cn = CityNetwork.from_geopandas(edges_gdf, crs=32632)
print(cn.feature_status.value_counts())
# active 142
# merged 6
# short_dangler 3
# duplicate 1
Saving and loading
Networks can be serialised to disk and restored later, preserving all computed metrics:
cn.save("my_network")
# Creates: my_network.nodes.parquet, my_network.state.pkl
cn_restored = CityNetwork.load("my_network")
Updating geometries
To analyse modified geometries, construct a fresh network from the updated data; construction is fast and stateless, and cleaning runs exactly once per construction. (Controlled in-place editing exists for the QGIS plugin via tools.dual.incremental_update, where the layer-edit workflow warrants it.)
cn = CityNetwork.from_geopandas(updated_edges_gdf, crs=32632)
cn.centrality_shortest(distances=[800])
Typical workflow
import geopandas as gpd
from cityseer.network import CityNetwork
from cityseer import decay
# 1. Load street network edges
edges_gdf = gpd.read_file("streets.gpkg")
# 2. Build the network
cn = CityNetwork.from_geopandas(edges_gdf, crs="EPSG:32632")
# 3. Compute centrality at multiple scales
cn.centrality_shortest(distances=[400, 800, 1600])
cn.centrality_simplest(distances=[400, 800, 1600])
# 4. Compute land-use accessibility
landuses_gdf = gpd.read_file("landuses.gpkg")
cn, landuses_gdf = cn.compute_accessibilities(
data_gdf=landuses_gdf,
landuse_column_label="category",
accessibility_keys=["retail", "park"],
distances=[400, 800],
decay_fn=decay.exponential(),
)
# 5. Compute statistical aggregations
prices_gdf = gpd.read_file("property_prices.gpkg")
cn, prices_gdf = cn.compute_stats(
data_gdf=prices_gdf,
stats_column_labels=["price"],
distances=[800, 1600],
decay_fn=decay.gaussian(peak=400, cutoff=1600),
)
# 6. Export results with original LineString geometries
result_gdf = cn.to_geopandas()
result_gdf.to_file("results.gpkg")
For end-to-end worked examples with real-world data, see the Cityseer Examples site, including the Quickstart notebook.
CityNetwork
network_structure: NetworkStructure
nodes_gdf: geopandas.geodataframe.GeoDataFrame
to_geopandas
Return a GeoDataFrame with the original input LineString geometries. The returned GeoDataFrame contains all computed columns (centrality metrics, layer results, etc.) joined to the original edge geometries rather than the midpoint representations used internally.
Returns
A new GeoDataFrame indexed by feature id with LineString geometries.
Notes
cn.centrality_shortest(distances=[800])
result_gdf = cn.to_geopandas()
# result_gdf has LineString geometries (not midpoint Points)
print(result_gdf.geometry.geom_type.unique()) # ['LineString']
# All computed columns are present
print(result_gdf["cc_harmonic_800"])
# Export to file
result_gdf.to_file("centrality_results.gpkg")
is_dual: bool
is_directed: bool
crs: pyproj.crs.crs.CRS | None
node_count: int
feature_status: pandas.Series
from_wkts
Construct a CityNetwork from a dictionary of WKT strings or Shapely geometries.
Parameters
A mapping from feature identifiers to WKT strings or Shapely LineString geometries. Input geometries may include z (elevation) coordinates, which are preserved and used for slope-based walking impedance calculations.
A projected coordinate reference system (EPSG code, CRS object, or proj string).
Optional polygon in the same projected CRS; nodes inside are marked as live, nodes outside as dead.
If True, build a directed network. Requires oneway_fids. Features in oneway_fids are one-way (in LineString coordinate order); all other features are bidirectional.
Feature IDs that are one-way when directed=True. Ignored if directed=False.
Optional mapping from primal feature ID to its impedance factor. Each dual edge’s imp_factor becomes the length-weighted mean of its two adjacent primal segments’ impedances; missing entries default to 1.0. See the Edge Impedance section of the guide.
Weld chains of segments meeting at filler (degree-2) endpoints into single segments, so a street subdivided during digitisation is treated as one segment. Welded features keep the id of the longest constituent; absorbed features are marked "merged" in feature_status. Skipped for directed networks. Default True.
Remove dead-end stubs up to this length in metres (iteratively, after filler welding, so a welded chain is judged on its full length). 0 disables. Default 10.0.
Merge near-duplicate parallel edges: edges whose endpoints fall within this distance of another edge’s endpoints and whose lengths are near-identical are the same street drawn twice; the longest is kept. 0 disables. Skipped for directed networks. Default 2.0.
Default for the segment_weighted option of the centrality methods: weight each node by its street segment length rather than a unit count. Can be overridden per centrality call. Default False.
Returns
A new CityNetwork instance.
Raises
If directed=True but oneway_fids is not provided.
Notes
from shapely.geometry import LineString
from cityseer.network import CityNetwork
wkts = {
"street_a": LineString([(0, 0), (100, 0)]),
"street_b": LineString([(100, 0), (100, 100)]),
"street_c": LineString([(100, 0), (200, 0)]),
}
cn = CityNetwork.from_wkts(wkts, crs=32632)
cn.centrality_shortest(distances=[200])
from_geopandas
Construct a CityNetwork from a GeoDataFrame of LineString geometries. Extra columns from the input GeoDataFrame are carried through to the internal nodes GeoDataFrame. The CRS is read from the GeoDataFrame unless explicitly overridden.
Parameters
A GeoDataFrame with LineString or MultiLineString geometries. The index must be unique. Input geometries may include z (elevation) coordinates, which are preserved and used for slope-based walking impedance calculations.
Optional projected CRS override. If None, uses the GeoDataFrame’s CRS.
Optional polygon in the same projected CRS; nodes inside are marked as live, nodes outside as dead.
If True, build a directed network. Requires a boolean oneway column in the GeoDataFrame. Features with oneway=True are one-way in LineString coordinate order; features with oneway=False are bidirectional.
Weld chains of segments meeting at filler (degree-2) endpoints into single segments, so a street subdivided during digitisation is treated as one segment. Welded features keep the id of the longest constituent; absorbed features are marked "merged" in feature_status. Skipped for directed networks. Default True.
Remove dead-end stubs up to this length in metres (iteratively, after filler welding, so a welded chain is judged on its full length). 0 disables. Default 10.0.
Merge near-duplicate parallel edges: edges whose endpoints fall within this distance of another edge’s endpoints and whose lengths are near-identical are the same street drawn twice; the longest is kept. 0 disables. Skipped for directed networks. Default 2.0.
Default for the segment_weighted option of the centrality methods: weight each node by its street segment length rather than a unit count. Can be overridden per centrality call. Default False.
Returns
A new CityNetwork instance.
Raises
If directed=True but the GeoDataFrame has no oneway column.
Notes
An optional imp_factor column on the input GeoDataFrame is propagated to each dual edge as the length-weighted mean of the two adjacent primal segments’ impedances; omit it to leave every dual edge at the default 1.0. See the Edge Impedance section of the guide.
import geopandas as gpd
from shapely.geometry import LineString
from cityseer.network import CityNetwork
gdf = gpd.GeoDataFrame(
{
"geometry": [
LineString([(0, 0), (100, 0)]),
LineString([(100, 0), (100, 100)]),
LineString([(100, 0), (200, 0)]),
]
},
crs="EPSG:32632",
)
cn = CityNetwork.from_geopandas(gdf)
cn.centrality_shortest(distances=[200])
print(cn.nodes_gdf[["cc_harmonic_200", "cc_betweenness_200"]])
With directed one-way streets:
gdf = gpd.GeoDataFrame(
{
"geometry": [
LineString([(0, 0), (100, 0)]),
LineString([(100, 0), (200, 0)]),
],
"oneway": [True, False],
},
crs="EPSG:32632",
)
cn = CityNetwork.from_geopandas(gdf, directed=True)
from_nx
Construct a CityNetwork from a cityseer-compatible NetworkX graph. The input graph must be a primal edge graph (not a dual graph) with geom attributes on edges and a crs attribute on the graph. Node live attributes are preserved.
When a MultiDiGraph is passed, directed mode is enabled automatically: each directed edge becomes its own one-way dual node (in the coordinate order of the directed edge). Two-way streets should be represented as two reciprocal edges (A to B and B to A), which produce two separate dual nodes.
Parameters
A cityseer-compatible primal NetworkX graph. MultiDiGraph enables directed routing. Any imp_factor edge attribute is propagated to each dual edge as the length-weighted mean of the two adjacent primal segments’ impedances (default 1.0 if absent). See the Edge Impedance section of the guide.
Optional polygon in the same projected CRS; nodes inside are marked as live, nodes outside as dead.
Weld chains of segments meeting at filler (degree-2) endpoints into single segments, so a street subdivided during digitisation is treated as one segment. Welded features keep the id of the longest constituent; absorbed features are marked "merged" in feature_status. Skipped for directed networks. Default True.
Remove dead-end stubs up to this length in metres (iteratively, after filler welding, so a welded chain is judged on its full length). 0 disables. Default 10.0.
Merge near-duplicate parallel edges: edges whose endpoints fall within this distance of another edge’s endpoints and whose lengths are near-identical are the same street drawn twice; the longest is kept. 0 disables. Skipped for directed networks. Default 2.0.
Default for the segment_weighted option of the centrality methods: weight each node by its street segment length rather than a unit count. Can be overridden per centrality call. Default False.
Returns
A new CityNetwork instance.
Raises
If the input graph is a dual graph.
Notes
From an undirected graph:
import networkx as nx
from shapely.geometry import LineString
from cityseer.network import CityNetwork
G = nx.MultiGraph(crs="EPSG:32632")
G.add_node("a", x=0.0, y=0.0)
G.add_node("b", x=100.0, y=0.0)
G.add_node("c", x=200.0, y=0.0)
G.add_edge("a", "b", geom=LineString([(0, 0), (100, 0)]))
G.add_edge("b", "c", geom=LineString([(100, 0), (200, 0)]))
cn = CityNetwork.from_nx(G)
From a directed MultiDiGraph (e.g. via OSMnx):
G = nx.MultiDiGraph(crs="EPSG:32632")
G.add_node("a", x=0.0, y=0.0)
G.add_node("b", x=100.0, y=0.0)
# One-way: a -> b only
G.add_edge("a", "b", key=0, geom=LineString([(0, 0), (100, 0)]))
cn = CityNetwork.from_nx(G)
assert cn.is_directed
from_osm
Construct a CityNetwork from OpenStreetMap data within a bounding polygon. Downloads the road network and converts it to a dual CityNetwork.
For directed (one-way) routing with OSM data, fetch a directed graph via OSMnx <https://osmnx.readthedocs.io/>_ and pass it to :meth:from_nx or convert it with :func:io.nx_from_osm_nx(directed=True) <cityseer.tools.io.nx_from_osm_nx>.
Parameters
A Shapely polygon defining the area of interest.
EPSG code for poly_geom. Defaults to 4326 (WGS84).
Target projected EPSG code. If None, an appropriate UTM zone is inferred.
Whether to simplify the OSM graph topology (the aggressive, OSM-tuned pipeline: junction consolidation, parallel carriageway merging by midline, ironing). Defaults to True.
Optional polygon for live/dead node assignment (in the target projected CRS).
Weld chains of segments meeting at filler (degree-2) endpoints into single segments during dual construction. Default True.
Remove dead-end stubs up to this length in metres during dual construction. 0 disables. Default 10.0.
Merge near-duplicate parallel edges (endpoints within this distance, near-identical lengths) during dual construction. 0 disables. Default 2.0.
Default for the segment_weighted option of the centrality methods. Can be overridden per centrality call. Default False.
Additional keyword arguments passed to io.osm_graph_from_poly.
Returns
A new CityNetwork instance.
Notes
from shapely.geometry import box
from cityseer.network import CityNetwork
# Bounding box in WGS84 (lon/lat)
polygon = box(-0.13, 51.51, -0.12, 51.52)
cn = CityNetwork.from_osm(polygon, to_crs_code=32630)
cn.centrality_shortest(distances=[400, 800])
set_boundary
Set live/dead node status based on a boundary polygon. Nodes whose midpoints fall inside the polygon are marked live; others are marked dead. Dead nodes participate in traversal but their own values are not reported. Closeness skips dead sources in exact mode (a cost saving); betweenness sources from every node so that routes through the study area — including those between dead nodes — credit the live nodes they traverse.
Parameters
A Shapely polygon in the same projected CRS as the network.
Returns
Returns self for method chaining.
set_all_live
Mark all nodes as live, clearing any boundary restriction.
Returns
Returns self for method chaining.
save
Save the network to disk as a parquet/pickle pair. Creates two files: <path>.nodes.parquet (the nodes GeoDataFrame with all computed columns) and <path>.state.pkl (source and cleaned WKTs, boundary, cleaning parameters, and feature status). Use load to restore.
Parameters
Base file path. File extensions are replaced automatically.
Notes
cn.centrality_shortest(distances=[800])
cn.save("my_network")
# Creates: my_network.nodes.parquet, my_network.state.pkl
# Later, restore the full network with all metrics
cn_restored = CityNetwork.load("my_network")
print(cn_restored.nodes_gdf["cc_harmonic_800"])
load
Load a previously saved CityNetwork from disk. Reconstructs the graph topology from the saved cleaned geometries with cleaning disabled, so the loaded network is identical to the saved one (cleaning runs exactly once, at construction), and merges any previously computed columns (centrality metrics, layer results) from the saved nodes GeoDataFrame.
Parameters
Base file path (same as was passed to save).
Returns
The restored CityNetwork instance.
centrality_shortest
Compute shortest-path (metric) centrality. Wraps centrality_shortest. All keyword arguments are forwarded; see that function for the full parameter list including distances, minutes, closeness, betweenness, cycles, postprocess, segment_weighted, sample, and epsilon.
Returns
Returns self for method chaining. Results are written to nodes_gdf.
Notes
# Multiple distance thresholds
cn.centrality_shortest(distances=[400, 800, 1600])
# With segment-length weighting
cn.centrality_shortest(distances=[400, 800], segment_weighted=True)
# With sampling for large networks
cn.centrality_shortest(
distances=[800, 2000, 5000],
sample=True,
epsilon=0.05,
)
# Custom closeness metric
cn.centrality_shortest(
distances=[800],
closeness={"harmonic": "1/c", "gravity": "exp(-0.005 * c)"},
betweenness={},
)
By default this emits just cc_harmonic_{d} (closeness) and cc_betweenness_{d}, with cycles off. Pass closeness / betweenness expression dicts (and cycles=True) to compute any of the metrics below (see Column Naming Conventions):
| Column | Description |
|---|---|
cc_density_{d} | Count of reachable nodes (or total reachable street length if segment_weighted). |
cc_harmonic_{d} | Harmonic closeness: sum of inverse distances to reachable nodes (default). |
cc_farness_{d} | Sum of distances to reachable nodes. |
cc_hillier_{d} | Hillier normalisation (density² / farness). |
cc_cycles_{d} | Circuit rank: count of independent loops in the reachable subgraph. |
cc_decay_{d} | Decay-weighted closeness (e.g. exp(-4 * p)). |
cc_betweenness_{d} | Betweenness: count of shortest paths passing through each node (default). |
cc_betweenness_decay_{d} | Decay-weighted betweenness (e.g. exp(-4 * p)). |
centrality_simplest
Compute simplest-path (angular) centrality. Wraps centrality_simplest. All keyword arguments are forwarded; see that function for the full parameter list.
Returns
Returns self for method chaining. Results are written to nodes_gdf.
Notes
cn.centrality_simplest(distances=[400, 800, 1600])
By default this emits just cc_harmonic_{d}_ang (closeness) and cc_betweenness_{d}_ang. Pass closeness / betweenness expression dicts to compute any of the metrics below (note the _ang suffix):
| Column | Description |
|---|---|
cc_density_{d}_ang | Count of reachable nodes (or total reachable street length if segment_weighted). |
cc_harmonic_{d}_ang | Harmonic closeness using angular cost as impedance (default). |
cc_farness_{d}_ang | Sum of angular costs to reachable nodes. |
cc_hillier_{d}_ang | Hillier normalisation (density² / farness). |
cc_betweenness_{d}_ang | Betweenness via simplest angular paths (angular choice; default). |
build_od_matrix
Build an origin-destination (OD) matrix for OD-weighted betweenness. In standard betweenness, every pair of nodes contributes equally. OD-weighted betweenness instead weights each pair by observed trip counts between their respective zones (e.g. commuter cycling counts), so streets carrying more actual traffic receive higher scores.
Wraps build_od_matrix. See that function for the full parameter list.
Parameters
Origin-destination flow data with columns for origin zone, destination zone, and trip weight.
Zone boundaries (polygons) or centroids (points) corresponding to the OD matrix.
Returns
An OD matrix for use with betweenness_od.
betweenness_od
Compute OD-weighted betweenness centrality. Weights betweenness by actual origin-destination trip counts rather than treating all paths equally. Only source nodes with outbound trips are traversed, and each shortest-path contribution is scaled by the corresponding OD weight.
Wraps betweenness_od. See that function for the full parameter list.
Parameters
An OD matrix from build_od_matrix.
Returns
Returns self for method chaining. Results are written to nodes_gdf.
betweenness_demand
Compute demand-weighted (flow) betweenness from a spatial interaction model. Trips are allocated between weighted origins (e.g. population) and weighted destinations (e.g. attractors) using a singly (origin-)constrained spatial interaction model, then routed along shortest network paths so intermediate streets accumulate the flow passing through them. This is the modelled-matrix counterpart to betweenness_od: the per-pair weights are derived from the network distances revealed during routing, rather than supplied as an explicit OD matrix.
Wraps betweenness_demand. All additional keyword arguments are forwarded; see that function for the full parameter list including distances, minutes, decay_fn, closest_destination, participation, metric_name, max_netw_assign_dist, and barriers_gdf. Origins and destinations are assigned to the network with the same workflow as the data layers (representation-aware nearest-street assignment, offsets included in routed distances). The model has two distance levers, each with one job: decay_fn shapes destination choice within the allocation, and participation (the share of people at a typical location who make a trip; 1.0 by default, e.g. 0.2 for pedestrian flows) makes trip generation accessibility-elastic via a stay-home option in the choice set. The output is a single conserved cc_demand_{distance} column. For externally weighted or bespoke flow conventions use betweenness_od, which retains full expression support.
Parameters
A GeoDataFrame of demand origins (points or centroids).
A GeoDataFrame of demand destinations / attractors (points or centroids).
Column in origins_gdf giving each origin’s weight (e.g. population).
Column in destinations_gdf giving each destination’s attractiveness weight.
Returns
Returns self for method chaining. A cc_{metric_name}_{distance} column (default cc_demand_{distance}) is written to nodes_gdf.
Notes
cn.betweenness_demand(
origins_gdf=population_gdf,
destinations_gdf=amenities_gdf,
origin_weight_col="population",
destination_weight_col="weight",
distances=[800],
decay_fn="exp(-0.002 * c)",
)
result_gdf = cn.to_geopandas() # cc_demand_800 projected onto the street segments
compute_accessibilities
Compute land-use accessibility metrics. Counts how many instances of each specified land-use category (e.g. retail, parks) are reachable within each distance threshold, and records the nearest distance to each category.
Wraps compute_accessibilities. All additional keyword arguments are forwarded; see that function for the full parameter list including landuse_column_label, accessibility_keys, distances, minutes, decay_fn, and angular. decay_fn accepts a single expression or a {label: expression} dict that computes several decay variants in one network traversal, each label suffixing its output columns.
Parameters
A GeoDataFrame of land-use points with categorical columns.
Returns
Returns self with accessibility columns added to nodes_gdf.
The input data GeoDataFrame with nearest network assignments.
Notes
from cityseer import decay
cn, landuses_gdf = cn.compute_accessibilities(
data_gdf=landuses_gdf,
landuse_column_label="category",
accessibility_keys=["retail", "cafe", "park"],
distances=[400, 800],
decay_fn=decay.exponential(),
)
# Count of reachable "retail" within 800m
print(cn.nodes_gdf["cc_retail_800"])
# Nearest distance to "park" at the maximum threshold
print(cn.nodes_gdf["cc_park_nearest_max_800"])
compute_mixed_uses
Compute mixed-use diversity metrics. Measures the diversity of land-use categories reachable from each node using Hill numbers: q=0 counts how many distinct types are present, q=1 measures diversity accounting for how evenly types are represented, and q=2 gives greater weight to the most common types.
Wraps compute_mixed_uses. All additional keyword arguments are forwarded; see that function for the full parameter list including landuse_column_label, distances, minutes, compute_hill, compute_shannon, compute_gini, decay_fn, and angular. decay_fn accepts a single expression or a {label: expression} dict that computes several decay variants in one network traversal, each label suffixing its output columns (only the Hill measures vary with decay).
Parameters
A GeoDataFrame of land-use points with categorical columns.
Returns
Returns self with mixed-use columns added to nodes_gdf.
The input data GeoDataFrame with nearest network assignments.
Notes
cn, landuses_gdf = cn.compute_mixed_uses(
data_gdf=landuses_gdf,
landuse_column_label="category",
distances=[400, 800],
)
# Hill q=0 (count of distinct land-use types) at 800m
print(cn.nodes_gdf["cc_hill_q0_800"])
compute_stats
Compute statistical aggregations of numerical data over the network. Aggregates numerical attributes (e.g. property prices, floor areas) within network-distance thresholds, computing weighted statistics (mean, sum, min, max, variance, etc.) at each node.
Wraps compute_stats. All additional keyword arguments are forwarded; see that function for the full parameter list including stats_column_labels, distances, minutes, decay_fn, and angular. decay_fn accepts a single expression or a {label: expression} dict that computes several decay variants in one network traversal, each label suffixing its output columns.
Parameters
A GeoDataFrame of data points with numerical columns.
Returns
Returns self with statistical columns added to nodes_gdf.
The input data GeoDataFrame with nearest network assignments.
Notes
from cityseer import decay
cn, prices_gdf = cn.compute_stats(
data_gdf=prices_gdf,
stats_column_labels=["price", "floor_area"],
distances=[800, 1600],
decay_fn=decay.exponential(),
)
# Weighted mean of "price" at 800m
print(cn.nodes_gdf["cc_price_mean_800"])
# Weighted sum of "floor_area" at 1600m
print(cn.nodes_gdf["cc_floor_area_sum_1600"])
add_gtfs
Add GTFS (General Transit Feed Specification) public transport data to the network. Integrates transit stops and routes so that centrality and accessibility analyses account for public transport connections.
Wraps io.add_transport_gtfs.
Parameters
Path to a GTFS zip file or directory.
Optional CRS override for the GTFS data.
Maximum distance (metres) for assigning transit stops to the network (nearest street, via the shared assignment workflow). Stops beyond this distance are excluded. Defaults to 400.
Returns
Returns self for method chaining.
to_nx
Convert the network to a NetworkX MultiGraph (or MultiDiGraph if directed). If the network was built with from_nx, returns a copy of the original graph with computed centrality and layer columns added to each edge’s data dictionary. Otherwise builds a new cityseer-compatible undirected graph from the internal GeoDataFrame.
Returns
A primal edge graph with computed metrics added to edge data.
Raises
If the network is directed but was not built via from_nx (no source graph to export).
Notes
cn = CityNetwork.from_nx(G)
cn.centrality_shortest(distances=[800])
# Round-trip: get back a NetworkX graph with metrics on edges
G_with_metrics = cn.to_nx()
u, v, k, data = list(G_with_metrics.edges(keys=True, data=True))[0]
print(data["cc_harmonic_800"])