I am working in GeoDjango (with a Django 1.7 and PostGIS 2.1.4 backend). I have a model with a PointField in my database and I am trying to find all items within 10km radius of a point.

Here is my model code:

class Place(models.Model):
    id = models.IntegerField(primary_key=True)
    location = models.PointField(null=True, blank=True)
    objects = models.GeoManager()

Here is my view code (abridged for readability):

    from django.contrib.gis.geos import Point
    from django.contrib.gis.measure import Distance
    lat = 52.5
    lng = 1.0
    radius = 10
    point = Point(lng, lat)
    print point
    places = Place.objects.filter(location__dwithin=(point.location, Distance(km=radius)))

This gives me:

AttributeError: 'Point' object has no attribute 'location'

I see the point in the console: POINT (1.0000000000000000 52.5000000000000000).

How should I structure the query differently?

If I try just using point rather than point.location (as per the dwithin documentation) then I get a different error: ValueError: Only numeric values of degree units are allowed on geographic DWithin queries.


This seems to work, but I don't know if it's correct:

    radius = int(radius)
    degrees = radius / 111.325
    point = Point(float(lng), float(lat))
    places = Place.objects.filter(location__dwithin=(point, degrees))

The results look OK, but I don't know if my conversion to degrees is reasonable.

  • No it is not, this ratio only works for equator. Apr 6, 2015 at 18:13

2 Answers 2

from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance  

lat = 52.5
lng = 1.0
radius = 10
point = Point(lng, lat)    
Place.objects.filter(location__distance_lt=(point, Distance(km=radius)))

You can do this without GeoDjango if someone is interested, you just need 2 extensions on postgres: cube, earthdistance

from django.db.models import Lookup, Field, fields, Func, QuerySet

class LLToEarth(Func):
    function = 'll_to_earth'
    arg_joiner = ', '
    arity = 2  # The number of arguments the function accepts.

    def __init__(self, *expressions, output_field=None, **extra):
        if output_field is None:
            output_field = fields.Field()
        super().__init__(*expressions, output_field=output_field, **extra)

class EarthBox(LLToEarth):
    function = 'earth_box'
    arg_joiner = ', '

class Near(Lookup):
    lookup_name = 'in_georange'
    operator = '@>'

    def as_sql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = lhs_params + rhs_params
        return '%s @> %s' % (lhs, rhs), params

def filter_in_range(
        queryset: QuerySet,
        latitude: float,
        longitude: float,
        range_in_meters: int,
        latitude_column_name: str='latitude',
        longitude_column_name: str='longitude',
        field_name: str = 'earthbox',
        lookup_exp: str = 'in_georange',
    earthbox = {field_name: EarthBox(LLToEarth(latitude, longitude), range_in_meters)}
    lookup = '%s__%s' % (field_name, lookup_exp)
    in_range = {lookup: LLToEarth(latitude_column_name, longitude_column_name)}
    return queryset.annotate(**earthbox).filter(**in_range)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.