GeoHash解析及java实现
GeoHash解析请参考这里:
http://www.open-open.com/lib/view/open1417940079964.html
java实现GeoHash,代码已注释。
import java.util.BitSet; import java.util.HashMap; /** * 地理知识补充: Latitude(纬度)[-90, 90],Longitude(经度)[-180, 180] * * @author FengKang 2014-10-03 * */ 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' }; // decode时进行解码之用 final static HashMap<Character, Integer> lookup = new HashMap<>(); static { int i = 0; for (char ch : digits) { lookup.put(ch, i++); } } /** * 在[floor, ceil]区间内对lat进行编码 区间的划分次数为numbits次 * * @param lat * -待编码经/纬度 * @param floor * @param ceil * @return 二进制编码 */ private BitSet getBits(double lat, double floor, double ceil) { BitSet buffer = new BitSet(numbits); for (int i = 0; i < numbits; ++i) { double mid = (floor + ceil) / 2; if (lat >= mid) { buffer.set(i); floor = mid; } else { ceil = mid; } } return buffer; } /** * 将long型i的二进制位中,每5位映射为一个digits[]中的编码 * 这里的实现方法是参照Long.java中toString的实现 * * @param i * @return */ private static String base32(long i) { char[] buf = new char[65]; // buf[0]备用,若为负数则存储'-' 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 String encode(double lat, double lon) { BitSet latbits = getBits(lat, -90, 90); BitSet lonbits = getBits(lon, -180, 180); /* ! 先经度后纬度交错组合编码 */ 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'); } System.out.println(buffer.toString()); // 将2进制字符串转换为10进制long型 return base32(Long.parseLong(buffer.toString(), 2)); } /** * 根据bs对区间[floor, ceil]进行划分 * 0表示位于区间[floor, (floor + ceil)/2] * 1表示位于区间[(floor + ceil)/2, ceil] * * @param bs * 二进制序列 * @param floor * 初始区间边界[floor, ceil] * @param ceil * @return 区间边界,最接近bs序列的区间边界 */ private double decode(BitSet bs, double floor, double ceil) { double mid = 0; for (int i = 0; i < bs.length(); ++i) { mid = (floor + ceil) / 2; if (bs.get(i)) { floor = mid; // 右区间 } else { ceil = mid; // 左区间 } } return mid; } public double[] decode(String geohash) { StringBuilder buffer = new StringBuilder(); for (char ch : geohash.toCharArray()) { int i = lookup.get(ch) + 32; // 保证转换后的二进制字符串为5位 // +32转换为1#####,然后从下标为1处进行截取,即##### buffer.append(Integer.toString(i, 2).substring(1)); } // System.out.println(buffer); 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); } double lon = decode(lonset, -180, 180); double lat = decode(latSet, -90, 90); return new double[] { lat, lon }; } public static void main(String[] args) throws Exception { Geohash geoHash = new Geohash(); String encodes = geoHash.encode(45, 125); System.out.println(encodes); double[] decodes = geoHash.decode(encodes); System.out.println(decodes[0]); System.out.println(decodes[1]); /** * 输出结果 111100101000000010101000000010101000000010101000000010101000 * yb0bh2n0p058 45.0 124.9999999254942 */ } }
注:代码非本人所写,本人仅仅添加一些注释!