近隣の人を検索する検索アルゴリズムの実現
5934 ワード
モバイル端末の普及に伴い、多くのアプリケーションがLBS機能に基づいており、近くの某某(レストラン、銀行、妹紙など)が利用されている.
基礎データには、一般的に目標位置の経緯度が保存されている.ユーザが提供する緯度を用いて,比較を行い,近傍にあるか否かを得る.
ターゲット:
近くのXXXを探して、近いから远いまで结果を返して、しかも结果の中で目标点との距离があります.
近くのXXXを探すために、方案は以下の通りです.
Geohashアルゴリズム;geohashは2次元の緯度を1次元の文字列に符号化できるアドレス符号化である.
以下に、具体的な実装例を示します.
例えば、成都永豊立交の符号化はwm 3 yr 31 d 2524である
メリット:
1、1つのフィールドを利用して、経緯度を記憶することができる.検索時にインデックスを1つだけ使用すると、より効率的です.
2、符号化されたプレフィックスはより大きな領域を表すことができ、近くのものを探すのに便利である.SQLでは、LIKE「wm 3 yr 3%」で、近くのすべての場所を検索できます.
3、符号化精度により座標をぼかし、プライバシー保護などができる.
欠点:距離とソートには二次演算が必要です(フィルタ結果で実行されますが、実は速いです)
具体的なアルゴリズムの手順は次のとおりです.
1、geohashの符号化アルゴリズム
成都永豊立交経緯度(30.635578104.031601)
1.1、緯度範囲(-90,90)は2つの区間(-90,0)、(0,90)に分けられ、目標緯度が前の区間にある場合は0、そうでない場合は1となる.
30.625265は(0,90)に属するため、符号化は1とする.
次に(0,90)を(0,45)、(45,90)の2つの区間に分け、39.9324は(0,45)に位置するので0と符号化され、
次に(0,45)を(0,22.5)、(22.5,45)の2つの区間に分け、39.9324は(22.5,45)に位置するので1と符号化され、
順次類推すると、永豊立交緯度符号化は101010111001001001010100となる.
1.2、経度も同様のアルゴリズムで(-180,180)を順次細分化し、(-180,0)、(0180)符号化11001001111110100110000000000
1.3、経緯度符号化を合併し、高から低まで、まず1つの経度を取り、もう1つの緯度を取る.結果1110010010011111101011100011000010110000001001000100000100
1.4、0-9、b-z(a,i,l,oを除く)の32文字でbase 32符号化を行い、wm 3 yr 31 d 2524の符号化を得た(30.635578104.031601).
11100 10011 00011 11110 10111 00011 00001 01100 00010 00101 00010 00100 => wm3yr31d2524
10進0 1 2 3 4 5 6 7 8 9 10 11 13 14 15
base32 0 1 2 3 4 5 6 7 8 9 b c d e f g
十進法16 17 18 19 20 21 22 24 25 26 27 29 31
base32 h j k m n p q r s t u v w x y z
2、具体的な一致策略
1、緯度と経度が入庫する時、データベースは新しく1フィールドのgeohashをプラスして、この点のgeohash値を記録します
2、近くを探して、SQLの中でLIKE‘wm 3 yr 3%’;この結果をキャッシュできます.小さな領域では、緯度を変更するためにデータベース・クエリーを再実行することはありません.
3、検索した有限な結果、例えば距離或いは並べ替えを要求する場合、距離公式と2次元データの並べ替えを利用することができる.この時も少量のデータで、すぐにできます.
基礎データには、一般的に目標位置の経緯度が保存されている.ユーザが提供する緯度を用いて,比較を行い,近傍にあるか否かを得る.
ターゲット:
近くのXXXを探して、近いから远いまで结果を返して、しかも结果の中で目标点との距离があります.
近くのXXXを探すために、方案は以下の通りです.
Geohashアルゴリズム;geohashは2次元の緯度を1次元の文字列に符号化できるアドレス符号化である.
以下に、具体的な実装例を示します.
例えば、成都永豊立交の符号化はwm 3 yr 31 d 2524である
メリット:
1、1つのフィールドを利用して、経緯度を記憶することができる.検索時にインデックスを1つだけ使用すると、より効率的です.
2、符号化されたプレフィックスはより大きな領域を表すことができ、近くのものを探すのに便利である.SQLでは、LIKE「wm 3 yr 3%」で、近くのすべての場所を検索できます.
3、符号化精度により座標をぼかし、プライバシー保護などができる.
欠点:距離とソートには二次演算が必要です(フィルタ結果で実行されますが、実は速いです)
具体的なアルゴリズムの手順は次のとおりです.
1、geohashの符号化アルゴリズム
成都永豊立交経緯度(30.635578104.031601)
1.1、緯度範囲(-90,90)は2つの区間(-90,0)、(0,90)に分けられ、目標緯度が前の区間にある場合は0、そうでない場合は1となる.
30.625265は(0,90)に属するため、符号化は1とする.
次に(0,90)を(0,45)、(45,90)の2つの区間に分け、39.9324は(0,45)に位置するので0と符号化され、
次に(0,45)を(0,22.5)、(22.5,45)の2つの区間に分け、39.9324は(22.5,45)に位置するので1と符号化され、
順次類推すると、永豊立交緯度符号化は101010111001001001010100となる.
1.2、経度も同様のアルゴリズムで(-180,180)を順次細分化し、(-180,0)、(0180)符号化11001001111110100110000000000
1.3、経緯度符号化を合併し、高から低まで、まず1つの経度を取り、もう1つの緯度を取る.結果1110010010011111101011100011000010110000001001000100000100
1.4、0-9、b-z(a,i,l,oを除く)の32文字でbase 32符号化を行い、wm 3 yr 31 d 2524の符号化を得た(30.635578104.031601).
11100 10011 00011 11110 10111 00011 00001 01100 00010 00101 00010 00100 => wm3yr31d2524
10進0 1 2 3 4 5 6 7 8 9 10 11 13 14 15
base32 0 1 2 3 4 5 6 7 8 9 b c d e f g
十進法16 17 18 19 20 21 22 24 25 26 27 29 31
base32 h j k m n p q r s t u v w x y z
2、具体的な一致策略
1、緯度と経度が入庫する時、データベースは新しく1フィールドのgeohashをプラスして、この点のgeohash値を記録します
2、近くを探して、SQLの中でLIKE‘wm 3 yr 3%’;この結果をキャッシュできます.小さな領域では、緯度を変更するためにデータベース・クエリーを再実行することはありません.
3、検索した有限な結果、例えば距離或いは並べ替えを要求する場合、距離公式と2次元データの並べ替えを利用することができる.この時も少量のデータで、すぐにできます.
import java.util.HashMap;
import java.util.Map;
public class GeoHashKit {
private static char[] base32 = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' };
private final static Map<Character, Integer> decodemap = new HashMap<Character, Integer>();
static {
int sz = base32.length;
for (int i = 0; i < sz; i++) {
decodemap.put(base32[i], i);
}
}
private static int precision = 10;
private static int[] bits = { 16, 8, 4, 2, 1 };
/**
*
* @param int precision ,
*
* */
public static void setPrecision(int precision) {
GeoHashKit.precision = precision;
}
public static double getPrecision(double x, double precision) {
double base = Math.pow(10, -precision);
double diff = x % base;
return x - diff;
}
public static String encode(double latitude, double longitude) {
double[] lat_interval = { -90.0, 90.0 };
double[] lon_interval = { -180.0, 180.0 };
StringBuilder geohash = new StringBuilder();
boolean is_even = true;
int bit = 0, ch = 0;
while (geohash.length() < precision) {
double mid = 0.0;
if (is_even) {
mid = (lon_interval[0] + lon_interval[1]) / 2;
if (longitude > mid) {
ch |= bits[bit];
lon_interval[0] = mid;
} else {
lon_interval[1] = mid;
}
} else {
mid = (lat_interval[0] + lat_interval[1]) / 2;
if (latitude > mid) {
ch |= bits[bit];
lat_interval[0] = mid;
} else {
lat_interval[1] = mid;
}
}
is_even = is_even ? false : true;
if (bit < 4) {
bit++;
} else {
geohash.append(base32[ch]);
bit = 0;
ch = 0;
}
}
return geohash.toString();
}
public static double[] decode(String geohash) {
double[] ge = decode_exactly(geohash);
double lat, lon, lat_err, lon_err;
lat = ge[0];
lon = ge[1];
lat_err = ge[2];
lon_err = ge[3];
double lat_precision = Math.max(1, Math.round(-Math.log10(lat_err))) - 1;
double lon_precision = Math.max(1, Math.round(-Math.log10(lon_err))) - 1;
lat = getPrecision(lat, lat_precision);
lon = getPrecision(lon, lon_precision);
return new double[] { lat, lon };
}
public static double[] decode_exactly(String geohash) {
double[] lat_interval = { -90.0, 90.0 };
double[] lon_interval = { -180.0, 180.0 };
double lat_err = 90.0;
double lon_err = 180.0;
boolean is_even = true;
int sz = geohash.length();
int bsz = bits.length;
double latitude, longitude;
for (int i = 0; i < sz; i++) {
int cd = decodemap.get(geohash.charAt(i));
for (int z = 0; z < bsz; z++) {
int mask = bits[z];
if (is_even) {
lon_err /= 2;
if ((cd & mask) != 0) {
lon_interval[0] = (lon_interval[0] + lon_interval[1]) / 2;
} else {
lon_interval[1] = (lon_interval[0] + lon_interval[1]) / 2;
}
} else {
lat_err /= 2;
if ((cd & mask) != 0) {
lat_interval[0] = (lat_interval[0] + lat_interval[1]) / 2;
} else {
lat_interval[1] = (lat_interval[0] + lat_interval[1]) / 2;
}
}
is_even = is_even ? false : true;
}
}
latitude = (lat_interval[0] + lat_interval[1]) / 2;
longitude = (lon_interval[0] + lon_interval[1]) / 2;
return new double[] { latitude, longitude, lat_err, lon_err };
}
public static void main(String[] args) {
GeoHashKit ghf = new GeoHashKit();
String gc1 = ghf.encode(31.277631, 120.53916300000003);
String gc2 = ghf.encode(51.4797, -0.0124);
System.out.println(gc1);
System.out.println(gc2);
double[] gd1 = ghf.decode(gc1);
double[] gd2 = ghf.decode(gc2);
System.out.println(gd1[0] + ", " + gd1[1]);
System.out.println(gd2[0] + ", " + gd2[1]);
}
}