Automating Memories With Python & Folium

by Vihaan Punjabi in Circuits > Assistive Tech

50 Views, 2 Favorites, 0 Comments

Automating Memories With Python & Folium

download.jpeg
Equal-Earth-Physical-No-Type_(cropped).jpg

Most travelers snap countless pictures during their trips. Yet once home, these images often remain untouched inside folders on a device. Displaying them by location seemed appealing - though placing each one individually onto a map felt impractical. Managing so many markers would demand far more time than expected. One step at a time, the Maps Contest pushed me toward automation. A custom-built Python script now handles what used to take hours manually. Hidden details tucked inside photo files, GPS points stored in EXIF are pulled out without effort. Grouping these locations happens through pattern recognition, specifically K-Means, an approach from machine learning. Regions form on their own, shaped by where pictures were taken. Then comes visualization: each cluster lands on a dynamic web-based map made with Folium. What emerges is not just dots on terrain but stories mapped across space, built quietly behind code. Here’s how to prepare the workspace, add sample GPS details to saved pictures, due to online downloads often removing location tags, and build the core script that creates your map.

Supplies

You don't need any physical hardware for this project, just your computer.

Software Needed:

  1. Python: Make sure Python 3 is installed on your computer.
  2. Visual Studio Code (VS Code): My preferred code editor, though any IDE will work.

Python Libraries: We will be using a few powerful data science and mapping libraries. We will install these in the next step:

  1. pillow
  2. piexif
  3. pandas
  4. scikit-learn
  5. folium

Test Files:

  1. Create a folder on your desktop called Instructables_Map.
  2. Inside that, create a subfolder called photos.
  3. Download three random landscape photos from the internet (e.g., a mountain, a beach, and a coastline) and save them into the photos folder as banff.jpg, miami.jpg, and cali.jpg.

Creating the Ecosytem

Screenshot 2026-05-25 at 11.22.47 PM.png

Open VS Code, go to File > Open Folder, and select your Instructables_Map folder. Open the integrated terminal by going to Terminal > New Terminal at the top of the screen. Install our required libraries by pasting this command into the terminal and hitting Enter:

pip install pillow piexif pandas scikit-learn folium

If this doesn't work, you might need to type pip3 instead of pip.

GPS Injecting Test Data (Optional)

A little-known detail of digital photos: snapping one on a phone tucks precise location details quietly inside the file. Sites remove those markers, by default, to protect user privacy.

Because this tutorial relies on pre-downloaded test images, location data must be added manually. Otherwise, the primary mapping script would have nothing to interpret. Without embedded coordinates, the system cannot process positional information from these files.

A new file named inject_gps.py opens in VS Code; then, insert the code shown below. Starting with basic decimal values, it reshapes location data into the precise structure needed for EXIF metadata. Each of the three sample images receives this geographic detail directly, locking it in place. Precision matters; formatting follows strict rules embedded in the script. Once processed, the photos carry fixed GPS tags without external files.

import piexif
from PIL import Image
import os

def inject_gps(image_path, lat, lon):
if not os.path.exists(image_path):
print(f"Could not find {image_path}.")
return

# Convert decimal to degrees, minutes, seconds for EXIF format
def to_deg(value):
abs_value = abs(value)
deg = int(abs_value)
min_float = (abs_value - deg) * 60
minute = int(min_float)
sec = int((min_float - minute) * 60 * 100)
return ((deg, 1), (minute, 1), (sec, 100))

lat_deg = to_deg(lat)
lon_deg = to_deg(lon)
lat_ref = 'N' if lat >= 0 else 'S'
lon_ref = 'E' if lon >= 0 else 'W'

gps_ifd = {
piexif.GPSIFD.GPSLatitudeRef: lat_ref,
piexif.GPSIFD.GPSLatitude: lat_deg,
piexif.GPSIFD.GPSLongitudeRef: lon_ref,
piexif.GPSIFD.GPSLongitude: lon_deg,
}

exif_dict = {"0th": {}, "Exif": {}, "GPS": gps_ifd, "1st": {}, "thumbnail": None}
exif_bytes = piexif.dump(exif_dict)
img = Image.open(image_path)
img.save(image_path, exif=exif_bytes)
print(f" Injected GPS data into {image_path}")

# Injecting coordinates for 3 distinct regions
inject_gps("./photos/banff.jpg", 51.4254, -116.1773) # Canada
inject_gps("./photos/miami.jpg", 25.7906, -80.1300) # Florida
inject_gps("./photos/cali.jpg", 36.5552, -121.9233) # California Coast

Run this script by typing python inject_gps.py in your terminal.


The Main Engine (Extracting, Clustering, and Mapping)

Step 4: The Main Engine (Extracting, Clustering, and Mapping)

Create a new file called photo_map.py.

