如何从数据库中获取最近的位置条目?

7
在我的数据库中,我将坐标以纬度和经度对的形式存储。现在,我需要检索所有与给定纬度-经度坐标中的给定半径内的条目。例如,如果给定48.796777,2.371140坐标和20公里的距离,则会检索到该点20公里内的所有记录。
如果这样做可以简化或减少CPU时间,那么计算点之间的“直线”距离就足够精确,即不需要考虑地球曲率。
如何在Django中实现这一点?更具体地说,应该为这样的查询构建视图(可能还要构建模型)?
5个回答

10

您应该使用GeoDjango。它允许您在地理数据库条目上执行距离查询

from django.contrib.gis.measure import D
from django.contrib.gis.geos import *
from myapp.models import MyResult

pnt = fromstr('POINT(48.796777 2.371140 )', srid=4326)
qs = MyResult.objects.filter(point__distance_lte=(pnt, D(km=20)))

谢谢!我会研究一下的。 - manabreak
1
@Timmy,我已经将坐标存储在两列中(纬度和经度),如何将上述查询添加到其中?我没有使用 GIS。 - pbms
我可以使用MySQL吗? - Hardik Gajjar

3

您可以使用geopy

from geopy import distance  

_, ne = g.geocode('Newport, RI')  
_, cl = g.geocode('Cleveland, OH')  
distance.distance(ne, cl).miles  
# 538.37173614757057

为了优化一点,您可以过滤用户对象以得到附近用户的粗略估计。这样,您就不必循环遍历数据库中的所有用户。这个粗略估计是可选的。为了满足您的所有项目要求,您可能需要编写一些额外的逻辑:
#The location of your user.
lat, lng = 41.512107999999998, -81.607044999999999 

min_lat = lat - 1 # You have to calculate this offsets based on the user location.
max_lat = lat + 1 # Because the distance of one degree varies over the planet.
min_lng = lng - 1
max_lng = lng + 1    

users = User.objects.filter(lat__gt=min_lat, lat__lt=max__lat, lat__gt=min_lat, lat__lt=max__lat)

# If not 20 fall back to all users.
if users.count() <= 20:
    users = User.objects.all()

1
我有类似的需求,最终也使用了geopy。我发现这个gist很有用:https://gist.github.com/renyi/3385043。它包括一个粗略的距离计算,使查询更加高效。 - Jordan

0

试试这段代码。 我正在使用Python和SQLAlchemy

这个SQLAlchemy查询返回给定点最近的对象。它能够正确工作,但是这个查询会返回所有模型,不过它仍然能够正常工作。

我的模型类:

#
class City(Base):
   __tablename__ = "tbl_City"
  city_id = Column(Integer, primary_key=True, unique=True) 
  geo_coordinates = Column(Geometry('POINT', srid=4326))

我的查询

#
point = city.geo_coordinates
near_citylist = City.query.order_by(City.geo_coordinates.distance_box(point))

0

没有GeoDjango

你可以这样做,

objects = YourModel.objects.raw('SELECT *,  ( 6371 * acos( cos( radians('+my_lat+') ) * cos( radians( latitude ) ) * cos( radians( longitude ) - radians('+my_long+') ) + sin( radians('+my_lat+') ) * sin( radians( latitude ) ) ) ) AS distance FROM "YourModel" WHERE (6371 * acos( cos( radians('+my_lat+') ) * cos( radians(latitude) ) * cos( radians(longitude) - radians('+my_long+') ) + sin( radians('+my_lat+') ) * sin( radians(latitude) ) ) ) <= 100')

其中my_lat是输入的纬度,my_long是输入的经度,latitude是Latitude模型字段,longitude是Longitude模型字段,100是距离。

我在我的模型中使用了FloatField来表示纬度和经度。

现在,由于这返回一个RawQueryset,如果你需要在查询集上使用其他过滤器,你可以通过以下方式获得通常的Django Queryset:

objects = YourModel.objects.filter(pk__in=[i.pk for i in objects])



-1

我认为最好的方法是像Timmy建议的那样使用GeoDjango,但是你必须拥有一个启用地理位置的后端(如PosGis)。然而,如果你的模型包含自定义的纬度和经度字段,我认为这是不可能使用的。

class CustomGeoModel(models.Model):
  lat = models.CharField(max_length=30)
  lon = models.CharField(max_length=30)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接