Skip to content

Morphology Module

The morphology module provides functions for creating morphological graphs from urban form data, capturing spatial relationships between place spaces (e.g., tessellation cells) and movement spaces (e.g., street segments).

Module for creating morphological graphs from urban data.

This module provides comprehensive functionality for analyzing urban morphology through graph representations, focusing on the relationships between place spaces (buildings and their tessellations) and movement spaces (street segments). It creates heterogeneous graphs that capture the complex spatial relationships inherent in urban environments. Both GeoDataFrame and NetworkX objects can be converted to PyTorch Geometric Data or HeteroData by functions from graph.py.

The module specializes in three types of spatial relationships: 1. Place-to-place: Adjacency relationships between building tessellations 2. Movement-to-movement: Topological connectivity between street segments 3. Place-to-movement: Interface relationships between place and movement spaces

Functions:

Name Description
morphological_graph

Create a morphological graph from buildings and street segments.

place_to_place_graph

Create edges between contiguous place polygons based on spatial adjacency.

place_to_movement_graph

Create edges between place polygons and nearby movement geometries.

movement_to_movement_graph

Create edges between connected movement segments based on topological connectivity.

segments_to_graph

Convert a GeoDataFrame of LineString segments into a graph structure.

morphological_graph

morphological_graph(
    buildings_gdf,
    segments_gdf,
    center_point=None,
    distance=None,
    clipping_buffer=inf,
    extent_buffer=100.0,
    limit=None,
    primary_barrier_col="barrier_geometry",
    contiguity="queen",
    keep_buildings=False,
    keep_segments=True,
    tolerance=1e-06,
    include_unenclosed_buildings=False,
    as_nx=False,
    duplicate_edges=False,
    non_movement_barrier_col=None,
    tessellation_fallback=False,
    tessellation_n_jobs=-1,
)

Create a morphological graph from buildings and street segments.

This function creates a comprehensive morphological graph that captures relationships between place spaces (building tessellations) and movement spaces (street segments). The graph includes three types of relationships: place-to-place adjacency, movement-to-movement connectivity, and place-to-movement interfaces.

The 'place_id' for tessellation cells is derived from 'tess_id' (generated by create_tessellation) or assigned sequentially if 'tess_id' doesn't directly map. The 'movement_id' for street segments is taken directly from the index of segments_gdf.

Parameters:

Name Type Description Default
buildings_gdf GeoDataFrame

GeoDataFrame containing building polygons. Should contain Polygon or MultiPolygon geometries.

required
segments_gdf GeoDataFrame

GeoDataFrame containing street segments. Should contain LineString geometries.

required
center_point GeoSeries or GeoDataFrame

Center point(s) for spatial filtering. If provided with distance parameter, only segments within the specified distance will be included.

None
distance float

Maximum distance from center_point for spatial filtering. When specified, street segments beyond this shortest-path distance are removed and tessellation cells are kept only if their own distance via these segments does not exceed this value. To build graphs for several distances in one shared pass, use :func:morphological_graphs.

None
clipping_buffer float

Buffer distance to ensure adequate context for generating tessellation. Must be non-negative and not smaller than extent_buffer.

math.inf
extent_buffer float

Maximum perpendicular access distance (a bounded catchment cap) from a street to a building/cell for it to be retained, and the maximum length of a faced_to connection. Unlike distance this access term is never added to the walking-network budget, so a building whose only nearby street is disconnected from the reachable network is not retained on the strength of a long straight-line access leg across barriers. Must be non-negative and not larger than clipping_buffer.

100.0
limit geopandas.GeoDataFrame, geopandas.GeoSeries, shapely geometry, or None

Boundary passed to momepy.enclosures when creating enclosed tessellation. When None, create_tessellation computes one from the buildings and barriers.

None
primary_barrier_col str

Column name containing alternative geometry for movement spaces. If specified and exists, this geometry will be used instead of the main geometry column for tessellation barriers. This only substitutes the geometry of a segment; it never removes a row from the movement layer, so every segment it applies to still becomes a movement node. To make individual rows act as barriers only (excluded from the movement layer), use non_movement_barrier_col, which is an orthogonal setting.

