最近工作需要,頁面展示某個城市附近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緩存
在項目啟動時,spring緩存所有的城市信息遍歷城市集合,判斷每個城市是否在范圍內然后返回頁面展示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><<tr><td>
${city.id}</td><td>
${city.province}</td><td>
${city.city}</td><td>
${city.longitude}</td><td>
${city.latitude}</td></tr></
</table>
復制代碼2. 測試
url:http://localhost:8090/aroundCity/aroundCity/蘇州;
3. 結果
4. 驗證
使用百度地圖的測距功能逐個驗證
總結
- 想了解更詳細的內容可以直接下載代碼:鏈接:http://pan.baidu.com/s/1ge5fMxX 密碼:j2qv *?BasicInfoService緩存也可以換成redis和memcache等主流的nosql數據庫管理,這里主要是為了方便演示
- 參考內容1:百度百科-球面距離
- 參考內容2:百度百科-地球半徑
- 地球是一個兩極稍扁、赤道略鼓的不規則球體,平均半徑6371千米,這只是近似計算
轉載于:https://juejin.im/post/5a34e6bef265da432f3130e2
總結
以上是生活随笔為你收集整理的spring计算方圆300km内其它城市(附完整代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。