在开发接口的时候也许你会遇到这样的情景:从数据库中筛选商家的时候需要在指定范围内按距离排序,你们是怎么处理的呢?
我看过有的童鞋是这样处理的,在sql里直接计算距离,然后排序,大概是以下的写法:
ACOS( SIN((#{lat} * 3.1415) / 180 ) *SIN((merchant.latitude * 3.1415) / 180 ) + COS((#{lat} * 3.1415) / 180 ) * COS((merchant.latitude * 3.1415) / 180 ) * COS((#{lng}* 3.1415) / 180 - (merchant.longitude * 3.1415) / 180 ) ) * 6380 AS distance
这种写法一看就不妥,在sql中复杂运算是不推荐的。这个需求可以优化下筛选方圆指定范围内的商家然后在内存中进行计算距离排序,无需在数据库中全部计算一遍距离然后排序。如果以用户为中心,筛选方圆一公里范围的商家然后按距离排序,计算出前后1公里范围的经纬度差值,大概如下图
![f0543f316b584df045ccc2a9db0ea234.png](https://i-blog.csdnimg.cn/blog_migrate/d791aa62a6e35dff3da124e9b3b1d613.jpeg)
简化成这个模型后,黑点地方的数据是不需要的,但是这部分数据可以忽略不计,稍后在内存中计算距离可以去除。我们知道地球可以看成是一个圆球
![dbd02de4b3af2a665063ecac7cf48634.png](https://i-blog.csdnimg.cn/blog_migrate/424df7e006a24e1f8f888cab2d1c1c37.jpeg)
地球周长是2*6371*3.14 那么1公里对应的经度差值是(1/(2*6371*3.14))*180 大概0.004498, 1公里对应的纬度差值(1/(2*6371*3.14))*90 大概0.002249,所以在sql中筛选between lng-0.004498 and lng+0.004498 纬度同样如此。
不过有人帮我们处理好了这些事情,只需要引入jar包即可
com.spatial4j spatial4j 0.5int radius = 1; //筛选范围 公里//lon, lat用户当前的经纬度SpatialContext geo = SpatialContext.GEO;Rectangle rectangle = geo.getDistCalc().calcBoxByDistFromPt( geo.makePoint(lon, lat), radius * DistanceUtils.KM_TO_DEG, geo, null);rectangle.getMinX() getMaxX() getMinY() getMaxY()既是表示方圆1公里内的经纬度。这样数据中的筛选条件就可以优化下了
![7ad34f2e22a32be1bd6695a3acdc3bd3.png](https://i-blog.csdnimg.cn/blog_migrate/542c870fb78fc4dc025cf27e16d8bf22.jpeg)
这样效率就能提高了很多,筛选出数据之后,还需要计算具体的距离然后排序,计算距离如下
SpatialContext geo = SpatialContext.GEO;double distance = geo.calcDistance(geo.makePoint(lon, lat), geo.makePoint(memberLon, memberLat)) * DistanceUtils.DEG_TO_KM;
当然这只是初步优化方案,如果你有更好的方案欢迎在下方留言。