title | layout | include_vega | visible_lec | visible_n |
---|---|---|---|---|
Lecture 12, Part 2 |
lecture |
true |
true |
true |
We're going to learn a very small bit of d3.js.
The easiest way to utilize D3 is through observablehq.com:
d3 = require("d3@5");
although it is straightforward to include the necessary code in an HTML page:
<script src="https://d3js.org/d3.v5.min.js"></script>
The basic concepts here we will convey:
.select()
and.selectAll()
.enter()
.attr()
and.style()
d3.scaleLinear()
When we manipulate items in d3, we connect the concept of a data item to an element in a document.
Typically, this will be an element in an SVG -- for instance, a line
,
circle
, rect
or text
element.
Our typical workflow:
- Select all items of a specific criteria
- Bind these items to a set of data
- Update, append or remove items based on their corresponding item.
We will very frequently run into the case where we call something, and supply a
function to it, rather than supplying a value. For instance, both of these
calls to attr
are valid in a typical d3 workflow:
.attr("cx", 1.0)
// or
.attr("cx", d => d.x)
In one, we are setting the value static; in the other, we base the value on the datapoint supplied.
For our first exploration, let's just make some circles. I will demonstrate this in observable, but here is the key snippet of code:
var dataset = [ {'x': 100, 'y': 200, 'radius': 15},
{'x': 150, 'y': 300, 'radius': 30} ];
svg.selectAll("circle").data(dataset).enter()
.append("circle")
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => d.radius)
.style("fill", "black");
What does this do?
We will mostly use the objects
rect
-- which hasx
,y
,width
andheight
circle
-- which hascx
,cy
, andr
line
-- which hasx1
,x2
,y1
andy2
.
Each of these objects can be controlled in style, appearance, position, etc, according to standard SVG and CSS rules.
Let's experiment!
To map from a given range to a different range in a linear fashion, we can construct a linear scale:
var xScale = d3.scaleLinear().range([0, 100]).domain([0.0, 1.0]);
This is now an object we can use to map the values 0 .. 1 to 0 .. 100. This is useful for, among other things, computing the position of a given value:
.attr("cx", d => xScale(d.x))
d3.axisBottom(xScale)
can be used to create ticks and axes; however, it
requires manual translation using the transform
attribute, using something
like .attr("transform", "translate(0, 30)")
. This then brings up our concept
of margins, width, height, and the like! How can we manage these?
Let's try it out!