给定一个坐标和一个距离,查询数据库所有在这个范围的商店。
首先计算在这个范围的四个点,这四个点链接成一个正方形这个范围相切。
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语句实现,这里就不赘述了,不同公司使用的算法各不相同。如果有什么错误欢迎留言指正,也可私信给我,欢迎转载记得贴上出处
(。
◕
ˇ
∀
ˇ
◕)。