複数のGPSログからイイ感じな地図を生成する

はじめに

自転車旅行やトレッキングのような自分の足で移動するのが好きだ。TV番組でいうと水曜どうでしょうが好きなタイプ。観光地や名所などの特定のスポットに行くのも良いけど、個人的には移動そのものが好きだということに気づき位置情報をログとして残すようにしている。その結果大量のGPXファイル(ログデータのファイル)が溜まったので、今までの趣味を振り返るためにも1つの地図上に可視化してみることにしました。

GPSデータを用意する

まず表示する位置情報のデータが必要です。私はスマホやスマートウォッチで日頃から取得しておいてStravaなどで管理しています。最近だと格安かつGPS内臓のスマートウォッチが増えてきているのでおすすめです。

また過去の移動経路を覚えているけど、位置情報のデータがない場合はルートラボ等のWebサービスを利用することでルート情報をGPSで得られるものと同一のログファイルを作成することができます。今回は特にGPX形式の位置情報データを扱います。

Pythonでデータを可視化する

今回は可視化のためにPythonと関連ライブラリを用いて地図を生成します。スクリプトに必要な機能は以下の2点です。

  1. GPXファイルの読み込み
  2. 地図を画像やHTMLとして生成

GPXファイルの読み込み

GPXファイルの扱いですが、gpxpyというライブラリがあるのでこれを使います。

github.com

ライブラリはpipやconda経由で導入可能です。描画には緯度経度がわかれば良いので、単に読み込むだけのサンプルが以下です。

import gpxpy

points = []
gpx_file = open(filename, 'r')
gpx = gpxpy.parse(gpx_file)
for track in gpx.tracks:
    for segment in track.segments:
        for point in segment.points:
            points.append([point.latitude, point.longitude])

画像として地図を生成

次に地図の生成です。最も単純な方法はgpxpyで取得した点をすべてプロットする方法でしょうか。試しにmatplotlibで描画すると下の画像みたいな感じです。大量に位置情報があるのでそれっぽく見えてますが、できたら地図の上に重ねたりズームしたりしたいところです。

f:id:chari_ngo:20181217132303p:plain
matplotlibでGPXファイルを描画

import glob
import matplotlib.pyplot as plt
import gpxpy

lat, lon = [], []

fig = plt.figure(facecolor='black')
ax = plt.Axes(fig, [0., 0., 1., 1.], )
ax.set_aspect('equal')
ax.set_axis_off()
fig.add_axes(ax)

for filename in glob.glob('log/*'):
    gpx_file = open(filename, 'r')
    gpx = gpxpy.parse(gpx_file)
    for track in gpx.tracks:
        for segment in track.segments:
            for point in segmen.points[::50]:
                lat.append(point.latitude)
                lon.append(point.longitude)
    plt.plot(lon, lat, color='cyan', lw=0.5)
    lat, lon = [], []
plt.savefig("result.png", facecolor=fig.get_facecolor(), bbox_inches='tight', pad_inches=0, dpi=300)

イイ感じな地図を生成

画像ではなくHTMLなどでよりリッチな地図が描けないかなと悩んでいたところfoliumというライブラリを見つけました。こちらもpipやcondaで導入可能です。

github.com

一言で言えばGoogleMapのようにマウスで自由に動かせる地図をHTML形式で生成できるライブラリで、たった2行でそんな地図が生成できてしまいます。

map = folium.Map(location=[38.2586, 137.6850], zoom_start=6)
map.save("./map.html")

location が地図の中心, zoom_startが地図の拡大倍率です。

また単純に地図を生成するだけでなく地図に対してマーカや経路などをはじめとしたデータの可視化を簡単に描画することができます。詳細はドキュメントを参照してください。今回は位置情報を線として描画したいのでPolylineというメソッドを使用します。pointsはgpxpyのサンプルで読み込んでいるpointsと同じデータ形式です。

folium.PolyLine(points).add_to(map)

これを使って描画するとこんな感じになります。

f:id:chari_ngo:20181217135546p:plain
foliumでGPSデータを可視化

ソースコードはこんな感じです。マップタイルを変更できるように序盤で設定している以外は先程紹介したgpxpyとfoliumのサンプルを組み合わせるだけです。4重ループがあり頭を抱えたくなりますが、頻繁に実行するスクリプトでもないので今回は目を瞑ってください…。

import glob
import gpxpy
import folium

points = []
# create map
map = folium.Map(location=[38.2586, 137.6850], zoom_start=6)
# add map tiling options
folium.TileLayer('Mapbox Bright').add_to(map)
folium.TileLayer('cartodbdark_matter').add_to(map)
folium.TileLayer('openstreetmap').add_to(map)
folium.LayerControl().add_to(map)
# draw log data
for filename in glob.glob('log/*'):
    gpx_file = open(filename, 'r')
    gpx = gpxpy.parse(gpx_file)
    for track in gpx.tracks:
        for segment in track.segments:
            for point in segment.points:
                points.append([point.latitude, point.longitude])
    folium.PolyLine(points).add_to(my_map)
    points = []
map.save("./map.html")

できた地図を眺める

ここ数年で自転車旅行したルートを可視化してみました。Github Pagesにアップしたのでぜひ見てください。

https://ryoheinagao.github.io/bikelog/

例えば日本列島全体を見てみましょう。淡路島・佐渡ヶ島能登半島などは海岸をなぞるとくっきりとその形が現れています。一方でルートが引かれていない中部地方や九州地方はあまり行っていないことが地図からもわかります。数万kmは自転車で国内を旅行して大体の場所を知り尽くした気になっていましたが、まだまだ行ったところの無い場所が日本にたくさんあるということが実感できます。

f:id:chari_ngo:20181217141050p:plain
全国バージョン

自転車で走ってめちゃくちゃしんどかった長野県の付近をズームしてみると、道がかなりうねうねして険しい山岳であることも位置情報のログから読み取ることもできます。

f:id:chari_ngo:20181217141720p:plain
長野県のあたりは山しかなくて熱中症でぶっ倒れた記憶

北海道のオホーツク海沿いなどは非常に美しい直線です。だんだん書道のように感じてきました。

f:id:chari_ngo:20181217142752p:plain
実際はずっと向かい風で死にそうになったオホーツク海沿いの道

おわりに

いままでの位置情報ログをPythonを使って可視化することができました。自転車旅行のログを可視化することで当時の辛さや景色を思い出すことができ再訪したくなったり、まだ行ったことのない場所を発見できました。是非旅行好きの人同士で地図にログを可視化して、互いに見せあったり当時の思い出を語って欲しいです。