NTTドコモR&Dの技術ブログです。

city2graphで簡単に異種グラフをつくって広島駅大規模再開発をネットワーク科学的に検証してみた

自己紹介

こんにちは、NTTドコモ データプラットフォーム部の江口公基です。
普段はドコモが保有する位置情報データや決済データを活用した価値創出・プロダクト企画/開発に取り組んでおり、特に「まちづくり×データ」の領域に強い関心を持っています。
昨年は能登半島地震における人流データの活用について書かせていただきましたが、今年は少し趣向を変えて、話題のOSSライブラリ「city2graph」を使った技術的な検証の話をしたいと思います。

きっかけ

今回の記事を書くに至ったきっかけは、主に3つあります。

  1. city2graphってなんだ: 2025年秋頃、SNSで「city2graph」というライブラリがバズっているのを見かけ、その名前から直感的な面白さを感じたためちょっと触ってみることにしました。
  2. 広島駅再開発への注目: 私自身、広島駅周辺の大規模再開発(路面電車の2階乗り入れなど)に以前から注目していました。ちょうど執筆中の12月上旬に広島へ出張の機会もあったため、広島の街を題材にしようと決意しました。
  3. データ×まちづくりの可能性: 現在業務で利用している人流・決済データは実績のデータですが、街の構造的なデータであるネットワークトポロジーと組み合わせることで、ドコモデータのまちづくりへの適用可能性を最大化させたいという想いがありました。

今回は「2025年の広島駅市電の2階コンコース直結によって、広島の街はどう変わったのか?」 この問いをテーマに、簡単に分析を行う過程で、city2graph の実力を試してみたいと思います。また、本稿での検証は簡易なもので、再開発の影響を裏付けるものではないことにご留意ください。

city2graphとは

city2graph は、都市のネットワーク構造を異種グラフとして表現・分析するためのPythonライブラリです。

ライブラリの開発者のブログには以下のような記述がありました。

city2graphは、地理空間データをグラフ表現に変換し、グラフニューラルネットワークでの利用を可能にするPythonライブラリです。GeoPandas、NetworkX、PyTorch Geometricを統合したインターフェースを提供し、都市形態、公共交通、モビリティ(および人口移動)など、複数のドメインに対応しています。

本稿はGNN*1を用いた分析ではなく、交通ネットワークに対して古典的なネットワーク科学の分析を行っていきますが、そのような分析であってもこのライブラリは効力を十分に発揮します。従来のネットワーク分析では、道路ネットワークと鉄道ネットワークを別々に扱うことが多く、「駅から店舗までの実際の移動経路(徒歩→電車→徒歩)」などを統合的に評価するのはそれぞれのネットワークを地理的に紐づける処理を書かなければいけないので大変です。しかし、このライブラリを使うと以下のことがかなり容易になります。

  1. マルチモーダルネットワークの自動構築(bridge_nodes)

    • 機能: 空間的に独立したレイヤー(例:店舗や施設などの地点情報と道路網)を、k近傍法(KNN)などの空間インデックスを用いて物理的に接続します。
    • 利点: 従来はGISソフト上で手動、あるいは複雑な幾何計算を用いて行っていた「ラストワンマイルの接続(ノード紐付け)」を自動化します。これにより、鉄道(広域移動)と歩行者空間(回遊行動)がシームレスに接続された単一のネットワーク構造を、再現性を保ちながら即座に構築可能です。
  2. 移動コンテキストの構造化(add_metapaths)

    • 機能: グラフ上の複合的な経路(例:駅→道路...→店舗)を、分析意図に基づいた直接的なエッジ(駅→店舗)として再定義(短絡化)します。
    • 利点: 複雑なマルチホップ経路を「アクセシビリティ」や「回遊性」といった高次の関係性(エッジ)として抽象化します。これにより、毎回の経路探索計算を省略できるだけでなく、都市構造の変化をグラフの構造的特徴量(次数やエッジ重み)の変化として定量的に評価することが可能になります。

今回は、歩行者ネットワーク・鉄道ネットワーク・店舗などの地点情報(POI:Point of Interest)を統合的に扱い、再開発による「導線の変化」をリアルに捉えるためにこのライブラリを利用しています。

広島駅前の再開発について

今回の分析対象である広島駅エリアの再開発概要を押さえておきます。

2025年春、広島駅には新駅ビル「minamoa(ミナモア)」が開業し、駅周辺の景色は劇的に変化しています。今回対象とするのは、その中でも2025年8月3日に開業した広島電鉄の「駅前大橋ルート」をピックアップしました。従来、駅から迂回していた路面電車が、駅前大橋を渡り最短距離で駅へアプローチする新ルートが開通されました。ただ高架化されただけでなく、このルートにより駅ビルへの路面電車の2階乗り入れが行われています。

詳しくはこちらの報道もご覧ください。2:00ごろから見ていただくとわかりやすいです。

これまで2FにあるJR広島駅改札を抜けて、階段でちょっと遠い地上のりばに行かなければ乗れなかった路面電車が、高架化して新駅ビルの2階コンコースへ直接乗り入れています。これにより、JR(2階改札)と路面電車(2階)がフラットに繋がり、乗り換え時間が大幅に短縮されます。

実際、2025/12/1に私が訪れた際の写真が以下です。実際に路面電車で市街へ向かいましたがJR改札がある2Fコンコースへ直結しているため、以前の1階に駅があったときに比べて非常に乗りやすかったです。また、紫の循環ルートについては2026年春の開業予定のようです。

広島電鉄 広島駅2階コンコース

路線図 紫の循環ルートは未開業

今回は、この物理的な構造変化がネットワークデータ上でどう表現されるかを検証し、再開発の影響を概観します。

広島市街ネットワークデータの取得

分析にあたり、Overpass APIを使ってOpenStreetMap(以下OSM)のデータを取得しました。
比較対象として、駅前大橋ルート開業前の Before(2025年8月1日) と、開業後の After(2025年12月1日) の2時点を設定しています。

取得データの概要

市街である八丁堀を中心に半径1500mの範囲で、以下のデータをOverpass API経由で取得しました。

  • 歩行者ネットワーク: 歩道、歩行者専用道路、階段など
  • 路面電車ネットワーク: 路面電車の線路
  • 路面電車停留所: 停留所および駅
  • POI(店舗・施設): カフェ、レストラン、コンビニ、商業施設など

詳細な取得条件やコード実装については、末尾にあるデータ取得の詳細をご参照ください。

データ取得結果

データの取得結果を見ると、BeforeからAfterにかけて歩行者ノードとPOIが大幅に増加していることがわかります。駅ビルのリニューアルや周辺施設の整備が、データにも明確に表れています。実際にはOSMへの登録件数の増減もあるため、その効果であるとは言い切れないことにご留意ください。

データ種別 Before (2025年8月) After (2025年12月) 変化
歩行者ノード 2,488 2,667 +179
路面電車エッジ 49 49 +0
停留所 54 51 -3
POI 1,286 1,446 +160

歩行者ノードが179個、POIが160件増加したことは、駅ビルの開業と周辺の歩行者動線の拡充を反映しています。一方、路面電車の基本構造(エッジ数)は変化していませんが、これは±0になっているだけで場所の変化がないわけではありません。後ほど掲載する地図を見ていただければ路線ルートが変更されていることがわかるかと思います。

異種グラフの作成

データが揃ったので、さっそくcity2graph をつかって異なるレイヤーのデータを「異種グラフ」として統合していきます。異種グラフ(Heterogeneous Graph / Heterograph)とは、「種類の異なる要素(ノード)や、種類の異なる関係性(エッジ)が混在してつながっているネットワーク」のことです。

今回扱うのは先ほど用意した以下の3種類のレイヤーです。

レイヤー ノード/エッジ 説明 役割
歩行者ネットワーク ノード + エッジ 歩道や歩行者専用道路 徒歩移動の基盤となるネットワーク
路面電車ネットワーク ノード(停留所)+ エッジ(線路) 路面電車の停留所と線路 停留所間を結ぶ高速移動手段
POI(店舗・施設) ノード 商業施設や観光スポット 人々の移動の目的地

これらを統合することで、「店舗から歩いて停留所まで行き、路面電車に乗って別の停留所で降り、そこから歩いて別の店舗に到達する」といったマルチモーダルな移動経路を表現できるようになります。

異種グラフ統合のイメージ

実際にどのように統合されるのか視覚的に理解するため、以下の図で異種グラフの構築イメージを示します。この図では、POI、路面電車停留所、歩行者ネットワークの3つのレイヤーが、bridge_nodes()によって自動的に接続される様子を表現しています。

異種グラフ構築のイメージ

