MySQLのGIS関数は180度を跨いだ部分でもちゃんと最短距離を計算する

 2024年8月の FOSS4G TokaiMySQLの山﨑さんの発表後に「MySQLでは180度線を跨ぐ場合の距離など、正しく計算してくれるのか」という質問がありました。確か以前、(当然)それは大丈夫だったのを確認したよなぁと朧気に思いつつもその場でフォローはできなかったので、ご講演終了後に速攻で確認してみました。

結論

 「ちゃんと、180度をまたいでも、最短の距離で計算してくれる(ぐるりと0度のほうを回って、つまり例えば 東経175度から0度を経由して西経175度までの距離を計算するわけではない)」

実例

 赤道上での、東経165度から175度の距離、および東経175度から西経175度までの距離を求める。どちらも経度10度ぶんの距離(1000kmちょっと)になるはずである。

mysql> SELECT ST_Distance(
    ->     ST_GeomFromText('POINT(0 165)',4326)
    ->    ,ST_GeomFromText('POINT(0 175)',4326)
    ->    ) d;
+-------------------+
| d                 |
+-------------------+
| 1113194.907932733 |
+-------------------+
mysql> SELECT ST_Distance(
    ->     ST_GeomFromText('POINT(0 175)',4326)
    ->    ,ST_GeomFromText('POINT(0 -175)',4326)
    ->    ) d;
+-------------------+
| d                 |
+-------------------+
| 1113194.907932774 |
+-------------------+

期待通り「ほぼ一致」した。

少しの誤差が気になる

 どちらも赤道上での10度ぶんという、回転楕円体上では同じ距離となるはずなのに、やや異なる数値となったのが気になる。その差は、0.000000041メートル。つまり0.000041ミリ。1000kmぶんの距離を測って、0.000041ミリ。実務上は無視しても良い程度ですが、本来一致するはずなのに差が出るのが気になりますね。

ラジアンに変換する際の誤差ではないか

 MySQLでは(というか Boost::Geometryライブラリでは)、内部でラジアンで計算しているものと思われます。
ということで、「同じ経度10度でも、180度線をまたぐ場合の計算で差が発生するのではないか」を仮説と立てて、確認してみたいと思います。

まず値の準備。Piの値を設定し、経度 165, 175, -175 の3つをラジアン変換します。

irb(main):001:0> pi=3.1415926535897932384626433
irb(main):002:0> rad165=165*(pi/180)
irb(main):003:0> rad175=175*(pi/180)
irb(main):004:0> rad_175=-175*(pi/180))

irb(main):014:0> rad165
=> 2.8797932657906435
irb(main):015:0> rad175
=> 3.0543261909900767
irb(main):016:0> rad_175
=> -3.0543261909900767


ラジアンうしの引き算として、東経177度と179度の差と、東経179度と西経179度の差(どちらも度で表すと2度の差)を求めます。
165度と175度は単純に引き算すれば良く、175度と-175度は、これらの差(非常に大きい)を一周の角である2*piから減ずれば良いです。

irb(main):009:0> (rad175-rad165).abs
=> 0.1745329251994332
irb(main):010:0> (2*pi-(rad175-rad_175)).abs
=> 0.17453292519943275

0.00000000000000045ラジアン程度の差が発生しました。

これは弧の長さにすると、半径rを用いて rθで求められるので、

>||irb(main):011:0> r=6378137
irb(main):009:0> (rad175-rad165).abs
=> 0.1745329251994332
irb(main):010:0> (2*pi-(rad175-rad_175)).abs
=> 0.17453292519943275
|

0.0000000028メートルほどの差が発生しました。これ、piの値を結構詳細に指定したので先ほどよりも小さい値となりましたが、試しに pi=3.141592653 で同様の計算をしたところ、差は0.0000000062メートルとなりました。

180度線を跨ぐ計算で発生する差は、これかなー。
確証はないので、みなさんの続報に期待したいと思います。

山﨑さんの発表資料

 質問を受けて、発表者山﨑さんのほうでもお手元で実際の動作の確認を行い、発表資料に追記した上で当日中に資料を公開されています。すばらしい機動力!
 私と山﨑さんそれぞれ独自に検証していたのですが、何処の緯度でも良いのにゼロ度を使った点や別の測地系でもいいのにWGS84をとりあえず使った点など、発想が類似していて嬉しかったです。違いは、私は10度という大づかみな距離を確認したのに対して、山﨑さんは精密に2度の距離を測ることにしたくらいですね。



speakerdeck.com