Building a 3D Elevation Photo Diary with deck.gl

During a recent two-week vacation in Nerja, Spain, I took hundreds of photos with my phone. Many of them captured GPS coordinates and altitude data. Instead of letting that metadata sit unused, I decided to build an interactive 3D visualization that plots each photo on the actual terrain where it was taken.

Quick demo of the interactive viewer

The Concept

The idea was simple: create a chronological journey through my vacation photos, where each photo appears on a 3D terrain map at its location. As you navigate through the photos, the camera flies to each spot, showing the landscape in 3D.

Tech Stack

deck.gl for 3D Visualization

I chose deck.gl for rendering the 3D terrain and photo markers. It's a WebGL-powered framework that handles complex 3D visualizations with impressive performance. Two key layers made this possible:

TerrainLayer - Renders the 3D elevation map using Terrarium-format tiles from AWS:

const terrain = new TerrainLayer({
    id: 'terrain',
    elevationData: 'https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png',
    texture: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
    ...
});

ScatterplotLayer - Displays photo locations as 3D points on the terrain. I used two layers: one for visited photos (gray, smaller) and one for the current photo (orange, larger).

Data Processing

The photos' EXIF data provided latitude, longitude, altitude, and timestamps. I extracted this into a CSV file with entries sorted chronologically:

filestem,latitude,longitude,altitude,timestamp
IMG_20260106_132810881,36.78705,-3.883663888888889,197.6,2026:01:06 13:28:12
...

Key Features

Smooth Camera Transitions

When navigating between photos, the camera smoothly flies to the new location using deck.gl's FlyToInterpolator:

deckInstance.setProps({
    initialViewState: {
        longitude: currentPhoto.longitude,
        latitude: currentPhoto.latitude,
        zoom: 15,
        bearing: 180,
        pitch: 40,
        transitionDuration: 2000,
        transitionInterpolator: new deck.FlyToInterpolator()
    }
});

Progressive Trail Visualization

As you move through the photos, previously visited locations remain visible in gray, creating a visual trail of your journey. The current photo is highlighted in orange.

Responsive Layout

The interface adapts to different screen sizes:

  • Landscape: Map takes 60-70% of width, photo/controls on the right
  • Portrait: Map takes 60% of height, photo/controls below
@media (max-aspect-ratio: 1/1) {
    #app-container {
        flex-direction: column;
    }
    #map-container {
        flex: 0 0 60%;
    }
    ...
}

Keyboard Navigation

Keyboard shortcuts for quick navigation:

  • Arrow keys: Previous/Next photo
  • Home/End: First/Last photo
  • Spacebar: Play/Pause auto-advance

Auto-Play Journey

A play button advances through photos automatically (3 seconds each), creating a cinematic journey through the vacation.

Challenges & Solutions

GPS Altitude Accuracy

Phone GPS altitude data can be unreliable, especially near sea level. I added a note in the info modal about this limitation. In future versions, I might cross-reference with the terrain elevation data to improve accuracy.

Photo Loading Performance

I selected 300 of the photos that had GPS data and created smaller web-optimized versions using wim to ensure quick loading without sacrificing too much quality.

Mobile Layout

Getting the controls to fit on small portrait screens required careful tweaking of photo max-height and padding values. The final solution uses a compact info display (icons instead of labels) and reduced button spacing.

What I Learned

  1. deck.gl is powerful but has a learning curve - The layer system is elegant once you understand it, but proper coordinate handling and view state management took some experimentation.
  2. GPS metadata opens creative possibilities - This project barely scratches the surface of what's possible with GPS metadata from photos.
  3. Responsive 3D is tricky - Balancing the 3D visualization with UI controls across different screen sizes required more iteration than expected.
  4. Small touches matter - The smooth camera transitions, progressive trail effect, and keyboard shortcuts help make the experience more engaging.

Try It Yourself

View the live project →

The project is built with vanilla JavaScript and deck.gl - no frameworks needed. If you have GPS-tagged photos from a trip, you could adapt this code to create your own elevation diary.

Have you built something similar or have ideas for improvements? Drop a comment below!


Tech Used:


Published by Ramiro Gómez on . Subscribe to the Geeksta RSS feed to be informed about new posts.

Tags: spain visualization map web development javascript

Disclosure: External links on this website may contain affiliate IDs, which means that I earn a commission if you make a purchase using these links. This allows me to offer hopefully valuable content for free while keeping this website sustainable. For more information, please see the disclosure section on the about page.


Share post: Facebook LinkedIn Reddit Twitter

Merchandise