Skip to content

Instantly share code, notes, and snippets.

@enjoylife
Forked from mbostock/.block
Created November 5, 2016 19:09
Show Gist options
  • Save enjoylife/f41418a234c9b801b0c0b393ffe7f239 to your computer and use it in GitHub Desktop.
Save enjoylife/f41418a234c9b801b0c0b393ffe7f239 to your computer and use it in GitHub Desktop.
Inline Labels
license: gpl-3.0

This example shows how to implement Ann K. Emery’s technique of placings labels directly on top of a line in D3 4.0 Alpha.

To construct the multi-series line chart, the data is first transformed into separate arrays for each series. (The series names are automatically derived from the columns in the TSV file, thanks to a new dsv.parse feature.)

var series = data.columns.slice(1).map(function(key) {
  return data.map(function(d) {
    return {
      key: key,
      date: d.date,
      value: d[key]
    };
  });
});

A label is rendered for each point in each series. Beneath this label, a white rectangle is added, whose size and position is computed automatically using element.getBBox plus a little bit of padding. The resulting label is thus legible. The last label for each series gets an additional tspan to show the series name.

date Apples Bananas
2009 130 40
2010 137 58
2011 166 97
2012 154 117
2013 179 98
2014 187 120
2015 189 84
<!DOCTYPE html>
<meta charset="utf-8">
<style>
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke-width: 1.5px;
}
.label {
text-anchor: middle;
}
.label rect {
fill: white;
}
.label-key {
font-weight: bold;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.9.min.js"></script>
<script>
var parseTime = d3.timeParse("%Y");
var svg = d3.select("svg");
var margin = {top: 30, right: 50, bottom: 30, left: 30},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
labelPadding = 3;
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.requestTsv("data.tsv", function(d) {
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
return d;
}, function(error, data) {
if (error) throw error;
var series = data.columns.slice(1).map(function(key) {
return data.map(function(d) {
return {
key: key,
date: d.date,
value: d[key]
};
});
});
var x = d3.scaleTime()
.domain([data[0].date, data[data.length - 1].date])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(series, function(s) { return d3.max(s, function(d) { return d.value; }); })])
.range([height, 0]);
var z = d3.scaleCategory10();
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var serie = g.selectAll(".serie")
.data(series)
.enter().append("g")
.attr("class", "serie");
serie.append("path")
.attr("class", "line")
.style("stroke", function(d) { return z(d[0].key); })
.attr("d", d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); }));
var label = serie.selectAll(".label")
.data(function(d) { return d; })
.enter().append("g")
.attr("class", "label")
.attr("transform", function(d, i) { return "translate(" + x(d.date) + "," + y(d.value) + ")"; });
label.append("text")
.attr("dy", ".35em")
.text(function(d) { return d.value; })
.filter(function(d, i) { return i === data.length - 1; })
.append("tspan")
.attr("class", "label-key")
.text(function(d) { return " " + d.key; });
label.append("rect", "text")
.datum(function() { return this.nextSibling.getBBox(); })
.attr("x", function(d) { return d.x - labelPadding; })
.attr("y", function(d) { return d.y - labelPadding; })
.attr("width", function(d) { return d.width + 2 * labelPadding; })
.attr("height", function(d) { return d.height + 2 * labelPadding; });
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment