ElasticSearchに入れたGISデータをGeoServer経由でQGISで表示する

## 概要
ElasticSearchに入れたGeoJSONデータをGeoServerで配信し、QGISで表示するのを試してみました。

以下のアプリをDockerを使ってセットアップしていきます。
- GeoServer 2.16.2
- ElasticSearch 7.9.3
- Kibana 7.9.3

## Dockerのセットアップ
Dockerがインストールされていない場合は、まず[公式サイト](https://www.docker.com/get-started)に従って、インストールしてください。

## GeoServerとElasticSearch、Kibanaを起動

### 起動方法
まず、本記事で扱う一連のファイルを[JinIgarashi/docker-geoserver-elasticgeo](https://github.com/JinIgarashi/docker-geoserver-elasticgeo)に置いてあります。

以下のようにするとGeoServerなど一連のアプリが立ち上がります。

```
git clone git@github.com:JinIgarashi/docker-geoserver-elasticgeo.git
cd docker-geoserver-elasticgeo
docker-compose up
```

### Dockerで起動するアプリについて
docker-composeで起動すると、GeoServerは8600ポート、ElasticSearchは9200ポート、Kibanaは5601ポートで立ち上がります。

GeoServerの設定は[kartoza/docker-geoserver](https://www.google.com/search?client=safari&rls=en&q=Kartoza%E3%80%80docker-geoserver&ie=UTF-8&oe=UTF-8)のデフォルトの`.env`と`docker-compose.yml`から流用しており、ElasticSearchの設定は公式のものをそのまま使っていますが、ノード数は一つだけにしています。

但し、GeoServerに[ElasticGeo](https://github.com/ngageoint/elasticgeo)というエクステンションを入れる必要があるため、独自にDockerfileを以下のように作っています。

```
FROM kartoza/geoserver:2.16.2

RUN wget https://github.com/ngageoint/elasticgeo/releases/download/2.16.0-RC1/elasticgeo-2.16.0.zip -O elasticgeo.zip
RUN unzip -o elasticgeo.zip -d /usr/local/tomcat/webapps/geoserver/WEB-INF/lib/

EXPOSE 8080
```

ElasticGeoのjarファイルをGeoServerの`WEB-INF`フォルダに置いてあげるだけでインストールは終了です。なお、ElasticGeoは現時点で最新のGeoServer2.18では動きませんので、2.16.2を使います。

## ElasticSearchの確認
ターミナルで以下のように実行して結果が返って来ればElasticSearchが無事インストールされています。

```
$ curl http://localhost:9200
{
  "name" : "es01",
  "cluster_name" : "es-docker-cluster",
  "cluster_uuid" : "W_ho6j3NSZWQvuL7CfNnXg",
  "version" : {
    "number" : "7.9.3",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "c4138e51121ef06a6404866cddc601906fe5c868",
    "build_date" : "2020-10-16T10:36:16.141335Z",
    "build_snapshot" : false,
    "lucene_version" : "8.6.2",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
```

## KibanaでGeoJSONをインポート

### ブラウザでKibanaを開く
`http://localhost:5601`にアクセスします。下みたいな画面がでればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/4042fcdb-0113-8aa3-21a8-4858a14d60fa.png)

### GeoJSONをElasticSearchにアップロードします
ここでは、私がボランティアで一緒に仕事をしているルワンダの水道公社の水道メーターのポイントデータ５万件超をアップロードしてみます。

左上のメニューを開いて`Map`を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/81d65153-8145-0a66-a9e7-c1855c65f680.png)

`Add layer`をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/39f16361-cbea-4076-1e88-968b3225e4d8.png)

`Upload GeoJSON`をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/41f38009-8fc3-4c8c-bd0a-c004b7a5c937.png)

`Select a file to upload`のスペースにお手持ちのGeoJSONをドラッグ&ドロップすると、自動で読み込まれますので`Import file`を続けてクリックするとインデックスの作成が始まります。

`Add layer`ボタンに変わったら続けてクリックします。Layer Settingsに適当に入力してSaveします。私は名前だけ`rw_connections`として後はデフォルトのままにしました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/a6f68575-4560-9df2-4def-5eb804dd0a16.png)

そうすると次のような感じでKibanaで表示できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/c863c650-9128-c3b3-d784-1bb954dd2cef.png)

