GeoHash实现代码

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));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值