Have you ever wondered how apps figure out exactly where you are or where you need to go? That’s geocoding at work, it transforms addresses into geographic coordinates and vice versa, which is fundamental in modern mapping technologies and location-based services. Learning to implement geocoding in Python is not just a great skill for developers; it also opens up a world of possibilities for anyone interested in exploring how digital maps function and interact with real-world locations.
In today’s tutorial, you’ll learn how to get geolocation in Python by building your own geocoding application. You’ll get to choose whether to input an address and fetch its latitude and longitude, or do the opposite; Start with geographic coordinates to discover the corresponding address. It’s a fantastic way to see coding in action and to understand how everyday technologies manage location data.
Let’s get started!
Table of Contents
- Necessary Libraries
- Imports
- GeocodingApp Class
- Initializing the Geocoding Application
- Creating Labels, Entry Fields and Buttons
- Result Text Widget
- Geocoding Functions
- Running the Application
- Example
- Full Code
Necessary Libraries
For the code to function properly, make sure to install the tkinter and geopy libraries via the terminal or your command prompt by running these commands:
$ pip install tk
$ pip install geopy
Imports
We aim to make this app user-friendly. So, we import the tkinter
library for its graphical user interface (GUI). Additionally, to ensure the app is interactive with the user, we import the messagebox
module from tkinter
.
Finally, since the script’s goal is to convert addresses into coordinates and vice versa, we import Nominatim
from geopy.geocoders
.
import tkinter as tk
from tkinter import messagebox
from geopy.geocoders import Nominatim
GeocodingApp Class
Next, we define a class named GeocodingApp
that inherits from tk.Tk
in the tkinter
library. This inheritance allows it to utilize Tkinter’s properties, such as widgets and GUI elements.
After that, we create a subclass of GeocodingApp
and initialize it using super().__init__()
to access the inherited functionality.
class GeocodingApp(tk.Tk):
def __init__(self):
super().__init__()
Initializing the Geocoding Application
Now, we set up our application:
- First, we create an instance of the
Nominatim
class, which we will callgeocoder
. - Second, we set the title for our app and define its geometry.
self.geocoder = Nominatim(user_agent="geoapp")
self.title("Geocoding Application - The Pycodes")
self.geometry("660x300")
Creating Labels, Entry Fields and Buttons
Creating Labels
Following that, we create labels to inform the user where to enter the address and coordinates, and we place them in the main window using the grid layout.
# Labels for user instruction
tk.Label(self, text="Enter address for geocoding:").grid(row=0, column=0, padx=10, pady=10, sticky="w")
tk.Label(self, text="Enter latitude and longitude for reverse geocoding:").grid(row=2, column=0, padx=10, pady=10, sticky="w")
tk.Label(self, text="Latitude:").grid(row=3, column=0, padx=10, pady=5)
tk.Label(self, text="Longitude:").grid(row=3, column=2, padx=10, pady=5)
Entry Fields
For this step, we create entry fields where users can enter their address and coordinates. We place these fields on the main window using the grid layout.
# Entry for Forward Geocoding
self.address_entry = tk.Entry(self, width=40)
self.address_entry.grid(row=1, column=0, columnspan=3, padx=10, sticky="we")
# Entries for Reverse Geocoding
self.latitude_entry = tk.Entry(self, width=20)
self.latitude_entry.grid(row=4, column=0, padx=10, pady=5)
self.longitude_entry = tk.Entry(self, width=20)
self.longitude_entry.grid(row=4, column=2, padx=10, pady=5, sticky="we")
Buttons
In this part, we create two buttons, each calling a different function. The first button, called “Geocode Address“, triggers the geocode_address
function. The second button, named “Reverse Geocode“, calls the reverse_geocode
function. As with the previous elements, we place these buttons on the main window using the grid layout.
# Buttons
self.geocode_button = tk.Button(self, text="Geocode Address", command=self.geocode_address)
self.geocode_button.grid(row=1, column=3, padx=10, pady=5)
self.reverse_geocode_button = tk.Button(self, text="Reverse Geocode", command=self.reverse_geocode)
self.reverse_geocode_button.grid(row=4, column=3, padx=10, pady=5)
Result Text Widget
Here, we create a text widget to display the results of the geocoding functions. We define its height and word wrapping to improve readability and position it on the main window using the grid layout.
# Result Text Widget
self.result_text = tk.Text(self, height=4, wrap='word')
self.result_text.grid(row=5, column=0, columnspan=4, padx=10, pady=10, sticky="we")
Geocoding Functions
Let’s define our functions—the heart of our script:
geocode_address Function
The geocode_address
function starts by retrieving the address entered by the user and then uses geocoder.geocode
to locate the address. If the location is found, it clears the result_text
widget, constructs a string with the address, its latitude, and longitude coordinates, and displays it on the result_text
widget.
Otherwise, a message indicating that the address wasn’t found will be displayed. Additionally, if any error occurs during the process, an error message will also be displayed.
def geocode_address(self):
address = self.address_entry.get()
try:
location = self.geocoder.geocode(address)
if location:
result = f"Address: {address}\nLatitude: {location.latitude}, Longitude: {location.longitude}"
self.result_text.delete('1.0', tk.END)
self.result_text.insert(tk.END, result)
else:
messagebox.showinfo("Result", "Address not found")
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
reverse_geocode Function
This one does the opposite of the previous function. It starts by getting the latitude and longitude coordinates entered by the user. It then tries to find the address that matches these coordinates:
- If it finds an address, the function displays the coordinates along with the address in the
result_text
widget, just like the previous function displayed its results. - If no address is found, it lets the user know by showing a message that says “No address found for these coordinates“. If something goes wrong during this process, it also shows an error message to explain what happened.
def reverse_geocode(self):
latitude = self.latitude_entry.get()
longitude = self.longitude_entry.get()
try:
location = self.geocoder.reverse(f"{latitude}, {longitude}")
if location:
result = f"Coordinates: {latitude}, {longitude}\nAddress: {location.address}"
self.result_text.delete('1.0', tk.END)
self.result_text.insert(tk.END, result)
else:
messagebox.showinfo("Result", "No address found for these coordinates")
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
Running the Application
This part ensures that the app runs directly and is not imported as a module. It also keeps the main window running and responsive to user actions until the user decides to exit.
if __name__ == "__main__":
app = GeocodingApp()
app.mainloop()
Example
As you can see here, our program takes the address as input and gives the coordinates as a result:
Here our program takes the coordinates as inputs and gives the address as a result:
Full Code
import tkinter as tk
from tkinter import messagebox
from geopy.geocoders import Nominatim
class GeocodingApp(tk.Tk):
def __init__(self):
super().__init__()
self.geocoder = Nominatim(user_agent="geoapp")
self.title("Geocoding Application - The Pycodes")
self.geometry("660x300")
# Labels for user instruction
tk.Label(self, text="Enter address for geocoding:").grid(row=0, column=0, padx=10, pady=10, sticky="w")
tk.Label(self, text="Enter latitude and longitude for reverse geocoding:").grid(row=2, column=0, padx=10, pady=10, sticky="w")
tk.Label(self, text="Latitude:").grid(row=3, column=0, padx=10, pady=5)
tk.Label(self, text="Longitude:").grid(row=3, column=2, padx=10, pady=5)
# Entry for Forward Geocoding
self.address_entry = tk.Entry(self, width=40)
self.address_entry.grid(row=1, column=0, columnspan=3, padx=10, sticky="we")
# Entries for Reverse Geocoding
self.latitude_entry = tk.Entry(self, width=20)
self.latitude_entry.grid(row=4, column=0, padx=10, pady=5)
self.longitude_entry = tk.Entry(self, width=20)
self.longitude_entry.grid(row=4, column=2, padx=10, pady=5, sticky="we")
# Buttons
self.geocode_button = tk.Button(self, text="Geocode Address", command=self.geocode_address)
self.geocode_button.grid(row=1, column=3, padx=10, pady=5)
self.reverse_geocode_button = tk.Button(self, text="Reverse Geocode", command=self.reverse_geocode)
self.reverse_geocode_button.grid(row=4, column=3, padx=10, pady=5)
# Result Text Widget
self.result_text = tk.Text(self, height=4, wrap='word')
self.result_text.grid(row=5, column=0, columnspan=4, padx=10, pady=10, sticky="we")
def geocode_address(self):
address = self.address_entry.get()
try:
location = self.geocoder.geocode(address)
if location:
result = f"Address: {address}\nLatitude: {location.latitude}, Longitude: {location.longitude}"
self.result_text.delete('1.0', tk.END)
self.result_text.insert(tk.END, result)
else:
messagebox.showinfo("Result", "Address not found")
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
def reverse_geocode(self):
latitude = self.latitude_entry.get()
longitude = self.longitude_entry.get()
try:
location = self.geocoder.reverse(f"{latitude}, {longitude}")
if location:
result = f"Coordinates: {latitude}, {longitude}\nAddress: {location.address}"
self.result_text.delete('1.0', tk.END)
self.result_text.insert(tk.END, result)
else:
messagebox.showinfo("Result", "No address found for these coordinates")
except Exception as e:
messagebox.showerror("Error", f"An error occurred: {e}")
if __name__ == "__main__":
app = GeocodingApp()
app.mainloop()
Happy Coding!