前言 當前大多數app都有查找附近的功能, 簡單的有查找周圍的運動場館, 複雜的有滴滴, 摩拜查找周圍的車輛. 本文主要闡述查找附近地點的一般實現. 搜索附近的人也是同樣的思路. 方案比較 方案1 (性能還不錯) 資料庫直接存經緯度, 然後計算矩形邊界值, 走索引查詢 方案2 (還沒試過) 將經緯度 ...
前言
當前大多數app都有查找附近的功能, 簡單的有查找周圍的運動場館, 複雜的有滴滴, 摩拜查找周圍的車輛. 本文主要闡述查找附近地點的一般實現. 搜索附近的人也是同樣的思路.
方案比較
方案1 (性能還不錯)
資料庫直接存經緯度, 然後計算矩形邊界值, 走索引查詢
方案2 (還沒試過)
將經緯度轉換成 一個值, 然後進行比較查詢 genhash
http://blog.csdn.net/newjueqi/article/details/18989867
方案3 (據說高性能, 性能怎樣?待測試)
mongodb 地理類型, 高性能 http://www.tuicool.com/articles/Jfu6fy
sqlserver 地理數據 geography https://msdn.microsoft.com/en-us/library/ff929109.aspx
方案1的實現(本文主要闡述此方案)
實現環境: java+MySQL
場景模擬: 張三用戶在成都天府五街查詢周圍10公裡內的地點
1. 首先建立經緯度數據, 比如常見地點的經緯度資料庫, 我這裡是網上下載的一個shop_area 表數據,裡面包含了一些常見地點的經緯度 ,如下圖
2. 然後根據張三用戶所在的經緯度, 以及他要查詢的距離10公裡, 得到查詢範圍矩形的四個頂點, 如下圖:
計算這四個點的 mybatis sql:
<select id="getCurrentLocationRectangle" parameterType="LocationFilter" resultType="LocationFilter"> SELECT #{myLongitude} - #{distance} / ABS(COS(RADIANS(#{myLatitude})) * 69) AS LongitudeMin, #{myLongitude} + #{distance} / ABS(COS(RADIANS(#{myLatitude})) * 69) AS LongitudeMax, #{myLatitude} - (#{distance} / 69) AS LatitudeMin, #{myLatitude} + (#{distance} / 69) AS LatitudeMax </select>
3. 將剛纔得到的矩形四個點的值代入如下sql 來進行經緯度查詢過濾, 記得經緯度欄位要建立索引
mybatis sql:
<select id="getUserNearbyAreaList" parameterType="com.anuo.app.modules.coach.entity.CoachFilter" resultType="ShopArea"> SELECT * FROM ( SELECT a.*, GetDistance(#{myLatitude}, #{myLongitude}, a.lat, a.lng) AS distance FROM shop_area a WHERE a.lat BETWEEN #{latitudeMin} AND #{latitudeMax} AND a.lng BETWEEN #{longitudeMin} AND #{longitudeMax} ) z <where> <if test="distance > 0 "> AND z.distance < #{distance} </if> </where> ORDER BY z.distance LIMIT #{pageStart},#{pageSize} </select>
因為先是通過四個點走索引過濾的經緯度數據, 所以大大提升了效率. 並且將我們想要的10公裡範圍內的經緯度數據過濾了出來, 雖然多查詢了點數據(見下圖四個叉叉處), 見下麵第四步, 將多餘的剔除掉
4.上面sql中的 AND z.distance < #{distance} 即 AND z.distance 小於指定距離 #{distance}, 是將下圖畫叉叉部分的經緯度數據剔除,這些數據是多餘的, 因為為我們要查詢的是圓圈內的數據
這裡用到了一個MySQL GetDistance 函數, 代碼如下
DELIMITER $$
USE `anuoapp`$$
DROP FUNCTION IF EXISTS `GetDistance`$$
CREATE DEFINER=`root`@`localhost` FUNCTION `GetDistance`(
myLatitude DECIMAL(11,8),#我當前位置的緯度
myLongitude DECIMAL(11,8),#我當前位置的經度
latitude DECIMAL(11,8),
Longitude DECIMAL(11,8)
) RETURNS DOUBLE
BEGIN
RETURN (
6371 * ACOS(
COS(RADIANS(myLatitude)) * COS(RADIANS(latitude)) * COS(RADIANS(Longitude) - RADIANS(myLongitude)) +
SIN(RADIANS(myLatitude)) * SIN(RADIANS(latitude))
)
);
END$$
DELIMITER ;
最後查詢出張三在成都天府五街周圍10公裡內的地點
請求url: http://localhost:8080/v1/apiGetUserNearbyArea
請求體:
{
"Token":"6850d1c361e9478ca1e94496ec6b27f9",
"Version": "1.8.0",
"Entities": [
{
"myLatitude":30.54286,
"myLongitude":104.075569,
"distance":10,
"pageSize":10,
"pageNumber":1
}
],
"IsMobile": true,
"PageIndex": 1,
"IsInnerTest": true,
"IsGetIp": false,
"PageSize": 38,
"IsEncrypt": true,
"Parameters": {}
}
響應:
{
"success": true,
"totalRow": 11,
"entities": [
{
"id": "510122004",
"areaname": "中和街道",
"parentid": 510122,
"shortname": "中和街道",
"lng": "104.082375",
"lat": "30.559141",
"level": true,
"position": "tr_0 tr_510000 tr_510100 tr_510122",
"sort": 25,
"distance": 1.9241037391984028
},
{
"id": "510107063",
"areaname": "石羊場街道",
"parentid": 510107,
"shortname": "石羊場街道",
"lng": "104.048271",
"lat": "30.590687",
"level": true,
"position": "tr_0 tr_510000 tr_510100 tr_510107",
"sort": 12,
"distance": 5.925643914100619
},
{
"id": "510122122",
"areaname": "萬安鎮",
"parentid": 510122,
"shortname": "萬安鎮",
"lng": "104.112701",
"lat": "30.487444",
"level": true,
"position": "tr_0 tr_510000 tr_510100 tr_510122",
"sort": 18,
"distance": 7.114938271111233
},
{
"id": "510122120",
"areaname": "新興鎮",
"parentid": 510122,
"shortname": "新興鎮",
"lng": "104.149757",
"lat": "30.52656",
"level": true,
"position": "tr_0 tr_510000 tr_510100 tr_510122",
"sort": 21,
"distance": 7.332851650201873
},
{
"id": "510107007",
"areaname": "火車南站街道",
"parentid": 510107,
"shortname": "火車南站街道",
"lng": "104.082924",
"lat": "30.619801",
"level": true,
"position": "tr_0 tr_510000 tr_510100 tr_510107",
"sort": 7,
"distance": 8.5843717771867
}
]
}
完整源碼見:
https://gitee.com/anuo/anuoapp
在此項目中搜索 apiGetUserNearbyArea 介面即可定位
完整資料庫見:
https://pan.baidu.com/s/1o9lUJMU