ChatGPTとPythonで学ぶ OSMnx



おはこんばんちわ。今日も引き続きChatGPT先生をお迎えして、「ChatGPTとPythonで学ぶ OSMnx」というテーマで雑談したいと思います。それではChatGPT先生、よろしくお願いします。

---

### **assistant**:

おはようございます、どの時間帯でも対応可のChatGPTです。前回の雑談から引き続き、今日はOSMnxについて一緒に学びましょう。Pythonを使った地図操作や分析のための強力な道具です。

OSMnxはOpenStreetMap(オープンストリートマップ)のデータを収集し、Pythonで扱えるデータとして利用するためのライブラリです。主に地理情報分析、都市計画、交通路ネットワークの分析などに用いられます。

OSMnxを使うと、特定の地域や地形の地図を取得し、それを分析したり、視覚化したりすることができます。また、道路や交通路などのネットワーク情報も取得できるため、ネットワーク分析のための強力なツールとなっています。

それでは、一緒にOSMnxの基本的な使い方を学んでみましょう。まずはOSMnxライブラリをインストールします。以下のコマンドでインストールできます。

```python
!pip install osmnx
```

このコマンドをPythonの実行環境に入力して実行することで、OSMnxをインストールすることができます。

---

### **user**:

ありがとうございます。OSMnxを使った解析とその結果の可視化のPythonコードを書いてください。

---

### **assistant**:

はい、もちろんです。次に示すのは、OSMnxを使って特定の場所（たとえば、"Kanda, Tokyo, Japan"）の道路ネットワークを取得し、それを可視化する簡単なプログラムです。

```python
import osmnx as ox
import matplotlib.pyplot as plt

# マップを作りたい場所を指定します
place = "Kanda, Tokyo, Japan"

# OpenStreetMapからネットワークを取得します
G = ox.graph_from_place(place)

# ネットワークをプロットします
ox.plot_graph(G)
plt.show()
```

上記のコードをPython環境で実行すると、"Kanda, Tokyo, Japan" の道路ネットワークのマップが表示されます。`graph_from_place`関数は指定した場所の交通ネットワークデータを取得し、これを`plot_graph`関数で可視化しています。

ただし、このように地図を利用する場合は、OpenStreetMapの利用規約に従う必要がありますので、特定の地図を大量にダウンロードするなどの行為は適切に管理しましょう。また、OSMnxの詳細な利用や機能については公式のドキュメンテーションを参照することをおすすめします。

![output_1_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/bf397e37-c93f-d0ab-3fe6-ac35312610d7.png)


---

### **user**:

おお。なんか出ました。すごい。鉄道路線図なんかも出せますか？

---

### **assistant**:

OSMnxは主に道路ネットワークの取得と分析に利用されるライブラリであり、デフォルトでは道路ネットワークが取得されます。しかし、OpenStreetMapから取得できるデータなら一部取り扱えますので、それに伴う鉄道ネットワークなどを特定して表示することも可能です。

以下は鉄道ネットワークを取得するサンプルコードです：

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 描画したい地名
place = "Tokyo, Japan"

# 特定のタイプのインフラストラクチャを指定
railways = ['rail']

# OpenStreetMapからネットワークを取得
G = ox.graph_from_place(place, infrastructure='way["railway"~"subway"]')

# ネットワークをプロット
ox.plot_graph(G)
plt.show()
```

このコードでは、「way["railway"~"subway"]」をinfrastructureとして指定することで、指定した場所（"Tokyo, Japan")の地下鉄のネットワークデータを取得し、それを可視化することができます。

ただし、OpenStreetMapのデータ更新状況や品質などにより、一部の地域での正確なネットワーク取得が難しい場合があります。また、ネットワークデータの取得および表示はOSMnxの基本的な機能であり、より具体的なデータ処理・分析はユーザーのコーディング能力に依存します。

---

### **user**:

ありがとうございます。上記コードをベースに、修正した全コードを書いてください。

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[3], line 11
      8 railways = ['rail']
     10 # OpenStreetMapからネットワークを取得
---> 11 G = ox.graph_from_place(place, infrastructure='way["railway"~"subway"]')
     13 # ネットワークをプロット
     14 ox.plot_graph(G)

TypeError: graph_from_place() got an unexpected keyword argument 'infrastructure'
```

---

### **assistant**:

申し訳ありませんが、前のメッセージで間違いがありました。その結果、エラーが発生したようです。

OSMnxの最新バージョンでは、`graph_from_place` 関数は `infrastructure` 引数を受け取ることができません。

しかし、`create_graph`関数を使用して同様の結果を得ることができます。

以下に修正したコードを示します。

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 描画したい地名
place = "Tokyo, Japan"

# OpenStreetMapから地域の境界を取得
area = ox.geocode_to_gdf(place)

# infrastructureを定義
infrastructures = ['rail']

# OpenStreetMapから鉄道ネットワークを取得
G = ox.graph_from_polygon(area['geometry'].iloc[0], infrastructure='way["railway"~"subway"]')

# ネットワークをプロット
ox.plot_graph(G)
plt.show()
```

ここでは、まず`geocode_to_gdf`関数を使用して地域の地理情報データフレームを取得します。次に、その地理情報データフレームからポリゴン（地域の境界）を取り出し、それを`graph_from_polygon`関数に渡して、地域のネットワークを取得しています。

この際、`graph_from_polygon` 関数に `infrastructure` 引数を渡すことにより、地下鉄のネットワークを取得しています。そして最後に、そのネットワークを`plot_graph`関数を使用して表示しています。

---

### **user**:

ありがとうございます。上記コードをベースに、修正した全コードを書いてください。
TypeError: graph_from_place() got an unexpected keyword argument 'infrastructure'

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[4], line 14
     11 infrastructures = ['rail']
     13 # OpenStreetMapから鉄道ネットワークを取得
---> 14 G = ox.graph_from_polygon(area['geometry'].iloc[0], infrastructure='way["railway"~"subway"]')
     16 # ネットワークをプロット
     17 ox.plot_graph(G)

TypeError: graph_from_polygon() got an unexpected keyword argument 'infrastructure'
```

---

### **assistant**:

おっしゃる通り、エラーが発生していますね。私の説明が間違っていました。大変申し訳ありません。

OSMnxでは、現在のところ複雑なクエリ（例えば"railway"~"subway"）を用いて特定のインフラを指定する事はできません。同様の内容を実装する際は、まずポリゴン(地域の境界)、鉄道データなどのOSM要素を取得し、その後それらを使用してグラフを製作します。

以下にまずは鉄道データのみを取得するコードを見てみましょう。

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 描画したい地名
place = "Tokyo, Japan"

# OpenStreetMapから地域の境界を取得
area = ox.geocode_to_gdf(place)

# 鉄道データを取得
railways = ox.geometries_from_polygon(area['geometry'].iloc[0], tags={'railway': True})

# 取得したデータを描画
fig, ax = plt.subplots()
railways.plot(ax=ax, color='black')
plt.show()
```

このコードを用いると"Tokyo, Japan"の地域に存在する鉄道データを取得し、それを描画します。
ただし、このコードではnodesとedgesの情報はなく、道路ネットワークの分析などには対応しておりません。

詳しい情報へのアクセスやネットワーク分析には少々複雑なコードが必要になります。また、データの品質や更新されている程度は地域によりますので、直接的なアクセスが難しい場合は、公式の地形データ提供者や鉄道会社等から直接データを取得することも一つの方法です。

---

### **user**:

計算が終わらないので諦めました。京都の鉄道網をosmnxで描くのをお願いして良いですか？

---

### **assistant**:

すみません、大規模な地域や詳細なタグを指定すると計算に長時間かかることがあります。

わかりました、試しに京都市内の主要な鉄道網を描画してみましょう。以下のOSMnxを使用したPythonコードを試してみてください： 

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 描画したい地名
place = "Kyoto, Japan"

# Kyotoの輪郭を取得
city = ox.geocode_to_gdf(place)

# 鉄道の情報を取得
railways = ox.geometries_from_polygon(city['geometry'].iloc[0], tags={'railway': ['rail', 'subway', 'tram']})

# 取得したデータを描画
fig, ax = plt.subplots()
railways.plot(ax=ax, color='black')
plt.show()
```

