How to Get Geolocation in Python

Learn how to use GeoPy library to geocode physical addresses into latitude and longitude and vice versa; getting latitude and longitude from addresses, towns, cities and more in Python.
  · 5 min read · Updated aug 2020 · General Python Topics


Geocoding is the process of transforming a description of a location (such as a physical address, or a name of a place) into a pair of latitude and longitude on the Earth's surface for that place.

It also refers to converting geographic coordinates to a description of a location (such as an address), this is often called reverse geocoding. In this tutorial, we will learn how to do both with the help of GeoPy library in Python.

GeoPy is a Python client that provides several popular geocoding web services, it makes it easy for Python developers to locate the coordinates of an address, a city, or a country and vice-versa.

To get started, let's install it:

pip3 install geopy

GeoPy provides many geocoding service wrappers, such as OpenStreetMap Nominatim, Google Geocoding API V3, Bing Maps and more. In this tutorial, we gonna stick with OpenStreetMap Nominatim.

Here is what we'll be covering:

Getting Latitude and Longitude from an Address (Geocoding)

In this section, we will be using OpenStreetMap Nominatim API to get latitude and longitude from a physical address, city, or any location name.

Let's import the library first:

from geopy.geocoders import Nominatim
import time
from pprint import pprint

Notice we chose Nominatim geocoder, now creating a new instance of it:

# instantiate a new Nominatim client
app = Nominatim(user_agent="tutorial")

Now let's try to get geographic data from an address:

# get location raw data
location = app.geocode("Nairobi, Kenya").raw
# print raw data
pprint(location)

Output:

{'boundingbox': ['-1.444471', '-1.163332', '36.6509378', '37.1038871'],
 'class': 'place',
 'display_name': 'Nairobi, Kenya',
 'icon': 'https://nominatim.openstreetmap.org/images/mapicons/poi_place_city.p.20.png',
 'importance': 0.845026759433763,
 'lat': '-1.2832533',
 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. '
            'https://osm.org/copyright',
 'lon': '36.8172449',
 'osm_id': 9185096,
 'osm_type': 'relation',
 'place_id': 273942566,
 'type': 'city'}

Awesome, we have latitude at lat attribute (in which we can access by location['lat']) and longitude at lon attribute, we also have access to the bounding box of the address at the boundingbox attribute.

As you can see, the Nominatim API does not require a full address (that consists of street, house number and city), you can also pass business addresses and points of your interests, it supports that!

However, if you call this function repeatedly (such as iterating over a list of addresses), you will encounter a timed out error, and that's because if you read the Nominatim Usage Policy, it requires you to use a maximum of 1 request per second, and that's absolutely acceptable, since it's a free service.

As a result, the below function respects that requirement and sleeps for one second before making a request:

def get_location_by_address(address):
    """This function returns a location as raw from an address
    will repeat until success"""
    time.sleep(1)
    try:
        return app.geocode(address).raw
    except:
        return get_location_by_address(address)

So whenever a timed out error is raised, we catch that and call the function recursively, and this function will sleep for another second and hopefully, retrieves the result:

address = "Makai Road, Masaki, Dar es Salaam, Tanzania"
location = get_location_by_address(address)
latitude = location["lat"]
longitude = location["lon"]
print(f"{latitude}, {longitude}")
# print all returned data
pprint(location)

Output:

-6.7460493, 39.2750804
{'boundingbox': ['-6.7467061', '-6.7454602', '39.2741806', '39.2760514'],
 'class': 'highway',
 'display_name': 'Makai Road, Masaki, Msasani, Dar es-Salaam, Dar es Salaam, '
                 'Coastal Zone, 2585, Tanzania',
 'importance': 0.82,
 'lat': '-6.7460493',
 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. '
            'https://osm.org/copyright',
 'lon': '39.2750804',
 'osm_id': 23347726,
 'osm_type': 'way',
 'place_id': 89652779,
 'type': 'residential'}

Getting Address from Latitude and Longitude (Reverse Geocoding)

Now to retrieve an address, city and country and various of other information, only from latitude and longitude, we simply use reverse() method instead of geocode(), which accepts coordinates (latitude and longitude) as string separated by a comma.

The following function reverse geocode the coordinates along with respecting Nominatim usage policy:

def get_address_by_location(latitude, longitude, language="en"):
    """This function returns an address as raw from a location
    will repeat until success"""
    # build coordinates string to pass to reverse() function
    coordinates = f"{latitude}, {longitude}"
    # sleep for a second to respect Usage Policy
    time.sleep(1)
    try:
        return app.reverse(coordinates, language=language).raw
    except:
        return get_address_by_location(latitude, longitude)

So this function expects latitude and longitude as parameters and returns the raw geographic data, here is an example usage:

# define your coordinates
latitude = 36.723
longitude = 3.188
# get the address info
address = get_address_by_location(latitude, longitude)
# print all returned data
pprint(address)

Output:

{'address': {'country': 'Algeria',
             'country_code': 'dz',
             'county': 'Dar El Beida District',
             'postcode': '16110',
             'state': 'Algiers',
             'town': 'Bab Ezzouar'},
 'boundingbox': ['36.7231765', '36.7242661', '3.1866439', '3.1903998'],
 'display_name': 'Bab Ezzouar, Dar El Beida District, Algiers, 16110, Algeria',
 'lat': '36.72380363740118',
 'licence': 'Data © OpenStreetMap contributors, ODbL 1.0. '
            'https://osm.org/copyright',
 'lon': '3.188236679492425',
 'osm_id': 42812185,
 'osm_type': 'way',
 'place_id': 98075368}

So this will return all address data, including state, town, postcode, districts and more. If you wish to return these information in a specific language, you can set language parameter to the language you desire, or you can set it to False for the default language for that specific location.

Conclusion

As always, we only saw simple examples of what GeoPy can do, I highly suggest you read the documentation if you're interested in more advanced utilities.

Learn also: How to Get Domain Name Information in Python.

Happy Coding ♥

View Full Code
Sharing is caring!



Read Also





Comment panel