Africamamba
Africamamba

Reputation: 21

Finding the nearest point to a user-specified location

For my application, I need to find a point in my database that is the nearest to the point specified by user. This is my model:

class WaysVerticesPgr(models.Model):
    id = models.BigIntegerField(primary_key=True)
    osm_id = models.BigIntegerField(unique=True, blank=True, null=True)
    cnt = models.IntegerField(blank=True, null=True)
    chk = models.IntegerField(blank=True, null=True)
    ein = models.IntegerField(blank=True, null=True)
    eout = models.IntegerField(blank=True, null=True)
    lon = models.DecimalField(max_digits=11, decimal_places=8, blank=True, null=True)
    lat = models.DecimalField(max_digits=11, decimal_places=8, blank=True, null=True)
    the_geom = models.PointField(blank=True, null=True)

And I am trying this code to find all points within a small radius (in this case o.oo5 degrees), annotate the distance between each of them and an input point, sort them by distance and return the first in the array:

from django.contrib.gis.db.models.functions import *
from .models import *
from django.contrib.gis.geos import *
from django.contrib.gis.measure import *

def get_closest_point(input):
    input_point_geometry=GEOSGeometry('POINT('+input+')')
    closest = WaysVerticesPgr.objects.filter(the_geom__dwithin=(input_point_geometry,0.005)).annotate(distance_between=input_point_geometry.distance('the_geom')).order_by(distance_between)[:1]
    return closest

But I get an error 'distance() works only on other GEOS Geometries'. When I try converting the_geom to GEOSGeometry format:

closest = WaysVerticesPgr.objects.filter(the_geom__dwithin=(input_point_geometry,0.005)).annotate(distance_between=input_point_geometry.distance(GEOSGeometry('the_geom'))).order_by(distance_between)[:1]

I get an error:

'String or unicode input unrecognized as WKT EWKT, and HEXEWKB'. Which is kind of strange because the_geom field has the format 0101000020E6100000E7525C55F65DA1BF8FF5793139C34940 that seems to be HEX.

I managed to do it in a long way with a loop but this does not look nice and the performance is not good. But interestingly, conversion to GEOSGeometry works in this case:

def get_closest_point(input):
    input_point_geometry=GEOSGeometry('POINT('+input+')')
    closest = WaysVerticesPgr.objects.filter(the_geom__dwithin=(input_point_geometry,0.005))

    dict_closest = {}
    list_closest = []
    for i in closest:
        i = GEOSGeometry(i.the_geom)
        distance_between=input_point_geometry.distance(i)
        i = str(i.wkt)
        dict_closest = {'Point':i,'Distance':distance_between}
        list_closest.append(dict_closest)
        sortlist=sorted(list_closest, key=itemgetter('Distance'), reverse=False)
    return sortlist[0]

Does anybody have an idea how to make it work in a short way? I work with Django 1.9, Python 3.5 and Postgres 9.5 with PostGIS. I saw people in other threads suggested using another distance function that looks like distance = Distance(point1,point2), but it gives me an error that 3 arguments are given while only 1 or 2 are accepted (an input was two points of GEOSGeometry format).

Your help will be highly appreciated!

Upvotes: 0

Views: 1070

Answers (1)

Africamamba
Africamamba

Reputation: 21

@ e4c5 Yes, I checked it. Turns out that GeoQuerySet.distance function has been deprecated since Django 1.9. Instead, this worked for me:

def get_closest_point(input):
    input_point=GEOSGeometry('POINT('+input+')')
    closest = WaysVerticesPgr.objects.filter(the_geom__dwithin=(input_point_geometry,0.005)).annotate(distance=Distance('the_geom', input_point)).order_by('distance').values('lon','lat')[0]

Thank you anyway (voted for your answer but it does not display because of my low reputation).

Upvotes: 2

Related Questions