Aurora PostgreSQLでベクトルタイルを扱う

いつの間にかAmazon AuroraのPostgreSQL 10.5互換版がリリースされていました。
これでようやくAuroraでベクトルタイルを扱えるようになったということで、試してみました。

## 経緯

はじめに「Aurora PostgreSQLでベクトルタイルが扱えるのが10.5から」ということの経緯を振り返ってみたいと思います。
特に興味のない方は[構築](#構築)までスキップしてください。

### PostGISでのベクトルタイル

PostGISでは2.4以降、 ST_AsMVT を使うことでベクトルタイルを扱えるようになりました。
ここで1つ注意点があり、MVTがProtoBufでエンコーディングされる仕様のため、PostGISインストール時にprotobuf-c supportを有効にする必要があります。

### Amazon RDSのPostGISサポート

RDS for PostgreSQLではPostGISをサポートしていますが、
RDSはマネージドサービスですので、利用できる拡張機能はRDSでのリリースバージョンに紐付いています。
PostgreSQL 9.6まではPostGIS 2.3系がサポートされており、
PostGIS 2.4はPostgreSQL 10系から対応しました。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.FeatureSupport.Extensions.96x
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/CHAP_PostgreSQL.html#PostgreSQL.Concepts.General.FeatureSupport.Extensions.101x

### RDSのベクトルタイルサポート

ですが、ここにはlibprotobuf-cが含まれていなかったため、PostgreSQL 10.4までは ST_AsMVT が使えない状態でした。

このことについてAWS Developer Forumsで要望が挙がり、開発チームで検討の結果サポートされたのが10.5からとなります。
https://forums.aws.amazon.com/thread.jspa?threadID=277371
https://aws.amazon.com/jp/about-aws/whats-new/2018/10/rds-postgresql-supports-minor-versions/

そしてこの度AuroraでもPostgreSQLの10.5互換がリリースされたため、MVTが使えるようになったことになります。

## 構築

### クラスターの起動

RDSの起動画面から、Auroraエンジンを選択します。
<img width="256" alt="2018-12-18 9.22.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/204890/6d7520af-ceba-7777-519a-89f0e2c756df.png">
画面下のエディションから「PostgreSQL対応」を選択します。
<img width="212" alt="2018-12-18 9.22.08.png" src="https://qiita-image-store.s3.amazonaws.com/0/204890/25d24088-ec31-96f6-2e7c-a56e880a27c9.png">
次の画面へ進み、DBエンジンのバージョンから「compatible with PostgreSQL 10.5)を選択します。
<img width="391" alt="2018-12-18 9.22.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/204890/1bd05f0c-0c9e-f165-d11b-08ae2b00df17.png">
あとは環境に合わせてユーザ名・クラスター名等を設定していき、作成します。

### 拡張機能の読み込み
PostGISの拡張機能を有効にします。
手順は下記AWSのドキュメントにある通りです。
https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.html#Appendix.PostgreSQL.CommonDBATasks.PostGIS

```sql
create extension postgis;
create extension fuzzystrmatch;
create extension postgis_tiger_geocoder;
create extension postgis_topology;

alter schema tiger owner to rds_superuser;
alter schema tiger_data owner to rds_superuser;
alter schema topology owner to rds_superuser;

CREATE FUNCTION exec(text) returns text language plpgsql volatile AS $f$ BEGIN EXECUTE $1; RETURN $1; END; $f$;

SELECT exec('ALTER TABLE ' || quote_ident(s.nspname) || '.' || quote_ident(s.relname) || ' OWNER TO rds_superuser;')
  FROM (
    SELECT nspname, relname
    FROM pg_class c JOIN pg_namespace n ON (c.relnamespace = n.oid) 
    WHERE nspname in ('tiger','topology') AND
    relkind IN ('r','S','v') ORDER BY relkind = 'S')
s;
```

### Tileと緯度経度を変換するFunctionの作成

Tileのxyzを緯度経度に変換するFunctionはOSMのWikiを参考に作成しておきます。
https://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#PostgreSQL

これで ST_AsMVT を含むPostGISの一通りの機能が使用できるようになります。

### 使用例

実際の使用法等に関してはこちらを参考にさせていただきました、
http://waigani.hatenablog.jp/entry/2017/12/22/060000

SQLクエリによってベクトルタイルを生成することが出来るということは動的にfeatureのpropertyを構築出来るということですので、featureデータの格納されたマスターテーブルとユーザーデータの格納されたデータテーブルをJOINしてみます。

```sql
SELECT st_asmvt(v, 'geom', 4096, 'geom') mvt FROM (
  SELECT l.keycode, l.hasData, st_asmvtgeom(l.geom, bbox, 4096, 0, true) AS geom
  FROM
  (SELECT j.keycode, j.geom, e.geom bbox, COUNT(user_data.id) > 0 AS hasData
   FROM
     tokachi j -- featureデータテーブル
   INNER JOIN
     (SELECT st_makeenvelope(tile2lon(58883, 16), tile2lat(24090, 16), tile2lon(58884, 16), tile2lat(24091, 16), 4326) as geom) e -- 指定のxyzで矩形を作成
   ON
     st_intersects(j.geom, e.geom) -- tileの矩形に含まれるfeatureデータをst_intersectsで抽出
   LEFT JOIN
     user_data -- ユーザーデータ
   ON
     j.keycode = records.keycode -- polygonのキーと関連するデータを連結
   GROUP BY j.keycode, j.geom, bbox) l
) v;
```

このクエリを実行すると、各featureのpropertyに"ユーザデータが存在するかどうか"を表すhasDataが追加されます。
あとはleafletなどでの表示時にhasdataの有無で色を変えたりCallbackを仕掛けること等が出来ます。

```js
var style = function(feature) {
    var s = {};
    if (feature.properties["hasdata"]) {
        s.clickable = true;
        s.fillColor = "#43C2FF";
    } else {
        s.fillColor = "#E200FF";
    }
    return s;
};
```
タイルロードのイメージ:
![タイルロードイメージ](https://qiita-image-store.s3.amazonaws.com/0/204890/81908227-fa16-f396-b981-324981bae9bb.gif)


動的なデータを用いて高速なベクトルタイルを生成できるのは魅力的ですね。
マネージドでスケールさせやすいAuroraでベクトルタイルを提供することが出来るというのは夢が広がります。