Elastic Stack でジオフェンシングをやってみた

## Elasticsearch でジオフェンシング

ご存知の方も多いかもしれませんが、Elasticsearch はジオ情報（GPSの座標など）も扱えます。ジオ情報を使って実現したい事は様々だと思いますが、今日はElasticsearch を使ったジオフェンシング（例として、指定した領域や範囲内にデータが入った際にアラートをあげる等のアクションを行う事）についてお話ししたいと思います。

![geof04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/4d74a7b4-df45-2954-8b54-4341625efe4f.png)

## 前提

今回は Elastic Cloud 上のデプロイメント、バージョン 8.5.2 で実装等を行いました。

下記のページにある通り、ジオフェンシング（Tracking containment rule type）の機能は最低でも Gold サブスクリプションが必要になるのでご注意ください。
https://www.elastic.co/subscriptions/cloud


:::note warn
私の環境では、デプロイメントが Enterprise レベルの機能まで使えてしまうため、文中で意図せずに Gold サブスクリプション以上の機能を使ってしまっている可能性があります。
:::

## 概要

今回はジオフェンシングを試した時（デモ）の手順を残していこうと思います。

今回使った主な物は

- データ生成スクリプト
- Elastic maps + Dashboard
- トラッキングコンテインメントアラート

スクリプトは自作の ruby スクリプトで、定期的に `geo_point` を含めたデータを生成しています。 スクリプトは[こちら](https://gist.github.com/tjws052009/cd960e9e2d12d6c94e198dcd439253de) に共有しています。インデックスを削除したりしているので、実行時はご注意ください。

Elastic maps や ダッシュボードは可視化のために使っています。geo_point 等のジオ情報の入ったインデックスのデータを地図上に可視化できます。その地図をダッシュボードにも埋め込めるので、グラフとジオ情報をまとめて表示・分析できるようになります。

トラッキングコンテインメントアラートが、ジオフェンシングに使ったアラートです。 geo_shape の入ったインデックスのデータを境界線として（データは更新されない想定）、指定した geo_point データが境界線内に入った場合にアラートをあげます。

これらを組み合わせてジオフェンシングを試した時の手順を簡単にまとめてみました。

## 手順

### 1) データの準備

まずはデータを入れないと何も始まらないので、[スクリプト](https://gist.github.com/tjws052009/cd960e9e2d12d6c94e198dcd439253de)  でやっている事を簡単に説明します。詳細はコードを見てみてください。

作成しているデータの想定としては、移動する複数の車があり、それらの名前、GPS情報、センサー情報等を定期的に取得しているといったシナリオです。

- デモで使うインデックスを削除
- 今回使うインデックスを作成。マッピングは以下の通りです。一番重要なのは `location` でその他はデータをまとめたり、見た目を華やかにするために入れています。
 
フィールド名 | 型
--- | ---
`@timestamp` | `date`
`name` | `text` + `keyword`
`location` | `geo_point`
`battery` | `double`
`status` | `text` + `keyword`

- Kibana のデータビューを作成
- データポイントの作成
  - それっぽくデータを生成しつつ、デモのしやすさのために、ある程度の直線を動くようにしたり、データが皇居周辺から出ないように範囲を指定したりしています。

\# このスクリプトの動作保証などは出来ないので、実行する際は自己責任でお願いします。

### 2) Maps 上にデータを表示

Maps を使ってデータを表示する方法について少し話します。

今回はシンプルに、レイヤーをいくつか追加して、データを地図上に可視化します。派手な集計や地図をカスタマイズしたりといった事はやりません。

Maps でデータを使うには、geo_point や geo_shapes がマッピングされたデータビューが必要です。それがないと、ドロップダウンで選択しても、バリデーションで止められてしまい先に進めません。

まずは Kibana のメニューから Maps を選択し、"Create map" をクリックします。

世界地図が表示されるので、右上にあるレイヤーの凡例？の下にある "Add layer" をクリックします。

![maps01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/d2c3ac74-2b30-1804-c015-ba660551c9d3.png)

様々なレイヤーの種類が選択できますが、今回は "Top hits per entity" を使います。

![maps02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/a3fe994d-eb6d-0bf2-9dbc-1d5054481e9b.png)

あるエンティティ毎にデータをプロットできるので、今回は name 毎の最新のデータを表示するレイヤーを作ります。

![maps03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/749ae48c-b788-db30-5992-4a150a54749e.png)

ひとまずレイヤーを作成しので、データを見てみます。このままではデータの見栄えが寂しいので、データポイントのアイコンを車のアイコンに変えたり、名前毎に色を変えてみます。

![maps04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/b7c200f6-20c3-bc2b-8100-76bb3525709f.png)

![maps05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/35c7397e-bb30-5451-29d2-fda8dad299f0.png)

こんな感じにプロットされます。

![maps06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/8bc39405-1a5f-8d69-6686-0981b148bb59.png)

それなりの頻度でデータが入るのであれば（今回のデータ生成スクリプトでは２秒おき）、自動リフレッシュを使えばリアルタイムっぽく描画できているように見えます。右上のデートピッカーから自動リフレッシュが設定できます（ただし、頻度を高くしすぎて負荷がかかりすぎると、重くなってリロードが追いつかなくなるので注意）。

![maps09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/5cda7f64-0646-1f50-66b0-2d120da04783.png)

今のレイヤーだけだと、その時点のデータしか見れないので、その車がどの方向に進んでいるのか見ただけではわかりません。タイムスライダーの機能等を活用するとアニメーションで見れたりもしますが、今回は実際に地図上にデータポイントがどのように移動したかを描画しようと思います。

先程と同様 "Top hits per entity" のレイヤーを作成します。

今回は "Documents per entity" を増やし、過去のデータポイントも地図上に描画します。それでデータが進んだ軌跡を表現します。

![maps07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/2d672f36-1558-ddaa-1793-fd5731018975.png)

あとは先程と同様、見た目を調整します。（詳細は割愛します）

今回は２つのレイヤーを作ったので、凡例に２つのレイヤーが表示されているはずです。

![maps08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/3c9bd5a4-d906-e6df-94b2-c5cf3d43f5ff.png)

最終的には、 このような見た目になります。

![maps10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/4dc3c08a-c178-f388-4b2e-a6f3b254be0e.png)

車のアイコン（現在地）とその車がたどった経路（丸いアイコン）を表現した Maps の完成です。



### 3) ジオフェンシングのためのアラート設定

このままでは、車が走り回るだけの Maps の可視化なのですが、一歩踏み込んで、ジオフェンシングでアラートを実装してみます。それもすべて Kibana の画面で完結させる事ができます。

流れは以下の通りです。

1. "Create Index" レイヤーを作成
1. 任意の geo_shape を地図上に描く
1. Kibana の Stack Management > Rules and Connectors からトラッキングコンテインメントアラートルールを作成

最初に Maps の "Add layer" をクリックし、"Create index" という名のレイヤーを追加します。このレイヤーではお絵描きツールを使って任意のgeo_shape を描いて、インデックスできる機能です。

![geof01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/aa79c749-f5a5-8e44-b4e4-88646ed5e992.png)


この通り、インデックス名を指定する事になり、そこにデータが作成されていきます。

![geof02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/d856c002-ed9a-2f5e-20a6-86bbf402b36b.png)


レイヤーを作成すると、画面左側にツールバーが表示され、その中に描画ツールもいくつか並んでいます。

![geof03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/da751d78-0d6d-6927-5400-f5ff724b2e8e.png)


今回は皇居の輪郭をなぞりたいので、六角形のアイコンの ”Draw polygon” を選択しました。

![geof04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/2f0efb55-0cdf-78aa-8044-f38c8e3c94d4.png)!

間違えて完了してしまったり、画面を遷移してしまった場合は、レイヤーのメニュー（LAYERS の凡例から、該当するレイヤーの左側のアイコンをクリック）から、 "Edit features" を選択すると引き続けてお絵かきできます。

![geof05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/c0efa148-61e3-bb8a-be09-59f722d9cf57.png)


実際にデータが入ったインデックスを Discover で見ると、こんな感じです。

![geof06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/7cfd3de1-8171-01cc-914f-d7af487b8db4.png)

アラートをしたい境界線が描けたので、次はその境界線を使ったアラートを作成します。
Kibanaのメニューから Stack Management > Rules and Connectors に行きます。

![geof07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/2b6a8483-5402-ee3c-8a70-4eb38e70049e.png)

[](!geof08.png)![geof08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/86dfc731-02cd-ee42-583b-aa7db49319a0.png)

"Create rule" をクリックし、新しいルールを作成します。名前やチェック頻度等の設定も必要ですが、重要なのはルールタイプに "Tracking containment" を選択する事です。

![geof09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/c7b696b2-2679-97ed-a914-58de1b2c0d01.png)

選択すると、いくつか指定項目が出てきます。 "Select entity" と "Select boundary" です。
"Select entity" では、どのデータを監視するか、"Select boundary" では境界線の情報が入ったデータビューを指定します。

![geof10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/2b58a449-f92c-0644-cb35-fd750bec4a08.png)

次にアクションを設定します。さきほど設定した監視で何かがひっかかった際に、行うアクションの設定です。「アラートの状態が変化した時」、「アラートが解除された時」など、アラートを発報するタイミングを指定できます。サブスクリプションのレベルによって設定できるアクションの送り先が違いますのでご注意ください。

![geof11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/887ebd7d-04d9-5e16-5374-f5905c22a6b0.png)

今回はインデックスにドキュメントを書き込むようにします。初めて使う場合は、Connector を作成します。どのインデックスにアラートを書き込むか設定できます。

実際に書き込むドキュメントの内容も指定出来るのですが、テンプレート変数を使うとアラートの情報をドキュメントに埋め込む事も可能です。今回はこのようにいくつかアラートの詳細情報を残すようにします。最後に"Save" を押して保存します。

![geof12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/48042c44-f266-ae73-58fb-96bd942b433b.png)

アラートを作成すると、有効化されていると思いますが、アラートの一覧から有効・無効の確認ができます。他にも役にたつ情報がアラートの一覧や詳細にありますので運用のヒントに使えると思います。

では、実際に Maps を見ながらアラートが正しく発報されるか少し様子を見てみます。

![geof13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/8c452774-5f6e-068e-4ca4-a3cd24d362c9.png)

このようにデータがアラートの境界線内に入ったのを Maps で確認できたら、Discover で先ほど指定したアラートの保存先のインデックスの中を見てます。該当するデータがアラートに入っていたら成功です。

![geof14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2599974/3c73b725-8d6a-4abd-ba30-eb846ed3ca1f.png)



## まとめ

以上、Maps を使ってジオフェンシングを実装してみた話です。サブスクリプションレベルによっては実現できない場合もありますが、Elastic Stack で実現できる事の参考になれば幸いです。