1

I'm working on getting all events within 10 miles of the user's location. My models look something like this:

class User(models.Model):
    location = models.PointField()
    ...


class Event(models.Model):
    location = models.PointField()
    ...

In my tests, when I check the distance between the user and an event, I get the value 11.5122663513:

from geopy.distance import vincenty

print vincenty(request.user.location, event.location).miles # 11.5122663513

Yet, when I query for all events within 10 miles of the user's location, that event is returned:

Event.objects.filter(location__distance_lte=(request.user.location, D(mi=10))).count() # 1

Only when I drop the radius to less than 4 miles does the filter take effect:

Event.objects.filter(location__distance_lte=(request.user.location, D(mi=3))).count() # 0

I'm following the docs' example almost exactly, so I don't think my query is the problem.

What could be causing this discrepancy?

yndolok
  • 5,197
  • 2
  • 42
  • 46

1 Answers1

2

This very much depends on what type of database you are using.

Because cartesian math is much faster than geospatial math, the query likely treats coordinates as if they are on a plane rather than on a sphere.

The docs explain it this way:

Most people are familiar with using latitude and longitude to reference a location on the earth’s surface. However, latitude and longitude are angles, not distances. In other words, while the shortest path between two points on a flat surface is a straight line, the shortest path between two points on a curved surface (such as the earth) is an arc of a great circle. Thus, additional computation is required to obtain distances in planar units (e.g., kilometers and miles). Using a geographic coordinate system may introduce complications for the developer later on. For example, Spatialite does not have the capability to perform distance calculations between geometries using geographic coordinate systems, e.g. constructing a query to find all points within 5 miles of a county boundary stored as WGS84.

Portions of the earth’s surface may projected onto a two-dimensional, or Cartesian, plane. Projected coordinate systems are especially convenient for region-specific applications, e.g., if you know that your database will only cover geometries in North Kansas, then you may consider using projection system specific to that region. Moreover, projected coordinate systems are defined in Cartesian units (such as meters or feet), easing distance calculations.

Furthermore, this may be influenced by your database choice. If you are using Postgres/PostGIS, it has the following note in the docs:

In PostGIS, ST_Distance_Sphere does not limit the geometry types geographic distance queries are performed with. However, these queries may take a long time, as great-circle distances must be calculated on the fly for every row in the query. This is because the spatial index on traditional geometry fields cannot be used.

For much better performance on WGS84 distance queries, consider using geography columns in your database instead because they are able to use their spatial index in distance queries. You can tell GeoDjango to use a geography column by setting geography=True in your field definition.

You can check this yourself by printing out the raw SQL:

qs = Event.objects.filter(location__distance_lte=(request.user.location, D(mi=10))
print qs.query

Depending on your database type, and the amount of data you plan to store, you have a couple options:

  • Filter the points a second time in python
  • Try setting geography=True
  • Set an explicit SRID
  • Take a point, buffer it out into a circle with the given radius and then find points within that circle using contains
  • Use a different database type

If you share the raw query it'll be easier to figure out what is happening.

Community
  • 1
  • 1
Nathan Villaescusa
  • 17,331
  • 4
  • 53
  • 56
  • I ended up going with the buffer out a point into a circle option. For anyone else who goes with that option, make sure to [convert your radius to degrees](http://stackoverflow.com/questions/5217348/how-do-i-convert-kilometres-to-degrees-in-geodjango-geos). – yndolok Nov 24 '15 at 21:45