# Web Mercator Projection Formula in the Google Timeline Visualizer: Implementation Guide

> Discover the Web Mercator projection formula x = R * lambda and y = R * ln(tan(pi/4 + phi/2)) used in the Google Timeline Visualizer. Implement geographic conversions with this guide.

- Repository: [mahlernim/google-timeline-visualizer](https://github.com/mahlernim/google-timeline-visualizer)
- Tags: implementation-guide
- Published: 2026-08-22

---

**The Google Timeline Visualizer converts geographic coordinates (latitude φ and longitude λ) into Web Mercator (Pseudo-Mercator) coordinates using the formulas x = R × λ and y = R × ln(tan(π/4 + φ/2)), where R equals 6,378,137 meters.**

The Web Mercator projection formula enables seamless alignment between real-world GPS data and standard map tile systems. In the `mahlernim/google-timeline-visualizer` repository, this critical conversion is implemented as a Kotlin singleton that bridges geographic coordinates with the SVG canvas used for rendering timeline visualizations.

## Mathematical Foundation of the Web Mercator Projection

The implementation follows the standard Web Mercator (EPSG:3857) mathematical model. The projection transforms spherical coordinates into a cylindrical map projection suitable for web mapping applications.

The core formulas implemented in the codebase are:

| Component | Formula | Description |
|-----------|---------|-------------|
| **X-coordinate** | `x = R × Math.toRadians(lon)` | East-west position in meters |
| **Y-coordinate** | `y = R × Math.log(Math.tan(Math.PI / 4 + Math.toRadians(lat) / 2))` | North-south position in meters |

Where **R = 6378137.0** represents the Earth's authalic radius in meters, which is the standard radius used by the Web Mercator specification.

## Kotlin Implementation in TimelineModels.kt

The projection logic resides in the `WebMercator` object within [`app/src/main/java/dev/mahlernim/timelinevisualizer/model/TimelineModels.kt`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/app/src/main/java/dev/mahlernim/timelinevisualizer/model/TimelineModels.kt) (lines 445-462). This singleton provides two overloaded `project` methods for coordinate conversion.

```kotlin
object WebMercator {
    private const val RADIUS = 6378137.0               // metres

    /** Convert a latitude / longitude pair to Web‑Mercator X/Y. */
    fun project(lat: Double, lon: Double): Point {
        val x = RADIUS * Math.toRadians(lon)
        val y = RADIUS *
                Math.log(
                    Math.tan(Math.PI / 4 + Math.toRadians(lat) / 2)
                )
        return Point(x, y)
    }

    /** Overload that accepts a GeoPoint and returns a ProjectedPoint. */
    fun project(p: GeoPoint): ProjectedPoint = project(p.latitude, p.longitude)
}

```

The `project` function applies the standard Web Mercator projection formula by first converting degrees to radians, then computing the logarithmic transformation for the Y-coordinate. This implementation ensures precise alignment with map tiles served by standard web mapping services.

## Integration with the Rendering Pipeline

The visualizer invokes `WebMercator.project` throughout the rendering pipeline to position timeline elements accurately on the map canvas. Specifically, in [`app/src/main/java/dev/mahlernim/timelinevisualizer/render/TimelinePainter.kt`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/app/src/main/java/dev/mahlernim/timelinevisualizer/render/TimelinePainter.kt) (lines 169-171), the projection converts geographic points into SVG-compatible coordinates.

```kotlin
// Example from TimelinePainter.kt
val currentPoint = timelinePainter.currentPoint   // GeoPoint
val mercatorPos = WebMercator.project(currentPoint)   // Point used for SVG positioning
svgElement.setAttribute("transform",
    "translate(${mercatorPos.x}, ${mercatorPos.y})")

```

This integration ensures that GPS coordinates from Google Timeline data render at the correct pixel positions relative to the Web Mercator base map.

## Practical Code Examples

### Projecting Individual Coordinates

Convert specific latitude and longitude values to planar coordinates:

```kotlin
// Example 1: Project a single coordinate
val projected = WebMercator.project(37.7749, -122.4194)   // San Francisco
println("X = ${projected.x}, Y = ${projected.y}")

```

### Processing GeoPoint Objects

Transform timeline data points using the overloaded method:

```kotlin
// Example 2: Project a GeoPoint from the timeline model
val geo = GeoPoint(
    instant = Instant.parse("2023-01-01T00:00:00Z"),
    latitude = 48.8566,
    longitude = 2.3522
)
val projected = WebMercator.project(geo)
println(projected)   // prints a Point with Mercator X/Y in metres

```

### Canvas Positioning

Apply the projection for SVG element placement:

```kotlin
// Example 3: Using the projection inside the visualizer's painter
val currentPoint = timelinePainter.currentPoint   // GeoPoint
val mercatorPos = WebMercator.project(currentPoint)   // Point used for SVG positioning
svgElement.setAttribute("transform",
    "translate(${mercatorPos.x}, ${mercatorPos.y})")

```

## Verification and Testing

The implementation includes comprehensive test coverage to ensure mathematical accuracy. Unit tests in [`app/src/test/java/dev/mahlernim/timelinevisualizer/model/WebMercatorTest.kt`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/app/src/test/java/dev/mahlernim/timelinevisualizer/model/WebMercatorTest.kt) validate the projection behavior against known reference values. Additionally, JavaScript tests in [`web/src/geo.test.ts`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/web/src/geo.test.ts) (lines 13-20) verify that the client-side rendering logic matches the Kotlin server-side calculations, ensuring consistency across platforms.

## Summary

- The **Web Mercator projection formula** in this repository uses the standard authalic radius of **6,378,137 meters** for spherical calculations.
- The `WebMercator` object in [`TimelineModels.kt`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/TimelineModels.kt) provides both single-coordinate and `GeoPoint` projection methods.
- **X-coordinates** calculate as radius multiplied by longitude in radians, while **Y-coordinates** use the logarithmic tangent transformation.
- The rendering pipeline in [`TimelinePainter.kt`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/TimelinePainter.kt) consumes these projections to position SVG elements at correct geographic locations.
- Test suites in both Kotlin and TypeScript verify projection accuracy across the application stack.

## Frequently Asked Questions

### What is the exact radius constant used in the Web Mercator projection formula?

The implementation uses `RADIUS = 6378137.0` meters, which represents the Earth's authalic radius according to the WGS 84 datum. This constant matches the standard specification used by major web mapping providers like Google Maps and OpenStreetMap.

### Why does the visualizer use Web Mercator instead of other projections?

Web Mercator (EPSG:3857) is the de facto standard for web mapping because it preserves angles locally and enables seamless tiling across zoom levels. The visualizer adopts this projection to ensure that timeline points align precisely with the standard map tiles used as the background layer.

### How does the projection handle extreme latitudes near the poles?

While the mathematical formula supports latitudes up to approximately 85.05113 degrees, the implementation relies on Kotlin's `Math.log` and `Math.tan` functions to handle the asymptotic behavior. The projection becomes increasingly distorted beyond 85 degrees, which is consistent with standard Web Mercator limitations where tiles typically stop at these bounds.

### Where can I find the unit tests for the projection logic?

The primary unit tests reside in [`app/src/test/java/dev/mahlernim/timelinevisualizer/model/WebMercatorTest.kt`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/app/src/test/java/dev/mahlernim/timelinevisualizer/model/WebMercatorTest.kt), which validates the Kotlin implementation. JavaScript-side verification exists in [`web/src/geo.test.ts`](https://github.com/mahlernim/google-timeline-visualizer/blob/main/web/src/geo.test.ts), ensuring that the projection behaves identically across the Kotlin backend and TypeScript frontend components.