It loads each image to pull out the GPS coordinates recently embedded. Once opened, these files reveal location details hidden within their metadata. Reading them happens automatically after access is gained. The process targets only those pictures containing inserted geographic information. From start to finish, retrieval follows a fixed sequence without deviation.

Starting from location data, the system uses a K-Means method to form groups. Because proximity matters, images captured nearby tend to fall into one category. That grouping shows up as a distinct hue. The process detects patterns without being told where boundaries lie.

Starting the process loads Folium into memory; next, location markers appear on screen. Then, each point finds its place through geographic coordinates. Finally, the system saves everything as an interactive web page in HTML format.

Paste this into photo_map.py

import os
import pandas as pd
import folium
from PIL import Image
from PIL.ExifTags import TAGS, GPSTAGS
from sklearn.cluster import KMeans

PHOTO_FOLDER = './photos'
OUTPUT_MAP = 'index.html'

def get_exif_data(image_path):
image = Image.open(image_path)
exif_data = {}
info = image._getexif()
if info:
for tag, value in info.items():
decoded = TAGS.get(tag, tag)
if decoded == "GPSInfo":
gps_data = {}
for t in value:
sub_decoded = GPSTAGS.get(t, t)
gps_data[sub_decoded] = value[t]
exif_data[decoded] = gps_data
return exif_data

def get_decimal_coordinates(gps_data):
if not gps_data: return None
try:
lat = gps_data.get('GPSLatitude')
lat_ref = gps_data.get('GPSLatitudeRef')
lon = gps_data.get('GPSLongitude')
lon_ref = gps_data.get('GPSLongitudeRef')

lat_dec = float(lat[0][0])/float(lat[0][1]) + (float(lat[1][0])/float(lat[1][1]))/60 + (float(lat[2][0])/float(lat[2][1]))/3600
lon_dec = float(lon[0][0])/float(lon[0][1]) + (float(lon[1][0])/float(lon[1][1]))/60 + (float(lon[2][0])/float(lon[2][1]))/3600

if lat_ref == 'S': lat_dec = -lat_dec
if lon_ref == 'W': lon_dec = -lon_dec
return lat_dec, lon_dec
except Exception:
return None

def main():
print("📸 Scanning for photos...")
photo_data = []

for filename in os.listdir(PHOTO_FOLDER):
if filename.lower().endswith(('.png', '.jpg', '.jpeg')):
filepath = os.path.join(PHOTO_FOLDER, filename)
exif = get_exif_data(filepath)
if exif and 'GPSInfo' in exif:
coords = get_decimal_coordinates(exif['GPSInfo'])
if coords:
photo_data.append({'filename': filename, 'latitude': coords[0], 'longitude': coords[1]})
print(f"✅ Extracted GPS from: {filename}")

if not photo_data:
print("🛑 No GPS data found.")
return

df = pd.DataFrame(photo_data)

# Machine Learning Clustering
print("🧠 Running K-Means clustering...")
n_clusters = min(3, len(df))
kmeans = KMeans(n_clusters=n_clusters, random_state=42, n_init=10)
df['cluster'] = kmeans.fit_predict(df[['latitude', 'longitude']])

colors = ['red', 'blue', 'green', 'purple', 'orange']
df['color'] = df['cluster'].apply(lambda x: colors[x % len(colors)])

# Generate Folium Map
print("🗺️ Generating interactive map...")
map_center = [39.8283, -98.5795] # Center of USA
m = folium.Map(location=map_center, zoom_start=4, tiles='CartoDB positron')

for index, row in df.iterrows():
folium.Marker(
location=[row['latitude'], row['longitude']],
popup=f"<b>Photo:</b> {row['filename']}<br><b>Cluster:</b> {row['cluster']}",
icon=folium.Icon(color=row['color'], icon='camera', prefix='fa')
).add_to(m)

m.save(OUTPUT_MAP)
print(f"🎉 Success! Map saved to {OUTPUT_MAP}")

if __name__ == "__main__":
main()


Execution & Viewing Your Map

Screenshot 2026-05-25 at 11.35.15 PM.png

In your VS Code terminal, type python photo_map.py and hit enter. The script will output its progress. Once it says "Success!", look at your file explorer on the left. You will see a brand new file called index.html. Right-click index.html. Double-click the file to open it in Chrome, Safari, or Edge, whatever you use

You should now see an interactive map of North America. Notice how your pins are placed accurately in Canada, Florida, and California, and they are color-coded by the machine learning algorithm. You can click on any pin to see the pop-up name of the photo file.

Conclusion

This project only begins the journey. Since the map saves as a regular index.html file, simply place it on GitHub Pages, making your map live at no cost while letting others explore it with ease.

Later trips won’t need the injection script at all. Connect your phone to the computer instead. Move your original vacation pictures directly into the photos folder. Then start photo_map.py. The program plots hundreds of memories across the globe right away.