Visualizing terrains with Cesium

2018-11-26: For updated post see

Today we did some experiments to visualize terrains in Cesium, using Cesium Terrain Builder ( and Cesium Terrain Server (

Step 1] Get some raster data with height information

For this demonstration we use a raster file from the Dutch AHN2 in tif format with cells 0.5m X 0.5m, it can be obtained from  (500MB)

This image is about a part of Texel:


Step 2] Tile the raster with Cesium Terrain Builder

The original tif image is quite big (500MB) so we cut it into tiles using the  Cesium Terrain Builder. We use the tool ‘ctb-tile‘ for tiling inside a Docker container:

$ docker run -v  d:/gisdata/nederland/terrain:/data -ti -i homme/cesium-terrain-builder:latest bash

Now create an output directory ’tiles’ (in this example d:/gisdata/nederland/terrain/tiles)

Start generating the tiles with:

root@c1412e2fd51c:/# ctb-tile -o /data/tiles /data/i09bz1.tif


This takes some time to process (like 10 minutes), so grab a coffee and relax…

In the meantime binary .terrain files are generated for 20 zoom levels.


The terrain tiles are encoded in the so called ‘quantized-mesh-1.0 terrain format’, more info about this format at

Step 3: Run the Cesium Terrain Server

If all the tiles are generated we can start the Cesium Terrain Server. We use another Docker command for this:

$ docker run -p 9000:8000 -v d:/gisdata/nederland/terrain:/data/tilesets/terrain geodata/cesium-terrain-server

The server starts working on port 9000.


One tile we can retrieve from the server using a wget command:

$ wget http://localhost:9000/tilesets/tiles/0/0/0.terrain


Step 4: Visualize the terrain in Cesium

Cesium is a WebGL globe viewer that runs in a browser, see for more info.

Open apps/HelloWorld.html in an editor and change the part between script tags to:

Cesium.BingMapsApi.defaultKey = ‘Aht_LtBvKjJfKZu__96G0Uk15cTTM3HRZxp_MD9g69xyIYNtrnDNaRBUSEVDIICQ’;

