简体   繁体   中英

GeoDjango: How can I get the distance between two points?

My Profile model has this field:

location = models.PointField(geography=True, dim=2, srid=4326)

I'd like to calculate the distance between the two of these locations (taking into account that the Earth is a spheroid) using GeoDjango, so that I can store this distance in the database.

  1. How can I calculate this distance with GeoDjango?
  2. What units are the results in?
  3. Is there a 'best' way to store this data? Float? Decimal?

I've reviewed previous, similar questions, and haven't found them useful. No answer gives enough explanation of what's happening or why it works.

I'm using Django 1.8 and the latest versions of required libraries for GeoDjango.

Thanks!

Following Abhyudit Jain 's comment, I'm using geopy to calculate distance. I'm adding it as a property as opposed to storing it, as per e4c5 's advice:

from django.contrib.gis.measure import Distance, D
from geopy.distance import distance

@property
def distance(self):
    return Distance(m=distance(self.user_a.profile.location, self.user_b.profile.location).meters)

Geopy defaults to Vincenty's formulae , with an error of up to 0.5%, and contains a lot of other functionality I'll use in future.

The above returns a GeoDjango Distance object, ready for easy conversion between measurements.

Thanks for the help!

How can I calculate this distance with GeoDjango? For two objects:

a.location.distance(b.location)

Suppose you have an object a which is an instance of your profile model and you wish to find the distance to every other profile you can perform the following query as described in Geodjango reference :

for profile in Profile.objects.distance(a.location):
    print profile.distance

If you only want to compare with objects that are less than 1km distance away:

for profile in Profile.objects.filter(location__dwithin=(a.location, D(km=1)).distance(a.location):
        print profile.distance

What units are the results in?

the unit can be whatever you want it to be. What's returned is a distance object. However the default is in meter and that's what the print statement above will display.

Is there a 'best' way to store this data? Float? Decimal?

The best way is not to save it. Typically one does not save in a database what can be calculated by a simple query. And the number of records will grow expontially. For example if you have N profiles in your database it will have some distance property to N-1 other profiles. So you end up with N(N-1) number of records in your 'cache table'

To get the distance computed in a GeoQuerySet you can combine annotate with the GeoDjango Distance database function (not to be confused with the Distance measure )

from django.contrib.gis.db.models.functions import Distance

queryset = Profile.objects.annotate(
    distance=Distance('location', a.location)
)

The annotated distance will be a Distance measure . Meaning you can do the following:

for profile in queryset:
    print(profile.distance.mi)  # or km, etc

To filter for profiles within a certain radius you can add a filter to the QuerySet.

from django.contrib.gis.db.models.functions import Distance as DistanceDBFunction
from django.contrib.gis.measure import Distance as DistanceMeasure

queryset = Profile.objects.annotate(
    distance=DistanceDBFunction('location', a.location)
).filter(
    distance__lt=DistanceMeasure(mi=1)
)

If you do not need the annotated distance, you can simply use the distance lookups .

from django.contrib.gis.measure import Distance

queryset = Profile.objects.filter(
    location__distance_lt=(a.location, Distance(mi=1))
)

Note: the Profile.objects.distance(a.location) as noted in other answers has been deprecated since Django 1.9.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM