spring计算方圆300km内其它城市(附完整代码)

最近工作需要,页面展示某个城市附近300km范围内所有的其它城市。找了半天也没有合适的方法,这里给出一种解决方法,如果有更好的,请不吝赐教。

一、准备工作

问题抽象: 求城市300km范围内所有的其它城市,可以抽象为球面上两个点的距离是否在一定的范围内

球面距离: 球面上两点之间的最短连线的长度,就是经过这两点的大圆在这两点间的一段劣弧的长度。(大圆就是经过球心的平面截球面所得的圆)

球面距离计算公式: 设两点A、B的经、纬度分别为(jA,wA)(jB,wB),则半径为R的球面上两点间的最短距离(大圆弧)为:弧AB=R*arccos[sin(wA)sin(wB)+cos(wA)cos(wB)*cos(jA-jB)

地球半径: 地球的平均半径6371.393千米

表结构:

二、sql计算

SELECT province, city FROM city WHERE city != "苏州" and
6371.393
 * ACOS
(
    SIN(31.30703 * PI() / 180) * SIN(latitude * PI() / 180)
    +
    COS(31.30703 * PI() / 180) * COS(latitude * PI() / 180) * COS(120.591431 * PI() / 180
    -
    longitude * PI() / 180)
) <= 300
复制代码

使用sql计算直接给出结果,但是sql计算会占用较多cpu资源,如果并发量较高,不建议使用。

三、使用spring缓存

  1. 在项目启动时,spring缓存所有的城市信息
  2. 遍历城市集合,判断每个城市是否在范围内
  3. 然后返回页面展示
1. 代码展示
  • controller
    • url要符合restful风格,不建议使用中文,这里是为了演示方便
@RequestMapping("/aroundCity/{cityName:[\u4E00-\u9FFF]+}") // 汉字正则表达式
public String aroundCityMapping(Model model, @PathVariable String cityName) {

    List<City> listAroundcities = aroundCityService.listAroundcities(cityName, distance);
    if (listAroundcities != null && listAroundcities.size() > 0) {
        model.addAttribute("listAroundcities", listAroundcities);
        model.addAttribute("distance", distance);
        model.addAttribute("cityName", cityName);
    }
    return "aroundCity";
}

复制代码
  • service
    • dao层使用mybatis的逆向工程生成(逆向工程及mybatis语法以后单独总结一份)
public List<City> listAroundcities(String cityName, int distance) {
    List<City> cityList = basicInfoService.getCityList();
    CityExample cityExample = new CityExample();
    Criteria criteria = cityExample.createCriteria();
    criteria.andCityEqualTo(cityName);
    List<City> cities = cityMapper.selectByExample(cityExample);
    City city = null;
    if (cities == null || cities.size() < 1) {
        return null;
    }
    city = cities.get(0);
    List<City> aroundCitiesByDistance = addAroundCitiesByDistance(city, cityList, distance);
    return aroundCitiesByDistance;
}
复制代码
/**
 * 添加当前城市x公里内的所有城市
 */
private List<City> addAroundCitiesByDistance(City localCity, List<City> cityList, int distance) {
    List<City> aroundCityList = new ArrayList<City>(); // 周围城市列表
    for (City city : cityList) {
        Float lat = city.getLatitude();
        Float lng = city.getLongitude();
        if (!Objects.equals(city.getCity(), localCity.getCity()) && withinRange(localCity.getLatitude(), localCity.getLongitude(), lat, lng, distance)) {
            aroundCityList.add(city);
        }
    }
    return aroundCityList;
}
复制代码
/**
 * 判断两个经纬坐标之间的距离是否在distance km内
 */
private boolean withinRange(float lat1, float lng1, 
					        float lat2, float lng2, 
					        int distance) {
    double exp1 = Math.sin(lat1 * Math.PI / 180) * Math.sin(lat2 * Math.PI / 180);
    double exp2 = Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180)
            * Math.cos(lng1 * Math.PI / 180 - lng2 * Math.PI / 180);
    return EARTH_RADIUS * Math.acos(exp1 + exp2) <= distance;
}
复制代码
  • BasicInfoService
    • 使用@PostConstruct注解,可以在项目启动时执行
    • 获取所有的城市信息,并交给spring管理
    • spring默认是单例模式,而且List集合不是线程安全的,所有不要提供setter方法
private List<City> cityList;

@PostConstruct
public void init() {
	cityList = cityMapper.selectByExample(new CityExample());
}

public List<City> getCityList() {
	return cityList;
}
复制代码
  • view
    • freemarker作为视图
    • 多视图配置详见共享代码(下方有百度云地址)
    • freemarker的语法及使用技巧,以后总结
<table>
	<tr>
		<td>ID</td>
		<td>省份</td>
		<td>城市</td>
		<td>经度</td>
		<td>纬度</td>
	</tr>
	<#list listAroundcities as city>
		<tr>
			<td>${city.id}</td>
			<td>${city.province}</td>
			<td>${city.city}</td>
			<td>${city.longitude}</td>
			<td>${city.latitude}</td>
		</tr>
	</#list>
</table>
复制代码
2. 测试

url:http://localhost:8090/aroundCity/aroundCity/苏州;

3. 结果

4. 验证

使用百度地图的测距功能逐个验证

总结

  • 想了解更详细的内容可以直接下载代码:链接:http://pan.baidu.com/s/1ge5fMxX 密码:j2qvBasicInfoService缓存也可以换成redismemcache等主流的nosql数据库管理,这里主要是为了方便演示
  • 参考内容1:百度百科-球面距离
  • 参考内容2:百度百科-地球半径
  • 地球是一个两极稍扁、赤道略鼓的不规则球体,平均半径6371千米,这只是近似计算

转载于:https://juejin.im/post/5a34e6bef265da432f3130e2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值