#### KibanaのGeoJSONの最大サイズは50MBまで
Kibanaの最大サイズの制限50MBを超える場合は`ogr2ogr`で下みたいなコマンドでやるとインポートできます。但しogr2ogrでインポートした場合はKibanaでインデックスパターンを自分で作ってあげないといけません。

```
ogr2ogr -progress -lco BULK_SIZE=5000000 -f "Elasticsearch" http://localhost:9200 connections.geojson -skipfailures
```

## GeoServerの設定
いよいよデータの準備ができたのでGeoServerの設定をします。

`http://localhost:8600/geoserver`にアクセスします。お馴染みの画面が出てきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/a258b9d8-ad28-46c8-fcfa-78e60d711302.png)

以下のデフォルトのユーザーIDとパスワードでログインします。

- ID: admin
- Password: myawesomegeoserver

ワークスペースを作ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/d7e4329f-24c7-03b3-1e6a-6d17ac81c01f.png)

ストアを作ります。ElasticGeoが設定されているので、`Vector Data Sources`にElasticsearchという選択肢が出ていますので、それを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/7ae98a25-a2c7-d31e-c4ef-7c55d45077bb.png)

Kibanaでアップロードしたインデックスの情報を入力します。

- Data Source Name: 任意の名前をつけます
- elasticsearch_host: Dockerのコンテナ名の`es01`を入れます
- elasticsearch_port: 9200を入れます
- index_name: connectionsとします

それ以外はデフォルトのままで、Saveをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/dfb4abbf-f409-bf04-c45e-58753c75d56b.png)

次に、ElasticSearchのレイヤを追加します。きちんとストアの設定ができていれば、以下のような画面が表示されますので、`Publish`をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/f9f04e2d-d721-a94d-926c-f83a687c5239.png)

レイヤの編集画面になりますが、今回は全てデフォルトのままでBBOXだけ設定してあげてから、レイヤを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/349031ca-1cee-52ee-7004-83f09f29db05.png)

レイヤができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/0f1719f8-a218-516c-2a90-a8fa073afab2.png)

Layer PreviwでできたGeoServerレイヤを見てみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/6d8eab1f-4595-a51a-73a5-f6431c60007e.png)

OpenLayersをクリックすると地図が出ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/e47527db-004b-4daf-2888-19a3e7370355.png)


## QGISでデータを表示する
GeoServerでElasticSearchのデータを配信できるようになったので、[QGIS](https://qgis.org)で表示してみます。

今回は現時点で最新のQGIS3.16を使います。

QGISを起動したら、ブラウザパネルの`WFS / OGC API`で右クリックして`新しい接続`をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/f2e6e8de-5304-9599-f17d-b1d33beb9f02.png)

- 名称に`elasticsearch`
- URLに`http://localhost:8600/geoserver/elasticsearch/wfs`
- バージョンに`1.0.0`
- 地物の最大数に`10000`
のように記入し、OKをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/abe45de5-e9e2-a94c-ba7e-3d13ff406c45.png)

WFSのURLがわからなかったら、下のようなURLをブラウザで開くと`OnlineResource`というところで指定するURLを確認できます。
http://localhost:8600/geoserver/elasticsearch/ows?service=WFS&version=1.0.0&request=getcapabilities

```xml
<Service>
<Name>WFS</Name>
<Title/>
<Abstract/>
<Keywords/>
<OnlineResource>http://localhost:8600/geoserver/elasticsearch/wfs</OnlineResource>
```

ブラウザパネルに`connections`レイヤが表示されました。`connections`をダブルクリックしてレイヤを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/bd08d6cf-0621-d58a-08e2-e35086a1e119.png)

QGIS3.16でWFSレイヤをちゃんと表示できました。Kibanaでは表示した地物の情報をポップアップなどでは見れませんが、GeoServer経由でQGISで表示させてあげるとちゃんと属性情報も見れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/858331/b0606c0e-8ef0-ab50-5ce9-169f8c1a0d88.png)

## まとめ

- ElasticSearchに投入したGISデータはGeoServerにElasticGeo拡張を入れることで、WFS/WMS配信できる
- WFS/WMSの配信方式に乗っかってQGISで表示できる
- QGIS以外でもLeaflet、OpenLayers、Mapbox GL JSなどでもElasticSearchのデータをGeoServer経由で扱える（本記事の対象外）

ElasticSearchでのGISデータの扱いが、GeoServerを使うことでより身近になると感じました。