【MySQL】Geometry型で位置情報を扱う

# はじめに

MySQL8.0では位置情報に関する機能が充実した...という情報を見かけたので、試しにGeometry型というデータ型を使って、位置情報を格納するテーブルを作ってみることにしました。

※GIS関連は全く詳しくないので、誤りがありましたらご指摘頂けると幸いです。

# 使用した環境

* OS
 * CentOS7.7（1908）
* RDBMS
 * MySQL8.0.19

# テーブルの作成

* 駅の位置を表す`station`というテーブルを`nkojima`データベース内に作成しました。
* 位置情報は`location`というGeometry型のカラムに持たせています。
* 位置情報を表すデータ型はGeometry以外にも幾つか用意されており、以下に示したものは「OpenGIS クラスに対応するデータ型」となっています。
 * POINT：地点を表す。
 * LINESTRING：地点間を直線で結んだ線分を表す。
 * POLYGON：多辺の幾何図形を表す。
 * GEOMETRY：POINT、LINESTRING、POLYGONのいずれにも対応するルートクラス。

```sql:テーブルの作成
-- 駅情報テーブルの作成。
CREATE TABLE `nkojima`.`station` (
  `id` INT AUTO_INCREMENT comment '駅ID',
  `name` VARCHAR(50) NOT NULL comment '駅名', 
  `location` GEOMETRY NOT NULL comment '駅の座標',
  `note` VARCHAR(200) comment '備考',
  PRIMARY KEY (id),
  INDEX name_idx(name)
)
ENGINE = InnoDB,
default charset=utf8mb4
comment='駅情報';
```

# データの登録

* 都内のJRの駅から主要なものを選んで、大まかな座標を[国土地理院の地図](https://maps.gsi.go.jp/)で取得しました。
* Geometry型として利用するにはSRID（空間参照系識別子）が必要になりますが、ここでは4326（WGS84）にセットしました。
* MySQL5.7では`GeomFromText`関数が使われていましたが、MySQL8.0以降は`GeomFromText`関数は非推奨となっているため、[`ST_GeomFromText`](https://dev.mysql.com/doc/refman/8.0/en/gis-wkt-functions.html#function_st-geomfromtext)を使うことになります。
 * `GeomFromText`という名称はおそらく「Geometry From Text」の略で、「テキストからGeometry型に変換する」という処理がそのまま名称になっているのだと思われます。

```sql:INSERT文
USE nkojima;
INSERT INTO station (name, location, note) VALUES
("東京", ST_GeomFromText('POINT(35.681300 139.767165)', 4326), "23区"),
("新橋", ST_GeomFromText('POINT(35.666471 139.758357)', 4326), "23区"),
("品川", ST_GeomFromText('POINT(35.628829 139.739141)', 4326), "23区"),
("大崎", ST_GeomFromText('POINT(35.619131 139.728563)', 4326), "23区"),
("渋谷", ST_GeomFromText('POINT(35.658177 139.701741)', 4326), "23区"),
("新宿", ST_GeomFromText('POINT(35.688803 139.700925)', 4326), "23区"),
("池袋", ST_GeomFromText('POINT(35.730114 139.711053)', 4326), "23区"),
("日暮里", ST_GeomFromText('POINT(35.728163 139.770598)', 4326), "23区"),
("上野", ST_GeomFromText('POINT(35.713756 139.776950)', 4326), "23区"),
("四ツ谷", ST_GeomFromText('POINT(35.684834 139.730204)', 4326), "23区"),
("三鷹", ST_GeomFromText('POINT(35.702727 139.560978)', 4326), "多摩地区"),
("立川", ST_GeomFromText('POINT(35.698170 139.413671)', 4326), "多摩地区"),
("八王子", ST_GeomFromText('POINT(35.655370 139.339921)', 4326), "多摩地区"),
("北千住", ST_GeomFromText('POINT(35.749612 139.805059)', 4326), "23区"),
("赤羽", ST_GeomFromText('POINT(35.777732 139.721031)', 4326), "23区"),
("蒲田", ST_GeomFromText('POINT(35.562491 139.715924)', 4326), "23区");
```

# データの検索

* [`ST_AsText`](https://dev.mysql.com/doc/refman/8.0/en/gis-format-conversion-functions.html#function_st-astext)関数を使うことで、Geometry型のカラムを人が見て理解できる形式で取得できます。
 * [`ST_AsText`](https://dev.mysql.com/doc/refman/8.0/en/gis-format-conversion-functions.html#function_st-astext)関数を使わないと、Geometry型のカラムは16進数のデータとして表示されます。

```sql:
mysql> SELECT
    ->   name AS 駅名,
    ->   ST_AsText(location) AS 緯度経度
    -> FROM station
    -> WHERE note='多摩地区';
+-----------+-----------------------------+
| 駅名      | 緯度経度                    |
+-----------+-----------------------------+
| 三鷹      | POINT(35.702727 139.560978) |
| 立川      | POINT(35.69817 139.413671)  |
| 八王子    | POINT(35.65537 139.339921)  |
+-----------+-----------------------------+
```

# まとめ

* ここまでの内容だとGeometry型などのデータ型を使うメリットがほとんど感じられませんが、「地点間の距離を計測する」「最寄りの地点を調べる」などの処理で力を発揮してくれるそうです。
 * この辺りについては次に投稿する記事で触れたいと思います。

# 参考URL

* [MySQL5.6 - 11.5.1 空間データ型](https://dev.mysql.com/doc/refman/5.6/ja/spatial-datatypes.html)
* [MySQL5.6 - 11.5.2 OpenGIS 幾何モデル](https://dev.mysql.com/doc/refman/5.6/ja/opengis-geometry-model.html)
* [MySQL8.0 - Geometryクラス](https://dev.mysql.com/doc/refman/5.6/ja/gis-class-geometry.html)
* [MySQL8.0 - ST_GeomFromText関数](https://dev.mysql.com/doc/refman/8.0/en/gis-wkt-functions.html#function_st-geomfromtext)
* [MySQL8.0 - ST_AsText関数](https://dev.mysql.com/doc/refman/8.0/en/gis-format-conversion-functions.html#function_st-astext)
* [MySQLでGISデータを扱う](https://qiita.com/onunu/items/59ef2c050b35773ced0d)