GeoHash实现代码
代码
原理百度一查就能找到,如下是完整实现,实现了包括经纬度转Hash,以及经纬度hash块周边hash的提取方式
import java.util.BitSet;
public class GeoHash {
//定义经纬度边界
//纬度:北半球1 0-90,南半球0 -90-0
//经度:东半球1 0-180,西半球0 -180-0
private static final double MAX_LNG = 180;
private static final double MIN_LNG = -180;
private static final double MAX_LAT = 90;
private static final double MIN_LAT = -90;
//GeoHash位数
private int precision;
//该GeoHash位数对应的经度或纬度二进制位数
private int bitSize;
//base32,数组下标整数值和字符对应关系
private final 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' };
public GeoHash(){
//默认精度是6
setPrecision(6);
}
/**
* 设置精度
* @param precision
*/
private void setPrecision(int precision){
this.precision = precision;
//一个GeoHash字符是Base32,对应5个二进制位,GeoHash对应的原始二进制位是由经度和纬度的二进制位合并而成
this.bitSize = this.precision * 5 / 2;
}
/**
* 根据经纬度和精度,生成BitSet
* @param latOrLng
* @param minBorder
* @param maxBorder
* @return
*/
private BitSet getBit(double latOrLng, double minBorder, double maxBorder) {
BitSet bitSet = new BitSet(this.bitSize);
double left = minBorder;
double right = maxBorder;
double mid = 0 ;
for(int i=0; i<this.bitSize; i++) {
mid = (left + right) / 2 ;
if(latOrLng >= mid) {
bitSet.set(i);
left = mid;
}
else {
right = mid;
}
}
double hashUnit = 0;
//计算hash块的单位经度或纬度长度
if(bitSet.get(bitSize - 1)){
hashUnit = left - mid;
}
else {
hashUnit = mid - right;
}
return bitSet;
}
/**
* 将经度和纬度的BitSet合并,并转为二进制字符串
* @param lat
* @param lng
* @return
*/
private StringBuffer genBitMerge(Double lat, Double lng) {
BitSet lngBit = getBit(lng, MIN_LNG, MAX_LNG);
BitSet latBit = getBit(lat, MIN_LAT, MAX_LAT);
StringBuffer buffer = new StringBuffer();
for(int i = 0; i < this.bitSize; i++){
buffer.append((lngBit.get(i) ? '1' : '0'));
buffer.append((latBit.get(i) ? '1' : '0'));
}
return buffer;
}
/**
* 将二进制字符串,按5位切分,换算成整数idx,以idx作为数组下标查找digits中对应的字符,即为hash
* @param buffer
* @return
*/
private String bit2hash(StringBuffer buffer) {
int counter = 1;
int size = buffer.length();
String bitStr = "";
String finalStr = "";
for(int i = 0; i < size; i++) {
bitStr += buffer.charAt(i);
if(counter % 5 == 0 || counter == size ) {
int idx = Integer.parseInt(bitStr, 2);
finalStr += digits[idx];
bitStr = "";
}
counter++;
}
return finalStr;
}
/**
* 获取某个hash精度下的hash块经度或纬度边长
* @return
*/
private Double getHashUnit(double minBorder, double maxBorder){
double left = minBorder;
double right = maxBorder;
double mid = 0 ;
for(int i=0; i<this.bitSize; i++) {
mid = (left + right) / 2 ;
left = mid;
}
return right - mid;
}
/**
* 经度hash块单位边长
* @return
*/
private Double getLngHashUnit() {
return getHashUnit(MIN_LNG, MAX_LNG);
}
/**
* 纬度hash块单位边长
* @return
*/
private Double getLatHashUnit() {
return getHashUnit(MIN_LAT, MAX_LAT);
}
/**
* 生成GeoHash
* @param lat
* @param lng
* @return
*/
public String getGeoHash(double lat, double lng) {
return bit2hash(genBitMerge(lat, lng));
}
public String getGeoHash(double lat, double lng, int precision) {
setPrecision(precision);
return getGeoHash(lat,lng);
}
/**
* 获取经纬度当前hash块周边的八个hash块
* 获取上下左右需要考虑边界情况
* @param lat
* @param lng
* @return
*/
public String getAroundHashes(double lat, double lng){
double lngUnit = getLngHashUnit();
double latUnit = getLatHashUnit();
double upLat, downLat, leftLng, rightLng;
//左块经度
if(lng - lngUnit < -180) {
leftLng = 180 + (lng - lngUnit);
}
else {
leftLng = lng - lngUnit;
}
//右块经度
if(lng + lngUnit > 180) {
rightLng = -180 + (lng + lngUnit - 180);
}
else {
rightLng = lng + lngUnit;
}
//上块纬度
if(lat + latUnit > 90) {
upLat = 90 - ((lat + latUnit) - 90);
}
else {
upLat = lat + latUnit;
}
//下块纬度
if(lat - latUnit < -90) {
downLat = -90 - (lat - latUnit + 90);
}
else {
downLat = lat - latUnit;
}
StringBuffer buffer = new StringBuffer();
//leftUpHash
buffer.append(getGeoHash(upLat, leftLng, precision)).append(",");
//uphash
buffer.append(getGeoHash(upLat, lng, precision)).append(",");
//rightUphash
buffer.append(getGeoHash(upLat, rightLng, precision)).append(",");
//leftHash
buffer.append(getGeoHash(lat, leftLng, precision)).append(",");
//rightHash
buffer.append(getGeoHash(lat,rightLng, precision)).append(",");
//leftDownHash
buffer.append(getGeoHash(downLat, leftLng, precision)).append(",");
//downHash
buffer.append(getGeoHash(downLat, lng, precision)).append(",");
//rightDowHash
buffer.append(getGeoHash(downLat, rightLng, precision));
return buffer.toString();
}
public String getAroundHashes(double lat, double lng, int precision) {
setPrecision(precision);
return getAroundHashes(lat,lng);
}
public static void main(String[] args) {
GeoHash geohash = new GeoHash();
System.out.println(geohash.getGeoHash(39.923201, 116.390705, 12));
System.out.println(geohash.getGeoHash(-90.0, -180.0, 6));
System.out.println(geohash.getAroundHashes(39.923201, 116.390705, 10));
System.out.println(geohash.getAroundHashes(-90.0, -180.0, 7));
}
}