Chapter 05Areas and Area Radials

In this tutorial we will discuss how to draw areas and area radials.

Areas

D3 provides d3.area(), a method that returns an area generator that is used to create areas.

An area is an enclosure area of a graph using an x value and minimum and maximum y values. The area generator will shade everything between the minimum and maximum y points, provided you assign the fill attribute with a color.

Like lines, areas use points to make their graphs and we pass in an array of data for the area generator to compute points.

However unlike lines, areas make two points for every set of data passed into it. The first point we will be using the lower bounds of the graph and the second point we will use as the upper bounds.

We will use these methods to create the following area:

<script>
 var data = [
    {x: 0, y: 0},
    {x: 1, y: 3},
    {x: 2, y: 12},
    {x: 3, y: 8},
    {x: 4, y: 17},
    {x: 5, y: 15},
    {x: 6, y: 20}];

   var xScale = d3.scaleLinear().domain([0, 6]).range([25, 175]);
   var yScale = d3.scaleLinear().domain([0,20]).range([175, 25]);

   var area = d3.area()
      .x(d => xScale(d.x))
      .y0(yScale(0))
      .y1(d => yScale(d.y));

   d3.select("#demo1")
    .append("path")
    .attr("d", area(data))
    .attr("fill", "red")
    .attr("stroke", "black");
</script>

<svg id="demo1" width="200" height="200"></svg>

For this we will be using the same array of data and scales that we used with lines:

var data = [
    {x: 0, y: 0},
    {x: 1, y: 3},
    {x: 2, y: 12},
    {x: 3, y: 8},
    {x: 4, y: 17},
    {x: 5, y: 15},
    {x: 6, y: 20}];

var xScale = d3.scaleLinear().domain([0, 6]).range([25, 175]);
var yScale = d3.scaleLinear().domain([0,20]).range([175, 25]);

Next, we will create an area generator and set the x, y0 (first point), and y1 (second point) accessors.

We will make y0 (the lower bound or first point) at the graphs bottom, and y1 (the upper bounds or second point) to be from the data we pass in:

var area = d3.area()
      .x(d => xScale(d.x))
      .y0(yScale(0))
      .y1(d => yScale(d.y));

And finally, just like we did with lines, we will append a path element and call area(data) in our d attribute. However this time we need to make sure to set a color in our fill attribute to show the area filled in.

d3.select("#demo")
    .append("path")
    .attr("d", area(data))
    .attr("fill", "red")
    .attr("stroke", "black");

Setting y0

Instead of having the lower bounds be at the bottom throughout, we can change the y0 accessor to be a value relating to our data:

var area = d3.area()
      .x(d => xScale(d.x))
      .y0(d => yScale(d.y / 3))
      .y1(d => yScale(d.y));
<script>
 var data = [
    {x: 0, y: 0},
    {x: 1, y: 3},
    {x: 2, y: 12},
    {x: 3, y: 8},
    {x: 4, y: 17},
    {x: 5, y: 15},
    {x: 6, y: 20}];

   var xScale = d3.scaleLinear().domain([0, 6]).range([25, 175]);
   var yScale = d3.scaleLinear().domain([0,20]).range([175, 25]);

   var area = d3.area()
      .x(d => xScale(d.x))
      .y0(d => yScale(d.y / 3))
      .y1(d => yScale(d.y));

   d3.select("#demo2")
    .append("path")
    .attr("d", area(data))
    .attr("fill", "red")
    .attr("stroke", "black");
</script>

<svg id="demo2" width="200" height="200"></svg>

Setting x0 and x1

As we have used x, y0, and y1 to make points in a left-to-right manner, we can use y, x0, and x1 to make points in an bottom-to-top manner.

