Как отфильтровать модель django с координатами широты и долготы, которые попадают в определенный радиус

У меня следующая модель.

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

Если моя текущая широта и долгота:

current_lat = 43.648
current_long = 79.404

Я провел небольшое исследование и наткнулся на 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

Я хотел бы вернуть все объекты Location, которые попадают в радиус 10 км, как я могу отфильтровать его таким образом, чтобы он возвращал только все объекты Location, которые попадают в этот радиус 10 км?

LocationsNearMe = Location.objects.filter(#This is where I am stuck)

Могу ли я в любом случае внедрить уравнение Хаверсина в фильтрацию, чтобы она возвращала только объекты местоположения, которые попадают в радиус 10 км?

Жду подробного ответа. Цените помощь.


person deadlock    schedule 16.07.2013    source источник
comment
вам следует попробовать использовать geodjango docs.djangoproject.com/en/dev/ref /contrib/gis   -  person user710907    schedule 16.07.2013
comment
Обязательно загляните в geodjango, у меня есть решение этой проблемы, но оно требует geodjango: gist.github.com/omouse /5623772   -  person    schedule 16.07.2013
comment
@omouse спасибо, что направили меня в правильном направлении, в вашем решении у вас есть точка зрения, но здесь, в этой модели, она разделена на два поля: долгота и широта. Не могли бы вы поместить свой ответ для этого случая ниже в разделе ответов.   -  person deadlock    schedule 16.07.2013


Ответы (3)


Но вы всегда можете улучшить предложенный Брайаном подход, отфильтровав результаты предыдущего шага (который, надеюсь, должен быть меньшим подмножеством), и для каждого из них вы проверяете, находятся ли они в пределах радиуса.

Ваш пользователь в черной точке. Квадратное приближение, данное Брайаном, возвращает зеленые, но также и оранжевые точки. Разница в расстоянии может быть значительной, в худшем случае пользователю придется пройти sqrt(2) раз дальше, чем ожидалось (дополнительные 40% расстояния). Поэтому для всех оранжевых и зеленых точек стоит проверить, не превышает ли их расстояние от черной точки (например, евклидово, если это действительно короткие расстояния, например, навигация в городе) предполагаемый радиус.

введите здесь описание изображения

ОБНОВИТЬ:

Если вы хотите использовать расстояние Haversine или (лучше) упомянутый GeoDjango, взгляните на этот фрагмент, сравнивая два представления django, связанные с поиском поблизости:

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

person andilabs    schedule 29.01.2014
comment
спасибо за ваш ответ, я очень ценю ваше понимание того, как решить эту проблему! :) - person deadlock; 31.01.2014
comment
суть нарушена. - person Afshin Mehrabani; 10.07.2016
comment
Вот новая ссылка andilabs.github.io/django/devops/tools/postgres/postgis/2015/01/ - person Adil Malik; 05.10.2016

Вы можете выполнять запросы диапазона с помощью filter.

LocationsNearMe = Location.objects.filter(latitude__gte=(the minimal lat from distance()),
                                          latitude__lte=(the minimal lat from distance()),
                                          (repeat for longitude))

К сожалению, это возвращает результаты в виде геометрического квадрата (вместо круга)

person Brian    schedule 16.07.2013

Если вы не хотите использовать 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)
person Beolap    schedule 27.08.2019
comment
Как все это вписывается в ваш models.py? - person cjm; 25.11.2019
comment
Создайте класс LocationQuerySet, который наследуется от models.QuerySet, и добавьте objects = LocationQuerySet.as_manager() к вашему классу Location. Затем создайте пользовательскую функцию в LocationQuerySet def locations_near_x_within_y_km(self, current_lat, current_long, y_km), которая делает все, что упомянуто выше, и возвращает self.annotate(distance=d).order_by('distance').filter(distance__lt=y_km). Теперь вы можете использовать LocationsNearMe = Location.objects.locations_near_x_within_y_km(50,50,10). - person Beolap; 25.11.2019
comment
При чем тут 10? Это миля или км? - person Ashraful Islam; 21.04.2021
comment
Это параметр «y_km», то есть км. - person Beolap; 21.04.2021
comment
что такое distance в последней строке? - person Hamza Lachi; 03.06.2021
comment
@HamzaLachi Это аннотированное поле. Вы можете назвать это как угодно. В основном он остается с вашим набором запросов, так что вы можете, например. фильтровать или упорядочивать после него, как я делаю в примере. - person Beolap; 04.06.2021