図からわかるように、異なる色で表現された各レイヤー(赤:POI、緑:路面電車、青:歩行者ネットワーク)が、ブリッジエッジ(点線)によって相互に接続され、単一の統合されたネットワーク構造を形成します。これにより、「店舗→徒歩→停留所→路面電車→停留所→徒歩→店舗」といった複雑な移動経路を、一つのグラフ上でシームレスに扱えるようになります。 以下に実際のコードを示しながら、city2graphでいかに簡単に異種グラフの生成が可能なのかを解説していきます。

異種グラフ構築の準備

従来、異なる種類のデータを統合するには、空間インデックスの構築、最短経路探索、ジオメトリ処理など、複雑なコードが必要でした。city2graphでは、bridge_nodes()関数を使うことで、シンプルなコードで異種グラフを構築できます。

まず、bridge_nodes()を呼び出す前に必要なデータの準備を行います。

NetworkXグラフ→GeoDataFrame変換

import osmnx as ox
import city2graph as c2g
ped_nodes, ped_edges = ox.graph_to_gdfs(ped_graph_before, nodes=True, edges=True)
tram_edges = ox.graph_to_gdfs(tram_graph_before, nodes=False, edges=True)

CRS*2統一(広島エリア用の投影座標系)

target_crs = "EPSG:6677"
ped_nodes = ped_nodes.to_crs(target_crs)
ped_edges = ped_edges.to_crs(target_crs)
tram_edges = tram_edges.to_crs(target_crs)
tram_stops = tram_stops.to_crs(target_crs)
poi = poi.to_crs(target_crs)

OSMデータは緯度経度の地理座標系(EPSG:4326)で取得されますが、メートル単位での正確な距離計算が必要なため、平面直角座標系(EPSG:6677)に変換します。これにより、ネットワーク距離の計算が正確に行えるようになります。

交通モード別に重み付けを調整

# 徒歩と電車の速度差を考慮(歩行: 4km/h、路面電車: 20km/h → 速度比 = 4/20 = 0.2)
ped_edges['weight'] = ped_edges.geometry.length
tram_edges_weighted = tram_edges.copy()
tram_edges_weighted['weight'] = tram_edges['length'] * (4.0 / 20.0)

この調整により、「物理的距離は同じでも、路面電車なら5分の1の時間で移動できる」ことがグラフの重みとして表現されます。例えば1000mの距離なら: - 歩行者エッジ: weight = 1000m(所要時間15分相当) - 路面電車エッジ: weight = 200m(所要時間3分相当に換算)

ノード辞書とエッジ辞書を準備

#ノード辞書(3種類のノードタイプ)
nodes_dict = {
    "pedestrian": ped_nodes,      # 歩行者ネットワークの交差点
    "tram_stop": stop_nodes,       # 路面電車停留所
    "poi": poi_nodes               # 店舗・施設
}

#エッジ辞書(レイヤー内のエッジ = 既存のネットワーク)
#ここには異種ノード間の接続はまだ含まれていない
edges_dict = {
    ("pedestrian", "connects", "pedestrian"): ped_edges,
    ("tram_stop", "tram_line", "tram_stop"): tram_edges_weighted
}

ポイント: edges_dictには同種ノード間の接続のみを含めます。現在存在しない異種ノード間の接続(例: poi → pedestrian)は、次のステップのbridge_nodes()によって自動生成します。

bridge_nodes()で異種グラフを構築

準備が整ったので、city2graphのbridge_nodes()関数を使って異種グラフを構築します。

# city2graphのbridge_nodes()を呼び出し
nodes_dict_out, bridge_edges = c2g.bridge_nodes(
    nodes_dict,                     # ノード辞書
    proximity_method="knn",          # k近傍法で最寄りノードを探索
    k=3,                             # 各ノードから最寄り3ノードに接続
    distance_metric="network",       # ネットワーク距離で計算
    network_gdf=ped_edges,          # 歩行者ネットワークを基準に距離計算
    network_weight="weight"          # 重み属性名
)

# 最終的なエッジ辞書を統合
edges_dict_integrated = {
    **edges_dict,        # 元々のレイヤー内エッジ
    **bridge_edges       # bridge_nodes()が生成したブリッジエッジ
}
この処理で何が行われるのか
  1. ネットワーク距離での経路計算

    • 上記の"最寄り"をこの距離で定義
    • distance_metric="network"により、直線距離ではなく実際の歩行経路に沿った距離を計算
    • network_gdf=ped_edgesで指定した歩行者ネットワークをDijkstra法で探索
  2. k-NNアルゴリズムによる最寄りノード探索

    • 各ノードタイプから、他のノードタイプの最寄り3ノードを探索
    • 例: POIから最寄りの歩行者ノード3つを見つける
  3. ブリッジエッジの自動生成

    • POI ↔ 歩行者ノード
    • 停留所 ↔ 歩行者ノード
    • POI ↔ 停留所(直接接続)

