2009-12-16 27 views
10

मैं Django में एक बुनियादी स्टोर लोकेटर के लिए निकटता खोज को संभालने की कोशिश कर रहा हूँ। मेरे ऐप के साथ पोस्टजीआईएस को घुमाने के बजाय बस मैं जियोडजैंगो के दूरी फ़िल्टर का उपयोग कर सकता हूं, मैं मॉडल क्वेरी में कोसाइन दूरी फॉर्मूला के गोलाकार कानून का उपयोग करना चाहता हूं। मैं दक्षता के लिए, एक क्वेरी में डेटाबेस में सभी गणनाओं को करना चाहता हूं। इंटरनेट इस तरह कोसाइन की गोलाकार कानून लागू करने सेकोज़िन के गोलाकार कानून के साथ Django में निकटता से फ़िल्टर zipcodes

एक उदाहरण 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 को संदर्भित करने की जरूरत है। मैं Django मॉडल क्वेरी में यह सब काम कैसे कर सकता हूं?

+0

क्या आप अपने मॉडल पोस्ट कर सकते हैं? – cethegeek

+0

(ए) यह 'हावर्सिन' सूत्र नहीं है; यह कोसाइन के सूत्र का 'गोलाकार कानून' है; देखें (उदाहरण के लिए) 'http: // www.movable-type.co.uk/स्क्रिप्ट/latlong.html' और संबंधित विकिपीडिया लेख देखें। (बी) मुझे विश्वास है कि आप चर-कोड वाले उपयोगकर्ता निर्देशांक को चर के साथ प्रतिस्थापित करेंगे :-) (सी) कोमल पाठक को चेतावनी दी जानी चाहिए कि दूरी की आपकी इकाई कुछ हद तक पुरातन है (1000 * के साथ कुछ करने के लिए (रोमन की लंबाई सेना की मानक गति), मुझे विश्वास है) :-) –

+0

जीज़, जियोजंजो के बारे में इतना मुश्किल क्या है? बस इसे स्थापित करें :) –

उत्तर

8

यह संभव है raw SQL queries in Django निष्पादित करना संभव है।

मेरा सुझाव है, आईडी की एक सूची खींचने के लिए क्वेरी लिखें (जो ऐसा लगता है कि आप अभी कर रहे हैं), फिर संबंधित मॉडलों को खींचने के लिए आईडी का उपयोग करें (नियमित, गैर-कच्चे-एसक्यूएल डीजेगो क्वेरी में)। अपने एसक्यूएल को यथासंभव बोली-स्वतंत्र के रूप में रखने की कोशिश करें, ताकि आपको कभी भी डेटाबेस स्विच करने के लिए एक और चीज़ के बारे में चिंता न करें।

स्पष्ट करने के लिए, यहाँ यह कैसे करना है की एक उदाहरण है:

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 लिखा है हो गया है, लेकिन यह सही लाइनों के साथ होना चाहिए।

+0

यह ठीक काम करता है, केवल ट्रिपल-उद्धरण (या एक स्ट्रिंग में बदलना) की आवश्यकता होती है। – Tom

+0

बस इसका पालन करें। मूल क्वेरी एक "दूरी" फ़ील्ड देता है (जो लॉग/लैट के 2 सेट के बीच की दूरी दिखाती है)। मैं दूसरा भाग कैसे करूं, लेकिन इस अतिरिक्त 'दूरी' फ़ील्ड के साथ? – dotty

+0

"दूसरा भाग" क्या है? – jakeboxer

4

बस 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) 
1

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 मील की दूरी से लौटाता है, यह अच्छी तरह से काम करता है। हालांकि कच्चे प्रश्न ने लौटाया कि वे एक निश्चित स्थान से कितने दूर थे, मुझे लगता है कि फील्डनाम 'दूरी' था।

मैं अपनी कार वस्तुओं के साथ इस क्षेत्र 'दूरी' को कैसे वापस कर सकता हूं?

8

टॉम के उत्तर पर अनुवर्ती करने के लिए, यह डिफ़ॉल्ट रूप से 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) 
+0

जादू की तरह लगता है, यह कैसे काम करता है? मुझे लगता है कि यह बहुत अक्षम है? – nisc

+0

'mysite आयात सेटिंग्स से' को 'django.conf आयात सेटिंग्स' से उपयोग करके अधिक सामान्य बनाया जा सकता है –

+0

संदर्भ बिंदु कहां है? – Gocht

5

टॉम पर नजर रखने के लिए, यदि आप एक प्रश्न है कि यह भी PostgreSQL में काम करता है करना चाहते हैं, आप के रूप में उपयोग नहीं कर सकते क्योंकि आप एक त्रुटि कह मिल जाएगा 'दूरी' अस्तित्व में नहीं है।

आप कहां खंड में पूरे गोलाकार कानून expresion रखना चाहिए, इस तरह (यह भी 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 खंड में इसका उपयोग करें।

0

प्रस्तावित कुछ उत्तर का उपयोग करते हुए ऊपर, मैं incosistent परिणाम हो रही थी तो मैं समीकरण फिर से जाँच करने के लिए [इस लिंक] 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 चेक के रूप में की तरह दिखता है, मेरे परिणाम जहां संगत।

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^