Installing MapLibre GL with TileServer GL

Implementation Notes for Jura Mountains mapping

Jura Mountains mapping | Dynamic terrain3d map | Vector tiles (terrain-rgb) | Main map

In preparation for serving vector tiles for high-resolution elevation mapping together with terrain raster tiles, we need to set up a vector tile server on our Ubuntu 18.04 box.

TileServer GL for the server-side rendering of vector map tiles by Mapbox GL Native was designed to use mapbox-gl.js. With licencing changes, mapbox-gl users have migrated to MapLibre GL, with the comment "If you depend on mapbox-gl directly, simply replace mapbox-gl with maplibre-gl in package.json".

A simple test to run maplibre-gl-js (as the successor for mapbox-gl-js) with tileserver-gl has been developed.

TileServer GL with MapLibre GL cannot currently be installed using Node. Docker is needed.

In the case of say Ubuntu 18.04 with Docker installed, to install TileServer GL with MapLibre GL, simply:

  • git clone
  • cd poc-maplibre-gl-js-with-tileserver-gl

in console in a home, working or temporary directory. Then edit docker-compose.yaml to change ports, if necessary, and to set the latest TileServer GL version (currently 3.1.1). One can use for the docker-compose.yaml for example:

version: "3.4"




- tileserver


context: ./map-client

dockerfile: Dockerfile


- "/usr/app/node_modules"

- "./map-client:/usr/app"


- "3004:3004"


image: maptiler/tileserver-gl:v3.1.1


- "./tileserver-gl:/data/"

command: ["-p", "80", "-c", "/data/config.json"]


- "8188:80"

Then run:

  • docker-compose up

Docker throws an error ("Service 'mapclient' failed to build: When using COPY with more than one source file, the destination must be a directory and end with a /"). No matter. Simply run:

  • docker run --rm -it -v $(pwd):/data -p 8188:8080 maptiler/tileserver-gl:v3.1.1 -c config.json

The default zurich_switzerland.mbtiles are automatically downloaded on running Docker for the first time. These are used. The Docker "Start-up complete" message in the console is "Listening at http://[::];8080/" even if the port is changed. The message can be ignored.

To view one's own tiles, download to the working directory (in our case /home/user/tileserver-maplibre311, where the map-client and tileserver-gl directories are found) a test switzerland.mbtiles from

Our Docker installed TileServer GL is found at

TileServer GL with Mapbox GL

The zurich_switzerland.mbtiles can also be found in the working directory when one installs TileSever GL in the normal way to use Node (as an alternative to using Docker).

For a fresh Node installation of TileServer GL with mapbox-gl, first make sure one has the requirements (for Ubuntu 18.04 in our case):

  • sudo apt-get install -y software-properties-common protobuf-compiler pkg-config libcairo2-dev libjpeg-dev libgif-dev git libgl1-mesa-glx build-essential g++ curl

It is also necessary to use Node version 10. Then run in console:

  • git clone
  • cd tileserver-gl
  • sudo npm install -g --unsafe-perm

A simple way to run the server is:

  • cd src

and to set the port in tileserver/src/main.js and run using:

  • node main.js

TileServer GL will display its landing page with a link to the default zurich_switzerland.mbtiles located in the working directory (tileserver/src).

Raster elevation (terrain RGB) tiles

The raster GL tiles we currently link to using TileServer GL with MapLibre GL are terrain-rgb tiles as a first step towards a more sophisticated hill-shading than the simple overlay of a Digital Elevation Model (DEM) that we use for the main Jura Mountains map.

Our ultimate aim is to explore the use of adjustable vector shading as has been discussed (and proposed as long ago as 2012) to enhance hillshading. Currently, for the Mapbox Terrain version 2 tileset, the hillshade layer contains polygons that can be styled to display the shaded relief of hills (i.e., it is posterised hillshading based on vector shapes).

An interim step is to explore adjustable raster hillshading using terrain rgb raster tiles that contain Digital Elevation Model (DEM) elevation data encoded into a RGB color model (maybe originally attributed to Mapbox). The elevation is generally (but not always) calculated as:

  • height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)

for the red, green and blue bands.

There are several recipies for creating terrain RGB tilsets (see the National Scenic Trails Guide, for example). In our case we used QGIS to merge the Swisstopo swissALTI3D two-metre DEM product for the Jura Mountains. After reprojection to Web Mercator (including setting the no-data value to -999999), the merged geotiff was exported as a geotiff. QGIS's Grass r.mapcalc.simple was then used to calculate the terrain RGB raster which was again exported as a geotiff. The tileset was created as mbtiles using QGIS's GenerateXYZ Tiles.

Hillshading was set up using the Leaflet.Tilelayer.GLOperations javascript plugin with the terrain RGB mbtiles served using TileServer GL with MapLibre GL setup described above.

GLOperations used a white colour for the colorValue that is the hillshade's background, and the offset was set to transparent:

  • const colorScale = [{ offset: 0, color: 'transparent'},{ offset: 0.0, color: '#ffffff'},];

The nodataValue of -999999 is transparent by default.

Simple hillshading (i.e., hillShadeType :'simple') with a slope scale of 0.001 gave the elevation map at the default zoom level of 12 (a coloured DEM generated using QGIS's gdal2tiles and having an opacity of 0.5 overlays the hillshade).

It is rare to see terrain RGB tiles displayed at zoom levels above 12 (a zoom level limit of 10 is common). This is presumably because tile borders become noticeable at this zoom level (an artifact that can be removed by rendering a group of tiles and extracting one). More disturbing is the banding that is evident a zoom levels of 12 and above. To avoid this effect, some recent terrain RGB hillshading seems to use small slope scales that lead to low contrast levels and presumeably suppress artifacts such as banding (see, for example, the National Scienic Trail Guide's map at zoom level 13 where hillshading was generated using a DEM with a 30-m resolution).

We are far from expert in producing terrain RGB tilesets so it may be necessary to see if improvements in performance are possible. At this stage, the possibility to adjust hillshading interactively (possibly with advanced hillshading techniques) using terrain RGB tilsets with GLOperations and similar approaches does not appear to offer a useful tool for exploring terrain at relatively high zoom levels.

Coupling adjustable vector hillshading to raster hillshading with Leaflet - and indeed, WebGL hillshading in general - therefore has an uncertain future, for us at least. A completely different approach may be needed (for example, Procedural GL that is built on top of THREE.js).


A terrain3d branch has been added to a maplibre-gl-js fork. As pointed out by contributors, the branch's maplibre-gl.js and maplibre-gl.css can be built in the usual way, as described by maplibre-gl-js (for Ubuntu 18.04, check that you have Node version 14 installed; clone the repository; npm install; npm build-prod; npm build-css; maplibre-gl.js and mapibre-gl.css are created in the dist directory).

A simple web app can then be set up using the terrain3d MapLibre GL .js and .css files and the terrain3d terrain.html file.

Courtesy of the terrain 3d developer (prozessor13 and Tourspring GmbH), we have done this (see terrain3d map).

MapLibre GL.js supports dynamic hillshading (see dynamic terrain3d map) where we used the UK Ordinance Survey demo and not the MapTiler Mapbox demo (note that labelling is displaced on tilting the map).

Whether or not terrain-rgb hillshading can approach the resolution given by simply displaying DEM map tiles (see the main map) remains to be established.

16 October 2021