Getting the nearest point using GPS coordinates

Python | Friday April 22 2011 17:39 | Comments (0)

One obvious requirement of this app is to get the closest bike station, or LUAS stop etc etc. This is the feature I’ve been working on lately.

Inititally I was a bit worried when I stumbled across the Great-Circle distance and the Haversine Formula as I’m not a big fan of calculations. Its one of the reasons I went into programming….so I could get computers to calculate hard stuff for me!!!

Luckily, I stumbled across a python library that has it all done for you!! Brilliant! Its called Geopy and can be installed with easy-install. My host doesnt allow for easy-install installs for some reason so I had to do it another way. I had to modify the django.fcgi file to point to a folder I named “modules” which contained the geopy tar.gz and django did the rest! Easy! I know I’m not giving much specifics here, but I’d imagine every host is different so you’ll need to figure out your own hosts methods for installing libraries yourself.

Anyway, with geopy all you need are 2 coordinates. You call a function called “distance.distance(coords1, coords2).miles” and it returns the distance in miles.

Here’s my code to find the nearest station based on the values in my XML file.

def nearestcoords(request, coords="53.345633+-6.267014"):
 user_coords = coords.replace("+", ", ")        ## convert from "lon+lat" to "lon,lat"
 from googlemaps import GoogleMaps
 from geopy import geocoders
 from geopy import distance
 g = geocoders.Google()

 # read xml file
 file = open('lockup.xml','r')
 data = file.read()
 dom = parseString(data)
 rows = dom.getElementsByTagName("root")[0].getElementsByTagName("subroot")[0].getElementsByTagName("stations")

 # setup vars
 shortestStation="None"        # Store temp nearest station
 shortestDistance=""
 for row in rows:
     # Get coords for current record
     curr_coords=row.getAttribute("lat")+','+row.getAttribute("lng")
     # Get distance
     tempDistance=distance.distance(user_coords,curr_coords).miles
     # check if distance is nearer/less
     if tempDistance<shortestDistance:
         shortestDistance=tempDistance
         shortestStation=json.dumps(
         {'number': row.getAttribute("number"),
         'address': row.getAttribute("address"),
         'lat': row.getAttribute("lat"),
         'lng': row.getAttribute("lng"),
         'open': row.getAttribute("open")},
         sort_keys=True,
         indent=4)

 result = ( "["+shortestStation[:-1]+"]" )
 return HttpResponse(result)

Seems straight forward enough! Basically what I’m doing here is opening the XML file, reading the values into an array called “rows”, then looping through that array and comparing coordinates. If the distance is less than the previous distance, that now becomes the new “shortestStation”. I then store the details of that station as a JSON string, which is outputted at the end, ready for consumption!!

Next on the agenda is getting the nearest 3 stations! I’ve no idea how I’m gonna manage this but we’ll see how I get on!

 

EDIT: After a few hours, and some helpful advice from the friendly folks at StackOverflow, I’ve manged to get this part working!

def nearestcoords(request, coords="53.345633+-6.267014"):
    user_coords = coords.replace("+", ", ")	 ## convert from "lon+lat" to "lon,lat"
    from googlemaps import GoogleMaps
    from geopy import geocoders
    from geopy import distance
    g = geocoders.Google()

    # read xml file
    file = open('lockup.xml','r')
    data = file.read()
    dom = parseString(data)
    rows = dom.getElementsByTagName("root")[0].getElementsByTagName("subroot")[0].getElementsByTagName("stations")

    # setup vars
    shortestStation=""		# Store temp nearest station
    shortestDistance=""
    mypoints = []
    for row in rows:
	 # Get coords for current record
	 curr_coords = row.getAttribute("lat") + ',' + row.getAttribute("lng")
	 # Get distance
	 tempDistance = distance.distance(user_coords, curr_coords).miles
	 mypoints.append((tempDistance, row))

    mypoints.sort()
    #the three closest points:
    mythree_shorter = mypoints[0:3]
    for distance, row in mythree_shorter:
	shortestStation = shortestStation+json.dumps(
        	{'number': row.getAttribute("number"),
		 'address': row.getAttribute("address"),
		 'lat': row.getAttribute("lat"),
		 'lng': row.getAttribute("lng"),
		 'open': row.getAttribute("open")},
		 sort_keys=True,
		 indent=4)+","
    result = ( "["+shortestStation[:-1]+"]" )
    return HttpResponse(result)

Wooh!! Now I’m happy! I can put this feature down as DONE!! 😀

Project Progress Update

College,Mobile Development,Python | Thursday April 14 2011 14:11 | Comments (0)

I just realised I havent updated my project progress on here in a while, and since this is the reason I actually made this blog, I thought I better do so now.

So, as of this moment, I have every single bike station in Dublin being populated onto the iPhone UIMapView. I also have a dummy set of coordinates for places to lock your bike up, once you rent it. Additionally, I have all the LUAS stops appearing on the map. This was super tedious, as I had to manually get the coordinates of each stop from Google Maps, which took me about 3 times to get them right since Google Maps decided to give me the wrong coordinates, only to be discovered when I consumed the XML data and populated the map. I created the XML file by hand too!

I also have details of individual stations/lockups but not LUAS stop, as I’m currently trawling through the BeautifulSoup documentation to find a way to convert HTML into XML/JSON so as to be readable from the existing code. Once I figure out how to strip <div class=”xx”>xyz</div> so as to keep the xyz part, I should be good to go!

The public server is up and running too. I set it up using Django as the server, using a RESTful service to expose each “view”. My views are written in Python, which I am starting to like a lot. Its a very powerful language!

Also, since there are about 42 bike stations, lots of lockups, and about 50 LUAS station, getting the nearest station is going to take ~50 requests to the Google Maps server. I will need to implement some sort of client side “coordinate maths” to actually get the closest, then send that to Google, and return the directions. Google imposes a “2500 request per day” for non-premium users. Since the server is actually using shared hosting, I think it filters by IP. Usually I get one try, and then the limit is used up. As its only a school project, I’m not too worried about this. Obviously if it was going public, I’d actually pay for a dedicated IP! I have other hosting with an Irish company, but unfortunately I don’t think they do Python/Django hosting.

So, on my TODO list:

LUAS: Get individual station details (in JSON)

Directions: Figure something out client side for measuring coordinates

Custom start point: As a follow on from the “Directions”, the user should be able to choose a start point, other than their own, and get directions from there to the “nearest” station/stop.

 

So, not much to go. The beta is due up tomorrow, then I have a couple of weeks to polish it off. I also have that Android app to develop too…