1

I'm building a world-map and want users to be able to look-up locations and have them be sorted by distance from anywhere in the world. I'm using GeoDjango to calculate distances, however, the distances returned seemed wrong so I checked them against geopy's calculations.

The distances differ significantly to the point that if the results are sorted by the DB distance values in kilometers vs geopy values in km they would not be in the same order.

I'm assuming the geopy values are correct so I'm wondering, is there something wrong with the way I implemented GeoDjango?

Is it working as it should?
Does this have something to do with trying to calculate distances for the entire world and not just a specific area which could have a srid?

views.py

pnt = GEOSGeometry('POINT({} {})'.format(latitude, longitude), srid=4326)
qs = Place.filter(point__distance_lte=(pnt, D(km=radius*2))).annotate(distance=Distance('point', pnt)).order_by('distance')

settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'USER': '',
        'NAME': 'places',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

models.py:

from django.contrib.gis.db import models
from geopy.distance import vincenty

class Place(models.Model):
        latitude = models.DecimalField(max_digits=10, decimal_places=6, null=True, blank=True)
        longitude = models.DecimalField(max_digits=10, decimal_places=6, null=True, blank=True)
        point = models.PointField(null=True, geography=True)

    def distance_from_target(self, target_lat, target_lon):
        if not target_lat or not target_lon:
            return None
        instance_point = (self.latitude, self.longitude)
        target_point = (target_lat, target_lon)
        return vincenty(instance_point, target_point).kilometers
John Moutafis
  • 22,254
  • 11
  • 68
  • 112
Santiago Angel
  • 1,127
  • 15
  • 19

1 Answers1

0

You don't need to multiply radius * 2!

I was facing also a differences between postgres/postgis calculated distance and the distance from geopy.distance.vincenty, but very small one (on meters level not kilometers)

I do my querying like this

from django.contrib.gis.measure import Distance
from django.contrib.gis.db.models.functions import Distance as DistanceFun
from django.contrib.gis.geos import fromstr

user_location = fromstr(
    "POINT({value_lon} {value_lat})".format(value_lon=value_lon, value_lat=value_lat)
)
qs.filter(
    location__distance_lte=(user_location, Distance(m=value_radius_in_meters))
).annotate(
    distance=DistanceFun('location', user_location)
).order_by('distance')
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
andilabs
  • 22,159
  • 14
  • 114
  • 151