日曜日, 5月 4, 2025
ホームニューステックニュースfoliumを使って新潟県県央地域の路線バスの1日の動きを可視化してみた #Python - Qiita

foliumを使って新潟県県央地域の路線バスの1日の動きを可視化してみた #Python – Qiita



foliumを使って新潟県県央地域の路線バスの1日の動きを可視化してみた #Python - Qiita

前回の記事(新潟県三条市のコミュニティバスと新潟県燕市のコミュニティバスと燕市周辺の越後交通の路線バスの1日の動きを可視化してみた)では、GTFSデータとGTFS-RT(リアルタイム)データを用いて、GTFSデータとGTFS-RT(リアルタイム)データを用いて、越後交通と新潟県燕市・三条市のコミュニティバスを組み合わせた1日の運行状況を可視化しました。

この可視化には、Webサービスである「Mobmap Web(リンク)」を使用していましたが、今後は施設の利用状況なども含めて、より柔軟に可視化できる方法を模索したいと考えています。そこで今回は、その第一歩として、前回と同様の可視化をPythonの地図描画ライブラリ「folium」を使って再現することに取り組みました。

  • 三条市のコミュニティバスのデータについて

  • 燕市のコミュニティバスのデータについて

  • 越後交通の路線バスのデータ

    • GTFSリアルタイム(GTFS-RT)データ:
      2024年3月21日に取得した、越後交通のGTFS-RTデータ(vehicle_positions)。 なお、以下のサイトから5分毎にデータを取得しました

前回の記事(新潟県三条市のコミュニティバスと新潟県燕市のコミュニティバスと燕市周辺の越後交通の路線バスの1日の動きを可視化してみた)で作成した、三条市のコミュニティバスのデータと燕市コミュニティバスのデータと越後交通のGTFS-RTデータを一つにまとめたCSVを使用しました。

またそれぞれのデータが異なるバス事業者のデータであることを明示するため、燕市のコミュニティバスのデータはroute_id0とし、越後交通のデータはroute_id1とし、三条市のコミュニティバスのデータはroute_id2とすることで、事業者間の識別をできるようにしました。

最終的なCSVファイルは以下のような形式になっています。

id,timestamp,latitude,longitude,route_id
10658301001,2025-03-21 06:58:00+09:00,37.66339075,138.8242534,0
...
49906,2025-03-21 06:23:48+09:00,37.61711121,138.8234711,1
...
20720101001,2025-03-21 07:20:00+09:00,37.63088791,138.973589,2

アニメーション化用のプログラムを以下に示します。前回作成したデータを1分間隔に丸め込み、位置情報を線形補間しました。なぜこのようなことをしたかと言いますと、Mobmap Webを使ったときと比べてバスの動きに滑らかさがなかったためです。

bus_folium.py

import pandas as pd
import folium
from folium.plugins import TimestampedGeoJson

# CSV読み込み
df = pd.read_csv('0321_echigo_tsubame_sanjo_gtfs_realtime_data.csv', parse_dates=['timestamp'])

# タイムゾーンを削除し、60秒間隔で時刻を丸める
df['timestamp'] = df['timestamp'].dt.tz_localize(None)
df['rounded_timestamp'] = df['timestamp'].dt.round('60s')

# 補間処理(丸め後)
interpolated_rows = []

for bus_id, group in df.groupby('id'):
    group = group.drop_duplicates('rounded_timestamp').set_index('rounded_timestamp').sort_index()
    group_resampled = group.reindex(pd.date_range(group.index.min(), group.index.max(), freq='60s'))

    group_resampled['id'] = bus_id
    group_resampled['route_id'] = group['route_id'].ffill()
    group_resampled[['latitude', 'longitude']] = group_resampled[['latitude', 'longitude']].interpolate()
    group_resampled = group_resampled.dropna(subset=['latitude', 'longitude', 'route_id'])

    # interpolated_rows.append(group_resampled.reset_index().rename(columns={'index': 'timestamp'}))
    interpolated_rows.append(group_resampled.reset_index().rename(columns={'index': 'rounded_timestamp'}))

df_interpolated = pd.concat(interpolated_rows).reset_index(drop=True)

# route_idごとの色設定
color_dict = {0.0: 'red', 1.0: 'blue', 2.0: 'yellow'}

# GeoJSON用データ展開
expanded_rows = []
for bus_id, group in df_interpolated.groupby('id'):
    group = group.sort_values('timestamp').reset_index(drop=True)
    for i in range(len(group)-1):
        current_row = group.iloc[i].copy()
        current_row['next_timestamp'] = group.iloc[i+1]['timestamp']
        expanded_rows.append(current_row)

df_expanded = pd.DataFrame(expanded_rows)

# GeoJSON生成
def df_to_geojson(df):
    features = []
    for _, row in df.iterrows():
        color = color_dict[row['route_id']]
        feature = {
            'type': 'Feature',
            'geometry': {
                'type': 'Point',
                'coordinates': [row['longitude'], row['latitude']],
            },
            'properties': {
                'times': [
                    row['timestamp'].strftime('%Y-%m-%dT%H:%M:%S'),
                    row['next_timestamp'].strftime('%Y-%m-%dT%H:%M:%S')
                ],
                'icon': 'circle',
                'iconstyle': {
                    'fillColor': color,
                    'color': color,
                    'fillOpacity': 0.8,
                    'stroke': 'true',
                    'radius': 6
                },
                'popup': (
                    f"バスID: {row['id']}
" f"路線ID: {int(row['route_id'])}
" f"時刻: {row['timestamp'].strftime('%Y-%m-%d %H:%M:%S')}" ) } } features.append(feature) return {'type': 'FeatureCollection', 'features': features} geojson_data = df_to_geojson(df_expanded) # 地図作成 m = folium.Map(location=[df['latitude'].mean(), df['longitude'].mean()], zoom_start=13) # Foliumアニメーション設定 TimestampedGeoJson( geojson_data, period='PT1M', # 60秒間隔 add_last_point=False, auto_play=True, loop=False, max_speed=120, # 2分を1秒に高速再生 loop_button=True, date_options='YYYY/MM/DD HH:mm:ss', time_slider_drag_update=True, duration='PT1M' ).add_to(m) # 凡例表示 legend_html = '''

"position: fixed; bottom: 50px; left: 30px; width: 120px; z-index:9999; background-color:white; padding:10px; border:2px solid grey; border-radius:6px;"> 凡例
"background:red;width:10px;height:10px;border-radius:50%;display:inline-block;"> Route 0
"background:blue;width:10px;height:10px;border-radius:50%;display:inline-block;"> Route 1
"background:yellow;width:10px;height:10px;border-radius:50%;display:inline-block;"> Route 2

''' m.get_root().html.add_child(folium.Element(legend_html)) # HTMLとして保存 m.save('bus_animation.html') print("アニメーションHTML生成完了: bus_animation.html")

そして、新潟県県央地域のバスの1日の動きをパーティクルで表した動画を以下に示します。
燕市と三条市のコミュニティバスは時刻表通りに動いたらという前提の動きになっています。そして越後交通の路線バスは2025/3/21のGTFS-RTデータを処理した動きになっています。なお、可視化した動きは朝7時からの動きです。

なお、対象にしたバス路線は以下になります。各事業者ごとにパーティクルの色を分けています。

  • 越後交通の路線バス:青色
  • 燕市コミュニティバス:赤色
  • 三条市コミュニティバス:黄色

色々と試してみたのですが、あまり滑らかな動きにはできませんでした。

今回は、foliumを用いて、GTFSデータとGTFS-RT(リアルタイム)データを組み合わせ、越後交通および新潟県燕市・三条市のコミュニティバスの1日の運行状況を可視化しました。
ただし、以前使用したMobmap Webと比べると、動きの滑らかさにはやや劣る結果となってしまいました。

今後は、補間方法を工夫するなどしてこの課題に取り組みつつ、foliumを活用したオープンデータの可視化にも引き続き取り組んでいきたいと考えています。

注:

この記事は、以下の著作物を改変して利用しています。

CC BY 2.1(https://creativecommons.org/licenses/by/2.1/jp/deed.ja)

CC BY 4.0 https://creativecommons.org/licenses/by/4.0/legalcode.ja



フラッグシティパートナーズ海外不動産投資セミナー 【DMM FX】入金

Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -

Most Popular

Recent Comments