django 按经纬度计算距离排序
需求: 实现离用户距离最近的店铺排序
公式:
公式解释如下:
- Lung1 Lat1表示A点经纬度, Lung2 Lat2表示B点经纬度;
- a=Lat1–Lat2 为两点纬度之差,b=Long1-Long2为两点经度之差;
- 6378.137为地球半径,单位为千米;
- 计算出来的结果单位为千米,若将半径改为米为单位则计算的结果单位为米;
- 计算精度与谷歌地图的距离精度差不多,相差范围在0.2米以下.
SQL
postgresql
"6371 * acos(least(greatest(cos(radians(%s)) * cos(radians(lat)) * cos(radians(lng) - radians(%s)) + sin(radians(%s)) * sin(radians(lat)) , -1), 1))"
参数说明
- lat : 纬度字段
- lng: 精度字段
classmethod实现返回queryset
# 根据经纬度计算出距离排序
@classmethod
def get_locations_queryset(cls, queryset, lng, lat, max_distance):
"""
queryset: 查询集
latitude: 纬度
longitude: 精度
max_distance:最大距离
"""
gcd_formula = "6371 * acos(least(greatest(\
cos(radians(%s)) * cos(radians(lat)) \
* cos(radians(lng) - radians(%s)) + \
sin(radians(%s)) * sin(radians(lat)) \
, -1), 1))"
sql = RawSQL(gcd_formula, (lat, lng, lat))
qs = queryset.annotate(distance=sql).order_by('distance')
if max_instance:
qs = qs.filter(distance__lt=max_instance)
return qs
总结
- 上面classmethod方法也可以做扩展 比如超过范围的queryset不返回 加一个距离限制参数 用distance比较 (可以做超出配送范围过滤)
- 也可以使用GeoDjango去做 GeoDjango限制还挺多的有兴趣的可以去看看"
- 内容借鉴地址 自己做了个简单的业务封装 有兴趣可以去了解其它的实现方式