Google Maps Timelineのデータから任意の期間のロケーション履歴を抽出する

本記事は[品川Advent Calendar 2019](https://qiita.com/advent-calendar/2019/shinagawa)の10日目です。

こんにちは。品川の民10人目です。
普段ほとんどコードを書く機会ないんですが、お誘い頂いたので頑張って書きます。

##はじめに
みなさんはGoogle Maps Timeline使ってますか？便利ですよね。
私も飲み会帰りに身に覚えのない場所で気がついた時、どこを彷徨ってたのか確認するのに使ってます。

さて、この機能を使ってると今までの行動ログが全部Googleのアカウントに蓄積されているわけですが、特定期間のログを抽出するような機能はGoogle側では用意されていないようです。
([1日分だけダウンロードする機能はあるみたいです](https://somegoro.hatenablog.com/entry/2018/02/27/144323))
旅行中の行動履歴をまとめて見たかったので、今回は全ログデータから特定の期間(日付)のログのみをpythonで抽出し、マイマップにインポートする方法について書いてみます。

##Google Maps Timelineから全てのロケーション履歴を取得する
まずは以下から自分のGoogleアカウントに蓄積されたロケーション履歴をダウンロードします。
https://takeout.google.com/settings/takeout
<img width="656" alt="Screen Shot 2019-12-09 at 23.59.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540268/d53d46e8-24b4-700e-3efa-0655664a6b9a.png">
今回は[kmlファイル](https://developers.google.com/kml)を使います。
溜めてた期間にもよりますが、以下のような数M〜数十Mbytesの.kmlファイルがダウンロードされると思います。

```xml:Location&nbsp;History.kml
<?xml version='1.0' encoding='UTF-8'?>
<kml xmlns='http://www.opengis.net/kml/2.2' xmlns:gx='http://www.google.com/kml/ext/2.2'>
 <Document>
  <Placemark>
   <open>1</open>
   <gx:Track>
    <altitudeMode>clampToGround</altitudeMode>
    <when>2019-11-08T14:56:29Z</when>
    <gx:coord>120.28586750000001 22.6225019 9</gx:coord>
    <when>2019-11-08T15:04:28Z</when>
    <gx:coord>120.2858856 22.622469799999998 9</gx:coord>
    <when>2019-11-08T15:12:40Z</when>
    <gx:coord>120.28590349999999 22.6224629 9</gx:coord>
    ...
  </Placemark>
 </Document>
</kml>
```

&lt;gx:track&gt;以下の&lt;when&gt;と&lt;gx:coord&gt;のペアがそれぞれ日時と緯度経度の座標を表しています。
日時はどこで取得したデータかに関わらずGMT(UTC+0)なので注意が必要です。

なお、このkmlファイルには、Timeline機能を有効にしてからの全てのロケーション情報が入ってます。
自宅や職場の場所など容易に特定できるので取り扱いには気をつけましょう。

##必要な期間のデータだけ切り出す

取得したkmlファイルからPythonでいい感じに必要な期間分のデータだけ抜き出します。
今回は今年9月に行ったヨーロッパ・アフリカツーリングのログを抜き出してみます。

```python:location_extractor.py
import xml.etree.ElementTree as ET
import bisect
import datetime
import pytz
from dateutil import parser

#入出力ファイル設定
INPUT_FILENAME='location.kml'
OUTPUT_FILENAME='out.kml'

#切り出す期間の設定
TIMEZONE=pytz.timezone('Europe/Madrid')
BEGIN_DATE = TIMEZONE.localize(datetime.datetime(2019, 9, 9))
END_DATE = TIMEZONE.localize(datetime.datetime(2019, 9, 22))

#kmlをElementTreeにパース
def parse_kml(file_name):
    tree = ET.parse(file_name)
    ET.register_namespace('', 'http://www.opengis.net/kml/2.2')
    ET.register_namespace('gx', 'http://www.google.com/kml/ext/2.2')
    return tree

#タイムスタンプリストから指定期間のインデックスを取得
def search_begin_end_index(times):
    begin_index = bisect.bisect_left(times, BEGIN_DATE)
    end_index = bisect.bisect_left(times, END_DATE)
    return (begin_index, end_index)

#<when>,<gx:coord>を削除
def remove_children(target):
    for item in target.findall('{http://www.opengis.net/kml/2.2}when'):
        target.remove(item)

    for item in target.findall('{http://www.google.com/kml/ext/2.2}coord'):
        target.remove(item)

tree = parse_kml(INPUT_FILENAME)

#タイムスタンプと座標をリストに読み込み
times = list(map(lambda item: parser.parse(item.text), tree.getroot().iter('{http://www.opengis.net/kml/2.2}when')))
locations = list(map(lambda item: item.text, tree.getroot().iter('{http://www.google.com/kml/ext/2.2}coord')))

(begin_index, end_index) = search_begin_end_index(times)

#treeから<gx:Track>以下の<when>,<gx:coord>要素を削除
target = tree.getroot().find('{http://www.opengis.net/kml/2.2}Document/{http://www.opengis.net/kml/2.2}Placemark/{http://www.google.com/kml/ext/2.2}Track')
remove_children(target)

#treeの<gx:Track>以下に指定期間の<when>,<gx:coord>を挿入
for index, (when, coord) in enumerate(zip(times[begin_index:end_index], locations[begin_index:end_index])):
    when_element = ET.Element('when')
    when_element.text = when.strftime('%Y-%m-%dT%H:%M:%SZ')
    target.insert(1 + (index * 2), when_element)

    coord_element = ET.Element('gx:coord')
    coord_element.text = coord
    target.insert(2 + (index * 2), coord_element)

#出力ファイル書き出し
tree.write(OUTPUT_FILENAME)
```

実行したところ1分ほどで以下が出力されました。
パースしたxmlに改行が入ってなくて汚いですね。実用上問題はないようですがいずれ直します。

```xml:out.kml

<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2">
 <Document>
  <Placemark>
   <open>1</open>
   <gx:Track>
    <altitudeMode>clampToGround</altitudeMode>
    <when>2019-09-09T06:27:59Z</when><gx:coord>-3.5806324999999997 40.4715565 618</gx:coord><when>2019-09-09T06:34:06Z</when><gx:coord>-3.6233677 40.4731105 686</gx:coord>...
  </Placemark>
 </Document>
</kml>
```
##マイマップへのインポート

<img width="339" alt="Screen Shot 2019-12-09 at 23.44.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540268/d9811262-2059-6620-cee7-19506c3be09a.png">
出力したout.kmlをマイマップでインポートします。

<img width="1694" alt="Screen Shot 2019-12-09 at 23.49.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540268/432814c5-f701-1d5e-9cd1-2d3a412e7da1.png">
うまくいきました。
(線の色と太さはデフォルトから変更してます)
https://drive.google.com/open?id=1M7CjYvrfWMNc030CF5s3SeT8L_0PKCme&usp=sharing

##おわりに
Timeline機能を使っていると、GPSロガーや特別なアプリを用意しなくても気軽にロケーション履歴が勝手に取れてて楽しいです。
海外旅行・ツーリング・お遍路などのログデータを抽出して楽しんでみてはいかがでしょうか。

###謝意
この記事の執筆にあたり、@kimullaaさんに一杯助けてもらいました。
ありがとうござました。