Terrain maps are awesome, but creating them has been at best a complicated six-step process.

  1. Reproject the data with gdalwarp
  2. Create hillshades with gdaldem hillshade
  3. Generate color-relief with gdaldem color-relief
  4. Generate slope with gdaldem slope
  5. Generate slopeshading with gdaldem color-relief
  6. Render in TileMill

It’s now possible to take a raw digital elevation model and produce a detailed color-relief elevation map styled using TileMill’s CartoCSS language, bypassing the separate color-relief and slope shading steps entirely.

This works in the development version of TileMill which is leveraging the raster-colorizer, a functionality added to Mapnik by the Koordinates team. Expanding TileMill’s native support for raster data, the colorizer feature allows you to apply custom color rules to single-band raster data using CartoCSS. The raster-colorizer feature will be available in the next TileMill release. Below I’ll outline the streamlined process to turn raw elevation data into a finished terrain layer like this:

Hawaii Digital Elevation Model

Process Elevation Data

  1. Generate hillshades and slope - Use GDAL’s gdaldem utility to generate hillshades and slope GeoTIFFs from the source dataset.
  2. Warp to Google Mercator - Use GDAL’s gdalwarp utility to reproject the original dataset, hillshades GeoTIFFs, and slope geotiff to Google Mercator Projection (EPSG:900913).
  3. Scale Slope Geotiff - Use GDAL’s gdal_translate utility to convert the slope GeoTIFF to Byte, with values ranging from 0-255. gdal_translate -ot Byte -scale.
  4. Add Overviews - Add overviews to the three warped GeoTIFFs using GDAL’s gdaladdo utility.

TileMill Colorization

With the source data processed, we’re ready to load the layer in TileMill. First, load the hillshade layer in selecting “900913” for the data source SRS.

Next, load the slope and DEM GeoTIFF as separate layers, choosing “900913” for the data source SRS. In the Advanced input area, add band=1. If you omit this step, the colorizer will not function properly.

TileMill New Add Raster Layer

Choose the band of interest in the TileMill Add Layer window through the Advanced Options input

With the three layers in TileMill, we can now control the colorization of the slope and dem layer using CartoCSS. Since the hillshade layer was produced with gdaldem hillshade it does not need to be colorized.

#hillshade {
  raster-opacity:1;
  raster-scaling:bilinear;
}

There are three required inputs for the raster-colorizer:

  1. raster-colorizer-default-mode, sets the default coloring mode and can be discrete, linear, or exact.
  2. raster-colorizer-default-color, sets the color that is applied to all values outside of the range of the colorizer-stops.
  3. raster-colorizer-stops, assigns raster data values to colors. Stops must be listed in ascending order, and contain at a minimum the value and the associated color. You can also include the color-mode as a third argument, like stop(100,#fff, exact).

DEM Layer

Without adding the raster-colorizer options, the DEM layer renders as black and white in TileMill.

DEM layer, before raster-colorizer

Here’s how the DEM layer looks with raster-colorizer.

Colorized DEM

#dem {
  raster-opacity:1;
  raster-scaling:lanczos;
  raster-colorizer-default-mode: linear;
  raster-colorizer-default-color: transparent;
  raster-colorizer-stops:
    stop(0,#47443e)
    stop(50, #77654a)
    stop(100, rgb(85,107,50))
    stop(200, rgb(187, 187, 120))
    stop(255, rgb(217,222,170));
}

Slope

Without adding the raster-colorizer options, the slope layer renders as black and white in TileMill.

Slope before Colorizer

With the raster-colorizer, we can remap the colors, so that white appears when the slope is 0, becoming increasingly black as the slope approaches 90 degrees.

#slope {
  raster-opacity:1;
  raster-scaling:lanczos;
  raster-colorizer-default-mode: linear;
  raster-colorizer-default-color: transparent;
  raster-colorizer-stops:
    stop(0,#fff)
    stop(90, #000)
}

Compositing operations affect the way colors and textures of different elements and styles interact with each other. I take advantage of a few different ones to achieve the desired effect in the ultimate map, which layers the DEM first, the hillshade second, and the slope on top.

Raster Compositing

Compositing Operations

#hillshade {
  raster-opacity:1;
  raster-scaling:lanczos;
  raster-comp-op:grain-merge;
}
#slope {
  raster-comp-op:grain-extract;
  raster-opacity:1;
  raster-scaling:lanczos;
  raster-colorizer-default-mode: linear;
  raster-colorizer-default-color: transparent;
  raster-colorizer-stops:
    stop(0,#fff)
    stop(90, #000)
}
#dem {
  raster-opacity:1;
  raster-scaling:lanczos;
  raster-colorizer-default-mode: linear;
  raster-colorizer-default-color: transparent;
  raster-colorizer-stops:
    stop(0,#47443e)
    stop(50, #77654a)
    stop(100, rgb(85,107,50))
    stop(200, rgb(187, 187, 120))
    stop(255, rgb(217,222,170));
  raster-comp-op:color-dodge;
}

Terrain Map

Finished Terrain Map on MapBox