Haversine based Server returns data only when coordinates are an exact match

125 Views Asked by At

I have a location aware server in Django that's supposed to return data only when the user is within a given radius of the database entry.. I found a code snippet in python that does just that online (here or elsewhere, I really can't remember) and it seemed to work when I tested it against coordinates of 0,0 and not when I set two different coordinates. I now have a database entry that is within 300 meters of my current location and the radius is set to 10 kilometers but for some reason the server is not returning the results back to me. where am I going wrong with this code cos I'm totally clueless on how to fix it, newbie to python and helplessly lost and out of my element with haversine with 5 days till my project is due. here is my code for the haversine based query:

class StoreList(generics.ListAPIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                  IsOwnerOrReadOnly,)
serializer_class = StoreSerializer

def get_queryset(self):
    lat = self.request.query_params.get('lat', None)
    lon = self.request.query_params.get('lng', None)

    if lat and lon:
        lat = float(lat)
        lon = float(lon)

        # Haversine formula = https://en.wikipedia.org/wiki/Haversine_formula


        lat1 = math.radians(lat)  # lat in radians
        lng1 = math.radians(lon)  # lng in radians

        lat2 = math.asin(math.sin(lat1)*math.cos(distance/R) +
            math.cos(lat1)*math.sin(distance/R)*math.cos(bearing))

        lng2 = lng1 + math.atan2(math.sin(bearing)*math.sin(distance/R)*math.cos(lat1),
            math.cos(distance/R)-math.sin(lat1)*math.sin(lat2))

        lat2 = math.degrees(lat2)
        lng2 = math.degrees(lng2)

        return Store.objects.filter(latitude__gte=lat1, latitude__lte=lat2)\
            .filter(longitude__gte=lng1, longitude__lte=lng2)

this image shows that the request was received and the coordinates are right but the resultset is still empty :(

Server out put

3

There are 3 best solutions below

0
On BEST ANSWER

So.. I found out where the algorithm was going wrong.. thanks to @ncole458's post Answer source

here is the fully functional server code:

class StoreList(generics.ListAPIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,
                  IsOwnerOrReadOnly,)
serializer_class = StoreSerializer

def get_queryset(self):
    lat = self.request.query_params.get('lat', None)
    lon = self.request.query_params.get('lng', None)

    if lat and lon:
        lat = float(lat)
        lon = float(lon)

        # Haversine formula = https://en.wikipedia.org/wiki/Haversine_formula


        """
        lat1 = math.radians(lat)  # lat in radians
        lng1 = math.radians(lon)  # lng in radians

        lat2 = math.asin(math.sin(lat1)*math.cos(distance/R) +
            math.cos(lat1)*math.sin(distance/R)*math.cos(bearing))

        lng2 = lng1 + math.atan2(math.sin(bearing)*math.sin(distance/R)*math.cos(lat1),
            math.cos(distance/R)-math.sin(lat1)*math.sin(lat2))

        lat1 = math.degrees(lat1)
        lat2 = math.degrees(lat2)

        lat2 = math.degrees(lat2)
        lng2 = math.degrees(lng2)
        """
        lat1 = lat - math.degrees(distance / R)
        lat2 = lat + math.degrees(distance / R)
        lng1 = lon - math.degrees(distance / R / math.cos(math.degrees(lat)))
        lng2 = lon + math.degrees(distance / R / math.cos(math.degrees(lat)))

        return Store.objects.filter(latitude__gte=lat1, latitude__lte=lat2)\
            .filter(longitude__gte=lng1, longitude__lte=lng2)
3
On

It looks like you're passing lat1 and lng1 in radians, but lat2 and lng2 in degrees. (You converted lat1 and lng1 to radians but never changed them back to degrees.)

1
On

It would be far easier for you to switch to a location aware database likes postgresql (with postgis extension) or mysql 5.7. If you look at objects with a given distance from a point, is a trivial query for such a database and is fully supported by django

Dwithin Returns models where the distance to the geometry field from the lookup geometry are within the given distance from one another. Note that you can only provide Distance objects if the targeted geometries are in a projected system. For geographic geometries, you should use units of the geometry field (e.g. degrees for WGS84) .

Example:

Zipcode.objects.filter(poly__dwithin=(geom, D(m=5)))

Thus your complex code becomes a one liner. There are plenty of features in a geospatial database that you will find rather usefull.