This multi-line chart uses an invisible Voronoi tessellation to handle mouseover; the closest point to the mouse on any line is highlighted. Click the checkbox in the top-right to toggle the visibility of the Voronoi overlay.
forked from mbostock's block: Multi-Line Voronoi
forked from juanprq's block: Multi-Line Voronoi with colors
forked from jeremycflin's block: Multi-Line Voronoi with colors
xxxxxxxxxx
<meta charset="utf-8">
<style>
.axis--y path {
display: none;
}
.sites {
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
stroke-width: 1.5px;
}
.focus text {
text-anchor: middle;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
.voronoi path {
fill: none;
pointer-events: all;
}
.voronoi--show path {
stroke: red;
stroke-opacity: 0.2;
}
#form {
position: absolute;
top: 20px;
right: 30px;
}
</style>
<svg width="1160" height="600"></svg>
<label id="form" for="show-voronoi">
Show Voronoi
<input type="checkbox" id="show-voronoi" disabled>
</label>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var months,
monthKeys,
monthParse = d3.timeParse("%Y-%m");
var steps,
stepKeys;
var svg = d3.select("svg"),
margin = {top: 20, right: 30, bottom: 110, left: 40},
width = svg.attr("width") - margin.left - margin.right,
height = svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xScale = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.9);
var yScale = d3.scaleLinear()
.range([height, 0]);
var zScale = d3.scaleOrdinal(d3.schemeCategory10);
var voronoi = d3.voronoi()
.x(function(d) { return xScale(d.step); })
.y(function(d) { return yScale(d.value); })
.extent([[-margin.left, -margin.top], [width + margin.right, height + margin.bottom]]);
var line = d3.line()
.x(function(d) { return xScale(d.step); })
.y(function(d) { return yScale(d.value); })
.curve(d3.curveMonotoneX);
d3.csv("datanorth2_fullset.csv", type, function(error, data) {
if (error) throw error;
xScale.domain(data.columns.slice(1));
yScale.domain([0, 5500]);
//x.domain(d3.extent(months));
//y.domain([0, d3.max(data, function(c) { return d3.max(c.values, function(d) { return //d.value; }); })]).nice();
zScale.domain(data.map(function(d) { return d.origin; }))
var xAxis = d3.axisBottom(xScale);
var xAxisDraw = g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
xAxisDraw
// .call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
// g.append("g")
// .attr("class", "axis axis--x")
// .attr("transform", "translate(0," + height + ")")
// .call(d3.axisBottom(xScale));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(yScale).ticks(10))
.append("text")
.attr("x", 4)
.attr("y", 0.5)
.attr("dy", "0.32em")
.style("text-anchor", "start")
.style("fill", "#000")
.style("font-weight", "bold")
.text("Commuters");
g.append("g")
.attr("class", "sites")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("d", function(d) { d.line = this; return line(d.values); })
.style("stroke", function(d) { return zScale(d.origin); });
var focus = g.append("g")
.attr("transform", "translate(-100,-100)")
.attr("class", "focus");
focus.append("circle")
.attr("r", 3.5);
focus.append("text")
.attr("y", -10);
var voronoiGroup = g.append("g")
.attr("class", "voronoi");
voronoiGroup.selectAll("path")
.data(voronoi.polygons(d3.merge(data.map(function(d) { return d.values; }))))
.enter().append("path")
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; })
.on("mouseover", mouseover)
.on("mouseout", mouseout);
// data.map(function(d) { return console.log(d)})
// console.log(data)
// console.log(data.map(function(d) { return d.values; }))
d3.select("#show-voronoi")
.property("disabled", false)
.on("change", function() { voronoiGroup.classed("voronoi--show", this.checked); });
function mouseover(d) {
d3.select(d.data.site.line)
.style('stroke-width', 3)
.style('stroke', d3.hsl(zScale(d.data.site.origin)).brighter(1));
d.data.site.line.parentNode.appendChild(d.data.site.line);
focus.attr("transform", "translate(" + xScale(d.data.step) + "," + yScale(d.data.value) + ")");
focus.select("text").text(d.data.site.origin + ': ' + d.data.value);
}
function mouseout(d) {
d3.select(d.data.site.line)
.style("stroke-width", 1.5)
.style('stroke', zScale(d.data.site.origin));
focus.attr("transform", "translate(-100,-100)");
}
});
// function type(d, i, columns) {
// if (!months) monthKeys = columns.slice(1), months = monthKeys.map(monthParse);
// var c = {name: d.name.replace(/ (msa|necta div|met necta|met div)$/i, ""), values:
// null};
// c.values = monthKeys.map(function(k, i) { return {city: c, date: months[i], value: //d[k] / 100}; });
// return c;
// }
function type(d, i, columns) {
stepKeys = columns.slice(1); // steps = stepKeys; //.map();
var c = {origin: d.origin, values: null};
c.values = stepKeys.map(function(k, i) { return {site: c, step: stepKeys[i], value: d[k]}; });
return c;
}
</script>
https://d3js.org/d3.v4.min.js