[GIS] GeoDjango, distance filter based on database column

circledistancedjangogeodjangopostgis

I have a geodjango model with fields

coverage = 10 * 1609.344
reference_point = Point(-72,45) #or some other lat lng
class ServiceArea (geo_models.Model):
    center = geo_models.PointField(default=None, blank=True, null=True)
    radius = models.FloatField(default=floor(6 * 1609.344), null=True, blank=True)  # in meters

I am user center and radius to store a circle representing a service area. I would like to query this to return all objects where the "center" is less than (coverage + radius) away from "reference_point".

Or to put it another way, filter based on any part of the circlular service area being within a certain distance of a reference point.

This is simple with polygonfields, where I can do something like

ServiceArea.objects.filter(polygon__distance_lte=(reference_point, D(m=coverage)))

Although I haven't figured it out yet I imagine it would be possible to store the circles as polygons, and do the calculation as above, but this may have its own problems. And it is simpler for other reasons to just store coordinates and radius in the database to approximate circles.

I've tried using django.db.models.F, but it doesn't seem to work in this situation.

What do you guys think?

Best Answer

Maybe you can achieve this if you use Django 1.9, where you can use the new geographic database functions api.

If you buffer the reference point, then annotate the service areas with the distance to the buffer, and finally filter that distance against the radius.

I am not sure if annotations can be used with F expressions, but if they do, the following query should do what you are looking for (I split the query in two lines here for readability):

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

# Buffer point with coverage radius (assuming projected coordinates)
reference_buffer = reference_point.buffer(coverage)

# Get service area queryset annotated with distance of center to buffer
qs = ServiceArea.objects.annotate(distance=Distance('center', reference_buffer))

# Filter queryset using the annotated distance
qs = qs.filter(distance__lte=F('radius'))

I don't have a setup like yours at hand, so I can't try this out, but it might work.