【Python】#1 前編 foliumで地図上に複数の線をプロットする -バスの走行ルートを可視化-

Python

はじめに

こんにちは!今回は緯度・経度をもとに地図上にバスの走行ルートをプロットしていきます!以前Pythonのfoliumライブラリで位置情報を使って地図に公園の位置を描画してみましたが、foliumで他にもいろいろ可視化できそうだと思ったので試しながら実装していきます。

今回は主要な線描画の実装部分と表示のカスタマイズ方法の前編と後編に分けて解説していきます。

完成イメージ

最終的な完成イメージは上のようになります!オープンデータを活用し、バス停の情報と走行ルートが確認できる地図です。ぜひ一緒に実装していきましょう!

事前準備

実行環境

  • Windows11
  • Python:3.11.0
  • folium 0.13.0
  • pandas 1.5.2

データの準備

データは三重県菰野町のオープンデータライブラリから「コミュニティバスの時刻表」をダウンロードします。
ダウンロード→コミュニティバスの時刻表

今回使用するデータは2025年4月現在のものです。更新情報はダウンロード先サイトをご確認ください。

 【実践】Pythonで地図上にバスの走行ルートを描画する

全体としては以下の流れとなり、今回の記事では地図上への線の描画までを実装してみます!

  1. 必要な情報の洗い出しとデータの確認
  2. 地図上への線の描画
  3. 追加情報表示のカスタマイズ・見た目の変更

1. 必要な情報の洗い出しとデータの確認

走行ルートの線を描画するためにはバス停の位置情報(緯度・経度)とあわせて、どの点とどの点を結べばよいかの各便の通過順情報が必要になります。そしてルート別に色分けして表示したいので各走行便がどのルートを走行したかの情報も必要となります。

ダウンロードしたzipを解凍すると複数の.txtファイルが含まれており、今回使いたい情報は以下の3つのファイルに入っていることが分かりました!!

stops.txt → バス停の位置情報(stop_lat,stop_lon)
stop_times.txt → 各走行便のバス停通過情報 (trip_id, stop_id, stop_sequence)
trips.txt → 走行情報とルートの対応(trip_id と route_idの対応)
3つのファイルを使って実装を進めていきましょう!

2. 地図上への線の描画

1つのルートを地図に描画する

foliumで地図上に線を描画するためにはPolyLine() 関数を使います。今回の最終目標は複数ルートを地図に描画することですが第一段階として1つのルートを描画するところまで実装してみます。

import pandas as pd
import folium

def draw_route(
    stops_file,
    stop_times_file,
    trips_file,
    output_html="route_map.html"
    ):
    # データ読み込み
    stops = pd.read_csv(stops_file)
    stop_times = pd.read_csv(stop_times_file)
    trips = pd.read_csv(trips_file)
    trip_id = trips.iloc[0]['trip_id']
    print(f"trip_id: {trip_id}")

    #stop_timesを取得してstop_sequence順に並べる
    trip_stops = stop_times[stop_times['trip_id'] == trip_id].sort_values('stop_sequence')

    # stop_id をキーに緯度経度・名前を取得
    merged = trip_stops.merge(stops, on='stop_id')

    # 中心を最初のバス停に設定
    map_center = [merged.iloc[0]["stop_lat"], merged.iloc[0]["stop_lon"]]
    m = folium.Map(location=map_center, zoom_start=14)

    # バス停を線で結ぶ
    points = list(zip(merged["stop_lat"], merged["stop_lon"]))
    folium.PolyLine(points, color="blue", weight=4.5, opacity=1).add_to(m)

    # バス停にマーカーを追加する
    for _, row in merged.iterrows():
        folium.Marker(
            location=[row["stop_lat"], row["stop_lon"]],
            popup=f"{row['stop_name']} (通過順: {row['stop_sequence']})",
            icon=folium.Icon(color="green")
        ).add_to(m)

    # 地図を保存する
    m.save(output_html)
    print(f"地図を保存しました: {output_html}")

draw_route(
    stops_file=r"stops.txt",
    stop_times_file=r"stop_times.txt",
    trips_file=r"trips.txt"
)

実行すると地図に1ルート描画されたroute_map.htmlが出力されます。

ソースコード ポイント解説

    #stop_timesを取得してstop_sequence順に並べる
    trip_stops = stop_times[stop_times['trip_id'] == trip_id].sort_values('stop_sequence')

    # stop_id をキーに緯度経度・名前を取得
    merged = trip_stops.merge(stops, on='stop_id')
trip_stopsには通過順やtrip_id、stop_idが入っています。stopsにはstop_idごとの緯度・経度情報などが入っています。これらをstop_idをキーにして結合することで通過順と緯度・経度情報とバス停の名前を1つにまとめています。
  # バス停を線で結ぶ
    points = list(zip(merged["stop_lat"], merged["stop_lon"]))
    folium.PolyLine(points, color="blue", weight=4.5, opacity=1).add_to(m)

バス停を線で結んでいる部分を見てみましょう。pointsはバス停の緯度・経度をペアにして通過順に並べた座標のリストです。foliumで線を描画するにはfolium.PolyLine()関数を使います。座標リストのpointsをもとに線を描画しています!

