すべての地理情報データの基本は「点」
地理情報データでは、その場所ズバリを表す「点」の情報、川や道路あるいは経路などを表す「線」の情報、建物や敷地あるいは市町村の形などのエリアを表す「面」の情報などを扱うことができます。しばしば「地理情報の基本は“点・線・面”」というキーワードで語られますが、中でも「点」の情報の扱い方はすべての基本と言えます。線の情報も面の情報も、点の情報を繋げたものとして表されるからです。この連載では、あえて「地理情報データの基本は “点”である」と言い切りたいと思います。
点の情報とは何か
ある場所(地点)を表すには様々な方法がありますが、最初は緯度と経度で表す方法を理解しておきましょう。「緯度」「経度」そして「測地系」の3つの情報で場所を表します。温度の例では、温度を表す数字とそれが摂氏なのか華氏なのかの情報がセットになって初めて正しい温度を表現できるのでしたね。測地系も「摂氏か華氏か」といった一種の単位のようなものだと理解しておけば良いでしょう。
緯度、経度の表現方法は第1回で紹介したように色々ありましたが、MySQLで表すときは「度分秒表記」や「度分表記」ではなく「度表記」を使います。分や秒を使わずに「度」の単位となるように小数で表すものです。
Think IT編集部があるインプレスの場所は、世界測地系(JGD2011)の緯度・経度で表すと「北緯35.694572度」「東経139.760397度」になります。
| 場所名 | インプレス |
| 北緯 | 35.694572 |
| 東経 | 139.760397 |
| 測地系 | JGD2011(SRS_ID: 6668) |
Well Known Text
この測地系+緯度経度の情報をMySQLに伝えるために「Well Known Text」(ウェルノーンテキスト)という形式を覚えておきましょう。略して「WKT」と言われることもあります。WKTで点の情報は「POINT(35.694572 139.760397)」のように“POINT()”の括弧内に緯度と経度を記述します。緯度と経度の間はコンマではなく空白であることに注意してください。
線の情報を表す場合には「LINESTRING()」、面の情報を表すには「POLYGON()」を使用します。これらについては、後の連載で改めて紹介します。
MySQLにポイントデータを登録しよう
データ登録用テーブルの作成
では、この緯度経度を含むデータをMySQLに登録してみましょう。最初に登録用のテーブルを作成します。ここではID、名前、位置情報(ポイントデータ)の3つのカラムを持つテーブル「g1」を作成する例を示します。
mysql> CREATE TABLE g1 (
-> id INTEGER,
-> name VARCHAR(30),
-> g GEOMETRY SRID 6668
-> );
Query OK, 0 rows affected (0.132 sec)
「GEOMETRY」というのが地理情報データを格納する型です。実務上はより限定された型(今回の場合は点の情報だけを登録するためのカラムなので POINT型)を使うべきですが、この連載では当面、汎用的に使われるGEOMETRY型を使って進めていきます。SRID(SRS_ID)の指定は省略することもできますが、複数の測地系のデータが同一テーブルに混在するような設計はあまりしないので、誤った登録を防ぐ意味でもテーブル定義に含めておくことをお勧めします。
DESC(DESCRIBE)コマンドで定義を確認してみると、「geometry」という型で正しくテーブルが作成されていることが分かります。
mysql> DESC g1;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| name | varchar(30) | YES | | NULL | |
| g | geometry | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
3 rows in set (0.001 sec)
DESCの出力では測地系に関する情報は含まれていませんが、SHOW CREATE TABLE文では確認することができます。
mysql> SHOW CREATE TABLE g1\G
*************************** 1. row ***************************
Table: g1
Create Table: CREATE TABLE `g1` (
`id` int DEFAULT NULL,
`name` varchar(30) DEFAULT NULL,
`g` geometry /*!80003 SRID 6668 */ DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.000 sec)
データを登録してみよう
作ったテーブルにインプレスの緯度経度情報を含むレコードを1件登録してみます。通常のSQL文の1つであるINSERT文を使います。GEOMETRY型の列には「ST_GeomFromText()」という関数を使用してデータを与えます。
ST_GeomFromText(WKTによる地理情報[, SRS_ID])
この構文に、先ほど紹介したWKTを当てはめたのが、次のコマンドです。
mysql> INSERT INTO g1 VALUES (1, 'インプレス', ST_GeomFromText('POINT(35.694572 139.760397)', 6668));
Query OK, 1 row affected (0.046 sec)
登録したデータを見てみる
まずは何も細工をせず、シンプルにg1テーブルの中身を見てみましょう。
mysql> SELECT * FROM g1;
+------+-----------------+------------------------------------------------------+
| id | name | g |
+------+-----------------+------------------------------------------------------+
| 1 | インプレス | 0x0C1A0000010100000040DF162C55786140D15B3CBCE7D84140 |
+------+-----------------+------------------------------------------------------+
1 row in set (0.000 sec)
g列にはバイナリで値が格納されていることが分かります。mysqlコマンドラインクライアントでは、デフォルトでバイナリ値はHexadecimal(16進数)に変換して表示されます。
このままでは何の値が登録されているのか分からないので、人間にも分かる表現であるWKTに変換する「ST_AsText()」関数があります。
mysql> SELECT id, name, ST_AsText(g) g FROM g1;
+------+-----------------+-----------------------------+
| id | name | g |
+------+-----------------+-----------------------------+
| 1 | インプレス | POINT(35.694572 139.760397) |
+------+-----------------+-----------------------------+
1 row in set (0.000 sec)
無事に緯度経度のPOINTデータが格納されていることが確認できました。最後に、ここまでの操作の再確認として、データをもう1件登録してみましょう。雷門のデータを登録してからテーブルの内容を確認する例です。
mysql> INSERT INTO g1 VALUES (2, '雷門', ST_GeomFromText("POINT(35.711009 139.796355)", 6668));
Query OK, 1 row affected (0.041 sec)
mysql> SELECT id, name, ST_AsText(g) g FROM g1;
+------+-----------------+-----------------------------+
| id | name | g |
+------+-----------------+-----------------------------+
| 1 | インプレス | POINT(35.694572 139.760397) |
| 2 | 雷門 | POINT(35.711009 139.796355) |
+------+-----------------+-----------------------------+
2 rows in set (0.000 sec)
2件のデータが登録されていることが確認できました。
インプレスの緯度経度データは、バイナリ形式でMySQLに登録されていることを本文で確認しました。その値「0x0C1A0000010100000040DF162C55786140D15B3CBCE7D84140 」はどういった構造になっているのか、好奇心溢れるエンジニアならば、きっと関心があることと思います。解説しましょう。
このバイナリには測地系の情報(SRID)、データ種類に関する情報、そして緯度と経度の値が含まれています。
| バイト列 | 内容 | 値 |
|---|---|---|
| 0C1A 0000 | 測地系情報(SRID) | 0x1A0C=6668 |
| 01 | エンディアン情報 | 1:リトルエンディアン |
| 0100 0000 | WKBタイプ | 1:POINTデータ |
| 40DF162C55786140 | 経度 | 35.694572 |
| D15B3CBCE7D84140 | 緯度 | 139.760397 |
一覧表の中に“WKB”という言葉が出てきました。これは「Well Known Binary」(ウェルノーンバイナリ)の略です。WKTが人間に可読なテキスト表現であるならば、WKBはコンピュータが内部で保持する際のバイナリ表現です。MySQLではWKBの前に測地系情報4バイトを付加した「MySQL内部ジオメトリ表現」のバイナリとして地理情報データを格納します。