0

私が構築しているサイトでは、都市への外部キーを使用してイベントを保存しました。このような:

class Event(models.Model):
    name = models.CharField(max_length=255)
    ...
    ciudad = models.ForeignKey(City)

class City(models.Model):
    name = models.CharField(max_length=500)
    ...
    lat = models.FloatField()
    lon = models.FloatField()

ある都市のキロでイベントを照会したい。私が実際にやっていることはこれです:

# isInRange takes two cities and a distance in kms and calculates
# if the distance between the cities (by the fields lat and lon and
# one nice formula) is minor than the given distance.
results = []
for event in Event.objects.all():
    if isInRange(city, event.city, kms):
        results.append(event)

私は知っています、非常に非効率的です。GeoDjango でこれを行うことが可能であることは知っていますが、これはプロジェクト全体で行う必要がある唯一の「地理的なこと」です。その「複雑な」ソリューションを言い訳なしで使用する必要がありますか、それともより効率的な方法でこれを行う方法はありますか?

4

2 に答える 2

9

範囲をあまり正確にする必要がない場合は、概算を使用して緯度と経度の範囲を計算できます。ここで説明された概念:

都市の位置と距離を使用して、緯度の変化 (これはどこでも同じです) と経度の変化 (緯度によって異なります) を求めます。次に、境界ボックスを計算します。

import math

# earth_radius = 3960.0  # for miles
earth_radius = 6371.0  # for kms
degrees_to_radians = math.pi/180.0
radians_to_degrees = 180.0/math.pi

def change_in_latitude(distance):
    "Given a distance north, return the change in latitude."
    return (distance/earth_radius)*radians_to_degrees

def change_in_longitude(latitude, distance):
    "Given a latitude and a distance west, return the change in longitude."
    # Find the radius of a circle around the earth at given latitude.
    r = earth_radius*math.cos(latitude*degrees_to_radians)
    return (distance/r)*radians_to_degrees

def bounding_box(latitude, longitude, distance):
    lat_change = change_in_latitude(distance)
    lat_max = latitude + lat_change
    lat_min = latitude - lat_change
    lon_change = change_in_longitude(latitude, distance)
    lon_max = longitude + lon_change
    lon_min = longitude - lon_change
    return (lon_max, lon_min, lat_max, lat_min)

距離内のイベントを計算するkmsにはcity:

lon_max, lon_min, lat_max, lat_min = bounding_box(city.lat, city.lon, kms)
events = Event.objects.filter(
    city__lat__lte=lat_max,
    city__lat__gte=lat_min,
    city__lon__lte=lon_max,
    city__lon__gte=lon_min
)

距離が長くなるほど誤差が大きくなり、極に近づくほど誤差が大きくなることに注意してください。逆子午線 (国際日付変更線) に近い場所にも問題がありますが、簡単に確認できます (経度が 180 度を超えているか、-180 度を超えているかを確認してください)。

より正確な結果が必要な場合は、このメソッドを最初のパスとして使用してから関数を使用できるため、すべてのイベントを個別に実行する必要はありません。

于 2013-03-05T06:09:00.177 に答える
0

この投稿で説明するように、カスタムマネージャーを使用したより良いソリューションDjango sort by distance

于 2014-10-06T13:14:23.027 に答える