Written by Steven Hall
Over the past couple of weeks I have been experimenting with creating 2D maps that can be explored in three dimensional space using D3.js and Three.js. The goal was to produce some highly polished prototypes with multiple choropleth maps that could be easily navigated on a single page. Additionally, I wanted to make sure to address some of the common tasks that arise when presenting map data such as applying well-formatted titles, legends and elegantly handling mouse-over events. The two examples presented below use D3.js for for generating nested HTML elements that contain the maps, titles and labeling information and use Three.js to position the elements in 3D space using CSS 3D transforms. Importantly, there is no WebGL used in these examples. Everything is rendered in the DOM using CSS 3D transforms which, at the time of writing, has much wider browser support than WebGL.
This article is an extension of two of my previous articles on D3.js and Three.js that can be found here and here. Below, I'll go into more depth about how the examples are produced and some of the roadblocks I encountered in putting these demos together, but for more background on the general process it may be good to look at the first article in this series: D3.js, Three.js and CSS 3D Transforms.
Examples for this Article
You can view the two examples below in your browser or you can download all the code for the examples from this GitHub repository.
D3 SVG Map example
This example generates 10 separate SVG maps with independent scales and legends. This SVG example is the lighter weight of the two examples and tends to work well even on tablet devices. On an iPad Mini the performance is excellent and even firing the click events on each state seems to work without problems.
Mapbox Tiled Map Example
This example creates 10 tiled maps through the Mapbox API (leaflet). There is a lot more overhead needed to support these types of maps, but the performance on a desktop computer is impressive and the scaling offered by a tiled map may be the right choice in some cases. Testing this example on an iPad Mini, I saw that the colored geoJSON layer either lagged in rendering or never fully appeared on a few of the maps.
About the Data
The data for the example maps comes from the latest release of Center for Disease Control's (CDC) cancer incidence and mortality rates broken out by state for 2010 (latest available). The full set of data can be found here on the CDC's website. In these maps we are looking at only the incidence rate (per 100,000) for both genders, but the data file included with the code examples also has mortality rate information in it.
Looking at the Code
The basic process for creating the maps is the same as was used in the previous articles on CSS 3D transforms (mentioned above). The four main steps that are carried out in the code are (1) create the main HTML elements (using D3) which contain several sub-elements for things like titles and captions as well as the maps themselves (2) calculate the locations in 3D space for each of the elements for the layouts in the visualization (random, grid and sphere) and store this data on the element's data property (3) passing in each HTML element, create new THREE.CSS3DObjects and add those objects to a Three.js scene and (4) render and animate the scene. From there, the page is responding to mouse and touch events coming from the user and updating the transforms.
There is a small amount of code in each of the HTML file for each example that starts the process. If you look in the script tag in the HTML, once the data has loaded, the VIZ.drawElements function is invoked and D3 is used to create the HTML elements and maps. The drawing below highlights the basic structure of each of the elements.
Important Notes on the Mapbox HTML
If you compare the two HTML files for the examples, the Mapbox HTML has some important differences that are easy to miss. First, if you look at the top of the HTML file there is a script tag that has the text "L_DISABLE_3D = true;" inside of it. This tells Mapbox to not add its own CSS 3D transforms to the map elements. We want complete control over how the transforms are applied, so it important to have this tag at the top of the page.
Secondly, the Mapbox example has two additional imports at the top of the page for loading the Mapbox.js file and the CSS needed to properly format the map elements. It is possible to load these from you local server, but here they are loading from Mapbox API.
Some Notes on the CSS
The style.css file for these examples is not terribly long, but there are some important things happening there that should be considered if you want to create a similar type of project. In order to see the basic structure more clearly, I created the graphic below.
The elements created for these examples are composed roughly as follows:
The first thing to note is that there is heavy use of absolute positioning of elements. Each element contains sub-elements that are positioned absolutely using coordinates relative to the base element (map-div). Using this basic scheme I found it pretty easy to develop a layout that worked well with these maps, but there was a lot of experimentation needed. The examples here have a pretty basic layout that looks good and serve as good prototypes to demonstrate the basic principles, but using these as templates some more complex and impressive layouts could be achieved.
I am pretty happy with the final outcome of these demos. CSS 3D transforms are still not widely used and written about, so there is not a ton of information available out there to do more complex animations using this technology. As always, any feedback is welcome using the comments system on this page or via the contact page on the site. I would also be interested to hear about similar experiments that others are doing.