We can treat using y, x0, and x1 as rotating the area 90 degrees clockwise so that our y0 (lower bounds) turns into our `x0’ (right-most bounds).

All we need to change is the accessors in our area generator and the xScale:

var xScale = d3.scale.Linear().domain([0,6]).range([175, 25]);

var area = d3.area()
      .y(d => xScale(d.x))
      .x0(d => yScale(d.y / 3))
      .x1(d => yScale(d.y));

Note: It may seem weird to use the xScale and d.x in our y accessor, we are doing this so it acts like a rotation of our original data without renaming everything.

Also, because of where the origin point is for the area, we have to flip the ranges of the xScale to get this to work as a 90 degree rotation.

<script>
 var data = [
    {x: 0, y: 0},
    {x: 1, y: 3},
    {x: 2, y: 12},
    {x: 3, y: 8},
    {x: 4, y: 17},
    {x: 5, y: 15},
    {x: 6, y: 20}];

   var xScale = d3.scaleLinear().domain([0, 6]).range([175, 25]);
   var yScale = d3.scaleLinear().domain([0,20]).range([175, 25]);

   var area = d3.area()
      .y(d => xScale(d.x))
      .x0(d => yScale(d.y / 3))
      .x1(d => yScale(d.y));

   d3.select("#demo3")
    .append("path")
    .attr("d", area(data))
    .attr("fill", "red")
    .attr("stroke", "black");
</script>

<svg id="demo3" width="200" height="200"></svg>

Curve and Defined

Just like with lines we have areas have curve and defined accessors:

This is a previous area we made with a defined and curve definition that we have used in lines:

var area = d3.area()
      .x(d => xScale(d.x))
      .y0(yScale(0))
      .y1(d => yScale(d.y))
      .curve(d3.curveBasis)
      .defined((d,i) => (i != 4);
<script>
 var data = [
    {x: 0, y: 0},
    {x: 1, y: 3},
    {x: 2, y: 12},
    {x: 3, y: 8},
    {x: 4, y: 17},
    {x: 5, y: 15},
    {x: 6, y: 20}];

   var xScale = d3.scaleLinear().domain([0, 6]).range([25, 175]);
   var yScale = d3.scaleLinear().domain([0,20]).range([175, 25]);

   var area = d3.area()
     .x(d => xScale(d.x))
     .y0(d => yScale(d.y / 3))
     .y1(d => yScale(d.y))
     .curve(d3.curveBasis)
     .defined((d,i) => (i != 4));

   d3.select("#demo5")
    .append("path")
    .attr("d", area(data))
    .attr("fill", "red")
    .attr("stroke", "black");
</script>

<svg id="demo5" width="200" height="200"></svg>

Area Radials

Area Radials work like Line Radials, but have some additional accessors that work like d3.arc().

d3.areaRadial() - returns a area radial generator for creating radial lines. Area Radials work similarly to Line Radials, in that they convert an area into a radial by using the x and y to turn into an angle and radius.

If you recall the accessors from d3.arc(), you will find Area Radials include many of the same accessors, but what we plug into them is what matters.

We will make an area radial out of one of our previous examples. For this example the x’s will turn into the angles and the y’s will turn into the inner/outer radii.

To start we will change our ranges to work with the angles and radii:

var xScale = d3.scaleLinear().domain([0, 6]).range([0, 2 * Math.PI]);
var yScale = d3.scaleLinear().domain([0,20]).range([90,30]);

Next we will convert the area accessors into the new area radial accessors:

.x()  => .angle()
.y0() => .innerRadius()
.y1() => .outerRadius()

Addition accessors not used in this example: .y() => .radius() .x0() => .startAngle() .x1() => .endAngle()
<script>
 var data = [
    {x: 0, y: 0},
    {x: 1, y: 3},
    {x: 2, y: 12},
    {x: 3, y: 8},
    {x: 4, y: 17},
    {x: 5, y: 15},
    {x: 6, y: 20}];

   var xScale = d3.scaleLinear().domain([0, 6]).range([0, 2 * Math.PI]);
   var yScale = d3.scaleLinear().domain([0,20]).range([90,30]);

   var areaRadial = d3.areaRadial()
     .angle(d => xScale(d.x))
     .innerRadius(d => yScale(d.y / 3))
     .outerRadius(d => yScale(d.y))

   d3.select("#demo6")
    .select("g")
    .append("path")
    .attr("d", areaRadial(data))
    .attr("fill", "red")
    .attr("stroke", "black");
</script>

<svg id="demo6" width="200" height="200">
<g transform="translate(100,100)"></g>
</svg>

We can also use .curve() and .defined() with area radials exactly like we did for areas.

<script>
 var data = [
    {x: 0, y: 0},
    {x: 1, y: 3},
    {x: 2, y: 12},
    {x: 3, y: 8},
    {x: 4, y: 17},
    {x: 5, y: 15},
    {x: 6, y: 20}];

   var xScale = d3.scaleLinear().domain([0, 6]).range([0, 2 * Math.PI]);
   var yScale = d3.scaleLinear().domain([0,20]).range([90,30]);

   var areaRadial = d3.areaRadial()
     .angle(d => xScale(d.x))
     .innerRadius(d => yScale(d.y / 3))
     .outerRadius(d => yScale(d.y))
     .curve(d3.curveBasis)
     .defined((d,i) => (i != 4));

   d3.select("#demo7")
    .select("g")
    .append("path")
    .attr("d", areaRadial(data))
    .attr("fill", "red")
    .attr("stroke", "black");
</script>

<svg id="demo7" width="200" height="200">
<g transform="translate(100,100)"></g>
</svg>