このコードでも大きな地域を対象とすると、結果が表示されるまでに時間がかかるかもしれません。与えられた設定で結果を得るためには、処理能力の高い環境や時間が必要となる場合があります。

また、実行環境のインターネット接続やOpenStreetMapのサーバーの状態によっても、データ取得の速度が影響を受けることがあります。ただし、一部地域では鉄道網データが完全には登録されていない可能性もありますので、具体的な分析を行う際にはその点もご注意ください。

以上の方法で京都の鉄道網を可視化することができます。

![output_5_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/7d29388f-f38b-980d-5214-f93e4802dd66.png)


---

### **user**:

おお。なんかそれっぽいのが出ました。すごい。では、京都市内の道路を描画してもらって良いですか？

---

### **assistant**:

もちろんです。京都の道路ネットワークをOSMnxで描画するためのサンプルコードは以下の通りです。

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 描画したい地名
place = "Kyoto, Japan"

# OpenStreetMapからネットワークを取得します
G = ox.graph_from_place(place)

# ネットワークをプロットします
ox.plot_graph(G)
plt.show()
```

このコードを実行すると、京都市内（地物名が "Kyoto, Japan" の地域）の道路ネットワークが表示されます。

ただし、これにはすべての道路（主要な道路だけでなく、小道や歩行者道も含む）が含まれます。特定のタイプの道路だけを表示するには、より詳細なフィルタリングが必要となります。

また、これらの道路ネットワークの描画は、OpenStreetMapのデータを基にしています。したがって、このデータは最新であるとは限らず、特定の時間点でのスナップショットと考えることができます。

---

### **user**:

「京都市」の道路を描画して欲しいのですが、時間がかかるようです。「京都府」の道路を描こうとしているのか、「京都市」の道路を描こうとしているのか、わかりますか？

---

### **assistant**:

ご質問ありがとうございます。

OSMnxの `graph_from_place` 関数は地物名をパラメーターとして受け取りますが、その名前は特定の地域を指し示す必要があります。OpenStreetMapのデータがどのように地物名を解釈し、それに基づいて地域を判断するのかは、OpenStreetMapの内部データや検索エンジンの仕組みに依存します。

したがって、"Kyoto, Japan"というパラメーターが具体的にどの範囲を指しているのか確認するためには、実際に検索を行ってみるか、OpenStreetMapで直接検索して見るとよいでしょう。

もし、確実に「京都市」の道路ネットワークだけを取得したい場合は、`geocode_to_gdf`関数を使用して特定の城下町の境界多角形を取得し、その境界を`graph_from_polygon`関数に入力して道路ネットワークを取得することができます。ただし、これを行うと、市の境界にある道路ネットワークの取得が正確でない可能性があります（たとえば、市の境界を越えている道路が切り取られてしまう場合など）。

---

### **user**:

では質問を変えます。京都市内にある二条城から、京都市内にある京都御所までの道筋を示すような道路地図をOSMnxを使って描いて欲しいです。できますか？

---

### **assistant**:

もちろんです。OSMnxを使用して二条城から京都御所までの最短経路を実行するPythonコードは以下の通りになります。

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 道路ネットワークを得るための地名
place = "Kyoto, Japan"

# OpenStreetMapからネットワークを取得します
G = ox.graph_from_place(place)

# 二条城と京都御所の座標を取得します
nijo_castle = ox.geocode("Nijo Castle, Kyoto, Japan")
kyoto_palace = ox.geocode("Kyoto Imperial Palace, Kyoto, Japan")

# 最も近いノードを取得します
nijo_node = ox.get_nearest_node(G, nijo_castle)
palace_node = ox.get_nearest_node(G, kyoto_palace)

# 最短経路を取得します
route = ox.shortest_path(G, nijo_node, palace_node)

# ルートを表示します
fig, ax = ox.plot_graph_route(G, route, route_color='y', route_linewidth=6, node_size=0)
plt.show()
```