'barrier_geometry'
contiguity str

Type of spatial contiguity for place-to-place connections. Must be either "queen" or "rook".

"queen"
keep_buildings bool

If True, preserves building information in the tessellation output.

False
keep_segments bool

If True, preserves the original segment LineString geometry in a column named 'segment_geometry' in the movement nodes GeoDataFrame.

True
tolerance float

Buffer distance for movement geometries when creating place-to-movement connections. This parameter controls how close place spaces need to be to movement spaces to establish a connection.

1e-6
include_unenclosed_buildings bool

If True, buildings excluded by enclosed tessellation are added using barrier-free tessellation before distance filters are applied.

False
as_nx bool

If True, convert the output to a NetworkX graph.

False
duplicate_edges bool

If True, the undirected same-type edge GeoDataFrames — ("place", "touched_to", "place") and ("movement", "connected_to", "movement") — contain both (u, v) and (v, u) rows (roughly doubling their row count), so neighbourhood queries on the MultiIndex are complete. The heterogeneous ("place", "faced_to", "movement") edges are left unchanged because a reverse row would mix movement ids into the place index level. Incompatible with as_nx=True.

False
non_movement_barrier_col str

Name of a boolean column in segments_gdf flagging rows that act as barriers only. Flagged segments contribute to the tessellation barriers (clipped to the same radius as the buffered movement network) but are excluded from the movement nodes, the movement-to-movement graph and the network-distance computation. Rows where the column is missing or false are treated as ordinary movement segments. When None (default) every segment is a movement segment, preserving the previous behaviour. Whereas primary_barrier_col selects which geometry a segment uses, this flag decides whether a row becomes a movement node at all; the two settings are orthogonal.

None
tessellation_fallback bool

If True and the enclosed tessellation encloses no area (momepy raises "No objects to concatenate") or yields no cells while buildings and reachable segments exist, fall back to using the reachable building footprints themselves as place cells. When False (default) the enclosed tessellation result is returned unchanged, preserving the previous behaviour (the underlying error is propagated).

False
tessellation_n_jobs int

Number of parallel jobs forwarded to the enclosed tessellation. Use 1 to run serially, which avoids oversubscription when this function is itself called inside an outer parallel loop.

-1

Returns:

Type Description
tuple[dict[str, GeoDataFrame], dict[tuple[str, str, str], GeoDataFrame]] | Graph

If as_nx is False (default), returns a tuple (nodes, edges) where:

  • nodes: Dictionary containing node GeoDataFrames with keys:

    • "place": Tessellation cells (place spaces)
    • "movement": Street segments (movement spaces)
  • edges: Dictionary containing edge GeoDataFrames with keys:

    • ("place", "touched_to", "place"): Adjacency between tessellation cells
    • ("movement", "connected_to", "movement"): Connectivity between street segments
    • ("place", "faced_to", "movement"): Interface between tessellation cells and street segments

If as_nx is True, returns a NetworkX graph.

Raises:

Type Description
TypeError

If buildings_gdf or segments_gdf are not GeoDataFrames.

ValueError

If contiguity parameter is not "queen" or "rook". If clipping_buffer is negative. If duplicate_edges is True together with as_nx=True.

See Also

morphological_graphs : Build graphs for several distances in one shared pass. place_to_place_graph : Create adjacency between place spaces. place_to_movement_graph : Create connections between place and movement spaces. movement_to_movement_graph : Create connectivity between movement spaces.

Notes

The function first filters the street network by distance (resulting in segs). A segs_buffer GeoDataFrame is also created for tessellation context, potentially filtered by distance + clipping_buffer or distance if center_point and distance are provided. This segs_buffer is used to create enclosures and tessellations. It then establishes three types of relationships: 1. Place-to-place: Adjacency between tessellation cells (handled by place_to_place_graph) 2. Movement-to-movement: Topological connectivity between street segments 3. Place-to-movement: Spatial interfaces between tessellations and streets

