Visualising Photo Geolocation Data Using Python | by Pavel Cherepansky | May, 2022

Let’s leverage the exif and folium Python packages

Most modern GPS-enabled cameras and mobile phones record geolocation information when a photo is taken and this information is stored along with all the other metadata.

There are also plenty of usually web-based applications that allow you to create various visualisations using this metadata. But what if you’re like me and don’t trust your photos to third-party services with unknown levels of data security? Luckily, most of these visualisations are relatively simple to replicate using Python. In this guide, I will show how to create a travel map using photo GPS data.

For this project, you will need a few packages installed in your Python environment. We will use two main packages: exif will allow us to extract metadata from photos and we’ll use folium to create a map and add location markers. Finally, we will also use color maps from matplotlib, and pandas and numpy for data manipulations. To install all the necessary packages simply execute the following in your terminal:

pip install exif folium pandas numpy matplotlib

We’ll start with importing all the necessary packages. In addition to the ones listed above, we will also import jsonand Pathclass from the pathlib library, both of which are a part of the standard library.

Extracting and preparing metadata for plotting

Reading metadata with exifis very easy — we simply need to open a file in a binary format and create an Imageobject from it. Let’s define a convenience function for this:

Depending on the device a photo can contain dozens of types of metadata information. To see those that specifically relate to geolocation we can load a single photo and print out all the metadata categories that begin with “gps_“.

If a photo has embedded geolocation information you should see the following output:

gps_latitude_ref 
gps_latitude
gps_longitude_ref
gps_longitude
gps_altitude_ref
gps_altitude
gps_timestamp
gps_processing_method
gps_datestamp

Some of these categories are relatively easy to understand: gps_latitude, gps_longitude and gps_altitude store latitude, longitude and altitude information. Altitude is stored as elevation above sea level, usually in meters. Latitude and longitude are stored as tuples of degrees, minutes and seconds of arc.

Categories ending with _ref contain reference information on how the data should be interpreted. In the case of altitude, it simply informs that the value is altitude above sea level, but for latitude and longitude they contain the information of the reference hemisphere, “E” or ”W” for longitude, and ”N” or ”S” for latitude. These are important when converting the raw data to decimal format since the standard notation has coordinates west of Greenwich and south of the equator as negative.

Since volume doesn’t understand coordinates in degrees/minutes/seconds we’ll need to convert latitudes and longitudes to decimal representation and since we’ll also do it quite a few times we can make life easier and code cleaner by defining a function that will take a tuple of coordinate and a reference value and return its binary representation. For this, we convert minutes and seconds into fractions of a degree by dividing by 60 and 3600, respectively. The sign is then decided based on the reference data:

This function will convert latitudes and longitudes to decimal format and we don’t need to do anything with altitudes. With this, we can define another helper function that will take an exif.Image object and return a tuple of decimal coordinates in the format (latitude, longitude, altitude).

Finally, we will add another function that will recursively go through all subfolders of a selected folder, read spatial information from all the image files and return a dictionary of file names and coordinates.

Now that this is done we can easily read the location data from all files within the image folder:

Printing the resulting dictionary should result in an output similar to mine:

print(json.dumps(res, indent=4))Out:{     
"/media/pav/Storage/Photo/Phone/IMAG0036.jpg": {
"latitude": -37.79912566666666,
"longitude": 144.9850463611111,
"altitude": 0.0,
"timestamp": "2014:04:12 18:14:59"},
"/media/pav/Storage/Photo/Phone/IMAG0037.jpg": {
"latitude": -37.79912566666666,
"longitude": 144.9850463611111,
"altitude": 0.0,
"timestamp": "2014:04:12 18:15:36"},
...
}

Plotting the data

To simplify future operations we can convert the resulting dictionary to a pandas DataFrame. We then also convert the timestamp column to a datetime format and sort values ​​by date in ascending order.

In order to color-code our data points we can get one of the color maps that come as a part of the matplotlib package. In addition to this, we also need to create a Normalize object. This object will assign numerical values ​​to individual colors in a colourmap between 0 and 1 and we’ll be able to call any color in the colourspace by its numerical value. And for this, we will add a column to our dataframe using numpy.linspace() .

One small issue is that our colors will be in RGBA format but it doesn’t work with folium so we will also need to convert the color to its hexadecimal representation which is easily done:

Now let’s finally it’s time to visualize our data!

The main part of the folium package is Map object and simply creating an instance via folium.Map() will result in a map of the entire world.

Default volume map.

This is fine but I prefer to have the map automatically zoom to the level that all the markers can be seen. We can achieve this using fit_bounds() method of the resulting object and providing coordinates of the southwest and northeast corners of the map extents.

We can calculate the coordinates of the southwest corner by taking the maximum latitude and minimum longitude from our data. Similarly, the north-eastern corner coordinates are the minimum latitude and the maximum longitude. In the below code I’ve also adjusted the bounding box by 3 degrees in each direction so that the data fits neatly within the window.

So now finally we’re ready to create a map showing the locations of the photos colour-coded by the date taken.

In my case, the result shows a bunch of locations around Victoria, Australia with purple dots representing earlier pictures and brighter red and yellow dots — newer photos:

Here we only touched on a tiny part of all the opportunities that photo metadata offers. With this data, you can create your own databases of photos allowing you to easily categorise and manage them. You can also expand the use of the geolocation data to being able to search for photos by location within your image folders and many other things are available too.

The complete code for this project can be downloaded from my GitHub at the following location:

Leave a Comment