Using SRTM Elevation Data in Javascript


1 arc second SRTM Data was made publicly available by NASA a few months ago. SRTM Data is collected by the Shuttle Radar Topography Mission. It sweeps the surface of the earth to find irregularities which form the height map. According to the mission website:

To acquire topographic (elevation) data, the SRTM payload was outfitted with two radar antennas. One antenna was located in the shuttle’s payload bay, the other on the end of a 60-meter (200-foot) mast that extended from the payload pay once the Shuttle was in space.

For our application, we were using SRTM Data for two things:

  • to find the altitude of specific coordinates, and
  • to draw terrain maps

I will use javascript to demonstrate how to fetch an SRTM binary file using xhr and how to traverse the data to find the required height value.

Ajax Fetching

Please note that SRTM files are very large in size. Fetching them client side is very bad design and will slow down your app considerably.

SRTM files that we used are distributed in .hgt binary format with 16 bit integers consecutively stored. From the SRTM wiki:

The HGT files have a very simple format. Each file is a series of 16-bit integers giving the height of each cell in meters arranged from west to east and then north to south. Each 3-arc-second data tile has 1442401 integers representing a 1201×1201 grid, while each 1-arc-second data tile has 12967201 integers representing a 3601×3601 grid. The outermost rows and columns of each tile overlap with the corresponding rows and columns of adjacent tiles.

This means that for higher resolution 1 arc second data, these files can be as large 25MB for one square longitude value. The file name is standard and represents the square for which it has the heightmap stored. For example, N57E009.hgt is the file for North 57° Lat and East 9° longitude.

Being a binary format, you will need to specify to jquery that you are fetching a binary dataset. The support for arraybuffers is, however, very sketchy and I recommend using xhr requests to do the fetching.

var xhr = new XMLHttpRequest();
xhr.open('GET', fileurl, true);
xhr.responseType = 'arraybuffer';
xhr.send();

xhr.onload = function(e) {
	if (this.status == 200) {
		elevBuffer = this.response;
		}
	else {
	// handle errors here
	}
}

To use jquery for loading arraybuffers, you will need to attach an ajax transport. More details for that here. The response of the xhr request is the binary file you will be using in the next step.

Processing Data

For everything here, I am assuming that you are providing the correct elevation binary file (NxxExxx.hgt) to the function.

Let’s take a look at the module I am using to provide us with elevation data:

function getElevation(lat, lng, elevBuffer) {
			
	// resolution of dataset, 1 arc second here
	var resolution = 0.00027778;
	var hgtGridSize = 3601;

	return elevationFromlatlng(lat, lng);

	function wgs84ToPixel(lat, lng) {
		var y = Math.floor((1 - (lat % 1)) * (hgtGridSize));
		var x = Math.ceil((lng % 1) * (hgtGridSize));
		var px = (y * (hgtGridSize) * 2) + (x * 2);
		return px;
	}

	function elevationFromlatlng(lat, lng) {
		var elevation;
		var pixel = wgs84ToPixel(lat, lng);

		var g = new DataView(elevBuffer, pixel, 2);

		elevation = g.getInt16(0, false);
		return elevation;
	}

}

The function we use takes in a lat,lng point and the elevation buffer that we are using to provie us the heightmap with. This elevation buffer is the binary file we just fetched before.

The first part of the function is to figure out the position of the integer which contains the height for the specific lat,lng in the binary file. As per the wiki:

…Each file is a series of 16-bit integers giving the height of each cell in meters arranged from west to east and then north to south. …each 1-arc-second data tile has 12967201 integers representing a 3601×3601 grid. The outermost rows and columns of each tile overlap with the corresponding rows and columns of adjacent tiles.

Since the cells are west to east then north to south the formula follows:

function wgs84ToPixel(lat, lng) {
	//hgtGridSize is 3601
    var y = Math.floor((1 - (lat % 1)) * (hgtGridSize));
    var x = Math.ceil((lng % 1) * (hgtGridSize));
    var px = (y * (hgtGridSize) * 2) + (x * 2);
    return px;
}

This returns the position of the integer in the file (or the number of integers to skip before you will arrive at the required cell height in the binary file).

Next you use the position to fetch the height from the binary file. DataView is used to make the fetch. Remember the last argument in DataView should be 2 since we are using 16 bit integers instead of 8 bits.

function elevationFromlatlng(lat, lng) {
	var elevation;
	var pixel = wgs84ToPixel(lat, lng);
	
	var g = new DataView(elevBuffer, pixel, 2);
	
	elevation = g.getInt16(0, false);
	return elevation;
}

This will return the height in metres as stored in the binary file. I will demonstrate how to draw a terrain in three.js in my next post using this data.

Till next time.

-Taha


Tags// , , , ,
comments powered by Disqus