There are a couple of different ways to create 3D models in HTML5. The method we are going to use to is to project 3D data onto a 2D canvas. There are several JavaScript libraries available online that can do this. You can also find an in-depth article on how to do the math required for this projection here if you want to write your own library. For this blog post we will use the second method and make use of a JavaScript library called K3D.

In this blog post we will create a web page that uses Bing Maps to select an area on the map. We will then use the elevation service to get the elevation data for the selected area on the map. We will then generate a 3D model using the HTML5 canvas and the K3D library. We will also make use of sliders from the jQuery UI library and add functionality for rotating and translating the model.

Note that by using a helper library called ExplorerCanvas you can add HTML5 canvas support to Internet Explorer 7 and 8 which represent 13.8% of the current web browser market share.

Setting up the project

To help make things clean we will put all of our JavaScript into a file called 3DElevationModel.js and put all JavaScript files into a folder called scripts. We will also need to include JavaScript libraries for jQuery, jQuery UI, K3D, and ExplorerCanvas. We will also put all our HTML into a file called index.html. Your project structure should look like this:

The HTML Layout

For this application we will want to have a map that the user will be able to use to select what area the 3D model should be created for. Once the user has selected the area they are interested in they will be able to press a button to generate the 3D model. Once the model is created the user will be able to view and interact with the 3D model.

We will need to ad references to 7 JavaScript files. The first reference will be to the Bing Maps V7 AJAX control. We will need references to the jQuery and jQuery UI controls to create the sliders for manipulating the 3D model. A reference to the CSS stylesheet for the jQuery UI library will also be needed. The K3D and its associated MathLib JavaScript libraries will need to be referenced to help generate the 3D model. We will also load in the Explorer Canvas JavaScript library if the browser is Internet Explorer with a version less than 9. Finally, we will need a reference to the JavaScript file where we will be putting all our code: 3DElevationModel.js.

For the body of the HTML document, we will need a div to host the map control, a button to generate the 3D model, a canvas element for rendering the model and 6 div’s for generating sliders. With this, we can create the markup for the index.html file. The HTML should look like this:

All the JavaScript for this project will be added to a JavaScript file called 3DElevationModel.js. At the top of this file we will have a number of global variables which we will use throughout the application. Here are the global variables we will have at the top of the file:

var map, //Reference to Map control canvas, //Reference to the HTML5 canvas used to render the model sessionKey, //Bing Maps session key used with REST services mapBounds, //Area of where elevation data was requested zoom, //Zoom level used to request elevation data model, //Reference to the generate model k3d, //Reference to K3D control numRows = 30, //Number of rows to use for the elevation samples numCols = 30, //Number of columns to use for the elevation samples textureColor = [0, 255, 0]; //Color to render the model in

When the application starts, lets load the map. Since we will be using the REST elevation service, we will also generate a session key from the map which we can use with the REST services to make those transactions non-billable. And while we are at it, we will also store a reference to the canvas and put all this logic into the GetMap function (which is called when the page is loaded). Also, don’t forget to add in your Bing Maps key into this code.

//Store a reference to the canvas canvas = document.getElementById('canvas');}

Now we can start adding the logic for requesting the elevation data. When the “Generate Model” button is pressed the GenerateModel function will be fired. In this method, we will want to clear the canvas of any previous model, store a reference of the current zoom level and map bounds, and create the request to the Bing Maps Elevation service. To make things a bit cleaner, we will round off the coordinates to six decimal places as additional decimal places make no difference. This function will look like this:

//Only generates models when the user is zoomed in at a decent zoom level, otherwise the model will just be a flat sheet.if (zoom < 8) { alert("This zoom level is not supported. Please zoom in closer (>8).");return; }

When the elevation service responds to our request, it will fire our callback function called Render3DModel. This function will need all the logic to generate the 3D model and render it on the canvas. To create the 3D model, we will need to do the following:

(1) Loop through all the elevation data, calculate the relative coordinate, and then convert this coordinate to a pixel coordinate. We will also need to convert the elevation into a pixel length.

(2) Next, we can create a 3D mesh out of the data. To do this, we will need to specify all the data points as 3 dimensional coordinates, and then specify the texture coordinate vertices used to render the mesh. We will also need to specify the point indices used to create the triangles needed for the mesh.

(3) Finally, we can pass all this data to the K3D library to generate the 3D model.

//Calculate meters per pixel for given latitude so we can scale the elevation to pixels//This is based on: http://msdn.microsoft.com/en-us/library/aa940990.aspx m2p = 156543.04 * Math.cos(y * Math.PI / 180) / Math.pow(2, zoom);

The K3D library, when iterating through data in an array, uses a method called forEach. This method is only available in newer browsers. Trying to use this library in Internet Explorer 7 or 8 will result in an error. To get around this error we can easily detect if this method exists for arrays, and if it doesn’t, then create the needed method. The following code can be used to do this:

At this point we have everything we need to generate the 3D elevation model. Open up the web page in a browser and zoom into an area likely to have varying elevations and press the “Generate Model” button. Below is a screenshot of a rendered 3D elevation model of Niagara Falls.

/** This code makes use of the K3D libaray: http://www.kevs3d.co.uk/dev* Documentation for K3D http://en.wikibooks.org/wiki/K3D_JavaScript_Canvas_Library/Tutorial*/

var map, //Reference to Map control canvas, //Reference to the HTML5 canvas used to render the model sessionKey, //Bing Maps session key used with REST services mapBounds, //Area of where elevation data was requested zoom, //Zoom level used to request elevation data model, //Reference to the generate model k3d, //Reference to K3D control numRows = 30, //Number of rows to use for the elevation samples numCols = 30, //Number of columns to use for the elevation samples textureColor = [0, 255, 0]; //Color to render the model in

//Add support for the "forEach" method on arrays if the browser does not have support for this. //This is needed by the k3d library. Notable browsers that need this added are IE7 & IE8.if (!Array.prototype.forEach) { Array.prototype.forEach = function (fun) {var len = this.length;if (typeof fun != "function")thrownew TypeError();

//Only generates models when the user is zoomed in at a decent zoom level, otherwise the model will just be a flat sheet.if (zoom < 8) { alert("This zoom level is not supported. Please zoom in closer (>8).");return; }

//Calculate meters per pixel for given latitude so we can scale the elevation to pixels//This is based on: http://msdn.microsoft.com/en-us/library/aa940990.aspx m2p = 156543.04 * Math.cos(y * Math.PI / 180) / Math.pow(2, zoom);