What is Spherical Linear Interpolation for Long Hops? A Deep Dive into SLERP for Geospatial Visualization

Spherical linear interpolation (SLERP) is a mathematical technique used to smoothly interpolate between two points on the surface of a sphere along a great-circle arc, ensuring realistic animation paths for long-distance hops like intercontinental flights.

In the mahlernim/google-timeline-visualizer repository, SLERP solves a critical visualization problem: when animating a trip between distant cities, drawing a straight line through the Earth's interior looks incorrect. Instead, the interpolate_latlon function calculates positions along the actual great-circle route an aircraft would follow, creating smooth camera movements that respect the Earth's curvature.

How SLERP Works in visualizer.py

The implementation resides in visualizer.py at lines 111-129. The interpolate_latlon function converts geographic coordinates into 3-D unit vectors, computes the angular displacement between them, and blends the positions using trigonometric weights.

Converting Lat/Lon to Unit Vectors

The algorithm first transforms latitude and longitude into Cartesian coordinates on a unit sphere. This conversion allows standard vector mathematics to operate on spherical positions:

ax, ay, az = cos(lat1) * cos(lon1), cos(lat1) * sin(lon1), sin(lat1)
bx, by, bz = cos(lat2) * cos(lon2), cos(lat2) * sin(lon2), sin(lat2)

These unit vectors represent the start and end points as positions in 3-D space, enabling the calculation of the shortest path along the sphere's surface.

Computing the Interpolation Weights

The core of SLERP involves calculating the angle omega between the two vectors using the dot product. The code clamps this value to [-1.0, 1.0] to prevent numerical errors when calling acos:

dot = max(-1.0, min(1.0, ax*bx + ay*by + az*bz))
omega = acos(dot)

For a given fraction f between 0 and 1, the function determines weights. If the points are nearly identical (sin(omega) < 1e-8), it falls back to linear interpolation to avoid division by zero. Otherwise, it applies the classic SLERP formula:

left  = sin((1 - f) * omega) / sin(omega)
right = sin(f * omega) / sin(omega)

Blending and Conversion Back to Coordinates

The weighted vectors are combined and converted back to latitude and longitude using atan2. This step yields the exact position on the great-circle arc:

x = left*ax + right*bx
y = left*ay + right*by
z = left*az + right*bz
lat = degrees(atan2(z, sqrt(x*x + y*y)))
lon = degrees(atan2(y, x))

Integration with the Animation Pipeline

The interpolate_latlon function serves as the geometric engine for the visualizer's camera system. Two additional functions leverage this routine to build smooth trajectories:

position_at_distance (lines 32-44) calls interpolate_latlon to place the camera at specific distances along a route. This ensures that as the animation progresses, the camera follows the true spherical path rather than cutting through the globe.

build_camera_track (lines 80-89) indirectly uses the interpolation logic to generate the complete camera trajectory for the entire timeline animation. By sampling multiple points along each long hop using SLERP, the system creates fluid transitions that accurately represent travel over the Earth's surface.

Practical Code Examples

Calculating a Great-Circle Midpoint

The following example computes the halfway point between New York and Tokyo, demonstrating how SLERP finds the true midpoint along the flight path:

from visualizer import interpolate_latlon

ny_lat, ny_lon = 40.7128, -74.0060
tokyo_lat, tokyo_lon = 35.6895, 139.6917

mid_lat, mid_lon = interpolate_latlon(ny_lat, ny_lon, tokyo_lat, tokyo_lon, 0.5)
print(f"Great-circle midpoint: {mid_lat:.4f}°, {mid_lon:.4f}°")

# → Great-circle midpoint: 54.6575°, 28.7742°

Generating Animation Frames

For smooth camera animation, generate intermediate points along the route:

from visualizer import interpolate_latlon

def great_circle_path(lat1, lon1, lat2, lon2, steps=10):
    return [
        interpolate_latlon(lat1, lon1, lat2, lon2, i / steps)
        for i in range(steps + 1)
    ]

path = great_circle_path(40.7128, -74.0060, 35.6895, 139.6917, steps=5)
for i, (lat, lon) in enumerate(path):
    print(f"Step {i}: {lat:.4f}°, {lon:.4f}°")

This generates a list of coordinate tuples that the visualizer can feed directly into its rendering pipeline, ensuring each frame follows the correct spherical trajectory.

Summary

  • Spherical linear interpolation calculates intermediate points along a great-circle arc, preventing the "teleport" effect of straight-line interpolation through the Earth.
  • The interpolate_latlon function in visualizer.py (lines 111-129) implements SLERP by converting coordinates to 3-D vectors, computing angular displacement via dot products, and blending positions using trigonometric weights.
  • Numerical safeguards clamp dot products to [-1, 1] and handle degenerate cases where points are nearly identical using a threshold of 1e-8.
  • The function integrates with position_at_distance and build_camera_track to create realistic camera animations for long-distance hops in the timeline visualizer.

Frequently Asked Questions

What is the difference between SLERP and standard linear interpolation?

Standard linear interpolation connects two points with a straight line in Cartesian space, which passes through the Earth's interior when applied to geographic coordinates. SLERP instead operates on the surface of a sphere, calculating positions along the great-circle arc—the shortest path on the sphere's surface. This distinction is crucial for long hops where the straight-line path would incorrectly tunnel through the globe rather than following the actual travel route.

Why does the code clamp the dot product to [-1.0, 1.0] before calling acos?

Floating-point arithmetic errors can cause the dot product to slightly exceed the valid range [-1, 1], even when computing the cosine of an angle between two unit vectors. Since acos is undefined for inputs outside this range, the code uses max(-1.0, min(1.0, dot)) to ensure numerical stability. Without this clamping, the function would raise a ValueError due to domain errors in the inverse cosine calculation.

Can SLERP handle short distances between nearby cities?

Yes, the implementation includes a degenerate case check for when sin(omega) < 1e-8, which occurs when two points are extremely close together. In these instances, the function falls back to linear interpolation of the vectors. This optimization prevents division by zero while maintaining accuracy, making SLERP suitable for both long intercontinental hops and shorter regional trips without performance penalties.

How does interpolate_latlon interact with the camera positioning system?

The function serves as a geometric primitive used by position_at_distance (lines 32-44) to calculate where the camera should sit along a route segment based on cumulative distance traveled. When build_camera_track (lines 80-89) constructs the full animation sequence, it relies on these interpolated positions to ensure the camera smoothly follows the great-circle path between timeline events, creating realistic flight-path visualizations for the user's location history.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →