0

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

ItIsEntropy
  • 306
  • 3
  • 13

3 Answers3

1

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.)

BrenBarn
  • 242,874
  • 37
  • 412
  • 384
  • I have but the resultset is still empty – ItIsEntropy May 12 '16 at 10:22
  • 1
    @CoolBeans: It's hard to know what's going on without knowing what's in your database. Looking at your code again, it looks like you're checking for points whose latitude is greater than `lat1` --- but isn't `lat1` the latitiude of your original point? It seems like you'd want to use haversine to compute *two* new latitudes and longitudes (for NSEW boundaries), rather than passing in the lat/long of your original point. – BrenBarn May 12 '16 at 17:17
  • Tanks.. I eventually managed to find a workaround.. updating with the answer – ItIsEntropy May 12 '16 at 17:43
1

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.

e4c5
  • 52,766
  • 11
  • 101
  • 134
  • I wish I could, but I don't have vcvar.bat for using mysql, I wanted to but couldn't cos of that vcvar is missing error – ItIsEntropy May 12 '16 at 08:41
0

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)
ItIsEntropy
  • 306
  • 3
  • 13