# Chapter 05Areas and Area Radials

In this tutorial we will discuss how to draw areas and area radials. Areas can be used on their own or with stacks.

## Areas

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

An area is a path that can be thought of as the enclosure between two lines. These two “lines” are created by setting an `x` value, which is the same for each point on the lines, and two `y` values, `y0` and `y1`, which are the bottom lines `y` position and the top lines `y` position respectively.

Like lines, we have an area generator, d3.area() which has accessors we can call to set how the area is generated. We pass data into an area generator and it returns back a `string` that can be used inside a `path` `d` attribute.

• area(data) - Invokes the area generator using the array of data passed into it, returns back a `string` that can be used by the `d` attribute of a `path`.

However unlike lines, areas make two points for every set of data passed into it. These points create the upper and lower “lines” or bounds that our area encloses. The first point uses `y0` and is used as the lower bounds of the graph. The second point uses `y1` and is used as the upper bounds. Both points use the same `x` position.

• area.x([x]) - Sets the `x` accessor to the argument passed to it which may be either a number or a function that returns a number

• area.y0([y]) - Sets the `y0` accessor to the argument passed to it which may be either a number or a function that returns a number, this is the first point or our lower bounds

• area.y1([y]) - Sets the `y1` accessor to the argument passed to it which may be either a number or a function that returns a number, this is the second point or our upper bounds

For our examples 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` (lower bounds), and `y1` (upper bounds) accessors:

```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");
```

In Figure 1 we use these code snippets to create an area. We also set the fill of the area to red.

```<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>```

### Setting the lower bounds

Many times we will not want our lower bounds to be 0. Instead of setting `y0` to `0` we can instead set it to whatever custom value, property, or method we choose.

In Figure 2 we set `y0` to be one-third of `y1`.

```<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`

Setting `x`, `y0`, and `y1` is useful for creating left-to-right oriented areas, but to create a bottom-to-top oriented area we use different accessors.

The new accessors `y`, `x0`, and `x1` work very similarly to the previous accessors. Instead of `y0` and `y1` being the lower/upper bounds, we now have `x0` and `x1` to be our right/left bounds. When using the new accessors (along with an updated scale) we are effectively rotating our area 90 degrees from the original area.

• area.y([y]) - Sets the `y` accessor to the argument passed to it which may be either a number or a function that returns a number

• area.x0([x]) - Sets the `x0` accessor to the argument passed to it which may be either a number or a function that returns a number, this is our right-most bounds

• area.x1([x]) - Sets the `x1` accessor to the argument passed to it which may be either a number or a function that returns a number, this is our left-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 our data.

In Figure 3 we use `y`, `x0`, and `x1` to make a new area where it is orientated bottom-to-top with the baseline on the right side.

```<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:

• area.defined([defined]) - Takes as an argument a boolean or a function that returns a boolean. For each point, if the value returned is true, the point is kept, otherwise it is removed or omitted.

• area.curve([curve]) - Sets the curve the area uses.

In Figure 4 we use the following code to omit the fifth point and apply a curve to the area.

```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("#demo4")
.append("path")
.attr("d", area(data))
.attr("fill", "red")
.attr("stroke", "black");
</script>

<svg id="demo4" width="200" height="200"></svg>```

D3.js also has an area radial generator that works like line radial.

Since `d3.areaRadial` is an area, it can still be thought of as being the enclosure between two line radials. These line radials will have identical angles (formerly `x`) for each of their respective points, and now instead of `y0` and `y1` separating our lines, `innerRadius` and `outerRadius` will.

In Figure 5 we create an area radial by converting the scales and `d3.area` accessors into `d3.areaRadial` compatible ones.

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

```var angleScale = d3.scaleLinear().domain([0, 6]).range([0, 2 * Math.PI]);
```

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

```.x()  => .angle()
```
```<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]);

.angle(d => xScale(d.x))

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

<svg id="demo5" 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.

Figure 6 is identical to Figure 5 except that a curve is applied and the fifth point is omitted.

```<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]);

.angle(d => xScale(d.x))
.curve(d3.curveBasis)
.defined((d,i) => (i != 4));

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

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

## Canvasses

Our examples in the section all use an `SVG` as the graphic medium. If we want to work with a Canvas instead, we just pass in the `context` of a canvas into the `.context()` accessor of area or areaRadial.

In Figure 7 we use the same areas in Figure 2 and 5, but apply them to a canvas instead. Note that we have to use additional CanvasPathMethods for our graphics to display.

```<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 angleScale = d3.scaleLinear().domain([0, 6]).range([0, 2 * Math.PI]);

var areaContext = d3.select("#demo7a").node().getContext("2d");

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

areaContext.beginPath();
areaContext.strokeStyle = "black";
areaContext.fillStyle = "red";
area(data);
areaContext.fill();
areaContext.stroke();

.angle(d => angleScale(d.x))

</script>
<canvas id="demo7a" width=200 height=200></canvas>
<canvas id="demo7b" width=200 height=200></canvas>```

## d3.arealabel

Many times it is useful to the viewers of our visualizations to have labels indicating what every line or area represents. Luckily, Curran Kelleher created d3-area-label to dynamically add text labels inside of an area.

This module is not apart of the main D3.js files so we will have to separately add it to our page:

```<script src="https://unpkg.com/[email protected]/build/d3-area-label.js"></script>
```

`d3.arealabel` is a generator with many accessors on it to determine size, conditions, and format of the labels to add. To create a label we need to either pass the generator an area, or redefine an area to use. Note that `d3.areaLabel` only works on left-to-right areas (areas that use `x`, `y0`, and `y1`).

```var areaLabel = d3.areaLabel().area(areaGen);
```
• d3.areaLabel([area]) - Constructs an area label generator and calls `areaLabel.area(area)`.

• areaLabel.area(area) - Sets the `x`, `y0`, and `y1` accessors of the areaLabel to be the same as an instance of `d3.area`.

• areaLabel(data) - Calls the area label generator with the passed in data. Note that this data should be in the form [`area1`, `area2`, ...] where each `area#` is all the data points for an area. For example when working with a single area: `areaLabel([areaData])`.

`d3.areaLabel` works by first finding the bounding box or aspect ratio around a particular `text` element. Next, `d3.areaLabel` will use a bisection method to find the maximum size rectangle with the same aspect ratio as the text that fits within an area. Finally, `d3.areaLabel` modifies the `transform` attribute of a `text` element, so it returns a string that can be used when modifying the `transform` that properly places the label where it should be.

```selection
.append("text")
.text("Area")
.attr("transform", areaLabel([dataSet]);
```

In Figure 8 we apply a label with the text “Area” to the same area we have been using above. Try changing the text to see how `d3.areaLabel` dynamically changes the positioning and size.

```<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("#demo8")
.append("path")
.attr("d", area(data))
.attr("fill", "red")
.attr("stroke", "black");

var areaLabel = d3.areaLabel(area);

d3.select("#demo8")
.data([data])
.append("text")
.text("Area")
.attr("transform", areaLabel);
</script>
<svg id="demo8" width=200 height=200></svg>```

For most cases we will already have an instance of a `d3.area` generator so we can use `d3.areaLabel([area])`, however if for some reason we do not have an area generator, need to redefine an accessor, or get an accessor `d3.areaLabel` provides us with the following additional methods:

• areaLabel.x(x) - If `x` is specified, sets the areaLabel’s `x` accessor to the value/function of `x`. Otherwise returns the current `x` accessor.

• areaLabel.y0(y0) - If `y0` is specified, sets the areaLabel’s `y0` accessor to the value/function of `y0`. Otherwise returns the current `y0` accessor.

• areaLabel.y1(y1) - If `y1` is specified, sets the areaLabel’s `y1` accessor to the value/function of `y1`. Otherwise returns the current `y1` accessor.

### Format

When working with many areas (such as in the next section on stacks) we may have areas that are very thin. Applying a label to these thin areas is not always the best idea since we will not be able to actually read them. We can use `areaLabel.minHeight` to exclude labels that are smaller than a specified height.

An example of `areaLabel.minHeight` can be seen in the next section, stacks.

### Accuracy

Sometimes `d3.areaLabel` may output unoptimized or inaccurate positions/scales. In these cases `d3.areaLabel` provides us with additional accessors to adjust how the placement and positioning is found.

When finding the maximum size rectangle, `d3.areaLabel` looks at a set number of `x` values as the leftmost side of the rectangle and goes right from this `x` position to find the largest rectangle. We can set what `x` values `d3.areaLabel` looks at if the default values produce inaccurate positions or if our visualizations take too long to load.

areaLabel.interpolate(interpolate) takes a boolean value and determines whether or not the area label generator will use linear interpolation to compute label positions.

If set to `false`, the only `x` positions that will be used as a left-most side of a rectangle will be the `x` values in the data set. If we have a large amount of evenly spaced `x` values in our data set, setting this to `false` works well.

If set to `true`, the area label generator will use a linear interpolation over the data sets `x` positions to find a set number (`interpolateResolution`) of coordinates.

For instance if we have the `x` values `[1, 2, 3, 4, 5]` in our data set and we set `interpolateResolution` to `10` then our area generator will try to find the maximum size rectangle with the left side of the rectangle at positions: `[1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5]`

For large evenly spaced data, setting `areaLabel.interpolate` to `false` will work well. However, setting this to `false` for smaller sets of data will not produce the best positioned labels.

Setting `areaLabel.interpolate` to `true` helps smaller data sets have better positioned labels. However when used on larger data sets it can be taxing on our computers, so if our visualizations are taking some time to load, setting to `false` may be a better option.

`areaLabel.interpolate` is `true` by default.

areaLabel.interpolateResolution(interpolateResolution) sets how many positions will be used as the left-most side of any rectangles checked for maximum size. It only works when `areaLabel.interpolate` is set to `true`.

`areaLabel.interpolateResolution` is `200` by default.

`areaLabel.interpolate` and `areaLabel.interpolateResolution` should really only be changed if we notice any oddities in the placements of our labels.

We can apply a padding to each of the sides of the text within its bounding box. When applying a padding, we should make sure to not use large paddings that make the label hard to read. It is also important to remember that each padding should be set to a value from 0 to 1; larger values will technically still work, but will result in text labels usually too small to read. The default value for each padding is 0.

```var areaLabel = d3.areaLabel([area1]).paddingLeft(5);
```

`d3.areaLabel` also provides us with the following shortcut accessors:

In Figure 9 we apply a padding to every side by using `areaLabel.padding`.

```<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("#demo9")
.append("path")
.attr("d", area(data))
.attr("fill", "red")
.attr("stroke", "black");