Skip to content

Instantly share code, notes, and snippets.

@cse4qf
Last active June 22, 2018 12:25
Show Gist options
  • Save cse4qf/445036c05a7b819995be5bc0edb25a5a to your computer and use it in GitHub Desktop.
Save cse4qf/445036c05a7b819995be5bc0edb25a5a to your computer and use it in GitHub Desktop.
adding tooltips to brush & zoom
license: mit

This serves as an example of how to add a zoomable feature to a plot using a "brush". By selecting a window width in the bottom graph, the top graph is redrawn with that window extent. For this data this may not be all that interesting, but in principle it is an interesting technique.

forked from natemiller's block: Multiple Line Climate Plot with Zoom

We can make this file beautiful and searchable if this error is corrected: It looks like row 2 should actually have 4 columns, instead of 3. in line 1.
date,red,green,blue
20150928,774,600.382
20160505,744,601,396
20160823,798,706,553
20170410,1021,965,1088
We can make this file beautiful and searchable if this error is corrected: It looks like row 2 should actually have 4 columns, instead of 3. in line 1.
date,red,green,blue
20150928,774,600.382
20160505,744,601,396
20160823,798,706,553
20170410,1021,965,1088
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.y.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.line {
fill: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
//setting up margin space around
var margin = {top: 10, right: 10, bottom: 100, left: 40},
margin2 = {top: 430, right: 10, bottom: 20, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
//default 10 color palette
var color = d3.scale.category10();
var parseDate = d3.time.format("%Y%m%d").parse;
//setting up actual size of graph within the margins
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
//x and y axis formatting
var xAxis = d3.svg.axis().scale(x).orient("bottom"),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left");
//thickness of graph lines x2
var brush = d3.svg.brush()
.x(x2)
.on("brush", brush);
var line = d3.svg.line()
.defined(function(d) { return !isNaN(d.bande); }) // valid value
.interpolate("cubic") //makes graph slightly smoother
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.bande); }); // x and y var for line
//refers to graph underneath with the scrollbar
var line2 = d3.svg.line()
.defined(function(d) { return !isNaN(d.bande); })
.interpolate("cubic")
.x(function(d) {return x2(d.date); })
.y(function(d) {return y2(d.bande); });
//appending body to svg element to apply transformations
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
/*unfinished: adapting tooltips from wdickerson's block: Tooltip on a Multi-line Chart */
/*
const tooltip = d3.select('#tooltip')
const tooltipLine = svg.append('line')
//box with values in it
var tipBox = svg.append('rect')
.attr('width', width)
.attr('height', height)
.attr('opacity', 0)
.on('mousemove', drawTooltip)
.on('mouseout', removeTooltip);
//controlling when box isn't displayed
function removeTooltip() {
if (tooltip) tooltip.style('display', 'none');
if (tooltipLine) tooltipLine.attr('stroke', 'none');
}
function drawTooltip() {
const year = Math.floor((x.invert(d3.mouse(tipBox.node())[0]) + 5) / 10) * 10;
}
*/
//margins of bottom graph
var focus = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
//parsing in file
d3.csv("bandes - Sheet1.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var sources = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, bande: +d[name]};
})
};
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([d3.min(sources, function(c) { return d3.min(c.values, function(v) { return v.bande; }); }),
d3.max(sources, function(c) { return d3.max(c.values, function(v) { return v.bande; }); }) ]);
x2.domain(x.domain());
y2.domain(y.domain());
//adding focus lines to mini bottom chart to controll scrollbar
var focuslineGroups = focus.selectAll("g")
.data(sources)
.enter().append("g");
var focuslines = focuslineGroups.append("path")
.attr("class","line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) {return color(d.name);})
.attr("clip-path", "url(#clip)");
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis);
var contextlineGroups = context.selectAll("g")
.data(sources)
.enter().append("g");
var contextLines = contextlineGroups.append("path")
.attr("class", "line")
.attr("d", function(d) { return line2(d.values); })
.style("stroke", function(d) {return color(d.name);})
.attr("clip-path", "url(#clip)");
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 6);
});
//brush handling function
function brush() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.selectAll("path.line").attr("d", function(d) {return line(d.values)});
focus.select(".x.axis").call(xAxis);
focus.select(".y.axis").call(yAxis);
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment