코사인의 구형 법칙과 함께 Django의 근접성에 의한 우편 번호 필터
문제
Django의 기본 상점 위치에 대한 근접 검색을 처리하려고합니다. Geodjango의 거리 필터를 사용할 수 있도록 앱을 사용하여 Postgis를 운반하는 대신 모델 쿼리에서 Cosines 거리 공식의 구형 법칙을 사용하고 싶습니다. 효율성을 위해 모든 계산을 하나의 쿼리의 데이터베이스에서 수행하고 싶습니다.
인터넷의 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;
쿼리는 각 스토어의 LAT/LNG 값에 대해 ZipCode 외국 키를 참조해야합니다. 이 모든 작업을 Django 모델 쿼리에서 어떻게 만들 수 있습니까?
해결책
실행 가능합니다 Django의 원시 SQL 쿼리.
내 제안은 쿼리를 작성하여 ID 목록 (지금하고있는 것처럼 보이는 것)을 가져온 다음 ID를 사용하여 관련 모델을 끌어 당기는 것입니다 (정기적 인 비 RACL Django 쿼리에서). 데이터베이스를 전환 해야하는 경우 한 가지 더 걱정할 필요가 없도록 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를 작성한 지 몇 달이 지났기 때문에이 코드를 보증 할 수는 없지만 올바른 줄을 따라야합니다.
다른 팁
Tom의 답변을 추적하기 위해 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)
Tom에 대한 후속 조치를 취하려면 PostgreSQL에서도 작동하는 쿼리를 원한다면 '거리'가 존재하지 않는다는 오류가 발생하기 때문에 사용할 수 없습니다.
당신은 전체 구형 법률을 WHERE 절에 넣어야합니다 (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)
위도와 경도를 선택해야합니다. 그렇지 않으면 Where 절에서 사용할 수 없습니다.
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)
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 양의 마일로 되돌립니다. 이것은 잘 작동합니다. 그러나 원시 쿼리는 특정 위치에서 얼마나 멀리 떨어져 있는지 반환했는데 필드 이름은 '거리'라고 생각합니다.
자동차 물체 로이 필드 '거리'를 어떻게 반환 할 수 있습니까?
위의 제안 된 답변 중 일부를 사용하여 결과적으로 결과를 얻었으므로 [이 링크]를 사용하여 방정식을 다시 확인하기로 결정했습니다.http://www.movable-type.co.uk/scripts/latlong.html 참조로서, 방정식은 다음과 같습니다d = acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(lon2-lon1) ) * 6371
어디 d
계산하는 거리는
lat1,lon1
기본 점의 좌표입니다 lat2,lon2
우리의 경우 다른 점의 좌표는 데이터베이스의 점입니다.
위의 답변에서 LocationManager
수업은 이렇게 보입니다
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)
사이트 사용 [링크http://www.movable-type.co.uk/scripts/latlong.html 확인대로, 내 결과는 일관된 곳입니다.