Visualizing a mountain using Three.js, Landsat and SRTM

Orhun Özer
6 min readMar 11, 2019

--

TL;DR

Using freely available satellite data and Javascript you can visualize a topography on earth. Link to source code and demo. If you think it’s going to be fun let’s continue.

Mount What?

A few weeks back, I decided to spend some time with Three.js in my leisure. I decided to visualize the 3D model of Turkey’s highest mountain: Mount Ağrı, only using freely available satellite data and Javascript. Like 15th century romantic William Blake once said; “Great things are done when men and mountains meet”. So, Willy, it is gonna be hilarious baby.

Bootstrapping with ES6 and Webpack

There are plenty of good examples in Github but I’ll go forward with the https://github.com/jackdbd/threejs-es6-webpack-starter because it has all of the features I need and more and also easy to understand.

DATA

Mount Ağrı

The data I am going to use is freely available and provided by the USGS. Using earth explorer I downloaded DEM(Digital Elevation Model) and satellite image of Ağrı Mountain which is a passive volcano and also the highest mountain in Turkey.

I downloaded a few images using USGS application then tried to find an image with cloud coverage of less than 10%

Downloading image from https://earthexplorer.usgs.gov/

The Good, The Bad and The Landsat 🛰️

If you are not familiar with remote sensing and image processing you might not hear about the Landsat but, is a continuous satellite mission controlled by USGS and provides scientific satellite imagery for researchers with approx. 30m2 resolution for many years. One pixel of the image covers an area of 30m2 where the satellite’s camera is perpendicular to the earth. Nowadays some satellites have a resolution less than a meter but generally, they are not freely available. So, Landsat is not so good or bad, but it is enough for us, we are going to use this old loyal fella for our textures.

https://earthexplorer.usgs.gov/ is the source for downloading free satellite imagery. The images should have a cloud coverage of less than 10% and it should be added to the criteria. It was hard to find a good one since the mountain is so high that it is cloudy every time. After finding a suitable image I realized that Landsat covers a huge area and I needed to crop my area of interest. After cropping this is going to be the texture of the model. But more importantly, I need an elevation model to visualize the mountain.

Satellite image of Mount Ağrı with some clouds and snow (turquoise part)

SRTM — I don’t give a DEM 🗻

Yeah, actually it gives a DEM. DEM stands for Digital Elevation Model and it is raster based elevation data. SRTM which stands for “Shuttle Radar Topography Mission” is operated by NASA. (https://www2.jpl.nasa.gov/srtm/) The SRTM offers a DEM with 30m resolution. Let’s recall; it means one pixel covers the approximately 30m area and the average elevation of that area as the pixel value. This data is extremely valuable for generating our mountain model in three.js

Pre Processing

I used QGIS one of my favorite products for GIS. I cut and mask the DEM and related satellite image using QGIS raster tools and copy them to the working directory.

Clipping image and DEM in QGIS
Looks kind of scary, digital elevation model over satellite image.

Looks like the Mouth Doom but this is how the elevation model looks like in QGIS with default color palette. I clipped two different sizes concerning performance, you can find both at the repo. I use the small one in the demo to see results fast.

Three.js

Three.js is an excellent library for using WebGL easily. It might seem complicated at first glance, we will slice it into pieces to understand better.

Lights, Camera, Action

In order to start three.js in beginner level this tutorial is very helpful. https://aerotwist.com/tutorials/getting-started-with-three-js/

In Three.js world we need some basic setup. 4 basic components

  1. A scene
  2. A renderer
  3. A camera
  4. An object or two (with materials)

A note about materials

I don’t care about materials in this example because the satellite image is already enlightened by the sun itself when the image is acquired. Therefore we have natural cloud shadows according to sun angle. So, I don’t mind which angle the light is coming from also. If you wonder you can dig the metadata file of Landsat image to find out in which sun angle it has when the image is acquired. Then you can simulate the same light with shaders etc. If you want real reflections you can consider using some of the shapes above.

Materials and reflections

Let’s start

We’ll start with adding a scene, then we’ll set renderer, camera, controls, and light. Adding light is crucial otherwise you cannot see anything on the scene.

Shaping The Mountain

The geometry of our object is not modeled in Blender, Maya, or something similar. These kinds of models can be imported to three.js easily. But I am going to use DEM file to generate a model directly in js with the help of “geotiff” library.

Reading Image

Inside setupTerrainModel after adding clipped images to the project we use geotiff library to read DEM file and add a new PlaneGeometry object passing size of the image. Ok let’s generate the big boy.

Then read pixel values that each pixel corresponding to an elevation value. I just heuristically divide it to “20” to make it look nice and multiplied it with -1 otherwise model will be upside down because of the z-coordinate direction of three.js, I’ll explain it later. Using console.time let us see the performance.

From now on our model is ready to pop up in the browser but without a satellite image, it is only a 3D model and going to looks like the one above.

3D mesh of Mount Ağrı in three.js

Texture Fitting

After generating the model, we are going to use an RGB satellite image which is also clipped with QGIS earlier.

We are loading the satellite image and keep in a variable called “material” to use with Three.MESH just after. And don’t forget to rotate the object because three.js has start with the right-hand coordinate system which means, by default Z-axis is not towards upwards but you. For a detailed explanation: https://tweedegolf.nl/blog/5/threejs-rotations

You can either rotate the x-axis or rotate the camera at the beginning by camera.up.set(0,0,1) Rotating x-axis and putting out mountain just above and moving down a little bit in y-axis let’s run and swish. It is ready.

Demo & Source Code

There are two versions of DEM model (one is very large) you can test it in your local environment. I’ve used the small version for the demo. https://zhunor.github.io/threejs-dem-visualizer/ https://github.com/zhunor/threejs-dem-visualizer

Credits & Gains

Well, it is hard to find a nice cloudless image first of all. Secondly generating a 3D model with all detail is extremely costly. Maybe the DEM should be downscaled to a reasonable resolution if someone really willing to do it in client-side.

Tutorials of https://github.com/jonathanlurie/ThreejsDEM and http://blog.mastermaps.com/2013/10/terrain-building-with-threejs.html was extremely helpful. Thank you guys.

--

--

Orhun Özer
Orhun Özer

Responses (1)