A graphical toolkit for visualization
Protovis
Overview
Examples
Documentation
Download
Index

Anchors

Anchors are well-named positions on the perimeter of an existing mark. They can be used to place labels, dots, lines, and other adjacent marks.

See also: pv.Anchor API reference

Anchors with Area

For example, say we want to emphasize the edge of an area. While this can be done using the strokeStyle parameter, it may not produce the desired effect; this strokes the entire perimeter of the area, whereas we might want only the upper boundary of the area stroked. Instead, we can succinctly deriving a line, and optionally a dot, using anchors:

new pv.Panel()
    .width(150)
    .height(150)
  .add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .bottom(0)
    .height(function(d) d * 80)
    .left(function() this.index * 25)
  .anchor("top").add(pv.Line)
    .strokeStyle("black")
  .add(pv.Dot)
  .root.render();

Horizontally-oriented areas support a top anchor and a bottom anchor, shown here in green and red respectively:

var vis = new pv.Panel()
    .width(150)
    .height(150);

var area = vis.add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .bottom(10)
    .height(function(d) d * 60)
    .left(function() this.index * 20 + 10);

area.anchor("top").add(pv.Dot)
    .fillStyle("green");

area.anchor("bottom").add(pv.Dot)
    .fillStyle("red");

vis.render();

If we added another area to the top anchor, we could reproduce the stacked area chart shown previously. Alternatively, we can add the new area to the bottom anchor, producing a different visualization:

var vis = new pv.Panel()
    .width(150)
    .height(150);

var area = vis.add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .bottom(75)
    .height(function(d) d * 30)
    .left(function() this.index * 22 + 10)
    .fillStyle(pv.Colors.category20().by(pv.child));

area.anchor("bottom")
    .extend(area)
  .add(pv.Area)
    .data([.4, .2, .8, 1.2, 1.5, 1.1, .8]);

vis.render();

Note that the bottom anchor need not be a straight line. If the original area has a variable bottom, the bottom anchor will adjust appropriately. This can be used to produce yet another style of stacked area chart, sometimes referred to as a streamgraph:

var vis = new pv.Panel()
    .width(150)
    .height(150);

var area = vis.add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .bottom(function(d) d * 10 + 55)
    .height(function(d) d * 30)
    .left(function() this.index * 22 + 10)
    .fillStyle(pv.Colors.category20().by(pv.child));

area.anchor("bottom")
    .extend(area)
  .add(pv.Area)
    .data([.4, .2, .8, 1.2, 1.5, 1.1, .8]);

vis.render();

Of course, it may be better to use the stack layout rather than use anchors to achieve this design.

Vertically-oriented areas similarly provide two anchors, right and left. This time we demonstrate the use of anchors with lines rather than dots:

var vis = new pv.Panel()
    .width(150)
    .height(150);

var area = vis.add(pv.Area)
    .data([1, 1.2, 1.7, 1.5, .7, .5, .2])
    .left(10)
    .width(function(d) d * 60)
    .bottom(function() this.index * 20 + 10);

area.anchor("right").add(pv.Line).strokeStyle("green");
area.anchor("left").add(pv.Line).strokeStyle("red");

vis.render();

While it is always possible to specify the location of related marks explicitly, this section shows that anchors are usually the most convenient choice.

Anchors with Bar

Here is a reimplementation of an example from the label documentation, using the top anchor:

new pv.Panel()
    .width(150)
    .height(150)
  .add(pv.Bar)
    .data([1, 1.2, 1.7, 1.5, .7, .2])
    .bottom(0)
    .width(19)
    .height(function(d) d * 70)
    .left(function() this.index * 24 + 5)
  .anchor("top").add(pv.Label)
    .textStyle("#fff")
  .root.render();

What took a handful of lines of code earlier now takes only one line. Note that in addition the position, anchors can specify reasonable defaults for text placement as well. The top anchor for a bar uses centered text a top baseline. Any number of labels (or other types of marks) can be associated with any anchor. The following diagram shows four of the available anchors for bars:

var vis = new pv.Panel().width(150).height(150);
var bar = vis.add(pv.Bar);
bar.anchor("top").add(pv.Label).text("top");
bar.anchor("left").add(pv.Label).text("left");
bar.anchor("right").add(pv.Label).text("right");
bar.anchor("bottom").add(pv.Label).text("bottom");
vis.render();

Anchors with Dot

The behavior for dots is equivalent to bars:

var vis = new pv.Panel().width(150).height(150);
var dot = vis.add(pv.Dot).left(75).top(75).size(1000);
dot.anchor("top").add(pv.Label).text("top");
dot.anchor("left").add(pv.Label).text("left");
dot.anchor("right").add(pv.Label).text("right");
dot.anchor("bottom").add(pv.Label).text("bottom");
dot.anchor("center").add(pv.Label).text("center");
vis.render();

Here is an example of using the right anchor on a scatterplot:

var vis = new pv.Panel()
    .width(150)
    .height(150);

vis.add(pv.Dot)
    .data([[.1, 1], [.5, 1.2], [.9, 1.7], [.2, 1.5], [.7, 2.2]])
    .left(function(d) d[0] * 100)
    .bottom(function(d) d[1] * 50)
  .anchor("right").add(pv.Label);

vis.render();

Anchors with Wedge

Similarly, labels on a donut chart are easy with anchors, when they would be quite tricky to implement by hand. In this example note that we forego the use of pv.normalize and instead precompute the scale factor, so that the label displays the original data value:

var data = [1, 1.2, 1.7, 1.5, .7], sum = pv.sum(data);

var vis = new pv.Panel()
    .width(150)
    .height(150);

vis.add(pv.Wedge)
    .data(data)
    .left(75)
    .bottom(75)
    .innerRadius(50)
    .outerRadius(70)
    .angle(function(d) d / sum * 2 * Math.PI)
  .anchor("center").add(pv.Label);

vis.render();
Copyright 2010 Stanford Visualization Group