複数のルートを地図に描画する
先程のコードをもとに複数のルートを描画できるようにしていきましょう。完成イメージは以下です。
複数のルートを描画するためにはユニークなroute_idの数だけ繰り返す必要があります。1つのルートを描画した際のコードの#データ読み込み部分~#バス停にマーカーを追加する部分に処理を追加しています!
    # ユニークなroute_idごとにtripを1つずつ取得
    unique_routes = trips['route_id'].unique()
    print(f"route_id数: {len(unique_routes)}件")

    # 中心を最初のルートの最初のバス停に設定
    first_trip_id = trips[trips['route_id'] == unique_routes[0]].iloc[0]['trip_id']
    first_stop_id = stop_times[stop_times['trip_id'] == first_trip_id].sort_values('stop_sequence').iloc[0]['stop_id']
    first_stop = stops[stops['stop_id'] == first_stop_id].iloc[0]
    m = folium.Map(location=[first_stop['stop_lat'], first_stop['stop_lon']], zoom_start=14)

    # 色リスト
    colors = ['red', 'blue', 'green', 'orange', 'pink', 'purple', 'cadetblue', 'black',  'darkred','gray']

    for i, route_id in enumerate(unique_routes):
        # route_idに紐づくtripの中から1つ取得
        trip_id = trips[trips['route_id'] == route_id].iloc[0]['trip_id']

        # stop_timesから通過順にstopを取得
        trip_stops = stop_times[stop_times['trip_id'] == trip_id].sort_values('stop_sequence')
        merged = trip_stops.merge(stops, on='stop_id')

        color = colors[i % len(colors)]

        # 線の描画
        points = list(zip(merged["stop_lat"], merged["stop_lon"]))
        folium.PolyLine(
            points,
            color=color,
            weight=4.5,
            opacity=1,
            tooltip=f"route_id: {route_id}"
        ).add_to(m)

ソースコード ポイント解説

ポイントは各ルートから代表のtrip_idを選んでいる部分です。

    for i, route_id in enumerate(unique_routes):
        # route_idに紐づくtripの中から1つ取得
        trip_id = trips[trips['route_id'] == route_id].iloc[0]['trip_id']

for文の中で、全ルートの中からそれぞれ1便(trip_id)ずつ選び、選んだ便のバス停通過順を使って線を描画することで地図に複数ルートが表示されるようになりました。

今回はここまでにします!後編でポップアップやマーカーのカスタマイズなどをしていきましょう。

 前編の全体ソースコード

import pandas as pd
import folium
def draw_route(
    stops_file, stop_times_file, trips_file,
    output_html="all_routes_map.html",
    show_markers=True
):
    # データ読み込み
    stops = pd.read_csv(stops_file)
    stop_times = pd.read_csv(stop_times_file)
    trips = pd.read_csv(trips_file)
    # ユニークなroute_idごとにtripを1つずつ取得
    unique_routes = trips['route_id'].unique()
    print(f"route_id数: {len(unique_routes)}件")

    # 中心を最初のルートの最初のバス停に設定
    first_trip_id = trips[trips['route_id'] == unique_routes[0]].iloc[0]['trip_id']
    first_stop_id = stop_times[stop_times['trip_id'] == first_trip_id].sort_values('stop_sequence').iloc[0]['stop_id']
    first_stop = stops[stops['stop_id'] == first_stop_id].iloc[0]
    m = folium.Map(location=[first_stop['stop_lat'], first_stop['stop_lon']], zoom_start=14)

    # 色リスト
    colors = ['red', 'blue', 'green', 'orange', 'pink', 'purple', 'cadetblue', 'black',  'darkred','gray']

    for i, route_id in enumerate(unique_routes):
        # route_idに紐づくtripの中から1つ取得
        trip_id = trips[trips['route_id'] == route_id].iloc[0]['trip_id']

        # stop_timesから通過順にstopを取得
        trip_stops = stop_times[stop_times['trip_id'] == trip_id].sort_values('stop_sequence')
        merged = trip_stops.merge(stops, on='stop_id')

        color = colors[i % len(colors)]

        # 線の描画
        points = list(zip(merged["stop_lat"], merged["stop_lon"]))
        folium.PolyLine(
            points,
            color=color,
            weight=4.5,
            opacity=1,
            tooltip=f"route_id: {route_id}"
        ).add_to(m)

        # バス停にマーカーを追加する
        if show_markers:
            for _, row in merged.iterrows():
                folium.Marker(
                    location=[row['stop_lat'], row['stop_lon']],
                    popup=f"{row['stop_name']} (通過順: {row['stop_sequence']})",
                    icon=folium.Icon(color="green")
                ).add_to(m)
               
    # 地図を保存する
    m.save(output_html)
    print(f"地図を保存しました: {output_html}")

draw_route(
    stops_file=r"stops.txt",
    stop_times_file=r"stop_times.txt",
    trips_file=r"trips.txt"
)

前編の完成HTMLファイル

バス停もルートもたくさんありますね~!そのおかげで生活も便利になっているんですね

まとめ

今回は緯度・経度をもとに地図上にバスの走行ルートをプロットしてみました。様々な種類のデータから自分の使いたいデータを探して活用できるようになると可視化の幅も広がりますね。今回作った地図では、情報が見づらい部分があったり他にも情報が欲しい部分がまだあります。後編では見た目と表示内容をカスタマイズして完成させていきます!!

タイトルとURLをコピーしました