如何使用落在某个半径范围内的纬度和经度坐标过滤django模型

noa*_*ale 9 python django haversine

我有以下型号.

class Location(models.Model):
    name = models.CharField(max_length = 128, blank = True)
    address =models.CharField(max_length = 200, blank= True)
    latitude = models.DecimalField(max_digits=6, decimal_places=3)
    longitude = models.DecimalField(max_digits=6, decimal_places=3)

    def __unicode__(self):
        return self.name
Run Code Online (Sandbox Code Playgroud)

如果我目前的纬度和经度是:

current_lat = 43.648
current_long = 79.404
Run Code Online (Sandbox Code Playgroud)

我做了一些研究并遇到了Haversine方程,它计算了两个位置坐标之间的距离.以下是我发现的等式:

import math

def distance(origin, destination):
    lat1, lon1 = origin
    lat2, lon2 = destination
    radius = 6371 # km

    dlat = math.radians(lat2-lat1)
    dlon = math.radians(lon2-lon1)
    a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
        * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    d = radius * c

    return d
Run Code Online (Sandbox Code Playgroud)

我想返回半径10公里范围内的所有位置对象,如何过滤它只会返回落在这10公里范围内的所有位置对象?

LocationsNearMe = Location.objects.filter(#This is where I am stuck)
Run Code Online (Sandbox Code Playgroud)

无论如何,我可以将Haversine方程实现到滤波中,以便它只返回半径10公里范围内的位置对象吗?

我正在寻找一个详细的答案.感谢帮助.

Bri*_*ian 11

你可以用filter.做范围查询.

LocationsNearMe = Location.objects.filter(latitude__gte=(the minimal lat from distance()),
                                          latitude__lte=(the minimal lat from distance()),
                                          (repeat for longitude))
Run Code Online (Sandbox Code Playgroud)

不幸的是,这会以几何方形(而不是圆形)的形式返回结果


and*_*abs 10

但是你总是可以通过过滤前一步骤中的结果(可能是较小的子集)来更好地通过Brian方法提出建议,并且每次检查它们是否在半径范围内.

您的用户处于黑点.Brian给出的平方近似值返回绿色,但也返回橙色点.在最坏的情况下,距离的差异可能是显着的,用户必须比预期的更多(比额外的40%)达到sqrt(2)倍.因此,对于所有橙色和绿色点,有必要检查它们与黑点的距离(例如,如果这是真正的短距离,例如欧几里德,例如城市中的导航)不大于假定半径.

在此输入图像描述

更新:

如果您想使用Haversine距离或(更好)提到GeoDjango hava,请查看此片段,比较两个处理附近搜索的django视图:

https://gist.github.com/andilabs/4232b463e5ad2f19c155


Beo*_*lap 7

如果你不想使用GeoDjango,那么你可以考虑用Django的数据库函数来写出来。与原始 SQL 相比,这还为您提供了能够轻松附加/前置其他 ORM 过滤器的优势。

from django.db.models.functions import Radians, Power, Sin, Cos, ATan2, Sqrt, Radians
from django.db.models import F

dlat = Radians(F('latitude') - current_lat)
dlong = Radians(F('longitude') - current_long)

a = (Power(Sin(dlat/2), 2) + Cos(Radians(current_lat)) 
    * Cos(Radians(F('latitude'))) * Power(Sin(dlong/2), 2)
)

c = 2 * ATan2(Sqrt(a), Sqrt(1-a))
d = 6371 * c

LocationsNearMe = Location.objects.annotate(distance=d).order_by('distance').filter(distance__lt=10)
Run Code Online (Sandbox Code Playgroud)