matplotlibで山手線上の駅を移動する点をアニメーションで描写する
🚉 はじめに
このページでは、Pythonとmatplotlibを使って、山手線上の駅を1秒ごとに移動する点でアニメーション表示する方法を紹介します。
山手線の各駅の緯度・経度を使って、仮想的なGPSモックを作り、その位置をリアルタイムにXY平面上へ描画します。
地図は使わず、経度をX軸・緯度をY軸とした単純な平面で山手線の軌跡を視覚化します。
学習用途やアプリのモック開発、可視化ツール作成の出発点としても有効です。
🧭 全体の構成
このプロジェクトは以下の3つのモジュールで構成されています:
-
GPSMock
: CSVから座標を読み込み、1秒ごとに次の座標に進むクラス -
yamanote_line.csv
: 山手線30駅の緯度・経度を記載したデータファイル -
GPSMonitor
: matplotlibを使ってリアルタイムに座標を可視化するアニメーションツール
📍 GPSモックの作成(GPSMock)
🔧 概要
GPSMock
は、CSVファイルから緯度・経度のリストを読み込み、1秒ごとにそれを順に返していくクラスです。リストの末尾まで達したら先頭に戻ってループします。
✅ 主な仕様
-
入力:
緯度,経度
形式のCSVファイル -
内部にすべての座標をリストとして保持
-
1秒ごとにインデックスを進めて現在の座標を更新
-
公開されるプロパティ:
latitude
,longitude
コード
import csv
import time
import threading
class GPSMock:
def __init__(self, csv_file_path: str):
self._coordinates = self._load_coordinates(csv_file_path)
self._index = 0
self._latitude = 0.0
self._longitude = 0.0
self._lock = threading.Lock()
self._stop_event = threading.Event()
if not self._coordinates:
raise ValueError("CSVファイルに有効な座標が含まれていません。")
# 最初の値を設定
self._latitude, self._longitude = self._coordinates[0]
# スレッドで定期更新を開始
self._thread = threading.Thread(target=self._update_loop, daemon=True)
self._thread.start()
def _load_coordinates(self, csv_file_path):
coordinates = []
with open(csv_file_path, newline='', encoding='utf-8') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
try:
lat = float(row[0])
lon = float(row[1])
coordinates.append((lat, lon))
except (ValueError, IndexError):
continue # 無効な行をスキップ
return coordinates
def _update_loop(self):
while not self._stop_event.is_set():
time.sleep(1)
with self._lock:
self._index = (self._index + 1) % len(self._coordinates)
self._latitude, self._longitude = self._coordinates[self._index]
def stop(self):
self._stop_event.set()
self._thread.join()
@property
def latitude(self) -> float:
with self._lock:
return self._latitude
@property
def longitude(self) -> float:
with self._lock:
return self._longitude
if __name__ == '__main__':
gps = GPSMock('yamanote_line.csv')
try:
while True:
print(f"緯度: {gps.latitude}, 経度: {gps.longitude}")
time.sleep(1)
except KeyboardInterrupt:
gps.stop()
スレッドを使って非同期に更新されるため、他のモジュールから安全にアクセスできます。
🗺️ 山手線の駅座標CSV
📝 CSVファイル内容
35.6285,139.7388
35.6350,139.7400
35.6450,139.7470
35.6555,139.7570
35.6663,139.7584
35.6758,139.7635
35.6812,139.7671
35.6917,139.7708
35.6984,139.7730
35.7074,139.7745
35.7138,139.7770
35.7203,139.7780
35.7278,139.7708
35.7322,139.7660
35.7380,139.7575
35.7360,139.7470
35.7330,139.7390
35.7310,139.7280
35.7295,139.7100
35.7210,139.7060
35.7130,139.7030
35.7010,139.7000
35.6900,139.7000
35.6830,139.7020
35.6690,139.7020
35.6580,139.7010
35.6460,139.7100
35.6330,139.7150
35.6250,139.7230
35.6190,139.7280
このように、山手線の駅(高輪ゲートウェイを含む全30駅)の緯度・経度を1行ずつ記載したCSV(例:yamanote_line.csv
)を準備します。
データの取得元や参考資料としてWebの地理座標情報や鉄道APIを使うと、精度の高い座標を得られます。
📊 アニメーションモニタ(GPSMonitor)
🎥 動作概要
GPSMonitor
は GPSMock
から1秒ごとに現在の座標を取得し、matplotlibで点と軌跡をリアルタイムに描画します。
✅ 表示仕様
-
経度(longitude)をX軸
-
緯度(latitude)をY軸
-
現在位置は青い点(scatter)
-
通過した軌跡は線で描画(path)
コード
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from gps_mock import GPSMock # 先ほど作ったGPSMockをimport
import time
class GPSMonitor:
def __init__(self, gps_mock):
self.gps = gps_mock
self.lats = []
self.lons = []
# グラフの初期設定
self.fig, self.ax = plt.subplots()
self.scatter, = self.ax.plot([], [], 'bo') # 現在位置(青丸)
self.path, = self.ax.plot([], [], 'k-', linewidth=1) # 通過軌跡(黒線)
self.ax.set_title('GPS Plot')
self.ax.set_xlabel('lon')
self.ax.set_ylabel('lat')
self.ax.grid(True)
def init_plot(self):
self.ax.set_xlim(139.69, 139.83) # 経度(X軸)
self.ax.set_ylim(35.61, 35.75) # 緯度(Y軸)
self.scatter.set_data([], [])
self.path.set_data([], [])
return self.scatter, self.path
def update_plot(self, frame):
lat = self.gps.latitude
lon = self.gps.longitude
self.lats.append(lat)
self.lons.append(lon)
self.scatter.set_data(self.lons[-1:], self.lats[-1:]) # 最新点
self.path.set_data(self.lons, self.lats) # 全体の軌跡
return self.scatter, self.path
def run(self):
ani = animation.FuncAnimation(
self.fig,
self.update_plot,
init_func=self.init_plot,
interval=1000, # 1秒ごとに更新
blit=True
)
plt.show()
if __name__ == '__main__':
gps = GPSMock('yamanote_line.csv')
try:
monitor = GPSMonitor(gps)
monitor.run()
except KeyboardInterrupt:
gps.stop()
🔍 表示範囲の調整
山手線の範囲に合わせて、プロットのスケールを手動で設定します:
self.ax.set_xlim(139.685, 139.795)
self.ax.set_ylim(35.605, 35.755)
このスケールは東京の山手線の範囲にちょうどよく収まるように調整されています。
🧪 実行方法
pip
でmatplotlib
をインポートする-
yamanote_line.csv
を準備する -
GPSMock
とGPSMonitor
を実装 -
python gps_monitor.py
で起動
実行すると、1秒ごとに点が山手線の駅を順に巡回しながらXY平面上にアニメーションで描画されます。
matplotlibのアニメーションはウィンドウを閉じるまでループするため、停止したい場合は Ctrl + C
かウィンドウを閉じてください。
🔄 応用アイデア
-
駅名のラベル表示
-
駅間に矢印で方向を追加
-
軌道を閉じる(円環状に)
-
地図ベース(foliumやcartopy)への切り替え
-
リアルタイムGPS信号との置換え(実機応用)
この構成は「模擬GPS装置」として非常に柔軟なので、IoTデバイスや配送ルート可視化など、幅広い応用が期待できます。
🏁 おわりに
Pythonとmatplotlibだけで、地図なしでも「移動するGPS」の動きを可視化できるというのはとても楽しい体験です。
視覚的に動きを観察することで、ログの代わりに直感的にトラッキングでき、教育用途や開発のデモにも適しています。
ぜひ、自分の好きなルートや地域のデータに差し替えて、オリジナルの「移動可視化モック」を作ってみてください!