近接検索にジオハッシュを使用していますか?


30

ポイントプロキシミティの地理検索時間を最適化しようとしています。

私の入力は緯度、経度のポイントであり、n個の最も近いポイントまで事前に計算された場所のセットを検索しています。

事前に計算された場所のインデックスの作成にかかる時間/スペースは気にしませんが、クエリが非常に高速になることは気にします。

ジオハッシュを検索キーとして使用することを考えています。最初にキーのX文字の結果が得られるかどうかを確認し、次に結果が表示されるまでキーの最後から文字を減らし続けます。

私の(今のところ非常にまばらな)ジオインデックステクニックを理解するために、このアプローチは、他のすべての既知の実装(R Treeやco。


ジオハッシュを使用することと、緯度/経度を東向き/北向きに格納すること(たとえば)に大きな違いはありますか?おそらく両方で、文字/数字をトリミングして検索精度を変更できます。(これは純粋に好奇心からの質問です-私はこのトピックに精通していません)。
djq

これらのポイントはデータベースまたはメモリに保存されていますか?
マークフィスター14

@MarcPfisterこの問題は2年前(私のユースケースでは)ですが、それは常にコミュニティに関係があるので、積極的な議論を続けていきます。議論されたデータは確かにnosqlデータベースに保存されていました。
マキシムVeksler 14

また、この質問が回答されたときから、MongoDBはジオハッシュインデックスと検索を正常に実装しており、これがこの点を証明しています。実装に関するホワイトペーパーはまだ見ていませんが、コードは公開されており、興味のあるすべての関係者が利用できます。
マキシムVeksler 14

ああ、わかった。CouchDBには現在、おそらくジオハッシュを使用した空間インデックスもあります。
マークフィスター14

回答:


25

絶対にできます。そして、それは非常に高速です。(集中的な計算ビットも配布できます)

いくつかの方法がありますが、私が取り組んできた1つの方法は、整数ベースのジオハッシュの順序付きリストを使用して、特定のジオハッシュ解像度(解像度がdistance基準に近い)のすべての最も近いジオハッシュ範囲を見つけることです。これらのジオハッシュ範囲を照会して、近くのポイントのリストを取得します。これにはredisとnodejs(つまりjavascript)を使用します。Redisは非常に高速で、順序付けられた範囲を非常に高速に取得できますが、SQLデータベースで実行できるインデックス作成クエリ操作の多くは実行できません。

メソッドの概要は次のとおりです:https : //github.com/yinqiwen/ardb/wiki/Spatial-Index

しかし、その要点は次のとおりです(リンクを言い換えると)。

  1. ジオハッシュされたポイントはすべて、必要な最適な解像度(アクセス可能な場合は通常最大64ビット整数、javascriptの場合は52ビット)を順序付きセット(redisのzsetなど)に格納します。最近のほとんどのジオハッシュライブラリには、ジオハッシュ整数関数が組み込まれているため、より一般的なbase32ジオハッシュの代わりにこれらを使用する必要があります。
  2. 検索する半径に基づいて、検索領域に一致するビット深度/解像度を見つける必要があります。これは、格納されているジオハッシュビット深度以下でなければなりません。リンクされたサイトには、ジオハッシュのビット深度とその境界ボックス領域をメートル単位で関連付けるテーブルがあります。
  3. 次に、この低解像度で元の座標を再ハッシュします。
  4. その低解像度では、8つの近隣(n、ne、e、se、s、sw、w、nw)ジオハッシュエリアも検出されます。近隣メソッドを実行する必要がある理由は、2つの座標がほぼ真横にあり、ジオハッシュがまったく異なる可能性があるため、検索でカバーされるエリアの平均化を行う必要があるためです。
  5. この低解像度ですべての隣接ジオハッシュを取得したら、ステップ3からの座標のジオハッシュをリストに追加します。
  6. 次に、これら9つのエリアをカバーするジオハッシュ値の範囲を構築する必要があります。手順5の値は範囲の下限であり、それぞれに1を加算すると、範囲の上限が得られます。したがって、9つの範囲の配列が必要です。各範囲には下限と上限のジオハッシュ制限があります(合計18ジオハッシュ)。これらのジオハッシュは、ステップ2からの低解像度のままです。
  7. 次に、これらの18個すべてのジオハッシュを、データベースにすべてのジオハッシュを格納したビット深度/解像度に変換します。一般的には、目的のビット深度にビットシフトすることでこれを行います。
  8. これで、これらの9つの範囲内のポイントに対して範囲クエリを実行でき、元のポイントの距離内にあるすべてのポイントを取得できます。オーバーラップはないので、交差する必要はなく、純粋な範囲のクエリを非常に高速に行う必要があります。(例:redis:ZRANGEBYSCORE zsetname lowerLimit upperLimit、このステップで生成された9つの範囲)

次の方法でさらに最適化できます(速度的に)。

  1. これらの9つの範囲をステップ6から取得し、それらが互いにつながる場所を見つけます。通常、座標の場所に応じて、9つの個別の範囲を約4または5に減らすことができます。これにより、クエリ時間を半分に短縮できます。
  2. 最終レンジを取得したら、それらを再利用できるように保持する必要があります。これらの範囲の計算にはほとんどの処理時間がかかる可能性があるため、元の座標があまり変わらないが、同じ距離のクエリを再度行う必要がある場合は、毎回計算するのではなく、準備を整えておく必要があります。
  3. redisを使用している場合、クエリをMULTI / EXECに結合して、パイプライン処理を行い、パフォーマンスを少し向上させてください。
  4. 最良の部分:計算をすべて1か所で行う代わりに、ステップ2〜7をクライアントに分散できます。これにより、何百万ものリクエストが着信する状況でのCPU負荷が大幅に削減されます。

精度を重視する場合は、返された結果に円距離/ハバーシン型関数を使用することで、精度をさらに向上させることができます。

redisの代わりに通常のbase32ジオハッシュとSQLクエリを使用する同様の手法を次に示します。https: //github.com/davetroy/geohash-js

私は自分のものをプラグインするつもりはありませんが、これを本当に簡単に実装できるようにするnodejs&redisのモジュールを作成しました。必要に応じてコードをご覧くださいhttps : //github.com/arjunmehta/node-georedis


いくつかのフォローアップQ-隣人をどのように計算しますか?整数ハッシュはトリミングを許可します(たとえば、base32 z曲線ベースは許可しません(7はbase32 geohashの8から非常に遠いです)。geohash-jsgithub.com/davetroy/geohash-js /blob/ master / matrix.txt同様?このアルゴリズムは近接ジオポイントを生成すると想定されていますが、geohash-jsは隣接セルのみのO(1)計算を行います
Maxim Veksler

うわー、これはとても便利でした。この対応には非常に多くの専門知識があります。かなりやりがいのある仕事
サイモン

9

質問はいくつかの方法で読むことができます。多数のポイントがあり、座標ペアとして指定された任意のポイントで繰り返しプローブし、nを事前に固定して、プローブに最も近いn個のポイントを取得することを意味すると解釈します。(原則として、nが変化する場合、可能なn ごとにデータ構造を設定し、各プローブでO(1)時間で選択できます。これには非常に長いセットアップ時間がかかり、大量のRAMが必要になる可能性がありますが、そのような懸念を無視するように言われます。)

すべての点の次数nのボロノイ図を作成します。これにより、プレーンが接続された領域に分割され、各領域には同じn近傍があります。これにより、状況がポリゴン内の問題になり、多くの効率的な解決策があります。

ボロノイ図にベクトルデータ構造を使用すると、ポイントインポリゴン検索にはO(log(n))時間かかります。実際には、ダイアグラムのラスターバージョンを作成するだけで、このO(1)を非常に小さな暗黙の係数で作成できます。ラスター内のセルの値は、(i)n個の最も近いポイントのリストへのポインター、または(ii)このセルがダイアグラム内の2つ以上の領域にまたがることを示します。(x、y)の任意の点のテストは次のようになります。

Fetch the cell value for (x,y).
If the value is a list of points, return it.
Else apply a vector point-in-polygon algorithm to (x,y).

O(1)のパフォーマンスを実現するには、ラスタメッシュが十分に細かくなければ、複数のボロノイ領域にまたがるセルに比較的少数のプローブポイントが落ちます。これはいつでも達成できますが、グリッドのストレージに多大な費用がかかる可能性があります。


3

まさにこれのためにジオハッシュを使用します。私がいる理由は、ピラミッドスタイルの情報システムを使用して近接検索を実装する必要があったためです。 。これらの合計は、面積、グラウンドカバーの種類などでした。非常に派手な作業を行うには、非常に派手な方法でした。

したがって、第8レベルのジオハッシュには次のような情報が含まれます。

タイプ:草のエーカー:1.23

そして、7th、6th ..などには次のような情報が含まれます。

grass_types:123エーカー:6502

これは常に最低精度から構築されました。これにより、あらゆる種類の楽しい統計を非常にすばやく行うことができました。また、GeoJSONを使用して、各ジオハッシュリファレンスにジオメトリリファレンスを割り当てることができました。

現在のビューポートを構成する最大のジオハッシュを見つけるための関数をいくつか作成し、それらを使用してビューポート内で2番目に高い精度のジオハッシュを見つけることができました。これは、必要な精度で最小 '86ssaaaa'と最大 '86sszzzz'をクエリするインデックス付き範囲クエリに簡単に拡張できます。

MongoDBを使用してこれを行っています。


3

2018年の更新、およびいくつかの数学的な基金またはGeohashの歴史的実績:

  • ジオハッシュのためのインスピレーションとなった二進数字の単純interlaveおそらく、同様に、小数点以下の桁をインターリーブナイーブアルゴリズムの最適化C二乗

  • バイナリインターレースはZオーダーカーブインデックス戦略を自然にもたらし、Geohashの発明者「最高のフラクタルカーブを探して」開始しませんでした ...しかし、好奇心が強い、この設計最適化、より良いフラクタルカーブは可能です(!)。

S2ジオメトリライブラリを使用する

S2-ジオメトリアプローチは、それが地球の球状トポロジー(キューブ)を使用するため、任意に使用良好THAMジオハッシュである突起(全てのセルが同じ形状及び周辺領域の近くに持っているので)、とを有する割り出しためヒルベルト曲線が良好であるTHAM Z-順序曲線

...より良い結果が得られます...右上から左下のクワッドへの不連続性により、連続しない範囲を分割する必要が生じます。(...)不連続性を完全に排除することができます(...)
blog.notdot.net/2009でのクアッドツリーとヒルベルト曲線を使用した空間インデックス付け

今では無料で効率的なライブラリです。https://s2geometry.ioを参照してください

PS:NodeJSのs2-geometryような(良い)非公式の簡易バージョン、およびs2.sidewalklabs.comのような多くの「プレイグラウンド」、アドイン、デモもあります。


2

redisでGEORADIUSクエリを使用することをお勧めします。

GEOADD呼び出しを使用して、最適なジオハッシュレベルでシャーディングされたデータをプッシュします。

また、これを見てください-> ProximityHash

ProximityHashは、中心座標と半径を指定すると、円形領域をカバーする一連のジオハッシュを生成します。また、GeoRaptorを使用して、最高レベルから開始し、最適なブレンドが作成されるまで繰り返して、さまざまなレベルでジオハッシュの最適な組み合わせを作成して円を表す追加オプションもあります。結果の精度は、開始時のジオハッシュレベルと同じですが、データサイズが大幅に減少するため、速度とパフォーマンスが向上します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.