How is the Gradient Map Created?
Zooming into the Northern Alps and tracing the contour lines brings back a mix of curiosity and half-forgotten memories from past trips — where exactly was the Rappenseehütte? Which ridge did we cross to get there? What was the name of that valley?
The same questions come up when cycling through Stuttgart, where every route is shaped by the terrain beneath it.
In this article, I want to explore how elevation data works under the hood — what it takes to encode the shape of the Earth into something a browser can render, and how we can turn that data into a useful visualisation.
Table of Contents
- Introduction
- Requirements for the Elevation Profile
- Unterstanding Map Tiles - Terrarium
- Apply It
- References
Introduction
It all starts with NASA’s Digital Elevation Model (DEM): a product of several air and space missions — most notably the Shuttle Radar Topography Mission (SRTM) — that mapped nearly the entire Earth’s surface. The result is a global grid where every cell stores a single value: how many meters above sea level is this point? Originally built to support ecological conservation, wildfire planning, and flood risk modelling, this dataset has since become a foundation for countless mapping applications. (NASA Earth Data)
Requirements for the Elevation Profile
Encoding Elevation
To represent terrain digitally, we need to cover the full range of Earth’s surface — from the deepest ocean trench to the highest mountain peak:
- Mount Everest — 8,849 m above sea level (highest point)
- Challenger Deep — 10,994 m below sea level (deepest point)
For metre-accurate resolution, the encoding must span a total range of 19,843 m (8,849 + 10,994). Since depths are negative, we need a signed representation.
How many bits do we need?
| Bits (n) | Unsigned (2ⁿ) | Signed (±2ⁿ⁻¹) |
|---|---|---|
| 12 | 4,096 | 2,048 |
| 13 | 8,192 | 4,096 |
| 14 | 16,384 | 8,192 |
| 15 | 32,768 | 16,384 ← covers Challenger Deep |
| 16 | 65,536 | 32,768 ← Room for more and alignment with standard byte boundary |
| 17 | 131,072 | 65,536 |
| 18 | 262,144 | 131,072 |
| 19 | 524,288 | 262,144 |
| 20 | 1,048,576 | 524,288 |
| 21 | 2,097,152 | 1,048,576 |
| 22 | 4,194,304 | 2,097,152 |
| 23 | 8,388,608 | 4,194,304 |
| 24 | 16,777,216 | 8,388,608 |
| 32 | 4,294,967,296 | 2,147,483,648 |
A signed 15-bit integer covers ±16,384 m — just enough to represent Challenger Deep. In practice, 16 bits (2 bytes) is the better choice: it provides comfortable headroom and aligns with standard byte boundaries.
Reference Points on the Elevation Scale
| Location | Elevation |
|---|---|
| Mt Everest | +8,849 m |
| Mont Blanc | +4,808 m |
| Grand Canyon (Phantom Ranch) | +750 m |
| Stuttgart | +288 m |
| Hamburg | +6 m |
| Sea level | 0 m |
| Amsterdam | −2 m |
| New Orleans | −2 m |
| Baku | −28 m |
| Sunda Trench | −3,200 m |
| Calypso Deep | −5,200 m |
| Mariana Trench (Challenger Deep) | −10,994 m |
Encoding Location
Elevation tells us how high a point is — but we also need to know where it is. The most widely used system for pinpointing any location on Earth is latitude and longitude.
A Brief History
The Prime Meridian (0° longitude) was fixed at the Royal Observatory in Greenwich, London, by international agreement in 1884. At the time, Britain was the dominant naval and cartographic power, and their observatory became the global reference point. Angles are measured counter-clockwise from this line — eastward is positive — matching the direction of Earth’s rotation.
Latitude and Longitude
Latitude measures how far north or south a point lies from the equator:
- Equator = 0°
- North Pole = +90°
- South Pole = −90°
Longitude measures how far east or west a point lies from the Prime Meridian:
- Prime Meridian = 0°
- Eastward = positive, up to +180°
- Westward = negative, down to −180°
For example, Stuttgart sits at 48.7758° N, 9.1829° E — roughly halfway between the equator and the North Pole, and slightly east of London.
How Precise Can Coordinates Be?
The precision of a coordinate depends on how many decimal places we use. To calculate the ground distance represented by a given angular resolution, we model the Earth as a sphere with radius R = 6,371 km and apply:
x = R × sin(Δ°)
| Decimal places | Angular step | Ground distance | Typical use |
|---|---|---|---|
| 4 | 0.0001° | 11.12 m | Consumer GPS |
| 5 | 0.00001° | 1.11 m | Modern dual-frequency phones |
| 6 | 0.000001° | 0.11 m (11 cm) | Military GPS (PPS / M-code) |
For consumer-grade applications, four decimal places (≈ 11 m) is the practical limit. Military GPS achieves roughly 30 cm accuracy using encrypted dual-frequency signals and can reach 10 cm with differential corrections (DGPS).
Bit Requirements for Coordinates
At four-decimal-place precision (0.0001°), how many distinct values do we need?
| Axis | Range | Distinct values | Bits required |
|---|---|---|---|
| Latitude | −90° to +90° (180°) | 1,800,000 | 21 bits |
| Longitude | −180° to +180° (360°) | 3,600,000 | 22 bits |
Understanding Map Tiles - Terrarium
With the requirements established above, every point on Earth’s surface needs three values:
| Field | Bits |
|---|---|
| Latitude | 21 |
| Longitude | 22 |
| Elevation | 16 |
| Total | 59 bits ≈ 8 bytes per point |
At consumer GPS resolution, a global grid would contain roughly 6.5 billion points (1.8 M × 3.6 M). At 8 bytes each, that amounts to approximately 52 GB of raw data — far too large to serve directly over the web.
So how do we make this practical?
The Trick: Elevation Encoded in Pixels
Rather than transmitting raw coordinate-elevation tuples, the data is
sliced into a grid of 256 × 256 pixel PNG tiles — the same tile system
(z/x/y) that web maps already use for street and satellite
imagery.
Each tile covers a fixed geographic region, so the latitude and longitude are implicit from the tile’s position in the grid. That eliminates 43 of the 59 bits entirely. Only the elevation needs to be stored — and it is encoded directly into the pixel’s colour channels.
A normal image uses Red, Green, and Blue to represent colour. Terrarium tiles repurpose these three channels to store a number instead.
How Terrarium Decodes RGB into Elevation
Lets have a closer look how Terrarium calculates it
Elevation = (R × 256 + G + B / 256) − 32768
Terrarium’s decoding formula is a polynomial, the same mathematical concept used to convert decimal numbers to hexadecimal. The only difference is that Terrarium uses base 256:
Elevation = R × 256¹ + G × 256⁰ + B × 256⁻¹ − 32768
The history of polynomial equations traces back to Euclid and François Viète.
Compare this to how binary (base 2) represents the number 6:
1 × 2² + 1 × 2¹ + 0 × 2⁰ = 6
The only difference is the base. In Terrarium, each “digit” is a byte (0–255), so the base is 256.
Key Reference Points
| R | G | B | Elevation | Meaning |
|---|---|---|---|---|
| 0 | 0 | 0 | −32,768 m | Minimum (deepest encodable) |
| 128 | 0 | 0 | 0 m | Sea level |
| 255 | 255 | 255 | +32,767 m | Maximum (highest encodable) |
Worked Example: Stuttgart
With R = 129, G = 214, B = 0:
R × 256¹ + G × 256⁰ + B × 256⁻¹ = 33238.0
− 32768 = 470.0 m
When viewed in an image editor, a Terrarium tile looks like abstract pastel noise — because it is not meant to be seen. It is meant to be decoded.
Tiles and Zoom Levels
Terrarium divides the surface of the Earth into square tiles, organised by zoom level. At zoom 0, the entire world fits into a single tile. Each higher zoom level doubles the resolution in both directions.
Zoom 0 — The Whole World in One Tile
The OSM tile shows the familiar world map. The Terrarium tile encodes the same geography as colour values — mountain ranges appear as brighter bands, oceans as uniform dark regions.
Zoom 1 — The World in Four Tiles
At zoom 1, the world is split into a 2×2 grid. Each tile covers a quarter of the globe.
Ground Resolution per Zoom Level
Since every tile is 256 × 256 pixels, the ground distance per pixel depends on how many tiles the world is divided into:
Ground per pixel = 40,075 km ÷ (256 × 2ᶻ)
| Zoom | Calculation | Ground per pixel | Covers roughly |
|---|---|---|---|
| 0 | 40,075 ÷ (256 × 2⁰) = 40,075 ÷ 256 | 156.5 km | Paris → London |
| 1 | 40,075 ÷ (256 × 2¹) = 40,075 ÷ 512 | 78.3 km | Stuttgart → Ulm |
| 2 | 40,075 ÷ (256 × 2²) = 40,075 ÷ 1,024 | 39.1 km | Stuttgart → Tübingen |
| 3 | 40,075 ÷ (256 × 2³) = 40,075 ÷ 2,048 | 19.6 km | Greater Stuttgart |
| 4 | 40,075 ÷ (256 × 2⁴) = 40,075 ÷ 4,096 | 9.8 km | Half of Stuttgart |
| 8 | 40,075 ÷ (256 × 2⁸) = 40,075 ÷ 65,536 | 611 m | A neighbourhood |
| 11 | 40,075 ÷ (256 × 2¹¹) = 40,075 ÷ 524,288 | 76.4 m | A city block |
| 13 | 40,075 ÷ (256 × 2¹³) = 40,075 ÷ 2,097,152 | 19.1 m | A building |
| 14 | 40,075 ÷ (256 × 2¹⁴) = 40,075 ÷ 4,194,304 | 9.6 m | A room |
| 15 | 40,075 ÷ (256 × 2¹⁵) = 40,075 ÷ 8,388,608 | 4.8 m | A car |
The original Shuttle Radar Topography Mission (SRTM) source data has a native resolution of approximately 30 m (1 arc-second). This means zoom 13 (~19 m/px) is the sweet spot — close to the native precision of the data. Higher zoom levels interpolate, while lower zoom levels average millions of measurements into a single pixel.
What is an Arc-Second?
Angles — like coordinates — are subdivided the same way as time:
| Unit | Definition | As degrees |
|---|---|---|
| 1 degree | base unit | 1° |
| 1 arc-minute | 1/60 of a degree | 0.0167° |
| 1 arc-second | 1/60 of an arc-minute | 0.000278° |
At the equator, one arc-second translates to:
x = 6,371 km × sin(1/3600°) ≈ 30.87 m
This is the native resolution of SRTM — one elevation sample approximately every 30 metres.
Finding the Right Pixel: Latitude & Longitude to Tiles
The coordinates (latitude and longitude) are not stored in the pixels. Instead, they are calculated purely mathematically using the Mercator projection (EPSG:3857).
The entire map of the Earth is projected onto a massive square. The
reference point (0, 0) is always the top-left corner of
this square (which corresponds to the far north-west: 180° West,
~85.0511° North).
From there, the grid works like a coordinate system: - X-axis (Longitude): Moves left to right (West to East). This is a simple linear mapping from −180° to +180°. - Y-axis (Latitude): Moves top to bottom (North to South). Because the Mercator projection stretches the poles, this mapping is non-linear (it uses trigonometric functions).
At any zoom level z, the world is divided into a grid of
2ᶻ × 2ᶻ tiles.
The Math
To find which tile contains a specific coordinate, we use these formulas:
// Longitude to X (Linear)
int x = std::floor((longitude + 180.0) / 360.0 * std::pow(2.0, zoom));
// Latitude to Y (Non-Linear Mercator)
double latRad = latitude * M_PI / 180.0;
int y = std::floor(
(1.0 - std::log(std::tan(latRad) + 1.0 / std::cos(latRad)) / M_PI) / 2.0 * std::pow(2.0, zoom)
);
Example Visualization Tile - Stuttgart
To get a better understanding of how the Mercator projection works, let’s visualize how the entire Earth maps onto the Zoom 0 tile, and how we find Stuttgart’s position within it:
Stuttgart coordinates:
48.78° N, 9.18° E (Decimal Degrees)
🚧 Under Construction
I am currently working on a more detailed exploration of the Mercator projection. Stay tuned! ;-)
Crosshair placed mathematically at x: 134.5, y: 88.5
From Tile to Exact Pixel
The best way to think about the formulas is that they calculate your x/y position.
Let’s look at a coordinate that computes to x = 1076.45.
This single decimal number actually gives us two completely different
pieces of information:
The Whole Number (
1076) tells us WHICH file to download. Because tiles are distinct images on a server, we need whole numbers to request them (e.g.,https://.../11/1076/...). The1076means “skip the first 1,076 tiles starting from the left edge of the map.”The Fraction (
0.45) tells us WHERE we are inside that specific file. Once you download the tile1076, you have an image that might cover several square kilometres. But where exactly is your coordinate inside that image? The fraction0.45means you are 45% of the way across the tile.
Because every tile is exactly 256 pixels wide, you take that percentage and multiply it by the width of the image:
0.45 × 256 = 115.2
This tells the computer: “Open tile 1076, and look exactly at the 115th pixel from the left.”
If we didn’t use the fraction, we would only know which geographic tile we were in, but we would have no idea which of the 65,536 pixels inside that tile actually contained the elevation for our exact coordinate!
Apply It
With this foundation in place, we can build an interactive terrain visualisation. Here is an example applied to the Stuttgart region.
The following website is 100% vibe coded. Claude is just impressive. The only thing I entered were some guardrails, ideas, specification. This is the result: