これは、RDBMS-GIS(MySQL,PostgreSQLなど) Advent Calendar 2020 の2日目のエントリです。
MySQLのリファレンスマニュアルは、開発チーム内部でそれが実装されたタイミングで記述、公開されるルールになっているのか、まだリリースされていないバージョンの情報が掲載されていることがあります。Spatial (GIS) 関連の機能も同様なので、今後の機能をいち早く知るために、私は頻繁にチェックをしています。最近全然拡張がなかったSpatial関連が「再始動」したかに見えるような変化あったので、紹介したいと思います。詳しい方からの補足、誤りのご指摘等を歓迎いたします。
概要
MySQL 8.0.23 (2021年1月リリースとみられる)で追加されるのは、ST_FrechetDistance() と ST_HausdorffDistance() の2つ。
MySQL 8.0.24 (2021年4月リリースとみられる)で追加されるのは、ST_LineInterpolatePoint()、ST_LineInterpolatePoints()、ST_PointAtDistance() の3つです。
最近おとなしかった Spatial 機能にしては、大漁大漁です。マニュアルの (Spatialの)関数一覧のページを見ると、「introduced 8.0.2x」のように書いてあることで、新バージョンで加わった(加わる)機能であることを確認できます。
MySQL :: MySQL 8.0 Reference Manual :: 12.17.1 Spatial Function Reference
ST_FrechetDistance() と ST_HausdorffDistance()
8.0.23 で追加されるらしき、これらの2つの関数。私も初めて見たものなので付け焼き刃の知識ですが、説明を試みます。
ST_FrechetDistance() はフレシェ関数、、とくに今回のMySQLでの実装では「離散フレシェ距離」を求めるものです。2つのジオメトリの類似度を数値で返す、と説明されていますが、要するに「2つのジオメトリの点どうしの距離をそれぞれ比較して、それぞれ一番近いものの値のうち、一番遠いものの距離を返す」というものです。これでわかったら、あなたは天才(笑)。
マニュアルには、以下の例が示されているので、これを使います。地理座標系を使うと確認ややこしいので、とりあえずデカルトで考えます(結果は「距離」で返ってくるため)。
mysql> SET @ls1 = ST_GeomFromText('LINESTRING(0 0,0 5,5 5)'); mysql> SET @ls2 = ST_GeomFromText('LINESTRING(0 1,0 6,3 3,5 6)'); mysql> SELECT ST_FrechetDistance(@ls1, @ls2); +--------------------------------+ | ST_FrechetDistance(@ls1, @ls2) | +--------------------------------+ | 2.8284271247461903 | +--------------------------------+
これは略図を書くと、以下のような2つの LINESTRING を示しています。
@ls1 が、緑色の線 PQR
@ls2 が、紫色の線 ABCD
です。
この、@ls1 を構成する点(P, Q, R) それぞれから、@ls2 を構成する点 (A, B, C, D)それぞれへの長さを調べるのですが、順を追って書くと、まず 点Pから A, B, C, D それぞれへの距離を調べると一番近いのは、点Aで長さ1。点Qから、点Rからも同様に調べていき、このケースではどれも最小値は 1 となることがわかります。更に逆側、点Aから P,Q,Rそれぞれへの距離を調べると一番近いのはPで距離1(中略)、点Cからは 点Rが最も近く、距離は 2.828427。
これらをまとめたのが下表です。
つまり、フレシェ関数は、2つのジオメトリ(ここではLINESTRING)を構成するそれぞれの点の集合どうしの距離のうち、最も近い物の中で、もっとも大きい数字を返すという説明になるのです。線そのものはまったく見ずに、点のみに着目するので「離散」フレシェと呼ばれます。 個人的には、これ、すごく類似の2つのジオメトリがあったときに、その点の粒度が異なる(片方はある程度長い線分を多様して、もう一方はこまかく左右にぶれたところまで表現しているような)場合に、不用意に大きな数字が返されてしまい、比較の用に供せないのではないかと想像するのですが、どうなんでしょうね。 PostGISだったかな、他のRDBMSでは、第3引数に(unitではなく)密度を上げる=線分を何分割化にわけて点を増やす=ためのオプションがあります。
あと、MySQL 8.0.23 では、ジオメトリとして LINESTRING のみに対応しているそうです。
えー、フレシェ距離だけで、書き過ぎちゃったので、あとは軽めに。
ST_HausdorffDistance() はハウスドルフ距離を返す関数。こちらは、LINESTRING, POINT 各シングルとMULTIそれぞれ相互の比較に対応しているそうです。こちらも正確には「離散」ハウスドルフ距離(=線そのものは見ずに構成要素の点だけを比較の対象とする)です。
ST_HausdorffDistance() も用途としては、2つのジオメトリの類似度を判定するために使えるということなのですが、マニュアルに出ている実行例(フレシェ関数と同じ内容)の結果値が「1」というもので、なぜこの値が選ばれたのかイマイチわからず、もっと勉強します。。
ST_LineInterpolatePoint(), ST_LineInterpolatePoints(),ST_PointAtDistance()
MySQL 8.0.24 で実装されるとマニュアルでは表明されている、この3つの関数。どれも、LINESTRINGの経路上のある1点を返す関数です。
ST_LineInterpolatePoint() は、第一引数がLINESTRING、第二引数が 0~1 の範囲の割合。LINESTRINGの経路のうち、指定された割合の位置にあたる場所のPOINTを返すようです。0.5と与えて、ちょうど中間点の位置を得るとかそんな使い方かな。 MySQL独自の拡張の模様。
ST_LineInterpolatePoints() は、上の関数とだいたい似たような感じだけど、指定した割合1回で終わるのではなく、繰り返しになる(0.15と指定した場合は、上の関数(...Point())は、15%の箇所1つを返すが、この関数(...Points())は 15%, 30%, 45%....の各点の座標を MultiPointで返す)。これもMySQL独自。
ST_PointAtDistance() は、割合の代わりに距離を与えるもの。マラソン大会のコースをあらわす LINESTRING がある場合に、この関数を使って、10km地点のPOINTや、35km地点のPOINTを得られる、という使い方なのかな。これもMySQL独自だって。
8.0.24で追加されることになっている関数。わざわざ優先してこれらの独自な関数を入れた、というのが興味深いですね。大きなお客様からの要望が強かったと考えるのが妥当だと思いますし、そうすると、どの会社が、どのサービスで使うためにこの関数を渇望していたのか。現時点では想像もつきませんが、考えるのは楽しいものです。
以上、2021年1月と4月にリリースされるであろう MySQL 8.0 の新バージョンで追加される Spatial関係の関数紹介でした。