MySQL GIS拡張関数: STX_MakePoint()

MySQLにGIS(Spatial)関数を増やそうプロジェクトの関数紹介第3弾。

STX_MakePoint()

 座標の数値を与えてPOINT型データを作る関数です。SRIDを与えて一気に地理座標のデータにできるのがポイントです(POINTだけに)。

動作紹介

  • X,Y座標を与えて、デカルト座標上の POINT型データを生成します。確認のためにAsTextをカマしたものと、バイナリ(HEX表示)を並べて出力しています
mysql> SELECT ST_AsText(STX_MakePoint(1, 2)) g1,
    ->                  STX_MakePoint(1, 2) g2;
+------------+------------------------------------------------------+
| g1         | g2                                                   |
+------------+------------------------------------------------------+
| POINT(1 2) | 0x000000000101000000000000000000F03F0000000000000040 |
+------------+------------------------------------------------------+
1 row in set (0.000 sec)
  • 測地系を指定して生成することもできます。最初4バイトに 4326(0xE610)が入ったデータになっていることが確認できます
mysql> SELECT ST_AsText(STX_MakePoint(1, 2, 4326)) g1,
    ->                  STX_MakePoint(1, 2, 4326) g2;
+------------+------------------------------------------------------+
| g1         | g2                                                   |
+------------+------------------------------------------------------+
| POINT(1 2) | 0xE610000001010000000000000000000040000000000000F03F |
+------------+------------------------------------------------------+
1 row in set (0.000 sec)
  • 緯度経度の順は、information_schema.st_spatial_reference_systemsテーブルのそのSRIDの定義に従います。6668や4326では lat, lon の順です
mysql> SELECT ST_AsText(STX_MakePoint(35, 135, 6668)) g1,
    ->                  STX_MakePoint(35, 135, 6668) g2;
+---------------+------------------------------------------------------+
| g1            | g2                                                   |
+---------------+------------------------------------------------------+
| POINT(35 135) | 0x0C1A000001010000000000000000E060400000000000804140 |
+---------------+------------------------------------------------------+
1 row in set (0.000 sec)
  • 本当にlat lon の順で正しいデータが生成されたのかを、実績あるST_GeomFromTextを使って確認します。STX_MakePointで生成したのと同じになったので、データは正しいと言えそうです
mysql> SELECT ST_GeomFromText('POINT(35 135)',6668) g;
+------------------------------------------------------+
| g                                                    |
+------------------------------------------------------+
| 0x0C1A000001010000000000000000E060400000000000804140 |
+------------------------------------------------------+
1 row in set (0.000 sec)
  • lat,lon が常識の範囲を超えた場合エラーになります。
mysql> SELECT ST_AsText(STX_MakePoint(135, 35, 6668)) g1,
    ->                  STX_MakePoint(135, 35, 6668) g2;
ERROR 3617 (22S03): Latitude 135.000000 is out of range in function stx_makepoint. It must be within [-90.000000, 90.000000].

開発秘話

 当初の実装では、地理座標系を指定したときに lon, lat の順で与える必要がありました。たしかPostGISなどはそういう仕様でしたよね。しかしそれはおかしい!と私は思うのです!!(元青年の主張)
 4326 や 6668 ではSRSの定義としてlat lonの順であると明記されているので、それに従うべきです。ということで、ちゃんと測地系の定義に従った順序で動作するように変更しました。まぁここが結構大変で、AIさんは勝手に「地理座標ならだいたい lat lon だから(その判断できるのもすごいんだけど)、地理座標系のSRIDであることを以てlat lonにしました」って決めちゃったようで、最終的にこの動作にするまでに随分とディスカッションしました。最大の難点は ST_SRSテーブルをプラグインから参照することはできないという点で、結局、プラグイン側でも必要な情報(本関数では lat lon の順序情報)をMySQLのソースコードから生成して持つことになりました。CREATE SRS で作られた場合には対応できない点は、もう割り切りにしましょう。
 あとは、[-90, 90] [-180, 180]以外のデータをエラーにする処理がなかったので(バイナリとしてはコレを越えても別段問題なく格納はできる)、エラーにするようにという処理を追加しました。

  • lon-lat順の地理座標系の例。lon-latで動作が正常に行われる
mysql> SELECT ST_AsText(STX_MakePoint(135, 35, 7035)) g1,
    ->                  STX_MakePoint(135, 35, 7035) g2;
+---------------+------------------------------------------------------+
| g1            | g2                                                   |
+---------------+------------------------------------------------------+
| POINT(135 35) | 0x7B1B000001010000000000000000E060400000000000804140 |
+---------------+------------------------------------------------------+
1 row in set (0.000 sec)
  • 4326とかのように lat lon で与えてみた例。範囲を超えているとしてエラーになる。第2引数はlonではないことの左証
mysql> SELECT ST_AsText(STX_MakePoint(35, 135, 7035)) g1,
    ->                  STX_MakePoint(35, 135, 7035) g2;
ERROR 3617 (22S03): Latitude 135.000000 is out of range in function stx_makepoint. It must be within [-90.000000, 90.000000].


単純にPOINTデータを作るというだけですが、意外と深みのある STX_MakePoint() でした。