こんにちは!出前館開発本部のロバートです。
現在、出向先の出前館で地理情報サービスの開発に携わっています。
出前館では、位置情報(例えば、緯度経度)を基に関連するデータを検索したい場面が多くあります。典型的な例としては、以下のようなものがあります:
- 配達先がある店舗の配達領域に含まれているかどうかを確認したい場合
- 特定の配達先に配送可能な店舗の一覧を取得したい場合
- 行政区画(例:都道府県や市区町村)ごとにGMVやドライバーのマッチ率などのKPIを統計したい場合
- この場合、配達先やドライバーの緯度経度を基に、その位置を包含する領域を調べる必 要があります。
論理的には、行政区画などのポリゴン(多角形)データを持っていれば、上記のクエリを実現できます。しかし、実際に実装してみると、緯度経度に基づいてポリゴンをクエリする際に速度の問題がありました。そこで、高速な検索を実現するために、空間インデックスを導入することを決定し、H3という技術を採用しました。
本記事では、H3を用いてポリゴンを空間的にインデックス化する方法をいくつかご紹介したいと思います。
そもそもH3とは?
H3というのはUber社が開発した六角形に基づいた、オープンソースの空間インデックスです。
地理空間インデックスの一種で、地球を多数の六角形(セル)に分割する仕組みになっています。
- 正確に言うと、12個の五角形も入っているので、六角形の多いサッカーボールに近いイメージです。
- そして、16段階の解像度が提供されており、グリッドの細かさを調整できます。
各セルに識別子が付与されているため、インデックスとして利用可能です。
位置情報を持つデータをインデックス単位で集約するユースケースが一般的です。単なる位置情報(例:地点)の場合はその座標がどのセルに含まれているかを確認することで、H3インデックスを算出できます。アルゴリズムは比較的単純で、曖昧な部分がほとんどないため、把握しやすいです。
一方で、ポリゴン・多角形の場合はどのように対応すればいいでしょうか?座標と異なり、1つ以上のH3セルにマッピングするのが案外難 しいです。
例えば、H3セルの中心点がポリゴンに包含されている場合、対象にしますか?
- それとも、H3セルが完全に包含されている場合のみを対象にしますか?
- あるいは、ポリゴンに交差しているH3セルをすべて対象にしますか?
H3や空間インデックスの文脈において、こういう「マッピング方法」は「インデックス方法」とも呼びます。
これから上記に登場したインデックス方法をそれぞれ説明します。
インデックス方法(1):中心点(Center Point)
H3の標準的なインデックス方法はセルの中心点を基にした方法です。
H3ライブラリには対応する関数が備わっており、デフォルトで使用されているため、ポリゴンにおいて「H3インデックス」と言えば、最初に思い浮かぶのはこの方法でしょう。
アルゴリズムは上記に述べた通り、わりと簡単です:
- H3セルの中心点がポリゴンの内側に入っていれば、対象にします。
結果は以下のようになります:
このインデックス方法には以下のメリットとデメリットがあります。
メリット | デメリット |
---|---|
アルゴリズムが簡単で、処理が速い | H3セルがポリゴンに重なっていても対象外となる場合がある ・言い換えると、抜け穴・隙間ができてしまう |
標準的なアルゴリズムのため、追加開発が 不要 | H3セルがポリゴンの境界線からはみ出る場合がある |
理解しやすい・説明しやすい |
インデックス方法(2):交差(Intersection)
ポリゴンの境界線とH3セルの境界線が交差している場合、そのセルを対象にする方法です。
- 中心部分を省きたくないため、完全に包含されているセルも対象にします。
- つまり、H3セルが少しだけでもポリゴンに重なっていれば、対象となるということです。
H3ライブラリにはこのインデックス方法を実装する関数(polygonToCellsExperimental)があります。
- この機能は現時点ではexperimentalな機能として提供されています。
CONTAINMENT_OVERLAPPING
を渡すと、ここで話している交差のインデックス方法と同等の結果が得られます。
アルゴリズムは以下の通りです:
- まず、中心点に基づいたインデックス方法((1))を利用し、ベースとなるセルを算出します。
- このセルは全部結果セットに追加します。
- 各ベースセルについて:
- 既にチェック済みの場合はスキップします。
- 近隣のセルを算出し、ポリゴンの境界線に交差しているかどうかを確認します。
- gridDiskの関数を使用することで、近隣のセルを算出できます。
- 検索領域をどれだけ拡張するかはデータや要件によります。
- 交差している場合は結果セットに追加します。
結果は以下のようになります:
中心点アルゴリズムと比較すると、交差のアルゴリズムの方が複雑ですが、以下のメリット(とデメリット)があります。
メリット | デメリット |
---|---|
位置(緯度経度)がポリゴンの内側にあるけど、その位置を含むH3セルがポリゴンに該当しないケースが起きない | アルゴリズムがH3ライブラリに入っているが、最近追加された機能で、まだステーブルではない ・APIはまだ固まっていないので、変わる可能性がありそう |
ポリゴンの空間インデックスとしてH3セルを使う場合はミス・漏れがない | 中心点のアルゴリズムと比べると、実装が複雑になる ・交差チェックの範囲をどこまで広げるか決める必要がある |
理解しやすい・説明しやすい | H3セルがポリゴンの境界線からはみ出るケースが発生する(ほぼ確実に) |
インデックス方法(3):包含(Containment)
H3セルがポリゴン内に完全に包含されている場合、そのセルを対象にする方法です。
- この方法の特徴は、H3セルがポリゴンの境界線からはみ出るケースが発生しないことです。
また、このインデックス方法を実装する関数がH3ライブラリに存在します:
- この機能も現時点ではexperimentalな機能として提供されています。
- polygonToCellsExperimentalに
CONTAINMENT_FULL
を渡すと、ここで話している包含のインデックス方法と同等の結果が得られます。
アルゴリズムは以下の通りです:
- まず、中心点に基づいたインデックス方法((1))を利用し、ベースとなるセルを算出します。
- このセルはすべて結果セットに追加します。
- 各ベースセルについて:
- 既にチェック済みの場合はスキップします。
- ポリゴンの境界線に交差しているかどうかを確認します。
- 交差している場合は結果セットから削除します。
結果は以下のようになります:
メリット | デメリット |
---|---|
H3セルがポリゴンの境界線からはみ出るケースが発生しない | ポリゴンがあまりにも小さい、あるいは解像度が粗い場合はポリゴンに完全に包含されているH3セルが存在しないため、どのH3セルでも適用されない可能性がある |
アルゴリズムがH3ライブラリに入っているが、最近追加された機能で、まだステーブルではない ・APIはまだ固まっていないので、変わる可能性がありそう | |
中心点のアルゴリズムと比べると、実装が複雑になる |
どのインデックス方法を使えばいいの?
3つのインデックス 方法を紹介しましたが、どれが一番いいでしょうか?
実は正解がないのです。
ニーズによって、最も適切な方式が変わるので、ユースケース次第です。
とはいえ、それぞれの方法には特徴があるため、特定のユースケースに対してどのインデックス方法が適しているかを整理できます。
典型的なユースケースを見てみましょう:
ユースケース | インデックス方法 | 理由 |
---|---|---|
ポリゴンの形を表す | 中心点(Center Point) | 大抵の場合、ポリゴンの形に最も一致するH3セルの配列が生成される |
ある座標がポリゴンの内側にあるかどうかを正確に確認したい
| 交差(Intersection) | H3セルがポリゴンを完全にカバーするため、抜け穴が発生しない
|
包含(Containment) | H3セルがポリゴンからはみ出ることはない
|
上記の表からわかるように、1つのユースケースに対して複数のインデックス方法が適している場合があります。
さまざまな組み合わせを試して、自分のユースケースに最適なものを見つけましょう。