Индексы фильтра близостью в Django со Сферическим Законом Косинусов

Я пытаюсь обработать поиск с расстоянием основного локатора хранилища в Django. Вместо того, чтобы буксировать PostGIS вокруг с моим приложением именно так, я могу использовать фильтр расстояния GeoDjango, я хотел бы использовать Сферический Закон формулы расстояния Косинусов в образцовом запросе. Я хотел бы, чтобы все вычисления были сделаны в базе данных в одном запросе для эффективности.

MySQL в качестве примера запрашивает из Интернета, реализовывая Сферический Закон Косинусов как это:

SELECT id, ( 
    3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * 
    cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * 
    sin( radians( lat ) ) ) 
) 
AS distance FROM stores HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;

Запрос должен сослаться на Индекс ForeignKey для значений lat/lng каждого хранилища. Как я могу сделать всю из этой работы в запросе модели Django?

10
задан spiffytech 20 February 2010 в 03:34
поделиться

4 ответа

Возможно выполнение сырых SQL-запросов в Django .

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

Для пояснения, вот пример того, как это сделать:

def get_models_within_25 (self):
    from django.db import connection, transaction
    cursor = connection.cursor()

    cursor.execute("""SELECT id, ( 
        3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * 
        cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * 
        sin( radians( lat ) ) ) )
        AS distance FROM stores HAVING distance < 25
        ORDER BY distance LIMIT 0 , 20;""")
    ids = [row[0] for row in cursor.fetchall()]

    return MyModel.filter(id__in=ids)

] В качестве заявления об отказе от ответственности я не могу поручиться за этот код, так как прошло несколько месяцев с тех пор, как я написал любой Django, но он должен быть в правильном направлении.

8
ответ дан 3 December 2019 в 16:10
поделиться

В продолжение ответа jboxer'а, вот все это как часть пользовательского менеджера с некоторыми жестко закодированными вещами, превращенными в переменные:

class LocationManager(models.Manager):
    def nearby_locations(self, latitude, longitude, radius, max_results=100, use_miles=True):
        if use_miles:
            distance_unit = 3959
        else:
            distance_unit = 6371

        from django.db import connection, transaction
        cursor = connection.cursor()

        sql = """SELECT id, (%f * acos( cos( radians(%f) ) * cos( radians( latitude ) ) *
        cos( radians( longitude ) - radians(%f) ) + sin( radians(%f) ) * sin( radians( latitude ) ) ) )
        AS distance FROM locations_location HAVING distance < %d
        ORDER BY distance LIMIT 0 , %d;""" % (distance_unit, latitude, longitude, latitude, int(radius), max_results)
        cursor.execute(sql)
        ids = [row[0] for row in cursor.fetchall()]

        return self.filter(id__in=ids)
4
ответ дан 3 December 2019 в 16:10
поделиться

Следуя ответу jboxer'а

def find_cars_within_miles_from_postcode(request, miles, postcode=0):

    # create cursor for RAW query
    cursor = connection.cursor()

    # Get lat and lon from google
    lat, lon = getLonLatFromPostcode(postcode)

    # Gen query
    query = "SELECT id, ((ACOS(SIN("+lat+" * PI() / 180) * SIN(lat * PI() / 180) + COS("+lat+" * PI() / 180) * COS(lat * PI() / 180) * COS(("+lon+" - lon) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) AS distance FROM app_car HAVING distance<='"+miles+"' ORDER BY distance ASC"

    # execute the query
    cursor.execute(query)

    # grab all the IDS form the sql result
    ids = [row[0] for row in cursor.fetchall()]

    # find cars from ids
    cars = Car.objects.filter(id__in=ids)

    # return the Cars with these IDS
    return HttpResponse( cars )

Это возвращает мои автомобили с расстояния x миль, это работает хорошо. Однако необработанный запрос возвращает, как далеко они находятся от определенного места, я думаю, имя поля было 'distance'.

Как я могу вернуть это поле 'distance' с моими объектами автомобилей?

1
ответ дан 3 December 2019 в 16:10
поделиться

Чтобы продолжить ответ Тома, он не будет работать в SQLite по умолчанию из-за отсутствия в SQLite математических функций по умолчанию. Хорошо, добавить довольно просто:

class LocationManager(models.Manager):
    def nearby_locations(self, latitude, longitude, radius, max_results=100, use_miles=True):
        if use_miles:
            distance_unit = 3959
        else:
            distance_unit = 6371

        from django.db import connection, transaction
        from mysite import settings
        cursor = connection.cursor()
        if settings.DATABASE_ENGINE == 'sqlite3':
            connection.connection.create_function('acos', 1, math.acos)
            connection.connection.create_function('cos', 1, math.cos)
            connection.connection.create_function('radians', 1, math.radians)
            connection.connection.create_function('sin', 1, math.sin)

        sql = """SELECT id, (%f * acos( cos( radians(%f) ) * cos( radians( latitude ) ) *
        cos( radians( longitude ) - radians(%f) ) + sin( radians(%f) ) * sin( radians( latitude ) ) ) )
        AS distance FROM location_location WHERE distance < %d
        ORDER BY distance LIMIT 0 , %d;""" % (distance_unit, latitude, longitude, latitude, int(radius), max_results)
        cursor.execute(sql)
        ids = [row[0] for row in cursor.fetchall()]

        return self.filter(id__in=ids)
8
ответ дан 3 December 2019 в 16:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: