#はじめに
ただ発作的にデータ可視化で位置情報をKibanaで使ってみたかっただけです。
実験なので、セキュリティなどはあまり気にしていません。
#前提環境
- Xperia XZ1 (Android9)
- Google Playより「Termux」と「Termux:API」をインストール後、Node-REDとtermux-apiをインストール済み。
- 外部からアクセス可能なElasticsearchとKibanaの環境(ver7.2)
- 私はRaspberry Pi 3B+で構築して、serveoでElasticsearchのポート(9200)を外部からアクセス可能にしてます。
#Node-REDでのフロー作成
Elasticsearchにデータ投入する前に、インデックスの作成とマッピングをします。
curlでも良いですが、意地でもNode-REDからボタン一つでできるようにしましょう。(1回しか実行しません)
AndroidスマホのNode-RED上に作成したフローは以下です。
フローを書き出した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に投入するフローを作ります。
[{"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で可視化
あとは、「Maps(マップ)」か「Visualization(可視化)」の「座標マップ」で可視化しましょう。
Kibanaの初期状態だと地図が表示されないので、以下の設定をKibanaの設定ファイルに追記してください。
: (略)
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モードにしておきます。
私は10分間隔でElasticsearchにデータ投入していますが、急激にバッテリーを消耗する事はないです。
この方法のメリットとしては、スマホキャリアの付加サービスや、Googleを使わずに位置情報追跡ができます。
SIMなしスマホをコンビニなどのFreeWifiへ自動接続するようにして、GPSではなくWifiからの位置情報を送るようにできれば、眠ってるAndroidスマホで遊べるかなぁ?と考えてます。→接続回数制限やスポットがあまり無い、範囲が狭いなどで使い物になりませんでした。(※2019/8/23追記)
【想定される利用シーン】
・どこに行ったか可視化して自慢したい人
・そもそも自分がどこ行ったか覚えてない人
・徘徊癖のあるご老人がいる
・放浪癖のあるお子様がいる
・「出張だ」「女友達と旅行だ」と言い張るパートナーがいる