Python - Longitude, Latitude, and Address.

A collection of functions for working with GPS data and postal addresses.
March 26, 2019

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.

Python - Longitude, Latitude, and Address.

A collection of functions for working with GPS data and postal addresses.
March 26, 2019

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.


Python - Longitude, Latitude, and Address.

A collection of functions for working with GPS data and postal addresses.
March 26, 2019

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.


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.

The geocoder()

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 ("=================================")

>>>
Address is: 3801 Discovery Park Blvd, Seattle, WA 98199
The Longitue is: -122.406347146774
The Latitude is: 47.6576374
boundingbox: 47.6575326,47.6577424,-122.4068506,-122.4059097
type: information
=================================
>>>


The reverse_geocoder()

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 Longitue is: -122.406347146774
The Latitude is: 47.6576374
The address is: 3801 Discovery Park Boulevard, Seattle Washington 98199
=================================
>>>


The AtVenue() function:

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 ("=================================")

>>>
At the venue (test1): True
At the venue (test2): False
=================================
>>>


The haversine() function:

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 ("=================================")

>>>
The distances between the two points, in kilometers: 1.215808754013868
and in miles: 0.7554683012903511
=================================
>>>


The ReadGPX() function.

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

2018-12-28T21:57:43.614Z -122.1569813 47.7080542
2018-12-28T21:58:47.591Z -122.156955 47.7080691
2018-12-28T21:59:49.999Z -122.1568351 47.708057
2018-12-28T22:00:52.644Z -122.1569487 47.7080605
2018-12-28T22:02:18.283Z -122.1568442 47.7080455
2018-12-28T22:03:20.126Z -122.1569994 47.7080538
2018-12-28T22:04:23.871Z -122.1568949 47.7080767
2018-12-28T22:05:25.775Z -122.157037 47.7080467
2018-12-28T22:06:27.497Z -122.1569979 47.7080438
........



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



Written by Joe Roten

Computer tech, Graphic Artist, Photographer, Writer, Educator, Programmer, Jack of many trades, Social gadfly, and Scholar without portfolio. http://www.gsw7.net/joe/

Written by Joe Roten

http://www.gsw7.net/joe/

As always

The information on my website is FREE.
But donations to help pay for Coffee and Beer are always welcomed.
Thanks.