Real World Terrain Map

The Brief

I had the requirement to show a map of a geographical area, displaying the terrain. The following requirements were agreed:

  • The map imagery must not be limited to a fixed dataset, for example a satellite image may be provided.
  • Map imagery and terrain data must be cached by the system.
  • The map can be annotated with 3D models and information.
  • Allow the user to interact with the map in a natural way.
  • The interface must work on any device and any platform, including phone, tablet, PC, Windows, Apple, and Android.


Real World Terrain Map Demo

(opens in a new window)
The Options
Google Earth

Firstly I considered Google Earth and it’s associated API. It was clear from cursory investigations that this was not an option; there was no ability to define the imagery or cache the maps that were used. 3D models are possible however interaction with them and functionality is limited.

Cesium JS

Cesium JS did provide some advantages over Google Earth in terms of defining the imagery and the terrain data, however it would be a non trivial task to setup a service to feed the data and it would not be cached on the client.

Three JS

Three.js is a JavaScript API to display animated 3D graphics using WebGL. Going for this would be a completely blank slate and extremely intimidating particularly if I had to consider geographical accuracy, and dealing with large high-resolution imagery. However, the benefits of using a raw API like this meant virtually anything was possible and all the requirements could be ticked.

The Solution
Scope

To keep the scope limited the following limitations were agreed:

  • The area is relatively small, no larger that 20km by 20km.
  • Imagery resolution is not that important.
  • Geographical accuracy is not required.
With these limitations in place using Three.js made perfect sense, maximum flexibility, excellent cross platform support and no need for a mobile app. Imagery could be stored on the web server and cached on the client. Adding 3D models was simple as was interacting with them and adding annotations.

Real World Terrain Map Demo (opens in a new window).

Imagery

Making a flat plane and draping a map image over it was trivial. Three.js had all the tools to do it; the first pass took only a few hours to put together. The key to this project was adding the terrain data.

Terrain

I had seen games developers using and array of noise to generate terrains so all I needed was the terrain data for the area as a matrix of points. This led me to terrain.party, an amazing site that lets you download a greyscale bitmap of any area on Earth from 8kmx8km to 60kmx60km. You get a package with a number of images and a text file detailing the area and the height limits.

I went round in circles thinking how to extract the height data from the image; trying to find a simple way to make JSON file so it could be fed to Three.js. The solution was perfectly elegant, leave it as a PNG image and send this to the page, all the data is there in a very efficient little package. The image is loaded into memory and drawn onto an in memory canvas, pulled back off, then I have access to each pixel to get the height data.

The image gives a value from 0 to 255 for each pixel; remember the image is grey scale so the red, green, and blue components of each pixel are the same. I know the minimum and maximum pixel value and the minimum and maximum height values, so a simple linear mapping can convert the pixel value into a height value. After a bit of scaling to match the grid dimensions the terrain is there.

The Extra Mile

It all came together remarkably well but it felt a little truncated. Just having the 10km by 10km square map was a little abrupt, particularly if a point of interest was near the boundary. It would be too much to extend the terrain map and main imagery for much more that 10km. I had no interest in the work to tiling the maps and data.

As a quick fix I grabbed a map image covering 4 times the area of the main map, centred on the same point, at a very low resolution. This perfect, it was small in size, blended perfectly with the main image, and provided just enough interest in the distance to make it feel like a full mapping system. It is a little odd when the terrain drops off at the boundary but the users are rarely out there and don’t appear to mind.

Models

The next step was to drop on some 3D models and animate them. Three.js makes this really simple with the object loader. I took a model from the Google Earth library, used blender to make an object and material file, loaded them in and dropped them on the map. I kept things simple, using the latitude and longitude as a Cartesian coordinate system; this is accurate enough for the scale I am working at.

I discovered that it was possible to render an HTML5 canvas onto a surface allowing for models to be annotated with any HTML I want.

As there is already an animation loop running it is simple to animate and move the model as well as update the annotations. Interacting with the models was a bit harder but a simple click to select was eventually implemented.


Real World Terrain Map Demo with Models

(opens in a new window)
This is purely a demo and any data shown is simulated.