4

My view code (relevant part):

location = self.GOOGLE_MAPS.geocode(lookup)
if len(location):
    page = int(request.POST.get('page')) or 0
    start = page * settings.PHOTOS_PER_BATCH
    end = (page + 1) * settings.PHOTOS_PER_BATCH
    geo_range = int(request.POST.get('range',  self.DEFAULT_RANGE))
    geo = location[0].get('geometry', {}).get('location')
    pnt_str = 'SRID=4326;POINT({} {})'.format(geo['lng'], geo['lat'])
    pnt = GEOSGeometry(pnt_str)

    if geo_range > 0:
        # short version
        # photos = Photo.objects.filter(photographer__locations__point__distance_lte=(pnt, D(km=geo_range)))

        #debugable version
        locations = Location.objects.filter(point__distance_lte=(pnt, D(km=geo_range)))\
            .distance(pnt).order_by('distance')
        photographers = Photographer.objects.filter(locations__in=locations)
        photos = Photo.objects.filter(photographer__in=photographers).order_by('?')
    else:
        photos = Photo.objects.all()

    #render the view
    context = {
        'formatted_address': location[0].get('formatted_address'),
        'photos': photos[start:end],
        'geo': geo,
        'total': len(photos)
    }

    return render(request, self.TEMPLATE_NAME, context)

Distance display is made by a custom django filter:

from django import template
from proto.models import Location

register = template.Library()


def distance(origin, destination):
    return int(destination.distance(origin) * Location.SCALE)

register.filter('distance', distance)

Finally, the template code:

{{ point|distance:p.locations.all.0.point }} KM

The problem is that even when the range is set to 1000 M (10 KM), results that seem to have the distance of 14 of 16 KM are still included. With a range of 20 KM, result of 25 and 28 (and so on) are also included (although they are excluded from the previous result set).

Is it and SRID Issue?

Also, I don't quite understand why should I multiply by 1000 to get the filtering done, and then divide by only 100 to display the distance.

  • 3
    You need to use distance sphere (not sure if available in GeoDjango), or convert your geometries to something projected, so that distance in meters is meaningful. Using distance with 4326 (lat/lng) will get you very unreliable results. You might find this answer on SO useful. – John Powell Feb 16 '16 at 13:30
1

If you are using Django 1.9, you can use the Distance geographic database function to make annotations to your queryset. The disance then becomes an attribute of your queryset, so you can potentially avoid using a custom template tag alltogether.

from django.contrib.gis.db.models.functions import Distance
photos = photos.annotate(
    distance=Distance('photographer__locations__point', pnt)
)

I am not sure if this is exactly what you are looking for. However, if this works for you, the distance attribute is a Distance measure object. So in your template you can access your favourite measure directly without a custom tag (km in the example):

{{ photo.distance.km }}

This might also resolve your problems with the filtering, at least it would make sure the distance is displayed correct in your template.

| improve this answer | |
  • TESTING right now, will provide feedback today or tomorrow the latest! thank you! – Oleg Belousov Feb 18 '16 at 17:12

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.