11

Django で基本的な店舗ロケーターの近接検索を処理しようとしています。GeoDjango の距離フィルターを使用できるようにするためだけに PostGIS をアプリに持ち込むのではなく、モデル クエリで球面余弦法距離式を使用したいと考えています。効率のために、データベース内のすべての計算を 1 つのクエリで実行したいと考えています。

次のようなコサインの球面法則を実装するインターネットからの 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;

クエリは、各店舗の緯度/経度値の Zipcode ForeignKey を参照する必要があります。このすべてを Django モデルクエリで機能させるにはどうすればよいですか?

4

7 に答える 7

8

Django で生の SQL クエリを実行することは可能です。

私の提案は、ID のリストを取得するクエリを作成し (現在行っているように見えます)、ID を使用して関連するモデルを取得することです (通常の非生 SQL Django クエリで)。データベースを切り替える必要が生じた場合にもう 1 つ心配する必要がないように、できるだけ方言に依存しない 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 を作成してから数か月が経過しているため、このコードを保証することはできませんが、正しい線に沿っているはずです。

于 2009-12-16T19:30:08.967 に答える
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)
于 2010-06-13T23:02:33.870 に答える
5

Tom のフォローアップとして、postgresql でも機能するクエリが必要な場合は、'distance' が存在しないというエラーが表示されるため、AS を使用できません。

次のように、球面法則の式全体を 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 句で使用できません。

于 2010-12-24T02:15:15.803 に答える
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)
于 2010-02-13T17:36:04.890 に答える
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マイルから返します。これはうまく機能します。ただし、生のクエリは特定の場所からの距離を返しました。フィールド名は「距離」だったと思います。

このフィールドの「距離」を車のオブジェクトと一緒に返すにはどうすればよいですか?

于 2010-02-22T11:06:41.433 に答える
0

上記の提案された回答のいくつかを使用すると、一貫性のない結果が得られたため、[このリンク] http://www.movable-type.co.uk/scripts/latlong.htmlを参照として使用して方程式を再度確認することにしました。式は d = acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(lon2-lon1) ) * 6371dは計算す​​る距離、

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をチェックとして使用すると、私の結果は一貫しています。

于 2014-09-25T08:34:37.373 に答える