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
xxxxxxxxxx
<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="https://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>
Modified http://d3js.org/d3.v3.min.js to a secure url
https://d3js.org/d3.v3.min.js