これらの処理を経てbridge_edgesに異なるレイヤー間に以下のブリッジエッジが格納されます。

接続タイプ 意味 実際の移動
POI → 歩行者ノード 店舗から最寄りの道路へアクセス 「店を出て道路に出る」
歩行者ノード → POI 道路から店舗へアクセス 「道路から店に入る」
歩行者ノード → 停留所 道路から停留所へアクセス 「道を歩いて停留所に向かう」
停留所 → 歩行者ノード 停留所から道路へアクセス 「停留所から道路に出る」
停留所 → POI 停留所から店舗へ直接アクセス 「停留所のすぐ近くの店に入る」
POI → 停留所 店舗から停留所へ直接アクセス 「店のすぐ近くの停留所に向かう」

ここに、元々存在していた以下のレイヤー内のエッジを加えることで

エッジタイプ 意味
歩行者 → 歩行者 歩道に沿った移動(元々のped_edges)
停留所 → 停留所 路面電車での移動(元々のtram_edges_weighted)

以下のような複合的な経路を一つのグラフ上で計算できます。まさに赤と青の破線が異なるレイヤーとレイヤーを繋ぐブリッジエッジです。

複合的な経路のイメージ

POI(出発地)
  → 歩行者ノード(出発POI前の道路)
  → 歩行者ノード(歩道を歩く)
  → 停留所(X駅)
  → 停留所(Y駅)(路面電車で移動)
  → 歩行者ノード(Y駅近くの道路)
  → 歩行者ノード(目的POI前の道路)
  → POI(目的地)
distance_metric="network"の利点

この処理、地味に有難いのが、distance_metric="network" を指定することで、以下の処理が簡単に行われる点です。

  1. 現実的な近傍関係の構築

    • 直線距離ではなく、実際の歩行経路に沿った距離でk-NN探索
    • 川や障害物を考慮した「本当に近い」ノードを自動検出
    • 例:直線では近くても、橋を渡らないと到達できない場所は除外される
  2. 経路ジオメトリの保持

    • 接続の重み(距離)だけでなく、実際の経路形状(LineString)も自動生成
    • Dijkstra法による最短経路が自動計算され保存される
    • 可視化や詳細分析で、現実的なルートを地図上に描画可能

ちなみに、networkの他に距離指標として以下を用途に合わせて選択が可能です。

距離メトリック エッジ重み エッジジオメトリ 用途
euclidean 直線距離 直線 理論的な空間近接性分析
manhattan L1距離 L字型 碁盤目状の街路分析
network 実際の経路距離 実際の経路形状 現実的な移動分析

従来であれば、このようなネットワーク距離ベースの接続を実装するには、空間インデックスの構築、最短経路探索、ジオメトリ処理などが必要でした。city2graphでは、非常に簡単にこれらすべてがパラメータ1つで実現できます。「面倒だし一旦ユークリッド距離でいいか」と諦めることはもうありません。

異種グラフ構築結果

bridge_nodes()により、以下のような接続が自動的に作成されました。

左に2025年8月、右に2025年12月の異種グラフを可視化しています。 変化が最も顕著なのは、緑色で示した路面電車の線路構造です。駅前大橋ルートによって、広島駅へ直線的に線路が伸びている様子が見て取れます。8月開業までは駅前大橋を迂回した位置に停留所があったことがこのグラフからも見て取れます。

駅前大橋ルート開業前後のネットワーク

接続タイプ Before (2025年8月) After (2025年12月)
エッジ数 平均距離 中央値 エッジ数 平均距離 中央値
pedestrian → tram_stop 7,464 409.7m 376.8m 8,001 408.3m 371.3m
pedestrian → poi 7,464 140.8m 90.0m 8,001 138.6m 83.7m
tram_stop → pedestrian 162 7.8m 3.3m 153 9.3m 3.7m
tram_stop → poi 162 83.0m 33.0m 153 88.3m 41.4m
poi → pedestrian 3,858 20.7m 10.3m 4,338 21.3m 10.1m
poi → tram_stop 3,858 293.1m 256.9m 4,338 295.1m 259.0m

これにより、「店舗から最寄りの道路まで歩き、そこから電車に乗って別の店舗に行く」といった複雑な経路を分析するベースが整いました。

ネットワーク分析 〜広島の街はどう変わったのか〜

