Thursday, December 27, 2012

D3GL Tutorials 01: Creating globes

d3.gl.globe
is one of D3GL's data visualization templates that is useful for visualizing global, location-based data. 2D projections of the globe pose problems of distortion and misrepresentation. To take a simple, common example, a Mercator projection of the world's countries has a large “lie factor”, because countries like Greenland and Russia appear much larger than they actually are.

3D visualization has a lie factor of one. Through projection, rotation, zooming, etc, 3D visualization leverages the human brain's powerful faculties for understanding spatial data. Flight paths, for instance, make much more sense when depicted on a three-dimensional Earth because the best paths fit the curve of the Earth, which may look strange and arbitrary on a flat projection of the world. Stellar bodies other than the Earth may also benefit from three- dimensional visualizations, particularly because unlike the world map, most viewers are not familiar with flat maps of the sun, moon, Mars, and more.

D3GL's globe template can overlay the following data primitives:
points, painter, arcs, bars, shapes
. In this tutorial, we will focus on rendering naked globes without any overlays using custom globe templates.

Creating a globe template


Before you can render a globe or several, you need to create a globe template and set its properties using chaining, as in
D3.js
:
var globe = d3.gl.globe()
  .width(300)
  .height(300)
  .texture("earth.jpg");

The globe template serves as a rendering template for multiple globes. For properties shared among all globes, static values can be passed in, as in the width, height, and texture calls in the above snippet of code.

Customizing per globe


To configure a specific property per globe, a function is passed in as argument, as in the texture call in the modified code below:
var globe = d3.gl.globe()
  .width(300)
  .height(300)
  .texture(function(d) {
    return "../" + d + ".jpg";
  });

The function passed into the texture call has a parameter
d
, which refers to the data element that is passed in per globe once data is bound to the globe template. In this code example, each data element can be used to create a path for a texture for each globe.

Binding data to the globe template


The globes can be rendered by binding data, appending DOM elements to contain them, and calling globe, which refers to the object previously initialized with a call to
d3.gl.globe()
:
var data = ['earth', 'moon'];

d3.select("body").selectAll("span")
  .data(data)
  .enter()
  .append("span")
  .call(globe);

This is analogous to the
D3.js
style for adding elements bound to data. The above code will pass the bound data, which is the array
['earth', 'moon']
, when it invokes
.call(globe)
.

Then for each element in the data that is passed in, a new D3
globe
object is created to be rendered on screen. Each element in the data array will be passed in as argument. In this example, the first globe is passed in
'earth'
and the second
'moon'
, which are used to determine the texture of each globe.

Demo


Hello Earth

Implementation


The conventions for this API were inspired by Michael Bostock's essay “Toward Reusable Charts,” which encourages the use of closures with getter and setter methods and outlines specific design patterns. Thus
d3.gl.globe
is a closure with properties such as
width, height, texture, transparency, zoom, and rotation.
It supports method chaining, which means each setter returns the closure.

When
.call(globe)
is invoked, a function is called for each data element in the bound data that evaluates the appropriate values for the properties of the globe -
width, height, texture, transparency, zoom, rotation
.

WebGL is initialized once per globe using Three.js. Unlike D3.js, the render function is is called 60 frames per second using RequestAnimationFrame. Since each property of the globe is evaluated per frame, any change will be reflected immediately.

← Prev    Next →

No comments:

Post a Comment