The output follows a heterogeneous graph structure suitable for network analysis of urban morphology.

Examples:

>>> # Create morphological graph from buildings and segments
>>> nodes, edges = morphological_graph(buildings_gdf, segments_gdf)
>>> place_nodes = nodes['place']
>>> movement_nodes = nodes['movement']

place_to_place_graph

place_to_place_graph(
    place_gdf,
    group_col=None,
    contiguity="queen",
    as_nx=False,
    duplicate_edges=False,
)

Create edges between contiguous place polygons based on spatial adjacency.

This function identifies spatial adjacency relationships between place polygons (e.g., tessellation cells) using either Queen or Rook contiguity criteria. Optionally groups connections within specified groups (e.g., enclosures). The input place_gdf is expected to have a 'place_id' column.

Parameters:

Name Type Description Default
place_gdf GeoDataFrame

GeoDataFrame containing place space polygons. Must contain a 'place_id' column.

required
group_col str

Column name for grouping connections. Only polygons within the same group will be connected. If None, all polygons are considered as one group.

None
contiguity str

Type of spatial contiguity to use. Must be either "queen" or "rook". Queen contiguity includes vertex neighbors, Rook includes only edge neighbors.

"queen"
as_nx bool

If True, convert the output to a NetworkX graph.

False
duplicate_edges bool

If True, the edges GeoDataFrame contains both (u, v) and (v, u) rows for every undirected edge (roughly doubling the row count), with the 'from_place_id' and 'to_place_id' values swapped on reverse rows. Incompatible with as_nx=True.

False

Returns:

Type Description
tuple[GeoDataFrame, GeoDataFrame] or Graph

If as_nx is False (default), returns a tuple (nodes, edges) where:

  • nodes is a geopandas.GeoDataFrame containing the place nodes.
  • edges is a geopandas.GeoDataFrame containing the adjacency connections.

If as_nx is True, returns a networkx.Graph representing the place adjacency.

Raises:

Type Description
TypeError

If place_gdf is not a GeoDataFrame.

ValueError

If contiguity not in {"queen", "rook"}, or if group_col doesn't exist. If duplicate_edges is True together with as_nx=True.

See Also

morphological_graph : Main function that creates comprehensive morphological graphs. place_to_movement_graph : Create connections between place and movement spaces. movement_to_movement_graph : Create connectivity between movement spaces.

Notes

The function uses libpysal's spatial weights to determine adjacency relationships. Edge geometries are created as LineStrings connecting polygon centroids. Self-connections and duplicate edges are automatically filtered out. The input place_gdf is expected to have a 'place_id' column.

Examples:

>>> # Create place-to-place adjacency graph
>>> nodes, edges = place_to_place_graph(tessellation_gdf)
>>> # With grouping by enclosures
>>> nodes, edges = place_to_place_graph(tessellation_gdf, group_col='enclosure_id')

place_to_movement_graph

place_to_movement_graph(
    place_gdf,
    movement_gdf,
    primary_barrier_col=None,
    tolerance=1e-06,
    as_nx=False,
    max_connection_distance=inf,
    duplicate_edges=False,
)

Create edges between place polygons and nearby movement geometries.

This function identifies spatial relationships between place spaces (tessellations) and movement spaces (street segments) by finding intersections between buffered movement geometries and place polygons. Input GDFs are expected to have 'place_id' and 'movement_id' columns respectively.

Parameters:

Name Type Description Default
place_gdf GeoDataFrame

GeoDataFrame containing place space polygons. Expected to have a 'place_id' column.

required
movement_gdf GeoDataFrame

GeoDataFrame containing movement space geometries (typically LineStrings). Expected to have a 'movement_id' column.

required
primary_barrier_col str

Column name for alternative movement geometry. If specified and exists, this geometry will be used instead of the main geometry column.

None
tolerance float

Buffer distance for movement geometries to detect proximity to place spaces.

1e-6
as_nx bool

If True, convert the output to a NetworkX graph.

False
max_connection_distance float

