java 整合Geohash 算法计算最近店面位置

Geohash 相关的算法就不多解释了.网上太多的详细的介绍了,现在贴出来.在java 中使用 geohash 算法.
需求: 根据客户车辆坐标.定位附近维修点的位置.按最近距离排序
package ******.biz.GeohashUtil;

import java.util.BitSet;
import java.util.HashMap;

/**
* Geohash算法.
* 是将所有的经纬度坐标,通过geohash,变成一个唯一的base32标识。
* 他将世界上的所有区域进行分块,每个维度都是32块,进而将范围逐渐变小、变小,最后的一堆数字,就成了这个base32的唯一标识
* @Auther :WYK
* @Date 2017/10/25 0025
*/
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<Character, Integer> lookup = new HashMap<Character, Integer>();

    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));
    }

    /**
     * 地区赤道半径
     */
    private static final double EARTH_RADIUS = 6371000;//赤道半径(单位m) // 6371000 6378200

    /**
     * 转化为弧度(rad)
     */
    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 基于googleMap中的算法得到两经纬度之间的距离,
     *
     * @param lon1 第一点的经度
     * @param lat1 第一点的纬度
     * @param lon2 第二点的经度
     * @param lat2 第二点的纬度
     * @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;
    }
}
===========RPC 向外提供的服务==========
/**
* 根据用户坐标查询店铺位置按 最近距离排序
* //@param shopModel
* @link lat 纬度
* @link lng 经度
* @link type 店面类型 1 体验店 2 服务中心
* @return
*/
List<ShopModel> compareAxis( QryShopPositionModel qryShopPositionModel);

========BIZ实现类============
@Override
public List<ShopModel> compareAxis(QryShopPositionModel qryShopPositionModel){
Assert.notNull(qryShopPositionModel.getLat(),"纬度不能为空");// 本项目中的断言. 借鉴可以直接去掉.但是参数包装不能为空
Assert.notNull(qryShopPositionModel.getLng(),"经度不能为空");
try{
Example example = new Example(BizShop.class);

Example.Criteria criteria = example.createCriteria();
if(!StringUtils.isEmpty(qryShopPositionModel.getType())){
criteria.andEqualTo("type",qryShopPositionModel.getType());
}
criteria.andEqualTo("isDelete",0);
List<BizShop> shopList = bizShopService.findByExample(example);//从数据库中取出的数据源
Map<ShopModel,Double> mapData = new HashMap<>();
for (BizShop bizShop : shopList) {
double distance = GeoHash.GetDistance(qryShopPositionModel.getLat(), qryShopPositionModel.getLng(),bizShop.getLat().doubleValue(),bizShop.getLng().doubleValue() );
ShopModel from = shopMapper.from(bizShop);
from.setDistance(distance);
mapData.put(from,distance);
}
List<ShopModel> returnData = new ArrayList<>();
//这里将map.entrySet()转换成list
List<Map.Entry<ShopModel,Double>> list = new ArrayList<Map.Entry<ShopModel,Double>>(mapData.entrySet());
//然后通过比较器来实现排序
Collections.sort(list,new Comparator<Map.Entry<ShopModel,Double>>() {
//升序排序
public int compare(Map.Entry<ShopModel, Double> o1,
Map.Entry<ShopModel, Double> o2) {
return o1.getValue().compareTo(o2.getValue());
}
});
for(Map.Entry<ShopModel,Double> mapping:list){
returnData.add(mapping.getKey());
}
// 取 集合中前几条数据
if (StringUtils.isEmpty(qryShopPositionModel.getLimitNumber())){
qryShopPositionModel.setLimitNumber(5);
}
int size = returnData.size();
if (size ==0){
return null;
}else if (qryShopPositionModel.getLimitNumber() >size){
return returnData.subList(0,size);
}else {
return returnData.subList(0,qryShopPositionModel.getLimitNumber());
}
}catch (RuntimeException e){
logger.error("坐标搜索店面信息异常={}",e);
throw e;
}

代码中的 异常和日志和断言 替换成自己项目中的 就可以了.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值