Java语言查询附近店铺算法

给定一个坐标和一个距离,查询数据库所有在这个范围的商店。

首先计算在这个范围的四个点,这四个点链接成一个正方形这个范围相切。

double minlat = 0;//定义经纬度四个极限值。
double maxlat = 0;
double minlng = 0;
double maxlng = 0;
//我数据保存的经纬度是string,所以要转一下类型方便计算距离。  shopParam是传参。
double longitude = Double.parseDouble(shopParam.getLongitude());
double latitude = Double.parseDouble(shopParam.getLatitude());

// 先计算查询点的经纬度范围
double r = 6371;// 地球半径千米
double dis = 20;// 距离(单位:千米),查询范围 20km内的所有商铺
double dlng = 2 * Math.asin(Math.sin(dis / (2 * r))
        / Math.cos(longitude * Math.PI / 180));
dlng = dlng * 180 / Math.PI;// 角度转为弧度
double dlat = dis / r;
dlat = dlat * 180 / Math.PI;
if (dlng < 0) {
    minlng = longitude + dlng;//拿到最大经度和最小经度
    maxlng = longitude - dlng;
} else {
    minlng = longitude - dlng;
    maxlng = longitude + dlng;
}
if (dlat < 0) {
    minlat = latitude + dlat;//拿到最大纬度和最小纬度
    maxlat = latitude - dlat;
} else {
    minlat = latitude - dlat;
    maxlat = latitude + dlat;
}


log.info("最大经度:{},最小经度:{}",maxlng,minlng);
log.info("最大纬度:{},最小纬度:{}",maxlat,minlat);
final List<Shop> shops = Lists.newArrayList();//定义一个空 list保存范围内的店铺
final List<Shop> shopList = shopMapper.selectNearShopList();//查询所有商铺
for (Shop shop:shopList) {
    double _long = Double.parseDouble(shop.getLongitude());//拿到店铺的坐标,判断是否在这个范围内
    double _lat = Double.parseDouble(shop.getLatitude());
    if (_long >= minlng && _long <= maxlng && _lat >= minlat && _lat <= maxlat){
        shops.add(shop);//将在这个范围内的店铺添加到一个空list中。
    }
}
Map<Shop,Double> map = new HashMap<Shop, Double>();//定义一个空的map存储店铺
//计算范围内每个店铺距离给定坐标的距离
for (Shop _shop:shops) {
    Double distance = LocationUtils.getDistance(Double.parseDouble(_shop.getLatitude()),Double.parseDouble(_shop.getLongitude()),latitude,longitude);
    log.info("店铺名称:{},经度:{},纬度:{},距离:{}",_shop.getShopName(),_shop.getLongitude(),_shop.getLatitude(),distance);
    map.put(_shop,distance);//将计算出来的距离作为value,该店铺作为key
}
final List<Shop> list = Lists.newArrayList();//存储排序之后的店铺
//这里使用Java8的map根据value排序,距离最近排序,按value排序,升序
Map<Shop,Double> doubleMap = map
        .entrySet()
        .stream()
        .sorted(comparingByValue())
        .collect(
                toMap(e -> e.getKey(), e -> e.getValue(), (e1, e2) -> e2,
                        LinkedHashMap::new));
for (Map.Entry<Shop, Double> entry:doubleMap.entrySet()) {
    list.add(entry.getKey());//循环map拿到key并保存到list中。
}

下面这个是上述代码中用到的LocationUtils工具类

public class LocationUtils {

    private static double EARTH_RADIUS = 6378.137;

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 通过经纬度获取距离(单位:米)
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistance(double lat1, double lng1, double lat2,
                                     double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        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 * 10000d) / 10000d;
        s = s*1000;
        return s;
    }

}
这个方法是用纯Java语言实现,只是方便用来学习的,真正的企业项目不建议使用,如果数据量较小可以使用。数据量过大可以使用sql语句实现,这里就不赘述了,不同公司使用的算法各不相同。如果有什么错误欢迎留言指正,也可私信给我,欢迎转载记得贴上出处 (。 ˇ ˇ ◕)
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值