códigos postales de filtro por la proximidad de Django con la ley de los cosenos esférica

StackOverflow https://stackoverflow.com/questions/1916953

Pregunta

Estoy tratando de manejar búsqueda de proximidad para un localizador de tienda básico en Django. En lugar de PostGIS recorrido alrededor con mi aplicación sólo por lo que se pueden usar filtro de la distancia a GeoDjango, me gustaría utilizar la ley de los cosenos esférica fórmula de la distancia en una consulta de modelo. Me gustaría que todos los cálculos que se realizan en la base de datos en una consulta, para la eficiencia.

Un ejemplo de MySQL consulta a través de Internet la aplicación de la Ley de los cosenos esférica como esto:

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;

La consulta tiene que hacer referencia a la CP ForeignKey para los valores / LNG lat de cada tienda. ¿Cómo puedo hacer todo este trabajo en una consulta de modelo de Django?

¿Fue útil?

Solución

Es posible que la ejecución prima consultas SQL en Django .

Mi sugerencia es, escribir la consulta para extraer una lista de ID (que parece que está haciendo ahora), a continuación, utilizar los identificadores para tirar de los modelos asociados (en un no-crudo-SQL consulta regular, Django) . Trate de mantener su dialecto SQL como independiente como sea posible, de modo que usted no tendrá que preocuparse de una cosa más, si alguna vez tiene que cambiar las bases de datos.

Para aclarar, aquí está un ejemplo de cómo hacerlo:

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)

Como un descargo de responsabilidad, no puedo responder por este código, ya que han pasado unos meses desde que he dejado de Django, pero debe ser en la dirección correcta.

Otros consejos

Para el seguimiento de la respuesta de Tom, no va a funcionar en SQLite de forma predeterminada debido a la falta de SQLite de funciones matemáticas de forma predeterminada. No hay problema, es bastante sencilla de añadir:

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)

Para dar seguimiento a Tom, si usted quiere tener una consulta que también funciona en PostgreSQL, no se puede utilizar AS porque obtendrá un error que dice 'distancia' no existe.

Usted debe poner toda la expresion ley esférica en la cláusula WHERE, como esto (También funciona en MySQL):

import math
from django.db import connection, transaction
from django.conf import settings

from django .db import models

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

        cursor = connection.cursor()

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

        return self.filter(id__in=ids)

Tenga en cuenta que usted tiene que seleccionar la latitud y longitud, de lo contrario no se puede utilizar en la cláusula WHERE.

Sólo para dar seguimiento a la respuesta de jboxer, aquí está todo el asunto como parte de un administrador personalizado con algunas de las cosas no modificable convirtieron en variables:

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)

Tras la respuesta de 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 )

Esto devuelve mis coches de x cantidad de millas, esto funciona bien. Sin embargo, la consulta devuelve prima lo lejos que estaban de un cierto lugar, creo que era el nombre del campo 'distancia'.

¿Cómo puedo devolver este campo 'distancia' con mis objetos de coche?

El uso de algunas de las respuestas propuestas anteriormente, conseguía resultados incosistent así que decidí ir a la ecuación de nuevo utilizando [enlace] http://www.movable-type.co.uk/ scripts / latlong.html como referencia, la ecuación es d = acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(lon2-lon1) ) * 6371 donde d es la distancia que se calcula,

lat1,lon1 es la coordenada del punto base y lat2,lon2 es la coordenada de los otros puntos que en nuestro caso son puntos en la base de datos.

A partir de las respuestas anteriores, la clase LocationManager se parece a esto

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, (acos(sin(radians(%f)) * sin(radians(latitude)) + cos(radians(%f))
          * cos(radians(latitude)) * cos(radians(%f-longitude))) * %d)
    AS distance FROM skills_coveragearea WHERE distance < %f
    ORDER BY distance LIMIT 0 , %d;""" % (latitude, latitude, longitude,distance_unit, radius, max_results)
    cursor.execute(sql)
    ids = [row[0] for row in cursor.fetchall()]

    return self.filter(id__in=ids)

Uso del sitio [link] http: //www.movable-type. co.uk/scripts/latlong.html como cheque, mis resultados cuando sean compatibles.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top