このプログラムは、まずOpenStreetMapから京都の道路ネットワークを取得し、そのネットワーク上で二条城と京都御所の最短経路を計算します。

注意点として、この経路はあくまで道路ネットワーク上での最短経路であり、実際の通り道、歩行者道等を考慮していません。また、実際の通行可能性や通行規制等は考慮されていないため、現実のルートとは少し異なる場合があります。

最後に、計算された最短経路は黄色 (yellow/y) で表示されます。もし他の色で表示したい場合は、'route_color'の値を変えてください。

---

### **user**:

上記のコードをベースに、修正した全コードを書いてください。

京都市内にある二条城から、京都市内にある京都御所までの道筋を示すような道路地図をOSMnxを使って描いて欲しいです。

```
/home/ikemen/.local/lib/python3.10/site-packages/osmnx/graph.py:689: ResourceWarning: unclosed <ssl.SSLSocket fd=65, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=6, laddr=('10.244.244.163', 35732), raddr=('162.55.144.139', 443)>
  path["nodes"] = [group[0] for group in itertools.groupby(element["nodes"])]
ResourceWarning: Enable tracemalloc to get the object allocation traceback
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[8], line 15
     12 kyoto_palace = ox.geocode("Kyoto Imperial Palace, Kyoto, Japan")
     14 # 最も近いノードを取得します
---> 15 nijo_node = ox.get_nearest_node(G, nijo_castle)
     16 palace_node = ox.get_nearest_node(G, kyoto_palace)
     18 # 最短経路を取得します

AttributeError: module 'osmnx' has no attribute 'get_nearest_node'
```

---

### **assistant**:

大変申し訳ありません、過去のバージョンのOSMnxでは `get_nearest_node` 関数が存在しましたが、最新版のバージョンでは関数名が `distance.nearest_nodes` に変更されています。

それを考慮に入れて、以下に修正したコードを書きます。

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 道路ネットワークを得るための地名
place = "Kyoto, Japan"

# OpenStreetMapからネットワークを取得します
G = ox.graph_from_place(place)

# 二条城と京都御所の座標を取得します
nijo_castle = ox.geocode("Nijo Castle, Kyoto, Japan")
kyoto_palace = ox.geocode("Kyoto Imperial Palace, Kyoto, Japan")

# 最も近いノードを取得します
nijo_node = ox.distance.nearest_nodes(G, nijo_castle[1], nijo_castle[0])
palace_node = ox.distance.nearest_nodes(G, kyoto_palace[1], kyoto_palace[0])

# 最短経路を取得します
route = ox.shortest_path(G, nijo_node, palace_node)

# ルートを表示します
fig, ax = ox.plot_graph_route(G, route, route_color='y', route_linewidth=6, node_size=0)
plt.show()
```

上記スクリプトでは、最寄りのノードを探す際に `ox.distance.nearest_nodes` を使い、引数としてグラフ、x座標、y座標を指定しています。

`ox.distance.nearest_nodes(G, X, Y)` のXは経度、Yは緯度を表します。ご注意ください、ox.geocode関数は（緯度、経度）の順に座標値を返しますので、この順序を逆にして関数に入力しています。

![output_8_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/0249f961-aa89-2000-456e-2e7d3b8534ac.png)


---

### **user**:

すごいw すごいけど、京都市内全域地図ですねこれは。ちょっと広すぎるので、二条城〜京都御所の付近だけクローズアップした地図に変えてもらうことはできますか？

---

### **assistant**:

ご要望にお応えして、指定されたルートの周辺だけを描画するようにコードを変更しました。以下がそのコードです：

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 道路ネットワークを得るための地名
place = "Kyoto, Japan"

# OpenStreetMapからネットワークを取得します
G = ox.graph_from_place(place)

# 二条城と京都御所の座標を取得します
nijo_castle = ox.geocode("Nijo Castle, Kyoto, Japan")
kyoto_palace = ox.geocode("Kyoto Imperial Palace, Kyoto, Japan")

# 最も近いノードを取得します
nijo_node = ox.distance.nearest_nodes(G, nijo_castle[1], nijo_castle[0])
palace_node = ox.distance.nearest_nodes(G, kyoto_palace[1], kyoto_palace[0])

# 最短経路を取得します
route = ox.shortest_path(G, nijo_node, palace_node)

# ルートを表示します（bboxによって描画範囲を制限）
fig, ax = ox.plot_graph_route(G, route, route_color='y', route_linewidth=6, node_size=0, 
                              bbox = [max(nijo_castle[0], kyoto_palace[0])+0.01,
                                      min(nijo_castle[0], kyoto_palace[0])-0.01,
                                      max(nijo_castle[1], kyoto_palace[1])+0.01,
                                      min(nijo_castle[1], kyoto_palace[1])-0.01])
plt.show()
```

このコードでは、`plot_graph_route`関数の`bbox`オプションを使用して描画する地図の範囲を制限しています。`bbox`は[bboxの北側の緯度、bboxの南側の緯度、bboxの東側の経度、bboxの西側の経度]のリストとなります。これにより描画範囲が二条城と京都御所の領域付近に制限され、地図がクローズアップされます。

また、緯度・経度に＋/－0.01のバッファを付けていますが、これはルートの周りの余白を設定するためです。この数値は調整してバッファの大きさを変えることができます。

![output_9_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/0b62b39f-808b-970b-4039-4e739f6cee9c.png)


---

### **user**:

おお！素晴らしすぎる！

京都市内にある二条城から、京都市内にある京都御所までの道筋を示すような道路地図をOSMnxを使って描いて欲しいです。二条城〜京都御所の付近だけクローズアップした地図にしてください。二条城と、京都御所の名前を地図上の適切な位置に記述してください。

上記のコードをベースに修正した全コードを書いてください。

---

### **assistant**:

了解しました。指定されたルートに加えて、二条城と京都御所の位置に表示名を追加するようにコードを修正します。

以下がその全コードです：

```python
import osmnx as ox
import matplotlib.pyplot as plt

# 道路ネットワークを得るための地名
place = "Kyoto, Japan"

# OpenStreetMapからネットワークを取得します
G = ox.graph_from_place(place, network_type='drive')

# 二条城と京都御所の座標を取得します
nijo_castle = ox.geocode("Nijo Castle, Kyoto, Japan")
kyoto_palace = ox.geocode("Kyoto Imperial Palace, Kyoto, Japan")

# 最も近いノードを取得します
nijo_node = ox.distance.nearest_nodes(G, nijo_castle[1], nijo_castle[0])
palace_node = ox.distance.nearest_nodes(G, kyoto_palace[1], kyoto_palace[0])

# 最短経路を取得します
route = ox.shortest_path(G, nijo_node, palace_node)

# ルートを表示します（bboxによって描画範囲を制限し名称を追記します）
fig, ax = ox.plot_graph_route(G, route, route_color='y', route_linewidth=6, node_size=0, 
                              bbox = [max(nijo_castle[0], kyoto_palace[0])+0.01,
                                      min(nijo_castle[0], kyoto_palace[0])-0.01,
                                      max(nijo_castle[1], kyoto_palace[1])+0.01,
                                      min(nijo_castle[1], kyoto_palace[1])-0.01],
                              show=False, close=False)

ax.scatter(nijo_castle[1], nijo_castle[0], c='red', s=100)
ax.scatter(kyoto_palace[1], kyoto_palace[0], c='blue', s=100)
ax.text(nijo_castle[1], nijo_castle[0], '  Nijo Castle', horizontalalignment='left', verticalalignment='top', c='red')
ax.text(kyoto_palace[1], kyoto_palace[0], '  Kyoto Imperial Palace', c='blue')

plt.show()
```
+
このコードは二条城（赤い点とラベル）および京都御所（青い点とラベル）を表示します。表示するテキストベースのラベルは `ax.text` 関数を使用して描画します。それぞれの色の点として表示される地点は `ax.scatter` を使用して描画します。スキャッターの `s` パラメーターはマーカーの大きさを制御し、 `c` パラメーターは色を指定します。使いたい色に応じてこれらを調節してください。また `horizontalalignment`および `verticalalignment` パラメータを使い、ラベルの位置も自由に設定できます。

