軽い雰囲気でGo言語のS2 Geometry Libaryを使ってみたけど、使い方が全然分からなくて挫折。そして猛烈に反省。
というのも、ライブラリ中で多用されているCell
という構造体がよく分からない。
にっちもさっちも行かなそうなので、S2 Cellsを読んで理解することにした。
主に、space-filling curve(空間充填曲線)という技術の話である。
S2 Cellsとは
S2ライブラリにおける空間の表現方法
S2では、単位球面上をcell
と呼ばれる領域の集合に分解する。
各cell
は4つの測地線を境界とする四角形である。
cell
は階層を成している
最上位の階層は、正六面体の6つの表面を単位球に被せて得られる。こんな感じか・・・
最下位の階層は、各cell
を再帰的に4つの子cell
に分割し得られる。
例えば、下の図には緑と赤の2つの最上位のcell
がある。赤のcell
は再帰的に4つの子cell
に分割されていることがわかる。
各cell
を構成する辺は曲線である理由は、この辺が測地線であるためである。(測地線は曲線になる。飛行機の航路と同じように。)
cell
はレベルを持つ
cell
が分割された回数が、そのcell
のレベルとなる。レベルの範囲は0から30(計31のレベルがある。)。レベル30のcell
はleaf cell
と呼ばれる。故に、cell
の個数の最大値は以下である。
レベル30のcell
は、地球上の距離に換算すると約1平方cmになる。(細か!すごい!)
各cell
の換算表はこちら。
cell
階層のメリット
cell
階層により、空間インデックスを実装している。
そして、点集合や領域の集合は、cell
によって表現(近似)される。
一般的に、点はleaf cell
、領域は任意のレベルのcell
として表現される。
例えば、ハワイは以下のような22個のcell
の集合で近似される。
S2CellIdとは
各cell
にはユニークな識別子(64ビットの整数)が割り当てられている。これをS2CellId
と呼ぶ。
これにより、空間インデックスを用いた参照局所性が可能となる。
識別子の割り当て方
space-filling curve(空間充填曲線)順に識別子が割り当てられている。
S2では、各曲線をS2 space-filling curve
と呼ぶ。
この曲線は、互いにリンクされた6つのヒルベルト曲線を含む。
これにより、球の表面を1本の連続曲線で埋め尽くすことができ、この連続曲線は全てのcell
を通過する。例えば、レベル5の分割を行なった後、ヒルベルト曲線を描いた図が以下である。
レベル5のcell
は曲線に従い、昇順に識別子が割り当てられる。
したがって、2つのcell
が隣接していれば、それらのS2CellId
も隣接した識別子となる。これにより、局所参照性を実現している。
S2 space-filling curve
曲線はヒルベルト曲線に基づいているため、より詳細に識別子の割り当て方を解説するために、ここから暫くはヒルベルト曲線についての説明が続く。
既知の方は読み飛ばしても構わない。
ヒルベルト曲線の定義
ヒルベルト曲線は、定義域(一次元の範囲)[0,1]から値域(二次元平面上の矩形)[0,1]x[0,1]を持つ関数である。この関数は、単位球上の全ての点を通る曲線となる。ヒルベルト曲線は連続であるが微分可能ではない。また、無限の長さを持つと見なす。
Hをヒルベルト曲線の定義域と値域。
ヒルベルト曲線の作り方
下の図のように、再帰的に正方形を分割しながら作る。
最初の分割において、正方形を4つに分割し、分割された全ての領域を通るように曲線を描く。領域を通る順番は決まっている。この例の場合、左下、左上、右上、右下、の順番で曲線が通る。
次の分割において、最初の分割で得られた領域を、さらに4つに分割し、分割された全ての領域を通るように曲線を描く。(曲線は連続するように上手いこと描かれるが、ここでは詳細は述べない。)
一次元の範囲をどのようにして二次元平面上の矩形へ写すの?
具体例で説明する。H(0.5)を二次元平面上へ写す場合を考える。
0.5を二進数表記にすると、
0.1000000000000...
となる。この2進数の小数部を2ビットで区切る。
[10,00,00,00,00,...]
区切られたものを左から順に見ていく。
最初は10
で、10進数で2。次が00
で、10進数で0。以下同様にして
[2,0,0,0,0,...]
が得られる。
そして、0に左下、1に左上、2に右上、3に右下と割り当てる。
すると、
[右上、左下、左下、左下、左下、...]
となる。
これに従い、以下の図のように再帰的に正方形を分割すると・・・
に収束する。
このようにして、一次元の範囲をどのようにして二次元平面上の矩形へ写す。
S2 space-filling curve
曲線の定義
S2 space-filling curve
は以下の関数である。
この関数の定義域は一次元の範囲[0,6]、値域は単位球面上となる。
一次元の範囲をどのようにして単位球面上へ写すの?
例えば、S(1.4)を単位球面上へ写す場合を考える。
小数部を除いた整数が1。これは、正六面体の6つの表面を単位球に被せて得られる6つの面のどれであるか?を表している。
1の面は、インドが含まれている面。
そして小数部は、先ほど説明したヒルベルト曲線の0.4となる。
このようにして、一次元の範囲をどのようにして単位球面上へ写す。
識別子の割り当て方(再)
具体的なS2CellId
の割り当て方は以下である。
S2CellIdは、そのcellの中心におけるS2 curveパラメータである。(ただし、S2CellIdを整数値にするためにスケーリングされる)
具体的には例で説明する。
レベル0の面2に割り当てられる識別子は
010.1000000000...
小数部を除いた010は2(面2を表す)。
小数部0.10000000は、その面の中心を表す。
010.1000000000...
は整数ではないので、スケーリングして整数にする。具体的には2の61乗をかける。得られる整数値の16進数表記は0x5000000000000000
である。これがレベル0の面2に割り当てられるS2CellId
である。
ヒルベルト曲線の性質上、全てのcellの中心が重なることはないため、全てのS2CellId
は異なる数値となる。
<・・・ここから、ちょっと省略。いつか訳す>