1、使用geohash.encode();方法计算出当前经纬度的位置编码;
例如:(113.76630306243896,34.772396824521024的编码是t4813ym59twz)
2、字符串相似的表示距离相近,使用sql的like查询't481%'即可获得附近的位置,模糊查询位数约大表示距离约近;
3、如果位置位于格子边界两侧的两点,距离非常近,但是位置编码大不相同,可同时搜索当前格子周围的8个格子。使用expand.calculateAdjacent();方法获取当前位置周围8个格子。
4、sql中使用foreach 结合or查询9个格子的位置编码;
5、最后使用DisUtil.GetDistance()获取位置的距离;
参考文章https://blog.csdn.net/shixiaoguo90/article/details/23909687
public class geohash {
private static int numbits = 6 * 5; final static char[] digits = { '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' };
final static HashMap static { int i = 0; for (char c : digits) lookup.put(c, i++); }
public double[] decode(String geohash) { StringBuilder buffer = new StringBuilder(); for (char c : geohash.toCharArray()) {
int i = lookup.get(c) + 32; buffer.append(Integer.toString(i, 2).substring(1)); }
BitSet lonset = new BitSet(); BitSet latset = new BitSet();
// even bits int j = 0; for (int i = 0; i < numbits * 2; i += 2) { boolean isSet = false; if (i < buffer.length()) isSet = buffer.charAt(i) == '1'; lonset.set(j++, isSet); }
// odd bits j = 0; for (int i = 1; i < numbits * 2; i += 2) { boolean isSet = false; if (i < buffer.length()) isSet = buffer.charAt(i) == '1'; latset.set(j++, isSet); } // 中国地理坐标:东经73°至东经135°,北纬4°至北纬53° double lon = decode(lonset, 70, 140); double lat = decode(latset, 0, 60);
return new double[] { lat, lon }; }
private double decode(BitSet bs, double floor, double ceiling) { double mid = 0; for (int i = 0; i < bs.length(); i++) { mid = (floor + ceiling) / 2; if (bs.get(i)) floor = mid; else ceiling = mid; } return mid; }
public String encode(double lat, double lon) { BitSet latbits = getBits(lat, 0, 60); BitSet lonbits = getBits(lon, 70, 140); StringBuilder buffer = new StringBuilder(); for (int i = 0; i < numbits; i++) { buffer.append((lonbits.get(i)) ? '1' : '0'); buffer.append((latbits.get(i)) ? '1' : '0'); } return base32(Long.parseLong(buffer.toString(), 2)); }
private BitSet getBits(double lat, double floor, double ceiling) { BitSet buffer = new BitSet(numbits); for (int i = 0; i < numbits; i++) { double mid = (floor + ceiling) / 2; if (lat >= mid) { buffer.set(i); floor = mid; } else { ceiling = mid; } } return buffer; }
public static String base32(long i) { char[] buf = new char[65]; int charPos = 64; boolean negative = (i < 0); if (!negative) i = -i; while (i <= -32) { buf[charPos--] = digits[(int) (-(i % 32))]; i /= 32; } buf[charPos] = digits[(int) (-i)];
if (negative) buf[--charPos] = '-'; return new String(buf, charPos, (65 - charPos)); }
}
|
public class DisUtil { private static final double EARTH_RADIUS = 6371000;// 赤道半径(单位m)
/** * 转化为弧度(rad) */ private static double rad(double d) { return d * Math.PI / 180.0; }
/** * 基于googleMap中的算法得到两经纬度之间的距离,计算精度与谷歌地图的距离精度差点儿相同。相差范围在0.2米下面 * * @param lon1 第一点的精度 * @param lat1 第一点的纬度 * @param lon2 第二点的精度 * @param lat3 第二点的纬度 * @return 返回的距离,单位m */ public static double GetDistance(double lon1, double lat1, double lon2, double lat2) { double radLat1 = rad(lat1); double radLat2 = rad(lat2); double a = radLat1 - radLat2; double b = rad(lon1) - rad(lon2); double s = 2 * Math.asin(Math.sqrt( Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))); s = s * EARTH_RADIUS; s = Math.round(s * 10000) / 10000; return s; }
} |
public class expand {
public static int BITS[] = { 16, 8, 4, 2, 1 };
public static String BASE32 = "0123456789bcdefghjkmnpqrstuvwxyz";
public static int RIGHT = 0; public static int LEFT = 1; public static int TOP = 2; public static int BOTTOM = 3;
public static int EVEN = 0; public static int ODD = 1;
public static String[][] NEIGHBORS; public static String[][] BORDERS;
static { NEIGHBORS = new String[4][2]; BORDERS = new String[4][2];
NEIGHBORS[BOTTOM][EVEN] = "bc01fg45238967deuvhjyznpkmstqrwx"; NEIGHBORS[TOP][EVEN] = "238967debc01fg45kmstqrwxuvhjyznp"; NEIGHBORS[LEFT][EVEN] = "p0r21436x8zb9dcf5h7kjnmqesgutwvy"; NEIGHBORS[RIGHT][EVEN] = "14365h7k9dcfesgujnmqp0r2twvyx8zb";
BORDERS[BOTTOM][EVEN] = "bcfguvyz"; BORDERS[TOP][EVEN] = "0145hjnp"; BORDERS[LEFT][EVEN] = "prxz"; BORDERS[RIGHT][EVEN] = "028b";
NEIGHBORS[BOTTOM][ODD] = NEIGHBORS[LEFT][EVEN]; NEIGHBORS[TOP][ODD] = NEIGHBORS[RIGHT][EVEN]; NEIGHBORS[LEFT][ODD] = NEIGHBORS[BOTTOM][EVEN]; NEIGHBORS[RIGHT][ODD] = NEIGHBORS[TOP][EVEN];
BORDERS[BOTTOM][ODD] = BORDERS[LEFT][EVEN]; BORDERS[TOP][ODD] = BORDERS[RIGHT][EVEN]; BORDERS[LEFT][ODD] = BORDERS[BOTTOM][EVEN]; BORDERS[RIGHT][ODD] = BORDERS[TOP][EVEN]; }
public static String calculateAdjacent(String srcHash, int dir) { srcHash = srcHash.toLowerCase(); char lastChr = srcHash.charAt(srcHash.length() - 1); int type = (srcHash.length() % 2) == 1 ? ODD : EVEN; String base = srcHash.substring(0, srcHash.length() - 1); if (BORDERS[dir][type].indexOf(lastChr) != -1) { base = calculateAdjacent(base, dir); } return base + BASE32.charAt(NEIGHBORS[dir][type].indexOf(lastChr)); }
public static String[] getGeoHashExpand(String geohash) { try { String geohashTop = calculateAdjacent(geohash, TOP); String geohashBottom = calculateAdjacent(geohash, BOTTOM); String geohashRight = calculateAdjacent(geohash, RIGHT); String geohashLeft = calculateAdjacent(geohash, LEFT);
String geohashTopLeft = calculateAdjacent(geohashLeft, TOP); String geohashTopRight = calculateAdjacent(geohashRight, TOP); String geohashBottomRight = calculateAdjacent(geohashRight, BOTTOM); String geohashBottomLeft = calculateAdjacent(geohashLeft, BOTTOM);
String[] expand = { geohash, geohashTop, geohashBottom, geohashRight, geohashLeft, geohashTopLeft, geohashTopRight, geohashBottomRight, geohashBottomLeft }; return expand; } catch (Exception e) { return null; } } |