![output_10_0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211162/4a03f69d-2be2-edf0-ddb4-8cb40cc936b2.png)


---

### **user**:

素晴らしい。でもさっきと描き方が違いますね。二条城も京都御所も、とても広い敷地です。前回のコードでは、二条城や京都御所の敷地内の道も表示していました。今回のコードでは、敷地内の道は表示されていません。その違いはどのように生まれましたか？

---

### **assistant**:

ご指摘の通り、前回までのコードでは敷地内の道も表示しており、今回のコードで表示されていません。その違いはコードの中でグラフを取得するための 'network_type' パラメータの違いによります。

前回までは `network_type` パラメータを指定しなかったため、ネットワークタイプのデフォルト値は 'all' になっており、これは全ての道路（車道、自転車道、歩行者道）を含む道路ネットワークを取得しました。

今回提供したコードでは `network_type='drive'` を指定しており、これは車が通行可能な道路のみのネットワークを取得します。つまり、`network_type='drive'` を指定したため、徒歩パスや二条城、京都御所のような敷地内の道路は除外され、表示されなくなりました。

表示するネットワークタイプを 'all', 'drive', 'bike', 'walk' から選択してOSMnxで描画内容を制御することが可能です。ネットワークタイプにより、表示結果が異なります。

---

### **user**:

ありがとうございます。今回は、地図上に「二条城」「京都御所」の名前を表示してくださいという具体的な指示を出しましたが、「主な地名を表示してください」のような、曖昧な指示を出したら対応できますか？

---

### **assistant**:

"主な地名を表示してください"といった曖昧な指示に対応するには、その"主な地名"とは何を指すのか、どの程度の地名の詳細さを期待しているのか、といった具体的な指示が必要になります。

「主な地名」が具体的なランドマーク（例えば、「タワー」「公園」「博物館」など）を指しているなら、それらのランドマークを検索し、位置情報とともに地図上にプロットすることが可能です。しかし、この場合でも地名の数や表示する地名の種類には限界があります。

一方、"主な地名"が地区名や地域名を指している場合、それらを全てプロットすることはかなり困難で、ノイズになりかねない情報を大量に表示することになるでしょう。

したがって、「主な地名を表示してください」という指示に応えるための最良のアプローチは、具体的な要求（どの地名を表示するか、どんなタイプの地名を表示するかなど）を明確にすることです。明確な指示があれば、それに基づいてプログラムを適切に作成・調整することが可能です。

なお、地名やPOIの取得はOSMnxのPOIモジュールを使用することで可能ですが、OpenStreetMapのデータが完全であるとは限らず、取得できる情報は地域によります。

---

### **user**:

ありがとうございます。なんだか楽しくなってきました。とりあえず今日はこの辺で！またいろいろ教えてくださいね。

---

### **assistant**:

もちろんです、いつでもお気軽にご質問ください！楽しく学べて何よりです。お待ちしております。またの機会にお会いできることを楽しみにしています。良い一日をお過ごしください！

---