In D3GL globe, there are five data primitives:
Points, Shapes, Bars, Arcs,and
Painter. We will take
Pointsas an intro to primitives.
Customizing primitives for data
Just as each globe was an instance of a globe template which was tied to each data element, each point, shape, arc, etc. is an instance of a template. Just like with the globe template, you will either pass in fixed values that are shared across all instances or functions that will return customized values depending on the data element it takes as argument.
Let's take
Pointsas an example. First you would create a template that binds to data, as the following:
var points = globe.points() .data(function(k) { return datasets[k]; });As we discussed in the previous tutorial, the argument
kis bound on the globe level (i.e. per globe) and in this case is being used as the key to fetch a dataset, which is then bound to the primitive
Points.
We want to customize each point instance so that it represents each data element:
var points = globe.points() .data(function(k) { return datasets[k]; }) .radius(function(d) { return scaleRadius(d['precipitation']); }) .color(function(d) { return scaleColor(d['precipitation']); }) .latitude(function(d) { return d['latitude']; }) .longitude(function(d) { return d['longitude']; });The parameter
dused in the setter functions represents a data element in the dataset bound to
Points. In the above code snippet, the radius and color of each point is determined by the value of
d['precipitation']. The scaling functions
scaleRadiusand
scaleColortake the precipitation value and spit out an appropriately scaled radius in units of degrees, and color in "#
d3.scalethat is pretty useful for all types of scaling, including color gradients.
Demo
This demo marks the landings on Mars with
Points, using the color and size of each point to represent how recent the landing was: Landings on Mars
Implementation
As discussed in the previous tutorial, a data primitive for D3GL Globe is another closure within the
d3.gl.globeclosure.
Pointshas a rendering function, but unlike with the globe, this function is not invoked by the client. Instead, when
Pointsare added to the globe, the rendering function for
Pointsis pushed to the array of rendering functions that are called per frame by
d3.gl.globe.
In the rendering loop invoked with
.call(globe),a hidden canvas element that is programmatically created is passed in to the rendering functions for primitives, along with the datum the globe instance is bound to, and the WebGL environment:
// In rendering loop - called once per frame for(var i = 0; i < primitiveRenderingFunctions.length; i++){ primitiveRenderingFunctions[i]( webGLEnvironment, contextForHiddenCanvas, datumForGlobeInstance ); }Of course, it's JavaScript, not Objective-C, so the variable names are not actually this long.
In the rendering loop for
Points,the datum pass in is used to fetch the dataset. Then for each element in the dataset, a circle is drawn using
drawCircle()on the context that is passed in:
function points(gl, context, datum) { var dataset = fnData(datum); for (var i = 0; i < dataset.length; i++) { var elem = dataset[i]; // client-defined functions are used to fetch properties of each point var lat = fnLat(elem); var lon = fnLon(elem); // ... and so on drawCircle(context, plat, plon, pradius, color, strokeColor, lineWidth); } }After all rendering functions in queue are called and the overlay texture on the hidden canvas is complete, the entire scene is rendered via
gl.renderer.render(gl.scene, gl.camera);
The other primitives roughly use the same format, but there are differences. So stay tuned!
← Prev
No comments:
Post a Comment