Maximum distance for the nearest-movement fallback connection. Place cells matched by the tolerance proximity query are unaffected; cells connected only by the fallback are dropped when their nearest movement geometry lies farther than this distance, preventing long star-shaped edges to distant streets.

``math.inf``
duplicate_edges bool

If True, the edges GeoDataFrame contains both (u, v) and (v, u) rows for every edge (roughly doubling the row count). Note that on reverse rows the 'place_id' column holds a movement id and vice versa, because the endpoints are swapped. Incompatible with as_nx=True.

False

Returns:

Type Description
tuple[GeoDataFrame, GeoDataFrame] or Graph

If as_nx is False (default), returns a tuple (nodes, edges) where:

  • nodes is a geopandas.GeoDataFrame containing the combined place and movement nodes.
  • edges is a geopandas.GeoDataFrame containing the edges between place and movement geometries.

If as_nx is True, returns a networkx.Graph representing the place-to-movement connections.

Raises:

Type Description
TypeError

If place_gdf or movement_gdf are not GeoDataFrames.

ValueError

If 'place_id' or 'movement_id' columns are missing from input GDFs. If duplicate_edges is True together with as_nx=True.

See Also

morphological_graph : Main function that creates comprehensive morphological graphs. place_to_place_graph : Create adjacency between place spaces. movement_to_movement_graph : Create connectivity between movement spaces.

Notes

Edge geometries are created as LineStrings connecting the centroids of place polygons and movement geometries. The function uses spatial joins to identify overlapping areas within the specified tolerance. Input GDFs are expected to have 'place_id' and 'movement_id' columns respectively.

Examples:

>>> # Create place-to-movement interface graph
>>> nodes, edges = place_to_movement_graph(tessellation_gdf, segments_gdf)
>>> # With custom tolerance
>>> nodes, edges = place_to_movement_graph(tessellation_gdf, segments_gdf, tolerance=2.0)

movement_to_movement_graph

movement_to_movement_graph(movement_gdf, as_nx=False, duplicate_edges=False)

Create edges between connected movement segments based on topological connectivity.

This function identifies topological connections between movement space geometries (typically street segments) using the dual graph approach to find segments that share endpoints or connection points. The function automatically creates a unique identifier for each row if needed.

Parameters:

Name Type Description Default
movement_gdf GeoDataFrame

GeoDataFrame containing movement space geometries (typically LineString).

required
as_nx bool

If True, convert the output to a NetworkX graph.

False
duplicate_edges bool

If True, the edges GeoDataFrame contains both (u, v) and (v, u) rows for every undirected edge (roughly doubling the row count), with the 'from_movement_id' and 'to_movement_id' values swapped on reverse rows. Incompatible with as_nx=True.

False

Returns:

Type Description
tuple[GeoDataFrame, GeoDataFrame] or Graph

If as_nx is False (default), returns a tuple (nodes, edges) where:

  • nodes is a geopandas.GeoDataFrame containing the movement nodes.
  • edges is a geopandas.GeoDataFrame containing the topological connections.

If as_nx is True, returns a networkx.Graph representing the movement connectivity.

Raises:

Type Description
TypeError

If movement_gdf is not a GeoDataFrame.

ValueError

If duplicate_edges is True together with as_nx=True.

See Also

morphological_graph : Main function that creates comprehensive morphological graphs. place_to_place_graph : Create adjacency between place spaces. place_to_movement_graph : Create connections between place and movement spaces.

Notes

The function uses the dual graph approach where each LineString becomes a node, and edges represent topological connections between segments. Edge geometries are created as LineStrings connecting the centroids of connected segments.

Examples:

>>> # Create movement-to-movement connectivity graph
>>> nodes, edges = movement_to_movement_graph(segments_gdf)
>>> # Convert to NetworkX format
>>> graph = movement_to_movement_graph(segments_gdf, as_nx=True)

segments_to_graph

segments_to_graph(segments_gdf, multigraph=True, directed=True, as_nx=False)

Convert a GeoDataFrame of LineString segments into a graph structure.

