Kickstart your coding journey with our Python Code Assistant. An AI-powered assistant that's always ready to help. Don't miss out!
Geocoding is the process of transforming a description of a location (such as a physical address or the 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 the GeoPy library in Python.
However, if you want to geolocate IP addresses, then this tutorial is for you.
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:
In this section, we will use 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, retrieve 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'}
Now, to retrieve an address, city, country, and various other information only from latitude and longitude, we simply use the reverse()
method instead of geocode()
, which accepts coordinates (latitude
and longitude
) as a string separated by a comma.
The following function reverses the coordinates while 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 this 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.
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 ♥
Take the stress out of learning Python. Meet our Python Code Assistant – your new coding buddy. Give it a whirl!
View Full Code Fix My Code
Got a coding query or need some guidance before you comment? Check out this Python Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!