GeoPackageでも空間関数が使えるようになる話

## はじめに
わたしがGeoPackageの存在を知ったのは去年のちょうど今頃、こんな記事を読んだからでした。
[QGIS3からはGeopackageが使いやすいぞ！脱シェープファイル！](http://koutochas.seesaa.net/article/455606127.html)

GeoPackageはSqliteベースのRDBという情報もあり、Spatialiteの空間関数も使えるのでは？と個人的な期待が爆上がりしたのですが実際には素のSqliteでしかなく空間関数はありませんでした。
そんなこんなでSpatialite以上のメリットを見いだせず放置しておりました。

そんな折、[QGISとかその辺に関するチャットへのお誘い](https://qiita.com/AAEE/items/f8595234b60a1457cd5a)という記事からdiscordのQGIS雑談チャンネルに参加したところSpatialiteの日本第一人者と勝手に認識している[@yoh_chan](https://qiita.com/yoh_chan)さまをお見かけしましてGeoPackageにSpatialiteの空間関数拡張できないのか質問したところ、なんかいけそうというお話をいただきました。

キーワードは「CastAutomagic」

というわけでこの記事は@yoh_chanさまから教えて頂いた内容を多分に含んでおり、私自身は使ってみただけに等しいです。なにぶん初心者ですので勘違いや誤りを含んでいる可能性も否定いたしませんので、なにかお気づきの点があればお気軽にコメント欄でご指摘いただけると大変うれしく思います。
また、僅かながらでも参考になりましたら「いいね」を押していただけるとわたしの承認欲求が満たされて楽しいクリスマスが過ごせると思います。よろしくお願いいたします。

前置きが長すぎる。

##GeoPackageとは
GeoPackageの公式サイトはこちら
http://www.geopackage.org/

超掻い摘んで概要を説明すると。

1. GeoPackageはSQLiteコンテナ。
1. 拡張子はgpkg。
1. ベクタとラスタタイルを扱える。
1. ファイルエンコーディングとしてセキュリティ機能はもっていない。
1. Spatialiteとは分岐しているが、Spatialite4.2.0からGeoPackageをサポートしている。
1. GeoPackageはPostGISやSpatiaLiteとは異なるWell-Known Binary（WKB）エンコーディングを使用。
1. テーブルごとに１つのジオメトリ列のみをサポート。

WKBに互換がないというところと、1テーブル1ジオメトリ列というところは注意点です。

##空間関数を有効にする
SqliteにSpatialiteの地理空間拡張を有効にするmod_spatialiteをGeoPackageにも適用できます。
mod_spatialiteのダウンロードおよび使い方は以下が詳しい。
[mod_spatialiteのススメ](https://qiita.com/yoh_chan/items/0a3f026ac97bf2025f0c)
ちなみに、QGIS3.4のPythonコンソールから扱う場合はmod_spatialiteはすでにPATHが通っているのでダウンロードやPATH設定をすることなく利用できて超便利。


そして、実はSpatialite_guiでgkpgを開くとそのまま空間関数が利用できます。
Spatialite_guiのダウンロードおよび使い方は以下が詳しい。
[準備編：SpatiaLite-GUIを使ってデータベースファイルを用意する](https://qiita.com/yoh_chan/items/82fa113cd2806a384ae4)

ただし、SpatialiteとGeoPackageのジオメトリデータ（WKB）は互換がありませんのでSpatialiteの空間関数にGeoPackageのWKBを渡しても結果がNULLになってしまいます。

そこで、Spatialiteの`CastAutomagic関数`を使うことでGeoPackageのWKBを変換して実行することができるようになります。

```sql:sample
SELECT ST_Centroid(CastAutomagic(geomety)) FROM tbl_geom;
```

注意点は戻り値のジオメトリ型はSpatialiteのWKBということです。

## ベクタジオメトリ変換関数
ということでリファレンスを眺めてみましょう。
http://www.gaia-gis.it/gaia-sins/spatialite-sql-4.3.0.html

GeoPackageのWKB関連の変換関数として以下の4つを押さえておいたほうがよさそうです。

|関数名|引数|戻り値
|:---|:---:|:---:
|CastAutomagic|GeoPackageのWKB<br> or <br>SpatiaLiteのWKB|SpatiaLiteのWKB<br>エラーはNULL
|GeomFromGPB|GeoPackageのWKB|SpatiaLiteのWKB<br>エラーはNULL|
|AsGPB|SpatiaLiteのWKB|GeoPackageのWKB<br>エラーはNULL|
|IsValidGPB|Blob|1:GeoPackageのWKB<br>0:それ以外|

## 実験
以下のサンプルデータを使って実験してみましょう。
https://raw.githubusercontent.com/tohka/ne/master/GeoPackage/ne_50m_admin_0_countries.gpkg

Spatialite_guiで接続したらSQLを実行します
変換なしで空間関数に投げた場合

```sql
SELECT fid, ST_AsText(geom) wkt
FROM ne_50m_admin_0_countries LIMIT 1;
```

|fid|wkt
|:--:|:--:|
|1|NULL|

戻り値がNULLになってしまいました。
CastAutomagicでキャストした場合はどうでしょうか。

```sql
SELECT fid, ST_AsText(CastAutomagic(geom)) wkt
FROM ne_50m_admin_0_countries LIMIT 1;
```

|fid|wkt
|:--:|:--|
|1|POLYGON((31.287891 -22.402051, 31.197266 -22.344922, 31.073438 -22.307813, 30.916113…略|

いい感じになりました。

次はST_Centroidを使ってポリゴンの重心を求めて、それがGeoPackageのWKBかどうかを評価してみましょう。
まずは空間関数の戻り値をそのまま評価

```sql
SELECT fid, IsValidGPB(ST_Centroid(CastAutomagic(geom))) isvalid
FROM ne_50m_admin_0_countries LIMIT 1;
```

|fid|isvalid
|:--:|:--:|
|1|0|

GeoPackageのWKBではないといわれました。
AsGPBで変換したものを評価してみましょう。

```sql
SELECT fid, IsValidGPB(AsGPB(ST_Centroid(CastAutomagic(geom)))) isvalid
FROM ne_50m_admin_0_countries LIMIT 1;
```
|fid|isvalid
|:--:|:--:|
|1|1|

GeoPackageのWKBになりました。

##デフォルトWKBをGeoPackageとして扱う
空間関数を扱うたびにジオメトリフィールドに`CastAutomagic()`を書くのは冗長ですね。
他のRDBとのSQL互換性も低くなっちゃいますし。
そんな事を思ったあたなのためにデフォルトWKBをGeoPackageとして扱うスイッチが用意されています。

```sql
SELECT EnableGpkgMode();
```

このSQLを実行するとコネクションに対してGeoPackageモードが有効になり、空間関数の入出力がGeoPackageのWKBで利用できるようになります。

あら便利。

GeoPackageモードを無効にするにはDisableGpkgModeですね。

```sql
SELECT DisableGpkgMode();
```

現在のGeoPackageモードを調べるには

```sql
-- 0:false 1:true
SELECT GetGpkgMode();
```

## こちらも実験

ポリゴンから重心を求めてGeoPackageのWKBかどうか評価してみましょう。

```sql
SELECT EnableGpkgMode();
SELECT GetGpkgMode();
```

|GetGpkgMode()
|:--:|
|1|
切り替わった。

```sql
SELECT fid, IsValidGPB(ST_Centroid(geom)) isvalid FROM ne_50m_admin_0_countries LIMIT 1;
```

|fid|isvalid
|:--:|:--:|
|1|1|

うん、ばっちり。

# 最後に
キャストしても空間インデックスは有効なのか？とかキャストのコストは？とか気になることもありますがひとまず空間関数が使えるようになったことが素直に嬉しいです。

GeoPakageはSpatialiteと同様にインストール不要で軽量高機能な空間データベースとして利用できました。
手軽でジオなRDBをお探しのあなたに届いたら嬉しいです。

基本的な使い方など、これから記事を増やしていこうと思います。
ご意見、ご要望、ご質問などはお気軽にコメント欄におねがいします。

本記事のライセンス
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/">![クリエイティブ・コモンズ・ライセンス](https://i.creativecommons.org/l/by/4.0/88x31.png)</a>
この記事は <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">クリエイティブ・コモンズ 表示 4.0 国際 ライセンス</a>の下に提供されています。