SpatiaLite？なにそれおいしいの？

先日、オープンソースな地理空間ソフトウェア群である[FOSS4G](https://qiita.com/tags/foss4g)な[Advent Calendar](https://qiita.com/advent-calendar/2018/foss4g)に[SpatiaLiteについて投稿した](https://qiita.com/yoh_chan/items/b9c08b644897024b0862)ところ、たけやぶにお住まいのSQL使い@ozo360さまから畏れ多くもお誘いをいただいたので、こちらにもかんたんな紹介記事を投稿したいとおもいます。（@ozo360さま、お誘いいただきありがとうございます！）

# SpatiaLite...?
パブリックドメインな軽量データベースであるSQLiteにSpatial（空間的）な機能を拡張したのがSpatiaLiteです。正しい読みは知りませんが、「すぺーしゃらいと／すぺいしゃらいと」とか呼んでます。
配布元では、**Spatial Is Not Special**（空間的なことは特別なことじゃない：もっとましな訳があるはず）というスローガンを掲げていて、空間処理も簡単にできるようになるよ！的な想いがひしひしと伝わってきます。（個人の勝手な解釈）

わたくし、Accessを除いてほとんど他のRDBMSは触ったことがないので（必要に駆られて利用し始めた＆サーバーが必要なDBはなんだか難しそうだった）、紹介と言いつつ他のRDBMS（特にサーバーベース）と比べてここがいいですよorだめですよ！って言えないんです。すみません。（むしろそのへんぜひ聞きたい。）

ということで、注目度だけでも教えてもらおうと、Google先生に聞いてみました。

["PostGIS"と"SpatiaLite"で検索比較（過去5年間） - Google Trends](https://g.co/trends/838EC)
（MySQLは地理空間以外の情報も含まれてしまうので除外しました。）

**人気度の動向（平均）**

|PostGIS|SpatiaLite|
|:-:|:-:|
|76|6|

はい。人気ないっすね…（汗

さらに、上記トレンドの地域別インタレストを見ると、PostGISはアジアでも盛んに利用されていることをうかがわせますが、**SpatiaLiteはほぼ欧米諸国だけでアジアは韓国を除き皆無！**

どうりで巷で聞かないわけだよ…という結果でした^^;
（お隣韓国には熱心なユーザーがいるようですね～）


# わたしがSpatiaLiteを使い始めた経緯
かつて「渡されたGISデータがそうだったから」という理由で、**.mdb**という拡張子の地理空間データベース（わかる人はわかるはず）を見よう見まねで扱っていました。データベースに含まれるテーブルをいじったりするには、「それを開いて編集できるから」という理由で、**Microsoft Access**を利用していました。

しかし*いろいろな事情*から、たいへん扱いにくいなあと感じ、処理のメインをFOSS4Gの一つである**GRASS GIS**（以下GRASS）に移行しました。GRASSは、どちらかといえば画像などのラスタ処理を得意とするソフトウェアですが、独自のベクタモデルに基づく様々なベクタデータ処理も行うことができます。そして、GRASSベクタの属性を格納するデータベースとして、dBASE（DBF）、PostgreSQL、そして**SQLite**が対応していました。

これらのデータベースのうち、GISでは一般的なベクタフォーマットとして普及しているShapeファイルで採用されているdBASEは、古いフォーマットということもあり制限が多く扱いにくいことがわかっていました。またPostgreSQLはサーバーのハードルが高くて手が出せない、ということで残されたSQLite一択でした。（その後GRASSのバージョンアップによって、現在のGRASSデフォルトのDBはSQLiteになっています。）

当初ベクタ処理はGRASSベクタを介して行っていたため、SQLiteは属性データを格納する目的でのみ使用していましたが、そのうちに、**「そもそもSQLite上でベクタ処理もできちゃうSpatiaLiteというのがあるらしい」**ということがわかりました。（たぶん2014年ごろ）

最初は（とても）とっつきにくく、困ることが多かったのですが、だんだんわかってくると、**「これはかつて使っていた.mdbとよく似たデザインで、かつ高性能なベクタフォーマットだ」**ということがわかってきました。そこからずっと使い続けています。

こんな経緯なので、サーバーベースのRDBMSのことは全然わかりません。


# SpatiaLiteの使い方
一般的な使い方としては、

* SQLiteへのコネクションにエクステンション**mod_spatialite**をロードして利用する
* **spatialite-cli**（コマンドラインツール）を使う
* **spatialite-gui**（GUI）を使う

という感じだと思います。既存のSQLite操作環境がある場合や、Pythonなどの外部言語から操作する場合にはmod_spatialiteを、とりあえず触ってみるならspatialite-guiを利用するのがいいと思います。
これらの操作について詳しいことは[こちら](https://qiita.com/mima_ita/items/64f6c2b8bb47c4b5b391)や[こちら](https://qiita.com/yoh_chan/items/82fa113cd2806a384ae4)をご参考ください。


# ジオメトリモデル
SpatiaLiteで扱えるジオメトリのタイプは以下のとおりです。

* シングルジオメトリ
    * POINT
    * LINESTRING
    * POLYGON
* マルチジオメトリ
    * MULTIPOINT
    * MULTILINESTRING
    * MULTIPOLYGON
* ジオメトリコレクション（GEOMETRYCOLLECTION）

それぞれのジオメトリモデルはXYの次元を持ちますが、さらにZ、Mの次元を持つことができます。
詳細については、[こちら](https://www.gaia-gis.it/spatialite-2.1/SpatiaLite-manual.html#t2)をご参照ください。


# ~~ここがヘンだよ~~SpatiaLiteの特徴
それではここで、簡単なSpatiaLiteの特徴を挙げてみたいと思います。

## ロゴが微妙！
それでは、重鎮PostGISパイセン、颯爽と現れた新星MySQLさんと比べてみましょう。

### PostGIS
ぞうさんかわええ
![](http://postgis.refractions.net/download/logo_suite/adbadge_square/adbadge_square.png)

### MySQL
イルカさんかわええ
[![](https://www.mysql.com/common/logos/logo-mysql-170x115.png)](https://www.mysql.com)

### SpatiaLite
なんか土星？に羽刺さってるんだけど…
![](http://www.gaia-gis.it/gaia-sins/spatialite-logo.png)
by Massimo Zedda [CC BY-NC-ND 3.0](https://creativecommons.org/licenses/by-nc-nd/3.0/)

この羽根はきっとSQLiteの羽なんだな。（てきとう）
![](https://qiita-image-store.s3.amazonaws.com/0/101923/a8af3672-4429-8d9a-b6a4-4982a5f1d89c.png)

ちなみに[新しいロゴ作らない？という話](https://groups.google.com/d/msg/spatialite-users/Yh_8A1yf4Qk/gzNBifM_AwAJ)もあるようです。（放置されてる気もするけど…）


## 開発が個人！

### PostGIS
開発チームたくさんいる。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/101923/b13c1852-921d-440d-349c-b2e539ea192a.png)
-- [PostGIS - Wikipedia](https://ja.wikipedia.org/wiki/PostGIS)


### MySQL
オラクルさん！

![image.png](https://qiita-image-store.s3.amazonaws.com/0/101923/d44c072e-8f74-49c6-3ba9-2ff95c4d500e.png)
-- [MySQL - Wikipedia](https://ja.wikipedia.org/wiki/MySQL)

### SpatiaLite
Alessandro Furieriさん！（誰）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/101923/68d8bc2b-19e9-69c2-f9fa-7abfa11fc720.png)
-- [SpatiaLite - Wikipedia](https://ja.wikipedia.org/wiki/SpatiaLite)

ちなみに検索したら、お写真も出てきます。

## 情報がわかりにくい！
[公式サイト](https://www.gaia-gis.it/gaia-sins/)が英語なのはまあいいとして、とうの昔にサポートを終了したプロジェクトが同列に配布されていたり、とても基本的な概要ページがすごく深いところにあったり、ほんとそこはかとなく情報が探しにくい…^^;

いちおう紹介記事なので、主な（特に有益だと思う）参照ページを以下に示します。（どれもトップページからたどり着くのはちょっとたいへんかも？）

* [トピックス](https://www.gaia-gis.it/gaia-sins/spatialite_topics.html)
* [関数一覧（現行安定版v4.3.0）](http://www.gaia-gis.it/gaia-sins/spatialite-sql-4.3.0.html)
* [関数一覧（次期beta版v5.0.0）](http://www.gaia-gis.it/gaia-sins/spatialite-sql-5.0.0.html)
* [SpatiaLite Users（メーリングリスト）](https://groups.google.com/forum/#!forum/spatialite-users)（たいてい新しい情報はここに投下されます。）

関数も、現在betaの次期リリース版ではずいぶん増えました。（数えたら733もありました。）
PostGISやMySQLと比べて、何ができて何ができないのかはわかりませんが、これだけあればいろいろできるような気がします。（ざっくり）

## ちょいちょい残念or面倒or謎な仕様がある
SQLiteの制限だったりするので、SpatiaLiteのせいではなかったりしますが。。。

### 列の削除ができない
SQLiteにはDROP COLUMNがないので、列の削除ができません（なぜ。。。
ADD COLUMNはあるので、列の追加はできます（謎

### PRIMARY KEYの事後追加はできない
SQLiteは、CREATE TABLE時にしかPRIMARY KEYを設定できません。
この事実は、@ozo360さまとの会話で初めて判明しました。（他のRDBMSでもそうだと思ってた。）
しかもNULLもOKらしい。

### RIGHT OUTER JOIN、FULL OUTER JOINがない
たまに困ることがあります。

### ジオメトリカラムの登録がめんどくさい
QGISなど、外部のGISから地物情報を利用できるようにするには、テーブルにジオメトリタイプのカラムがあるだけでは足りません。諸々のメタ情報などを追加するために`RecoverGeometryColumn()`という処理が必要になります。

```sql
CREATE TABLE "hoge" ("pk_uid" INTEGER PRIMARY KEY, "geom" POLYGON); -- これだけでは他のGISから利用できない
SELECT RecoverGeometryColumn('hoge', 'geom', 4326/*ジオメトリの空間参照EPSG*/, 'POLYGON');
```

`AddGeometryColumn()`という関数で、あとからジオメトリカラムを追加するパターンもあります。

```sql
CREATE TABLE "hoge" ("pk_uid" INTEGER PRIMARY KEY); -- 最初ジオメトリカラムを用意しない
SELECT AddGeometryColumn('hoge', 'geom', 4326, 'POLYGON'); -- ADD COLUMNのジオメトリカラム版
```

### DROP TABLEでジオテーブルを削除できない
正確に言うと、DROP TABLEで削除すると、ジオメトリカラムなどのメタデータがゴミとしてそのまま残ってしまいます。そのため、ジオテーブルの削除には、`DropGeoTable()`を使います。

```sql
SELECT DropGeoTable('hoge');
```

空間インデックスなどが張られている場合は、これらも一緒に削除されます。

### ジオメトリカラムより後に追加したカラムがQGISから見えない
これはもしかしたらQGISの問題なのかもしれませんが、ジオメトリカラムより後に追加した列は、属性データとしてQGISから参照できないという残念仕様があります。

```sql
CREATE TABLE "hoge" ("pk_uid" INTEGER PRIMARY KEY, "geom" POLYGON);
SELECT RecoverGeometryColumn('hoge', 'geom', 4326, 'POLYGON');
ALTER TABLE "hoge" ADD COLUMN "new_column" TEXT;  -- この列は、QGISのレイヤとして追加した際に参照できない。
```

まあ、SQLite側だけで完結してれば特に問題ないのですけど…そうもいかないときは、ジオテーブルを複製する関数`CloneTable()`で複製すると、ジオメトリカラムが最後に移動するので、これを利用しています。（ああめんどくさい）

```sql
SELECT CloneTable(
  'main'  -- データベースのエイリアス（別のDBをアタッチした場合。内部でコピーする場合はmain）
  , 'hoge'  -- コピー元
  , 'hoge_new' -- コピー先
  , 1  -- トランザクションフラグ（1でTrue）
);
SELECT DropGeoTable('hoge');  -- もとのジオテーブルを削除
SELECT DiscardGeometryColumn('hoge_new', 'geom');  -- ジオメトリカラムのメタデータを抹消する関数（RecoverGeometryColumnの逆）
ALTER TABLE "hoge_new" RENAME TO "hoge";  -- ジオメトリカラムを無効にしないとRENAMEできない。
SELECT RecoverGeometryColumn('hoge', 'geom', 4326, 'POLYGON');  -- ジオメトリカラムの再登録
```

これだけ見ると、とてもめんどくさいもののように見えてしまうのが悲しい。

# Furieriさんマジありがとう
いろいろ煽ってしまいましたが、なんだかんだ言ってSpatiaLiteにはほんとにお世話になってます。ということは取りも直さずFurieriさんにはほんとにお世話になっています。マジありがとう。


# とりあえず、これからも使うよ
なぜわたしはSpatiaLiteを使うのか。それは、とりあえずベクタデータをいじるのに必要なことがこれで済むからです。

自分は開発者ではないので、地物の長さや面積を調べたり、範囲に囲まれたものの数や情報を集計したり[面積按分したり](https://qiita.com/yoh_chan/items/a61f424f54b0686f608b)、地物を融合したり[分解したり](https://qiita.com/yoh_chan/items/a43da54cd30be8b42c54)[半分に割ってみたり](https://qiita.com/yoh_chan/items/1c7108244a745d55dad7)[Routingができたり](https://qiita.com/yoh_chan/items/b9c08b644897024b0862)すればよいので、ローカル環境でさくっとできればそれがいちばんありがたい。SQLiteの他に必要なライブラリも全部込みのspatialite-guiでも、インストール不要で40MB弱。データベースファイルのサイズ上限はありません。（各テーブルのカラム数の上限は事実上999だということが最近判明しました。）

データを納品したりする際にも、単一のデータベースファイルにベクタデータを全部まとめて格納できるので便利です。一千万行以上のデータになるとしんどいですが、数百万行くらいまでのデータなら十分使えると思います。


最近QGISなどで激推しの**GeoPackage**は、SpatiaLiteと同じくSQLiteベースのファイルであり、かつラスタも格納できる素敵なフォーマットですが、@ozo360さまとの対話から[GeoPackageのベクタジオメトリはSpatiaLiteのジオメトリと相互にCastできることがわかった](https://qiita.com/ozo360/items/8e9887aa2b637f1df95d)ので、まだしばらく使えるんじゃないかなあと思っている今日このごろです。


ということで、長々と微妙な紹介記事でしたが、SpatiaLiteにも興味を持っていただければ嬉しいなあ！


### （先人に倣って）本記事のライセンス

[![image.png](https://qiita-image-store.s3.amazonaws.com/0/101923/eb5a8a8f-6285-730d-c7fc-03d09fd2b169.png)](http://creativecommons.org/licenses/by/4.0/)

この記事は [クリエイティブ・コモンズ 表示 4.0 国際](https://creativecommons.org/licenses/by/4.0/) ライセンスの下に提供されています。