以前は、Luceneベースのコンテンツ検索プロセス中に、Luceneがテキスト情報と数値情報を取得できることを知り、空間距離がソースコードに実装されているようです。過去6か月間、私はSolRと接触しました。SolRには空間距離検索があります(緯度と経度)。最近、私は実装について学び、空間距離検索を実現するための比較的一般的なテクノロジーであるGeohashがあることを知りました。以下にGeohashを紹介させてください。
Geohash機能
したがって、距離検索を行うときは、Geohashを一致させるだけでいいです。具体的な理由は後で紹介されます。
Geohashの原則
Geohashの最も簡単な説明は、位置情報をソート可能で同等の文字列エンコードに変換することです。以下の実装プロセスについては、以下で詳しく説明します。
最初に、緯度(-90、90)を2つの間隔(-90、0)と(0、90)に分割します。座標位置の緯度値が最初の間隔である場合、エンコードは0です。そうでなければ、エンコードは1です。例として40.222012を使用します。 40.222012は(0、90)に属しているため、エンコードは1です。その後、(0、90)を2つの間隔(0、45)と(45、90)に分割し続け、40.222012は(0、45)にあり、エンコーディングは0、SO。 20回分割し、最終的に40.222012のエンコードが10111001001101000110であることを計算します。
同じ方法が経度に使用され、116.248283のエンコードは110100101010101010101010101010101010101010101010101010101011として取得されます。
次に、緯度と経度のエンコーディングをマージします。奇数は緯度であり、偶数は経度です。結果のエンコーディングは11100111010010010010001101101100110110011001101100100110110です(ここで特に注意が必要です。
最後に、base32がエンコードされます。バイナリ文字列に対応する小数は、それぞれ28、29、4、24、27、6、1、22です。 base32への変換はWX4SV61Qであるため、(40.222012、116.248283)はWX4SV61Qとしてエンコードされています。 (次の図は、base32の対応を紹介します)
マップ上のコードWX4SV61Qの対応する場所は次のとおりです。
ここでは、Geohashのエンコード長は8で、精度は19メートルです。次の表には、異なるエンコード長に対応する精度を示します。
上記の精度から、私(40.222012、116.248283)以内にアイテムを選択する場合、wx4SVをプレフィックスとして項目の座標に対応するジオハッシュを見つける必要があることがわかります。
Geohash拡張機能
これまでのところ、空間インデックスについては特定の理解がありますが、上記の紹介では、次の状況のいずれかを達成できません。
図から、赤いドットは上の緑のドットに近く、下の緑のドットから遠くにあることがわかりますが、赤い点は下の緑のドットのエンコードされた文字列と同じで、両方ともWX4G0です。 Geohashなどの境界問題を解決するという考えは非常に簡単です。検索またはクエリするとき、周囲の8つの領域を一致させます。これは、境界問題をうまく解決できます。次に、JavaにGeohashを実装します。
Javaの実装
実装前に、最初にLocationBeanを定義し、それを使用して緯度と経度の情報を表します。
/ ***@説明:緯度と経度情報を保存*/パッケージcom.lulei.geo.bean; public class locationbean {public static final double minlat = -90; public static final double maxlat = 90; public static final double minlng = -180; public static final double maxlng = 180;プライベートダブルラット; //緯度[-90,90] private double lng; //経度[-180,180] public locationbean(double lat、double lng){this.lat = lat; this.lng = lng; } public double getlat(){return lat; } public void setlat(double lat){this.lat = lat; } public double getlng(){return lng; } public void setlng(double lng){this.lng = lng; }}次に、Geohashを実装するためにクラスを書きます。 Geohashを実装する過程で、次のように、いくつかの定数と緯度および経度情報を定義する必要があります。
パブリッククラスGeohash {private locationbean location; /** * 1 2500km; 2 630km; 3 78km; 4 30km * 5 2.4km; 6 610M; 7 76m; 8 19m */ private int hashlength = 8; //緯度と経度は、geohashの長さに変換されますprivate int latlength = 20; //緯度と経度はバイナリ長に変換されますプライベートint lnglength = 20; //経度と経度はバイナリ長プライベートダブルミンラットに変換されます。 'f'、 'g' '、' h '、' j '、' k '、' m '、' n '、' p '、' q '、' r '、' s '、' t '、' u '、' v '、' w '、' x '、' y '、' z '}; } geohashをインスタンス化する場合、いくつかのプロパティを割り当てる必要があります。
public geohash(double lat、double lng){location = new locationbean(lat、lng); setMinlatlng(); } public int gethashlength(){return hashlength; } / *** @author:lulei* @description:緯度と経度の最小単位を設定* / private void setminlatlng(){minlat = locationbean.maxlat -locationbean.minlat; for(int i = 0; i <latlength; i ++){minlat /= 2.0; } minlng = locationbean.maxlng -locationbean.minlng; for(int i = 0; i <lnglength; i ++){minlng /= 2.0; }} Geohashを使用するときは、最終エンコード長を設定する必要があるため、Geohashの長さを設定する方法を作成します
public boolean sethashlength(int length){if(length <1){return false; } hashlength = length; latlength =(length * 5) / 2; if(length%2 == 0){lnglength = latlength; } else {lnglength = latlength + 1; } setMinlatlng(); trueを返します。 }これらの設定では、経度と緯度を対応するバイナリエンコーディングに変換する必要があります
private boolean [] gethasharray(double value、double min、double max、int length){if(value <min || value> max){return null; } if(length <1){return null; } boolean [] result = new boolean [length]; for(int i = 0; i <length; i ++){double mid =(min+max) / 2.0; if(value> mid){result [i] = true; min = mid; } else {result [i] = false; max = mid; }} return result; }それぞれ緯度と経度のバイナリエンコードを取得した後、2つのバイナリ文字列を1つにマージする必要があります
private boolean [] Merge(boolean [] latarray、boolean [] lngarray){if(latarray == null || lngarray == null){return null; } boolean [] result = new boolean [lngarray.length + latarray.length]; arrays.fill(result、false); for(int i = 0; i <lngarray.length; i ++){result [2 * i] = lngarray [i]; } for(int i = 0; i <latarray.length; i ++){result [2 * i+1] = latarray [i]; } return result; }最後に、取得したバイナリ変換のベース32変換が必要です
/ ** * @param lat * @param lng * @return * @author:lulei * @description:base32 latitude and rutitude */ private string getGeoHashBase32(double lat、double lng){boolean [] bools = getGeobinary(lat、lng); if(bools == null){return null; } stringbuffer sb = new StringBuffer(); for(int i = 0; i <bools.length; i = i + 5){boolean [] base32 = new boolean [5]; for(int j = 0; j <5; j ++){base32 [j] = bools [i+j]; } char cha = getBase32Char(base32); if( '' == cha){return null; } sb.append(cha); } return sb.toString(); } / ** * @param base32 * @return * @author:lulei * @description:5ビットのバイナリをbase32 * / private char getBase32Char(base32){if(base32 == null || base32.length!= 5){return ''; {return ''; } int num = 0; for(boolean bool:base32){num << = 1; if(bool){num += 1; }} return chars [num%chars.length]; } 8つの周辺地域のジオハッシュ値を取得する方法の問題については、次の変換を行うことができます。現在のポイントの緯度と経度をすでに知っており、各エリアの経度と緯度の幅も知っています。経度が添加または減算された場合、領域の左右の領域の経度に配置できます。緯度が追加または差し引かれた場合、エリアの上部および下部の緯度を取得して、エリア周辺の8つのエリアのポイントの座標を取得できるようにします。これらの8つのポイントの座標を計算します。これは、8つの領域に対応するGeohashコードです。
パブリックリスト<String> getGeoHashBase32For9(){double leftlat = location.getlat() - minlat; double rightlat = location.getlat() + minlat; double uplng = location.getlng() - minlng; Double downlng = location.getlng() + minlng; List <String> base32for9 = new ArrayList <String>(); // leftupの3つの文字列= getGeoHashBase32(Leftlat、uplng); if(!(leftup == null || "" .equals(leftup))){base32for9.add(leftup); } string leftmid = getgeohashbase32(leftlat、location.getlng()); if(!(leftmid == null || "" .equals(leftmid))){base32for9.add(leftmid); } string leftdown = getGeoHashBase32(Leftlat、downlng); if(!(leftdown == null || "" .equals(leftdown))){base32for9.add(leftdown); } //上から上から下まで中央の3つの文字列= getGeoHashBase32(location.getLat()、uplng); if(!(midup == null || "" .equals(midup))){base32for9.add(midup); } string midmid = getGeoHashBase32(location.getLat()、location.getlng()); if(!(midmid == null || "" .equals(midmid))){base32for9.add(midmid); } string middown = getGeoHashBase32(location.getLat()、downlng); if(!(middown == null || "" .equals(middown))){base32for9.add(middown); } //右側から右側に右に右に3つの文字列= getGeoHashBase32(右lat、uplng); if(!(rightup == null || "" .equals(rightup))){base32for9.add(rightup); } string rightmid = getGeoHashBase32(rightlat、uplng); if(!(rightup == null || "" .equals(rightup))){base32for9.add(rightup); } string rightmid = getGeoHashBase32(rightlat、location.getlng()); if(!(rightmid == null || "" .equals(rightmid))){base32for9.add(rightmid); } string rightdown = getGeoHashBase32(右lat、downlng); if(!(rightdown == null || "" .equals(rightdown))){base32for9.add(rightdown); } base32for9を返します。 }実行結果
完全なコード
上記のブログにはすでに完全なloactationbeanコードがあるので、ここには書きません。
/ ***@説明:Geohashは緯度と経度の変換を実現します*/パッケージcom.lulei.geo; java.util.arraylistをインポートします。 java.util.arraysをインポートします。 java.util.listをインポートします。 com.lulei.geo.bean.locationbeanをインポートします。 com.lulei.util.jsonutilをインポートします。パブリッククラスGeohash {private locationbean location; /** * 1 2500km; 2 630km; 3 78km; 4 30km * 5 2.4km; 6 610m; 7 76m; 8 19m */ private int hashlength = 8; //緯度と経度はgeohash lengthに変換されますprivate int latlength = 20; //緯度と経度はバイナリ長に変換されますプライベートint lnglength = 20; //緯度と経度は、バイナリ長プライベートダブルミンラットに変換されます。 //各緯度のプライベートダブルミンのユニットサイズ。 //各経度の私的な静的最終char [] chars = {'0'、 '1'、 '2'、 '3'、 '4'、 '5'、 '6'、 '7'、 '9'、 'b'、 'c'、 'd'、 'e'、 'f'、 'g' '、' '' 'p 's'、 't'、 'u'、 'v'、 'w'、 'x'、 'y'、 'z'}; public geohash(double lat、double lng){location = new locationbean(lat、lng); setMinlatlng(); } public int gethashlength(){return hashlength; } / *** @author:lulei* @description:緯度と経度の最小単位を設定* / private void setminlatlng(){minlat = locationbean.maxlat -locationbean.minlat; for(int i = 0; i <latlength; i ++){minlat /= 2.0; } minlng = locationbean.maxlng -locationbean.minlng; for(int i = 0; i <lnglength; i ++){minlng /= 2.0; }} / ** * @return * @author:lulei * @description:9つの座標ポイントと周囲のポイントを見つけます * / publicリスト<string> getGeoHashBase32for9(){double leftlat = location.getlat() - minlat; double rightlat = location.getlat() + minlat; double uplng = location.getlng() - minlng; Double downlng = location.getlng() + minlng; List <String> base32for9 = new ArrayList <String>(); // leftupの3つの文字列= getGeoHashBase32(Leftlat、uplng); if(!(leftup == null || "" .equals(leftup))){base32for9.add(leftup); } string leftmid = getgeohashbase32(leftlat、location.getlng()); if(!(leftmid == null || "" .equals(leftmid))){base32for9.add(leftmid); } string leftdown = getGeoHashBase32(Leftlat、downlng); if(!(leftdown == null || "" .equals(leftdown))){base32for9.add(leftdown); } //上から上への3つの文字列中央の途中で= getGeoHashBase32(location.getlat()、uplng); if(!(midup == null || "" .equals(midup))){base32for9.add(midup); } string midmid = getGeoHashBase32(location.getLat()、location.getlng()); if(!(midmid == null || "" .equals(midmid))){base32for9.add(midmid); } string middown = getGeoHashBase32(location.getLat()、downlng); if(!(middown == null || "" .equals(middown))){base32for9.add(middown); } //右側の上から下への3文字右if(!(rightup == null || "" .equals(rightup))){base32for9.add(rightup); } string rightmid = getGeoHashBase32(rightlat、location.getlng()); if(!(rightmid == null || "" .equals(rightmid))){base32for9.add(rightmid); } string rightdown = getGeoHashBase32(右lat、downlng); if(!(rightdown == null || "" .equals(rightdown))){base32for9.add(rightdown); } base32for9を返します。 } / ** * @param length * @return * @author:lulei * @description:緯度と経度をgeohash lengthに設定 * / public boolean sethashlength(int length){if(length <1){return false; } hashlength = length; latlength =(length * 5) / 2; if(length%2 == 0){lnglength = latlength; } else {lnglength = latlength + 1; } setMinlatlng(); trueを返します。 } / ** * @return * @author:lulei * @description:latitude and ratitudeのbase32文字列を取得 * / public string getgeohashbase32(){return getgeohashbase32(location.getlat()、location.getlng()); } / ** * @param lat * @param lng * @return * @author:lulei * @description:latitude and ratitude and経度のbase32文字列を取得 * / private string getgeohashbase32(double lat、double lng){boolean [] bools = getgeobinary(lat、lng); if(bools == null){return null; } stringbuffer sb = new StringBuffer(); for(int i = 0; i <bools.length; i = i + 5){boolean [] base32 = new boolean [5]; for(int j = 0; j <5; j ++){base32 [j] = bools [i+j]; } char cha = getBase32Char(base32); if( '' == cha){return null; } sb.append(cha); } return sb.toString(); } / ** * @param base32 * @return * @author:lulei * @description:5ビットのバイナリをbase32 * / private char getBase32Char(base32){if(base32 == null || base32.length!= 5){return ''; {return ''; } int num = 0; for(boolean bool:base32){num << = 1; if(bool){num += 1; }} return chars [num%chars.length]; } / ** * @param lat * @param lng * @return * @author:lulei * @description:desscription:get of Coordinates * / private boolean [] getGeobinary [] getgeobinary(double lat、double lng){boolean [] latarray = gethasray(lat、locationbean.minlat、locationbean.maxlat、minlat) boolean [] lngarray = gethasharray(lng、locationbean.minlng、locationbean.maxlng、lnglength); Return Merge(LatArray、lngarray); } / ** * @param latarray * @param lngarray * @return * @author:lulei * @description:マージ緯度と経度バイナリ * / private boolean [] merge(boolean [] latarray、boolean [] lngarray){if(latarray = null | lngarray == null) } boolean [] result = new boolean [lngarray.length + latarray.length]; arrays.fill(result、false); for(int i = 0; i <lngarray.length; i ++){result [2 * i] = lngarray [i]; } for(int i = 0; i <latarray.length; i ++){result [2 * i+1] = latarray [i]; } return result; } / ** * @param value * @param min * @param max * @return * @author:lulei * @description:数字をgeohash binary string * / private boolean [] gethasharray [] gethasharray [] gethasharray [] gethasharray(] gethasharray [] gethasharray [] gethasharray [] gethasharray(double min、double max、int length){if(value <min || value> max)null; } if(length <1){return null; } boolean [] result = new boolean [length]; for(int i = 0; i <length; i ++){double mid =(min+max) / 2.0; if(value> mid){result [i] = true; min = mid; } else {result [i] = false; max = mid; }} return result; } public static void main(string [] args){// todo auto-fenated method stub geohash g = new Geohash(40.222012、116.248283); System.out.println(g.getgeohashbase32()); System.out.println(jsonutil.parsejson(g.getgeohashbase32for9())); }}上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。