Skip to content

[BUG] contiguity_graph returns undirected edge table #156

@stefdesabbata

Description

@stefdesabbata

Contributing guidelines

  • I understand the contributing guidelines

Documentation

  • My proposal is not addressed by the documentation or examples

Existing issues

  • Nothing similar appears in an existing issue

Describe the bug

The contiguity_graph(..., as_nx=False) method returns an edges_gdf in which each contiguity edge appears only once, with the smaller (in the order of the type) node id as the first MultiIndex level. The total edge count is correct for undirected edges. However, when transformed into a PyG graph, the edge table is assumed to be undirected, leading to missing edges.

PyG treats every row of edge_index as a directed edge. The edge_index is a 2 x |E| matrix where (under the default flow="source_to_target") the messages from the nodes in edge_index[0] are sent to nodes in edge_index[1]. With gdf_to_pyg, level 0 of the multi-index becomes the source and level 1 becomes the target node that receives the message. As each edge appears only once in the edge table, only one direction is included in the resulting edge_index.

Image

To Reproduce

import geopandas as gpd
import pandas as pd
from shapely.geometry import Polygon
import city2graph as c2g

# Create simple geodataframe
polys, codes = [], []
for r in range(4):
    for c in range(4):
        polys.append(Polygon([(c, r), (c + 1, r), (c + 1, r + 1), (c, r + 1)]))
        codes.append(f"id{r}{c}")
gdf = (
    gpd.GeoDataFrame({"v": range(16)}, geometry=polys, crs="EPSG:27700")
    .set_index(pd.Index(codes, name="id"))
)

# Plot
ax = gdf.plot(color="lightgray", categorical=True, edgecolor="black")
for idx, row in gdf.iterrows():
    ax.annotate(idx, xy=(row.geometry.centroid.x, row.geometry.centroid.y),
                ha="center", va="center")
    ax.set_axis_off()

nodes, edges = c2g.contiguity_graph(gdf, contiguity="queen", as_nx=False)

pyg_graph = c2g.gdf_to_pyg(nodes, edges=edges)

# Check output, using id22 as an example
print("\n\nEdges with id22 as level 0 in the multi-index", flush=True)
print(edges.xs("id22", level=0), flush=True)
print("\n\nEdges with id22 as focal", flush=True)
print(pyg_graph.edge_index[:, pyg_graph.edge_index[0] == nodes.loc["id22"]["v"]], flush=True)

# Plot id22 and its neighbors where id22 is level 0 in the multi-index
id22_edges = edges.xs("id22", level=0)
ax = gdf.plot(color="lightgray", categorical=True, edgecolor="black")
nodes[nodes.index.isin(id22_edges.index)].plot(ax=ax, color="tab:orange")
nodes[nodes.index == "id22"].plot(ax=ax, color="tab:blue")
id22_edges.plot(ax=ax, color="tab:blue")
for idx, row in gdf.iterrows():
    ax.annotate(idx, xy=(row.geometry.centroid.x, row.geometry.centroid.y),
                ha="center", va="center")
    ax.set_axis_off()

# Plot id22 and its neighbors (both levels in the multi-index)
# this is what PyG expects
ax = gdf.plot(color="lightgray", categorical=True, edgecolor="black")
nodes[nodes.index.isin(id22_edges.index)].plot(ax=ax, color="tab:orange")
nodes[nodes.index == "id22"].plot(ax=ax, color="tab:blue")
id22_edges.plot(ax=ax, color="tab:blue")

# Add edges where id22 is on level 1
id22_lvl1 = edges.xs("id22", level=1)
nodes[nodes.index.isin(id22_lvl1.index)].plot(ax=ax, color="tab:red")
id22_lvl1.plot(ax=ax, color="tab:blue")

for idx, row in gdf.iterrows():
    ax.annotate(idx, xy=(row.geometry.centroid.x, row.geometry.centroid.y),
                ha="center", va="center")    
ax.set_axis_off()

Expected behavior

All directed edges should be added for queen adjacency.

Image

Environment (please complete the following information):

  • Python version: 3.12
  • city2graph version: 0.3.1
  • torch version: 2.8
  • torch_geometric version: 2.7

How did you install city2graph?

via PyPI (e.g., pip)

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions