先说一下我们可能会用到的一些场景,这样同学们可以先评估,该篇文章是否对你有帮助!
场景:
假设 美团,我点外卖时,系统会让我先进行定位,比如我定位在了 A 点,系统就会给我推荐,离 A 点最近的商家们,我们先排除评分等等条件,只看距离,优先距离(最近的商家)。
所以这个时候,就需要用到"两点之间的距离"来处理
多分享一点:
曾经我的解决方案是这样的:请求"腾讯位置服务",他们里面有 Web端的 JavaScript ,也有服务端的 API,就可以解决我们的一些问题。
但是后来,有一次甲方的需求是:希望能匹配最近的商家,按距离从小到大显示!
用以前的经验来讲,直接拿"腾讯位置服务"里面的 API 来用,就可以满足的,但是我想了一下,在程序的性能上,这并不是一个比较好的方案,所以我就思考了一下,一定有着我还未了解到的知识,有更好的方法,所以我先上网上浏览,最终找到了,mysql 有 "geometry"数据类型
"geometry" 是 几何 的意思
同学们想一下数学上,应该就能明白了。
我们依然要使用到 经纬度 来存储
按照以往,经度、纬度,我们可能都是分开存储的,在一些场景下,就好像上面说的,我们使用 "腾讯位置服务" 时,也就需要用到 经纬度。
而这里,我们额外创建了一个字段,数据类型为"geometry",
存储的值,是这样的。它必须要这样存储,才能使用相关的函数 查出对比的位置来!
而存储时,这并不是一个存 字符串,也是需要使用到一个函数来进行存储的: ST_GeomFromText
INSERT INTO table ( `lng`, `lat`, `lng_lat`) VALUES ('104.068544', '30.606452', ST_GeomFromText ( 'POINT(104.068544 30.606452)' ));
请要注意的是, "ST_GeomFromText" 里面还使用到了 "POINT"
只有这样,该字段类型存储的值,才能成功,并且可以使用后续的函数来查找距离。
我这边常用的函数是:
st_distance_sphere 计算出来的距离,单位是"米"
我这边就拿一个实际位置来做演示吧!
查询的 SQL 如下:
select site_name,st_distance_sphere(POINT(104.068544,30.606452),lng_lat) as distant from table_name
可以看到,因为我的 查询 经纬度,直接用的是 "成都南站站点",所以得出的结果就是 "0"米,就是代表原地!
而成都北站,则距离 成都南站 约为 "10097.7070"米,换算过来大概是:"10.10"千米(公里)。
我这边使用 "高德地图" 查询了一下,"火车南站(地铁站)" 距离 "火车北站(地铁站)"
驾车模式:约为 11 公里;
骑行(电动车、自行车)模式:约为 11.4 公里;
步行模式:约为 11.2 公里
首先,确实是没有达到百分百的准确,我认为是有两个点
1. 我的数据库中存储的 成都南站 和 高德地图中的 "成都南站(地铁站)" 应该不是百分百完美匹配的,就是说不是相同的一个 经纬度。
2. 官方的计算本身就是一个约值,并非全等值,因为这里涉及到圆周率"π",π 又为 3.1415926...
综合上述我的假设,所以我认为,最终就是相差1公里左右吧!如果第一点,能确定 高德地图 和 数据库中的 经纬度完全匹配,那么问题应该不大。这个可以放给同学们去实验!
为什么我这里不直接去高德扒一个经纬度一样的存储在数据库呢,一是这个经纬度是同事存储的,二是同事应该是用的腾讯地图,三是因为我以前做这一块的时候,相差一般都是几百米,偏差不会太大,所以我觉得差不多。
所以按照上述的,我们就可以得出 "distant" 字段的大小值了,就可以进行一个排序,做到 商家离我们最近的排序在最前面。。。。
扩展:
st_distance_sphere 确实是比较好用的,但有时考虑到 mysql 的版本,扩展等原因,所以如果出现 "st_distance_sphere " 这个方法不存在时,那么用不了这个方法时,我们又该如何是好呢?
那这个时候,就可以用到另外一个方法:st_distance
可以对比出 "st_distance" 、 "st_distance_sphere ",前面是一样的。
而 st_distance 也是比较好用的,只不过需要注意的是,这个方法,计算出来的结果单位,是"度",而不是"米"了。
没错,就是 圆 的 度!因为地球是圆的
所以我们还要用到 圆周率 "π",还有地球的半径
我们假设:
地球半径:6371.393千米
π:3.14159265359
180度
因为 st_distance 计算出来的结果单位是"度",所以我们要转为"米",所以就要这样:
需要乘111201(地球半径6371.393*PI/180)将值转化为米
所以 SQL 就是:
select site_name,st_distance(POINT(104.068544,30.606452),lng_lat)*111201 as distant from sp_site_config
结果为:
可以发现,两者相差的位置,其实并不远,我算了一下:
10102.1855 - 10097.707 = 4.4785
相差 4米左右,所以问题也不大!