var viewer = new Cesium.Viewer(‘cesiumContainer’, {
terrainExaggeration : 20.0

var terrainProvider = new Cesium.CesiumTerrainProvider({
url : ‘http://localhost:9000/tilesets/tiles

viewer.scene.terrainProvider = terrainProvider;

So we start a Cesium viewer with Bing maps, and use our local tiles as terrain provider for the heights.

Note in this viewer there is no file ‘layer.json’ needed.

Put the Cesium viewer on a webserver (for example caddy Start the webserver in directory  Cesium-1.36 (or other version).

$ cd Cesium-1.36

$ caddy
Activating privacy features… done.

Open a browser to http://localhost:2015/apps/HelloWorld.html and zoomin to western part of Texel.. If you change the perspective the terrain (hint use the alt key) should become visible:


If you inspect the browser web requests (F12) request to the local terrain server should be there (for example http://localhost:9000/tilesets/tiles/18/269017/208333.terrain).

40 thoughts on “Visualizing terrains with Cesium

  1. Hi Bert, I find this tutorial is amazing since really few information about cesium. But I have a problem in the 4th step, the terrain not showed when running in the browser. I follow your step and use the same data. I used the wget too and its work fine, the terrain can retrieve. When I see in the Network (web developer tools in Mozilla), the terrain not loaded. Can you give me some solution for this problem? thank you.

      • Hi Bertt. I actually made stupid mistake in the script and finally the terrain showed up. Do you have an idea how to handle a big size data of DTM? I have data for a city that have high vertical resolution (~10 cm). I think It will caused a problem if I run the cesium terrain builder for nearly 100 gb data. Thank you in advance.

  2. great its working now! About the scaling up: we tiled the Netherlands with the terrain builder without a problem, took like 1-2 days and of course some disk space… Just try some smaller batches first and get an estimate how long it will take.

  3. Hi Bertt, that is a nice tutorial, I also used this tutorial nearly a year ago and thankfully it saved my master thesis. I’m currently working on a nodejs tool that takes a raster image and replaces the height values in the pixels with “STK” server values, using “sampleTerrain” function. I tried it with the image in this tutorial and it worked :), after one and a half hour (500 MB) but it worked. I think it can work as an alternative for people who would like to build their city models and don;t have specific requirements regarding the terrain data, or don’t want to involved with too much code and docker.

  4. “” which is the default terrain provider for Cesium.
    I’m using node-gdal to read / write raster data.
    -Loop over the output pixels.
    – Use “SampleTerrain” method to interpolate the height value in the pixel’s location – from STK-World –
    – Assign the retrieved value to the output pixel.
    In theory using this raster as height reference to create a 3D city model will create a model that will be clamped / fit to the default ” STK-World” terrain in Cesium. I need to further test it and see.

  5. Hello Bertt. I am from Indonesia. Really interested in developing 3D city model. I am learning how to put my 3D building and terrain into Cesium. I want to ask you about the script you write above. In the docker container, what is the function of this statement ” root@c1412e2fd51c:/# “? I do realay have no idea when I look forward to homme ctb in github.. Hope you can help me. Thanks.

    • Hi Fadel, ‘root@c1412e2fd51c:/#’ is a Docker prompt, with the previous command (docker run -it homme/cesium-terrain-builder bash) you start bash terminal inside Docker container. You’ll have to type ‘exit’ to leave the Docker container.

      • Thank you so much. I am literally new to this and I am so happy that I just succesfully tiled the terrain asssisted by your tutorial. 😀

  6. Hi, one question, is it necessary to use Docker? … can it work by running the cesium-terrain-server without Docker ?, thanks in advance!

  7. Thanks for excellent documentation.
    I am able to publish the terrain files using terrain server but am not able to view in cesium map viewer.
    Is it to do with Projection difference or is the base terrain layers of cesium overlapping on my terrain data?

      • Yes. I could visualize the data you have mentioned, 09/i09bz1.tif .
        Is my data not matching the projection and gets projected somewhere else ? or is it getting overlapped by Bing map or the base map ?
        What is the projection of 09/i09bz1.tif ? Does projection actually matter ?

    • this error came .how to resolve this

      This application is using Cesium’s default Bing Maps key. Please create a new key for the application as soon as possible and prior to deployment by visiting, and provide your key to Cesium by setting the Cesium.BingMapsApi.defaultKey property before constructing the CesiumWidget or any other object that uses the Bing Maps API.
      :8080/tilesets/terrain/test/layer.json Failed to load resource: the server responded with a status of 404 (Not Found)
      :8080/tilesets/terrain/test/0/1/0.terrain?v=1.0.0 Failed to load resource: the server responded with a status of 404 (Not Found)
      :8080/tilesets/terrain/test/0/0/0.terrain?v=1.0.0 Failed to load resource: the server responded with a status of 404 (Not Found)
      Cesium.js:420 An error occurred in “T”: Failed to obtain terrain tile X: 1 Y: 0 Level: 0.
      Cesium.js:420 An error occurred in “T”: Failed to obtain terrain tile X: 0 Y: 0 Level: 0.
      :8080/favicon.ico Failed to load resource: the server responded with a status of 404 (Not Found)

  8. I’ve updated this post, adding a note about layer.json (not needed) and better description how to get the Cesium viewer working. Please check if you’ve still issues getting this running.

  9. I have multiple .tif files, every tif file when used in terrain builder creates its own terrain files. What should I do to access multiple tif (in terrain format) data

    • @sow you can run the ctb-tile command multiple times (for each tif file) with same output directory. 1 directory with all terrain tiles will be the result.

  10. Thanks for the solution.
    When I run Cesium with the created tiles, I get following error. May be in terrain folder I do not have terrain\tiles\0\1\0.terrain
    next directory tile is in terrain\tiles\1\2\1.terrain

    An error occurred in “T”: Failed to obtain terrain tile X: 0 Y: 0 Level: 0.
    17:31:16.497 Cesium.js:477 An error occurred in “T”: Failed to obtain terrain tile X: 1 Y: 0 Level: 0.

  11. Is there a chance that CTB/CTS could provide data from 32float .tif s and to make a use of it in Cesium? I know that QuantizedMeshTerrainData is of 16int values, but is there any workaround except BIL/DDS to use detailed sub meter accuracy terrain ?

  12. Hi, thank you for your post. I tried following the tutorial, but the files when they are rendered in CESIUM they look very weirdddd. I check the tiles and they occupy only 30 mb of the hard drive after tiling. Definitely, there is something going on on the tiling process. Please help.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s