それでは、実際に広島の街の変化を分析してみます。改めてですが、今回は具体例をもってcity2graphを試したかったので、あくまでも限られたオープンなデータに対して簡素に分析を行っています。実際の再開発の効果を示す分析ではないことにご留意ください。

仮説と検証方法

再開発(路面電車の2階直結・駅ビル開業)により、以下の変化が起きると仮説を立て、city2graphを用いて検証しました。

仮説 検証方法 使用した指標
1. POI間のアクセス性向上
路面電車の2階直結により、店舗間の移動が便利になる
メタパス分析で、徒歩のみ・電車経由の2パターンでPOI間の到達可能性を比較 到達距離分布の変化
2. 全体的なアクセシビリティの向上
駅ビル整備により、ネットワーク全体への到達性が改善される
近接中心性(Closeness Centrality)で、各地点からネットワーク全体へのアクセスのしやすさを分析 近接中心性の変化

検証結果1: メタパスによる到達距離分析

メタパスとは

メタパス(Metapath)とは、異種グラフにおける「特定のノードタイプを経由する経路」のことです。city2graphのadd_metapaths()関数を使うことで、複雑なマルチホップ経路を直接的なエッジとして表現できます。

今回は2種類のメタパスを定義・対象とします。

  1. 直接徒歩: POI → 歩行者ネットワーク → POI(2-hop)
  2. 路面電車経由: POI → 歩行者 → 停留所 → (路面電車) → 停留所 → 歩行者 → POI(5-hop)

これにより、「歩いて行ける距離」と「電車を使った移動距離」を分けて評価できます。

メタパスの実装

city2graphでは以下のような形で、メタパスを定義します。

コード例
# メタパス定義:直接徒歩(POI間を徒歩のみで移動)
metapath_direct = [
    ("poi", "is_nearby", "pedestrian"),      # POIから歩行者ネットワークへ
    ("pedestrian", "is_nearby", "poi")        # 歩行者ネットワークからPOIへ
]

# メタパス定義:路面電車経由(POI間を電車を使って移動)
metapath_via_tram = [
    ("poi", "is_nearby", "pedestrian"),           # POIから歩いて道路へ
    ("pedestrian", "is_nearby", "tram_stop"),     # 道路から歩いて停留所へ
    ("tram_stop", "tram_line", "tram_stop"),      # 路面電車で移動
    ("tram_stop", "is_nearby", "pedestrian"),     # 停留所から道路へ
    ("pedestrian", "is_nearby", "poi")            # 道路から目的のPOIへ
]

# 直接徒歩メタパスの計算
nodes_mp_direct, edges_mp_direct = c2g.add_metapaths(
    (nodes_dict, edges_dict_integrated),
    [metapath_direct],       # 定義したメタパス
    edge_attr="weight",      # エッジの重み属性を使用
    edge_attr_agg="sum"      # 経路上の重みを合計
)

# 路面電車経由メタパスの計算
nodes_mp_tram, edges_mp_tram = c2g.add_metapaths(
    (nodes_dict, edges_dict_integrated),
    [metapath_via_tram],     # 定義したメタパス
    edge_attr="weight",      # エッジの重み属性を使用
    edge_attr_agg="sum"      # 経路上の重みを合計
)

# また以下のようにpoiからpoiへのメタパスエッジをGeoDataFrameで取得できます
mp_key = ("poi", "metapath_0", "poi")
metapath_direct_edges = edges_mp_direct[mp_key]
metapath_tram_edges = edges_mp_tram[mp_key]

またおさらいですが、weight交通モード別に重み付けを調整 で説明したように「POI間を実際のネットワークに沿って移動した場合の距離」なので、交通モードの重みづけを考慮した実効距離となります。

メタパス分析結果

では、実際にメタパスを張ったネットワークにおいてどのような変化があったのでしょうか。 それぞれの指標の数表と距離帯に対するヒストグラムを可視化しました。

メタパス POIペア数 平均距離(m) 中央値(m) 最短距離(m) 最長距離(m)
直接徒歩 (Before) 7,392 58.4 26.8 0 2,059.9
直接徒歩 (After) 8,660 55.2 26.7 0 1,220.3
路面電車経由 (Before) 13,282 2,810.7 2,035.9 58.5 33,021.2
路面電車経由 (After) 13,974 3,038.1 2,495.2 113.5 33,021.2

わかること

  1. 直接徒歩:アクセス距離の変化

    • 直接徒歩の平均距離が58.4m → 55.2mに短縮(-5.5%
    • 中央値も26.8m → 26.7mとほぼ横ばい(-0.4%
    • 駅ビル整備により歩行者ネットワークの密度が高まり、より効率的に回遊できるようになった
  2. 直接徒歩:最長距離の短縮

    • 直接徒歩の最長距離が2,059.9m → 1,220.3mに大幅短縮(-40.8%
    • 歩行者ネットワークの拡張により、遠距離POI間の接続性が改善し、都市のコンパクト化が伺える
  3. 路面電車経由:アクセス範囲拡大

    • 路面電車経由の平均距離が2,810.7m → 3,038.1mに増加(+8.1%
    • 中央値は2,035.9m → 2,495.2mに大幅増加(+22.5%
    • 駅前大橋ルートの開業により、路面電車を利用した場合により遠距離のPOIへアクセス可能になった

路面電車経由:メタパス距離の分布比較

さらに詳しく、路面電車経由のメタパス距離の分布をみてみると1500mあたりからバイオリンプロットが膨らんでおり、距離が遠いメタパスの発生確率が増加していることがわかります。累積密度関数を見ても、8月から12月にかけて距離が300mほど伸びていることがわかります。ここから、駅前再開発の影響で従来よりもより遠い場所へアクセス可能になった。つまり、街全体のアクセス性が向上したことが推察されます。

検証結果2: 近接中心性による全体的なアクセシビリティ評価

近接中心性とは

近接中心性(Closeness Centrality)は、各ノードからネットワーク全体への「到達のしやすさ」を測る指標です。値が高いほど、そのノードから他のすべてのノードへのアクセスが良いことを意味します。

近接中心性は以下の式で定義されます:

 \displaystyle C_c(v) = \frac{n-1}{\sum_{u \neq v} d(v,u)}

ここで:

  •  C_c(v):ノード  v の近接中心性

  •  n:ネットワーク内の全ノード数

  •  d(v,u):ノード  v からノード  u への最短経路距離

  •  \sum_{u \neq v} d(v,u):ノード  v から他のすべてのノードへの最短経路距離の総和

この指標により、各地点がネットワーク全体の中でどれだけ「中心的」な位置にあるかを定量的に評価できます。

近接中心性の実装

今回はcity2graphとも相性の良いNetworkXの標準的な近接中心性を使用して、各ノードの重要度を評価し、ノードタイプ別に抽出してBefore/Afterで比較します。

コード例
import networkx as nx

# 異種グラフをcity2graphの関数でNetworkXの有向グラフに変換
G = c2g.to_directed_graph(nodes_dict, edges_dict_integrated)

# 近接中心性の計算(距離ベース)
closeness = nx.closeness_centrality(G, distance='weight')

このコードを実行すると、closenessには各ノードID(key)とその近接中心性の値(value)が辞書形式で格納されます。

{
    'node_1234': 0.000675,
    'node_5678': 0.000682,
    'node_9012': 0.000658,
    ...
}

この結果をBefore/Afterの2時点で計算し、全ノードの近接中心性を統計的に比較することで、再開発による全体的なアクセシビリティの変化を定量評価します。以下では、この計算結果を集計した統計値と分布の図を示します。

近接中心性分析結果

統計指標 Before After 改善率
平均値 0.000663 0.000676 +1.89%
中央値 0.000656 0.000667 +1.64%
標準偏差 0.000115 0.000124 +7.83%

近接中心性の変化

わかること

  1. 全体的なアクセシビリティの向上

    • 平均値が+1.89%、中央値が+1.64%向上
    • ヒストグラムから青色(Before)の分布が全体的に右側(数値が高い方)へスライドし、オレンジ色(After)の分布も同様に高数値側へシフトしている
    • ネットワーク全体への到達性が改善され、各地点からの総合的なアクセス性が向上
  2. スーパーハブの形成

    • ヒストグラムの右端(中心性が高いエリア)において、オレンジ色の棒が大きく突き出ている
    • 「極めてアクセス性の高いハブとなる場所」が現れたことを意味
    • この影響もあり標準偏差も大きくなっているように見受けられる

では実際に、どのあたりのアクセス性が向上したのでしょうか。簡単に地図でみてみることにします。以下の図は、Before/After両方の期間に存在するノードの近接中心性の差分(After - Before)を地図上にプロットしたものです。

近接中心性差分の地図プロット

比較対象期間両方に存在しているノードに対しての差分にはなりますが、比較的中心から遠いノードの近接中心性がUPしていることがわかります。検証結果1の結果とも整合する形になっていることがわかります。2つの検証から当初掲げていた仮説、POI間のアクセス性向上と全体的なアクセシビリティの向上はどうやらそれなりに正しそうです。

次にやりたいこと

ここまで city2graph とOSMデータを用いた分析を行いましたが、これはあくまで「道路や線路のつながり(トポロジ)」に基づいた理論上のアクセス性の評価に過ぎません。

実際の街づくりにおいては、以下のような課題が残ります。

  • 実際の人流は考慮されていない: 道がつながっていても、実際に人がそこを通っているかはわからない。
  • 時間帯による変動: 通勤時間帯と休日では人の流れが大きく異なる。
  • 店舗の集客力: 人気店舗とそうでない店舗の差は考慮されていない。

ここで、私たちが普段扱っているドコモの位置情報データ・決済データが真価を発揮します。
今回のネットワーク分析とドコモデータを掛け合わせることで、以下のような高度な分析が可能になります。

  1. 実際の人流パターンの検証: 新しく整備された動線が実際に使われているかの効果検証が可能です。
  2. 店舗への来客・消費変化の定量化: 駅直結効果によって、アクセシビリティが向上したエリアで、実際に来客数や消費額が増加したか。
  3. 属性別の利用実態の把握: 通勤・観光・買い物など、移動目的や時間帯による人流パターンの違いを可視化。今回のネットワーク分析では理論上のアクセス性のみを評価していますが、ドコモデータを組み合わせることで「実際にどの時間帯にどのような人が移動しているか」という実態を捉えることができます。
  4. 再開発の事前評価: 将来の開発計画に対して、ネットワーク構造の変化と過去の人流パターンを組み合わせることで、効果の予測が可能になります。

今回はドコモにある実データを用いない分析でしたが、構造(Graph)と実態(Data)を組み合わせることで、「計画段階での予測→実施後の検証→次の施策」という、真にデータドリブンな街づくりのPDCAサイクルを回すことが可能になります。

また、上記のデータも含めて異種グラフ化で、GNNなどの機械学習モデルにより分析をしていくことも可能なため活用方法は無限です。


さいごに

今回の検証で、city2graph は異種グラフを用いた都市構造の可視化や定量評価においても非常に強力なツールであることがわかりました。特に以下の点で優れています。

  • bridge_nodes(): ネットワーク距離を考慮した自動接続により、現実的なアクセス性評価が可能
  • add_metapaths(): マルチモーダルな移動経路を簡潔に定義・計算できる
  • 各種ツールとの連携: 今回はNetworkXとの統合により、既存の分析手法を活用可能

city2graph のような便利なツールを援用しながら、リアルな世界を映しだすドコモのデータの力で、今後も街づくり領域への価値提供を行っていきたく思います。

しかしながら、考慮しきれていない、定量的に示すことのできないような世界もあります。 広島出張の際に、「この広島駅ビル直結の路面電車、革命的ですね!」と広島市に住む方へ興奮気味に話したところ、「路面電車は速度が遅いので結局バスを使うんですよねー」と申し訳なさそうに言われてしまいました。実際足を運び、目で見て、手を動かすこの基本作法が大事だなと、それがリアルとデータを繋ぐんだなと思いました。それではよいお年を!

付録

データ取得の詳細(クリックで展開)

本文で説明したデータ取得について、詳細なコード実装とOSMタグの説明を記載します。

データ取得のコード実装

以下のコードでOverpass APIを使用して、指定日時のOSMデータを取得しています。

import requests
import osmnx as ox
import tempfile

def fetch_osm_data_at_date(center_point, dist, date_str, data_type='pedestrian'):
    """
    指定日時のOSMデータを取得
    
    Parameters:
        center_point: (lat, lon) タプル
        dist: 半径(メートル)
        date_str: "YYYY-MM-DD" 形式の日付
        data_type: 'pedestrian', 'tram', 'tram_stop', 'poi'
    
    Returns:
        NetworkX Graph または GeoDataFrame
    """
    lat, lon = center_point
    
    # Overpass クエリを構築
    if data_type == 'pedestrian':
        # 歩行者ネットワーク
        overpass_query = f"""
        [out:xml][timeout:300][date:"{date_str}T00:00:00Z"];
        (
          way["highway"~"footway|pedestrian|steps|living_street|path|cycleway"](around:{dist},{lat},{lon});
          node(w);
        );
        out body;
        >;
        out skel qt;
        """
    elif data_type == 'tram':
        # 路面電車の線路ネットワーク
        overpass_query = f"""
        [out:xml][timeout:300][date:"{date_str}T00:00:00Z"];
        (
          way["railway"="tram"](around:{dist},{lat},{lon});
          node(w);
        );
        out body;
        >;
        out skel qt;
        """
    elif data_type == 'tram_stop':
        # 路面電車の停留所・駅
        overpass_query = f"""
        [out:xml][timeout:300][date:"{date_str}T00:00:00Z"];
        (
          node["railway"="tram_stop"](around:{dist},{lat},{lon});
          node["railway"="station"]["station"="light_rail"](around:{dist},{lat},{lon});
        );
        out body;
        """
    elif data_type == 'poi':
        # POI(店舗等)
        overpass_query = f"""
        [out:xml][timeout:300][date:"{date_str}T00:00:00Z"];
        (
          node["amenity"~"cafe|restaurant|fast_food|bar|pub|food_court"](around:{dist},{lat},{lon});
          node["shop"~"convenience|supermarket|department_store|mall|bakery|clothes|books"](around:{dist},{lat},{lon});
        );
        out body;
        """
    
    # Overpass APIにリクエスト
    overpass_url = "https://overpass-api.de/api/interpreter"
    response = requests.post(overpass_url, data={'data': overpass_query}, timeout=300)
    
    # 一時ファイルに保存してOSMnxで読み込み
    with tempfile.NamedTemporaryFile(mode='w', suffix='.osm', delete=False) as tmp_file:
        tmp_file.write(response.text)
        tmp_filepath = tmp_file.name
    
    if data_type in ['pedestrian', 'tram']:
        # グラフとして読み込み
        G = ox.graph_from_xml(tmp_filepath, simplify=True)
        return G
    else:
        # GeoDataFrameとして読み込み
        gdf = gpd.read_file(tmp_filepath, layer='points')
        return gdf

# 使用例:広島駅周辺(八丁堀中心)のデータ取得
hiroshima_station = (34.39374585944368, 132.46345044322504)
dist = 1500

# Before(2025年8月1日)のデータ取得
ped_graph_before = fetch_osm_data_at_date(hiroshima_station, dist, "2025-08-01", "pedestrian")
tram_graph_before = fetch_osm_data_at_date(hiroshima_station, dist, "2025-08-01", "tram")
tram_stops_before = fetch_osm_data_at_date(hiroshima_station, dist, "2025-08-01", "tram_stop")
poi_before = fetch_osm_data_at_date(hiroshima_station, dist, "2025-08-01", "poi")

# After(2025年12月1日)のデータ取得
ped_graph_after = fetch_osm_data_at_date(hiroshima_station, dist, "2025-12-01", "pedestrian")
tram_graph_after = fetch_osm_data_at_date(hiroshima_station, dist, "2025-12-01", "tram")
tram_stops_after = fetch_osm_data_at_date(hiroshima_station, dist, "2025-12-01", "tram_stop")
poi_after = fetch_osm_data_at_date(hiroshima_station, dist, "2025-12-01", "poi")

OSMタグの詳細説明

歩行者ネットワーク

highwayタグで以下の値を持つ道路要素を取得しています:

  • footway: 歩道。車道に隣接する歩行者専用の道
  • pedestrian: 歩行者専用道路。広場や商店街など、車両が進入できない歩行者空間
  • steps: 階段。高低差のある場所を結ぶ歩行者用の段差
  • living_street: 生活道路。歩行者優先で、車両の速度制限がある道路
  • path: 小道。非舗装や幅の狭い歩行者・自転車用の道
  • cycleway: 自転車道。自転車専用または歩行者と共用の道

路面電車ネットワーク

  • railway=tram: 路面電車の線路

路面電車停留所

以下のいずれかの条件を満たすノードを取得:

  • railway=tram_stop: 路面電車の停留所
  • railway=station かつ station=light_rail: LRTの駅

POI(店舗・施設)

amenity(アメニティ施設)タグ: * cafe: カフェ * restaurant: レストラン * fast_food: ファストフード店 * bar: バー * pub: パブ * food_court: フードコート

shop(店舗)タグ:

  • convenience: コンビニエンスストア
  • supermarket: スーパーマーケット
  • department_store: デパート・百貨店
  • mall: ショッピングモール
  • bakery: ベーカリー
  • clothes: 衣料品店
  • books: 書店

*1:グラフニューラルネットワーク:ネットワーク構造を深層学習で扱うための手法

*2:Coordinate Reference System:地球上の位置を座標で表現するための基準