This time, I'm showing a collection of very useful functions, that work with GPS data and postal addresses. Please feel free to cut/past these into your own projects if you wish.
geocoder: A function or utility that when given a postal address, will return the longitude and latitude of that address. There are a number of on-line services that can be used to achieve this task, but I personally prefer to use OpenStreetMaps ( https://www.openstreetmap.org [link] ) because they don't require you to uses a registration key like some providers do, and their data has always been reliable. The website and database are hosted by the London's Global University ( UCL [link] ).
The function shown in the example bellow, also returns several other very useful bits on information. Some of which, like 'BoundingBox', I will be using a bit later in this post.
Note: The geocoder() function shown bellow, runs on Python 3.x, and requires an Internet connection to retrieve the data. It will NOT run on Python 2.x.
import urllib.request def geocoder(address): """This function will return a dict object, containing geocoder info for the postal address given.""" data = {} data['address'] = address url = "https://nominatim.openstreetmap.org/search/%1%?format=xml&polygon=1&addressdetails=1" url = url.replace("%1%", address).replace(" ", "%20") headers = {'User-Agent':"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:48.0) Gecko/20100101 Firefox/48.0"} req = urllib.request.Request(url, headers = headers) html = urllib.request.urlopen(req).read() str1 = str(html).replace("\\", "").replace("'", "").replace('"', "") list1 = str1.split(' ') for items in list1: parts = items.split("=") if len(parts) == 2: data[parts[0]] = parts[1] return data venue1 = geocoder("3801 Discovery Park Blvd, Seattle, WA 98199") print ("Address is: ", venue1.get('address')) print ("The Longitue is: ", venue1.get('lon')) print ("The Latitude is: ", venue1.get('lat')) print ("boundingbox: ", venue1.get('boundingbox')) print ("type: ", venue1.get('type')) print ("=================================")
reverse_geocoder(): A function or utility that when given a longitude and latitude, will return the postal address of that location. In short, it's the reverse of the geocoder() function above.
Note: The reverse_geocoder() function shown bellow, runs on Python 3.x, and requires an Internet connection to retrieve the data. It will NOT run on Python 2.x.
import urllib.request def reverse_geocoder(Latitude, Longitue): """This function returns a dict object, containing postal address info for the location given.""" data = {} data['lon'] = Longitue data['lat'] = Latitude url = "https://nominatim.openstreetmap.org/reverse?format=xml&lat=%1%&lon=%2%&zoom=18&addressdetails=1" url = url.replace("%2%", str(Longitue).strip()).replace("%1%", str(Latitude).strip()) headers = {'User-Agent':"Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:48.0) Gecko/20100101 Firefox/48.0"} req = urllib.request.Request(url, headers = headers) html = urllib.request.urlopen(req).read() list1 = str(html).split('<') for item in list1: if len(item)<300 and not item.startswith('/'): parts = item.split('>') if (len(parts)==2): if len(parts[0]) < 20: data[parts[0]] = parts[1] result = data.get('house_number',"") + " " result = result + data.get('road',"") + ", " result = result + data.get('city',"") + " " result = result + data.get('state',"") + " " result = result + data.get('postcode',"") data['result'] = result.strip() return data address1 = reverse_geocoder(47.6576374, -122.406347146774) print ("The Longitue is: ", address1.get('lon')) print ("The Latitude is: ", address1.get('lat')) print ("The address is: ", address1.get('result') ) print ("=================================")
The AtVenue() example shown bellow, will return True if the gps location (Latitude, Longitue) is within a 'BoundingBox', and False if not. The 'BoundingBox' is a python string, holding information about a venue's shape and size. It defines two corners (gps locations) of a geographical box, defining the venue space. So you can quickly check each of your points from your gps log, and quickly determine when you where, and where not, at said venue.
And where can one find this 'BoundingBox' string? It just happens to be one of the items returned by the geocodder() function shown above.
If you are creating a table of your way-points for your project, I highly recommend that you record the name, postal address, Latitude, Longitue, and the 'BoundingBox' string. I recommend that you create an update script, to search the way-points table for any that have a postal address, but no BoundingBox string. When one is found, uses the geocoder() function to retrieve the BoundingBox string and update the table.
But what if a record in your table only has Latitude and Longitude, but no postal address? Use the reverse_geocoder() function to retrieve the address, and then use the geocoder() function to retrieve the BoundingBox. Then update both fields.
def AtVenue(Latitude, Longitue, BoundingBox): """Returns True if the location given is within the BoundingBox.""" result = False parts = str(BoundingBox).split(",") if len(parts) == 4: if (Longitue >= float(parts[0])) and (Longitue <= float(parts[1])): if (Latitude >= float(parts[2])) and (Latitude <= float(parts[3])): result = True return result TheBox = "47.6575326,47.6577424,-122.4068506,-122.4059097" print ("At the venue (test1): ", AtVenue(-122.406347146774, 47.6576374, TheBox)) print ("At the venue (test2): ", AtVenue(-122.150426, 47.753233, TheBox)) print ("=================================")
The haversine() function will return the distances, in kilometers, between two gps points.
But, it calculates the distances as if the earth was a smooth sphere; It does NOT take into account any extra distances traveled by going up and down hills (added vertical distance) . So the odometer on your car, and your virtual odometer, might not be in agreement depending on how many hills you traveled.
What if you want the distances in miles instead of kilometers? Will, I have included a couple of conversion functions bellow to take care of that as well.
from math import pi,sqrt,sin,cos,atan2 def haversine(Latitude1, Longitue1, Latitude2, Longitue2): """Returns the distances, in kilometers, between two GPS points.""" # For info, do an Internet search for 'haversine formula'. # Inportant: This calculates the distances as if the earth was a # smooth sphear, it does NOT take into account any extra distances # from going up and down hills. dtr = float(pi / 180.0) # ratio of degrees to rad delta_lat = (Latitude2 - Latitude1) * dtr delta_long = (Longitue2 - Longitue2) * dtr a = cos(Latitude1 * dtr) * cos(Latitude2 * dtr) * pow(sin(delta_long / 2), 2) a = a + pow(sin(delta_lat / 2), 2) c = 2 * atan2(sqrt(a), sqrt(1 - a)) return 6367 * c def mi2km(mi): """This function converts miles to kilometers.""" return float(mi) * 1.60934 def km2mi(km): """This function converts kilometers to miles.""" return float(km) * 0.621371 distance = haversine(-122.4168506, 47.6575326, -122.4059097, 47.7687424) print ("The distances between the two points, in kilometers: ", distance) print (" and in miles: ", km2mi(distance)) print ("=================================")
Many gps logger applications and devices, save their data in a GPX file format. It's basically just an xml file holding the data.
The ReadGPX() example bellow, shows how to read such a file.
Note: Some of the records in a GPX file might hold other information, like speed, course, elevation, and number of satellites it's was using at the time. This data is also returned by the ReadGPX() function. So it may be worth the time to explore these records, and see what kind of useful information you application might be collecting.
def ReadGPX(filename): """Reads a gpx file and returns the data as a list.""" result = [] try: with open(filename, 'r') as inputfile: file_contints = inputfile.read() list1 = file_contints.split('trkpt') for item in list1: data = {} if 'time' in item: str1 = item.replace('<', ' ').replace('>', '=').replace('"', '') elems = str1.split(" ") for elem in elems: parts = elem.split('=') if len(parts) >= 2: if not parts[0].startswith('/'): data[parts[0]] = parts[1] result.append(data) except exception as e: print("Error: " + str(e)) finally: return result gpx_data = ReadGPX('20181228.gpx') for item in gpx_data: print (item.get('time'), item.get('lon'), item.get('lat'))
I hope you find these code samples useful.
Please feel free to cut/past these samples into your projects.
Joe.
Last updated: 2019-03-26