Androidスマホの位置情報をKibanaで可視化してみる

#はじめに
ただ発作的にデータ可視化で位置情報をKibanaで使ってみたかっただけです。
実験なので、セキュリティなどはあまり気にしていません。

#前提環境
- Xperia XZ1 (Android9)
 - Google Playより「[Termux](https://play.google.com/store/apps/details?id=com.termux&hl=ja)」と「[Termux:API](https://play.google.com/store/apps/details?id=com.termux.api&hl=ja)」をインストール後、[Node-REDとtermux-apiをインストール済み](https://nodered.jp/docs/getting-started/android)。
- 外部からアクセス可能なElasticsearchとKibanaの環境（ver7.2）
 - 私は[Raspberry Pi 3B+で構築](https://qiita.com/Y-Shikase/items/7e6025855454c443f620)して、serveoでElasticsearchのポート(9200)を外部からアクセス可能にしてます。

#Node-REDでのフロー作成

Elasticsearchにデータ投入する前に、インデックスの作成とマッピングをします。
curlでも良いですが、意地でもNode-REDからボタン一つでできるようにしましょう。（１回しか実行しません）

AndroidスマホのNode-RED上に作成したフローは以下です。
![スクリーンショット 2019-08-07 22.23.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/60668b24-c59a-51b9-5242-b75ae18de239.png)

フローを書き出したJSONは以下です。プロトコルやホスト名、認証などはご自身の環境に合わせて設定してください。

```json
[{"id":"cacfa802.88f3a8","type":"inject","z":"c5952fe0.4ee28","name":"ES mapping","topic":"","payload":"","payloadType":"str","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":110,"y":200,"wires":[["56d7d960.5e17c8"]]},{"id":"24fea173.0312ae","type":"template","z":"c5952fe0.4ee28","name":"data JSON","field":"payload","fieldType":"msg","format":"json","syntax":"plain","template":"{\n  \"mappings\": {\n    \"properties\": {\n      \"location\": {\n        \"type\": \"geo_point\"\n      }\n    }\n  }\n}","output":"str","x":450,"y":200,"wires":[["5e10f9ea.4ca208"]]},{"id":"56d7d960.5e17c8","type":"function","z":"c5952fe0.4ee28","name":"ES Query","func":"\nmsg.method = \"PUT\";\nmsg.url = \"http://your-elasticsearch-hostname:9200/phone_info\";\nmsg.headers = { 'Content-Type' : 'application/json' };\n\nreturn msg;","outputs":1,"noerr":0,"x":270,"y":200,"wires":[["24fea173.0312ae"]]},{"id":"5e10f9ea.4ca208","type":"http request","z":"c5952fe0.4ee28","name":"Elasticsearch","method":"use","ret":"txt","paytoqs":false,"url":"","tls":"","proxy":"","authType":"basic","x":620,"y":200,"wires":[["f9f4539f.95fc6"]]},{"id":"d95561b7.6a71c","type":"debug","z":"c5952fe0.4ee28","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":920,"y":200,"wires":[]},{"id":"f9f4539f.95fc6","type":"json","z":"c5952fe0.4ee28","name":"","property":"payload","action":"","pretty":false,"x":770,"y":200,"wires":[["d95561b7.6a71c"]]}]
```

フローをデプロイしたら、インジェクトノードのボタンを押して、実行します。
通常はElasticsearch側でデータ型を自動判定してくれるのですが、経度緯度の情報は自動判定できないかと思われます。なので、経度緯度情報だけ事前にマッピングします。

次にAndroidスマホの情報(バッテリー残量・温度とGPS情報）をElasticsearchに投入するフローを作ります。

![スクリーンショット 2019-08-07 22.28.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/69b2f436-b186-693a-943d-940e62f2acf5.png)

```json
[{"id":"ac5b09f5.d9f368","type":"inject","z":"c5952fe0.4ee28","name":"","topic":"","payload":"","payloadType":"str","repeat":"600","crontab":"","once":true,"onceDelay":0.1,"x":90,"y":60,"wires":[["e1f33b59.6f6fc8"]]},{"id":"2ecd2b0e.a2d7f4","type":"termux-location","z":"c5952fe0.4ee28","name":"","topic":"","provider":"gps","request":"once","x":230,"y":120,"wires":[["9f12a799.2bf858"]]},{"id":"23cddd43.93b012","type":"template","z":"c5952fe0.4ee28","name":"data JSON","field":"payload","fieldType":"msg","format":"json","syntax":"mustache","template":"{\n    \"date\": \"{{{date}}}\",\n    \"battery\": {{{phone.percentage}}},\n    \"temp\": {{{phone.temperature}}},\n    \"location\": {\n        \"lat\": \"{{{payload.latitude}}}\",\n        \"lon\": \"{{payload.longitude}}\"\n    }\n}","output":"str","x":540,"y":120,"wires":[["70bad6fc.039198"]]},{"id":"70bad6fc.039198","type":"http request","z":"c5952fe0.4ee28","name":"Elasticsearch","method":"use","ret":"txt","paytoqs":false,"url":"","tls":"","proxy":"","authType":"basic","x":710,"y":120,"wires":[["4140b9a5.514438"]]},{"id":"9f12a799.2bf858","type":"function","z":"c5952fe0.4ee28","name":"ES Query","func":"\nmsg.method = \"POST\";\nmsg.url = \"http://your-elasticsearch-host:9200/phone_info/_doc\";\nmsg.headers = { 'Content-Type' : 'application/json' };\n\nvar d = new Date();\nmsg.date = d.toISOString();\n\nreturn msg;","outputs":1,"noerr":0,"x":380,"y":120,"wires":[["23cddd43.93b012"]]},{"id":"c1b15c8.5a0b8a","type":"debug","z":"c5952fe0.4ee28","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":1000,"y":120,"wires":[]},{"id":"4140b9a5.514438","type":"json","z":"c5952fe0.4ee28","name":"","property":"payload","action":"","pretty":false,"x":860,"y":120,"wires":[["c1b15c8.5a0b8a"]]},{"id":"e1f33b59.6f6fc8","type":"termux-battery-status","z":"c5952fe0.4ee28","name":"","topic":"","x":250,"y":60,"wires":[["2c3b2119.e5abde"]]},{"id":"2c3b2119.e5abde","type":"change","z":"c5952fe0.4ee28","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"phone","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":60,"wires":[["2ecd2b0e.a2d7f4"]]}]
```

同じく、プロトコルやホスト名、認証などはご自身の環境に合わせて設定してくださいね。
あと、インジェクトノードでElasticsearchに情報を投げる間隔も好みで調整してください。
Node-REDでの開発は慣れるとなんのことは無いですね。

#Kibanaで可視化

Kibanaでインデックスパターンを作成します。
![スクリーンショット 2019-08-07 22.43.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/0252e53e-1688-aaca-abd0-6bf2da95a609.png)

あとは、「Maps(マップ)」か「Visualization(可視化)」の「座標マップ」で可視化しましょう。
![スクリーンショット 2019-08-07 22.47.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/410957/ae19766c-76b7-a632-0564-a140eba296f5.png)

Kibanaの初期状態だと地図が表示されないので、以下の設定をKibanaの設定ファイルに追記してください。

```text:config/kibana.yml
               : (略)
map.tilemap.url: "https://tiles.elastic.co/v2/default/{z}/{x}/{y}.png?elastic_tile_service_tos=agree&my_app_name=kibana"
map.includeElasticMapsService: false
```
tilemapのサーバを変えたり、ズーム倍率制限を変更したりもできるようです。

AndroidスマホのTermuxはWAKELOCKモードにしておきます。
私は１０分間隔でElasticsearchにデータ投入していますが、急激にバッテリーを消耗する事はないです。

この方法のメリットとしては、スマホキャリアの付加サービスや、Googleを使わずに位置情報追跡ができます。

SIMなしスマホをコンビニなどのFreeWifiへ自動接続するようにして、GPSではなくWifiからの位置情報を送るようにできれば、眠ってるAndroidスマホで遊べるかなぁ？と考えてます。→接続回数制限やスポットがあまり無い、範囲が狭いなどで使い物になりませんでした。(※2019/8/23追記)

【想定される利用シーン】
　・どこに行ったか可視化して自慢したい人
　・そもそも自分がどこ行ったか覚えてない人
　・徘徊癖のあるご老人がいる
　・放浪癖のあるお子様がいる
　・「出張だ」「女友達と旅行だ」と言い張るパートナーがいる