This function takes a GeoDataFrame of LineStrings and processes it into a topologically explicit graph representation, consisting of a GeoDataFrame of unique nodes (the endpoints of the lines) and a GeoDataFrame of edges.

The resulting nodes GeoDataFrame contains unique points representing the start and end points of the input line segments. The edges GeoDataFrame is a copy of the input, but with a new MultiIndex (from_node_id, to_node_id) that references the IDs in the new nodes GeoDataFrame. If multigraph is True (the default), an additional index level (edge_key) distinguishes multiple edges between the same pair of nodes.

Parameters:

Name Type Description Default
segments_gdf GeoDataFrame

A GeoDataFrame where each row represents a line segment, and the 'geometry' column contains LineString objects.

required
multigraph bool

If True, supports multiple edges between the same pair of nodes by adding an edge_key level to the MultiIndex. Real-world segment data (e.g. from OSMnx) commonly contains parallel edges, so this is the default. If False, duplicate node pairs raise a ValueError.

.. versionchanged:: 0.4 The default changed from False to True, and multigraph=False now raises on duplicate node pairs instead of silently returning a duplicated MultiIndex.

True
directed bool

If True, edge orientation follows the LineString draw order: the first coordinate becomes from_node_id and the last becomes to_node_id. If False, each non-self-loop edge is canonicalized to an unordered (min, max) node-id order, so the same road drawn in opposite directions yields parallel edges of one unordered pair instead of reciprocal (u, v) / (v, u) rows. Geometries are left unchanged; only the index order is normalized. Use directed=False when the output feeds an undirected pipeline such as gdf_to_pyg(..., directed=False).

True
as_nx bool

If True, returns a NetworkX graph instead of a tuple of GeoDataFrames.

False

Returns:

Type Description
tuple[GeoDataFrame, GeoDataFrame]

A tuple containing two GeoDataFrames:

  • nodes_gdf: A GeoDataFrame of unique nodes (Points), indexed by node_id.
  • edges_gdf: A GeoDataFrame of edges (LineStrings), with a MultiIndex mapping to the node_id in nodes_gdf. If multigraph is True, the index includes a third level (edge_key) for duplicate connections.

Raises:

Type Description
ValueError

If multigraph=False and the segments contain duplicate node pairs (after canonicalization when directed=False).

See Also

city2graph.canonicalize_edges : Collapse reciprocal/parallel edge rows.

Examples:

>>> import geopandas as gpd
>>> from shapely.geometry import LineString
>>> # Create a GeoDataFrame of line segments
>>> segments = gpd.GeoDataFrame(
...     {"road_name": ["A", "B"]},
...     geometry=[LineString([(0, 0), (1, 1)]), LineString([(1, 1), (1, 0)])],
...     crs="EPSG:32633"
... )
>>> # Convert to graph representation
>>> nodes_gdf, edges_gdf = segments_to_graph(segments)
>>> print(nodes_gdf)
>>> print(edges_gdf)
node_id  geometry
0        POINT (0 0)
1        POINT (1 1)
2        POINT (1 0)
                                         road_name   geometry
from_node_id to_node_id edge_key
0            1          0                A           LINESTRING (0 0, 1 1)
1            2          0                B           LINESTRING (1 1, 1 0)
>>> # Duplicate connections become parallel edges with distinct keys
>>> segments_with_duplicates = gpd.GeoDataFrame(
...     {"road_name": ["A", "B", "C"]},
...     geometry=[LineString([(0, 0), (1, 1)]),
...               LineString([(0, 0), (1, 1)]),
...               LineString([(1, 1), (1, 0)])],
...     crs="EPSG:32633"
... )
>>> nodes_gdf, edges_gdf = segments_to_graph(segments_with_duplicates)
>>> print(edges_gdf.index.names)
['from_node_id', 'to_node_id', 'edge_key']

Deprecated

The following aliases emit a DeprecationWarning and delegate to the renamed functions above:

  • private_to_private_graphplace_to_place_graph
  • private_to_public_graphplace_to_movement_graph
  • public_to_public_graphmovement_to_movement_graph