A static beeswarm plot implemented using d3-force’s collision constraint. A Voronoi overlay improves hover interaction.
forked from mbostock's block: Beeswarm
Added new Data, Legend, Colors Tittle
xxxxxxxxxx
<meta charset="utf-8">
<link href="https://fonts.googleapis.com/css?family=Merriweather" rel="stylesheet">
<style>
body {
font-family: 'Merriweather', serif;
}
a {
color: white;
}
div.tooltip {
position: absolute;
text-align: center;
padding: 2px;
font: 12px;
border: 0px;
border-radius: 10px;
}
.axis path,
.axis line {
fill: none;
stroke: lightsteelblue;
shape-rendering: geometricPrecision;
}
.cells path {
fill: none;
pointer-events: all;
}
.cells :hover circle {
fill: red;
}
</style>
<h1>Data Breaches</h1>
<h2>Number of stolen Records over Year</h2>
<svg width="1000" height="800"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {top: 60, right: 350, bottom: 110, left: 100},
margin2 = {top: 60, right: 590, bottom: 100, left: 400},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
width2 = +svg.attr("width") - margin2.right;
var formatValue = d3.format(",d");
var parseTime = d3.timeParse("%Y");
var x = d3.scaleTime().range([0, width]),
y = d3.scaleLinear().range([0, height])
y2 = y.copy();
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + 20 + "," + 58 + ")");
d3.csv("Data_Breaches.csv", type, function(error, data) {
if (error) throw error;
x.domain(d3.extent(data, function(d) { return d.Year; }));
y.domain(d3.extent(data, function(d) { return d.RecordsStolen; }));
y2.domain(d3.extent(data, function(d) { return d.RecordsStolen; }));
var simulation = d3.forceSimulation(data)
.force("x", d3.forceX(function(d) { return x(d.Year); }).strength(1))
.force("y", d3.forceY(function(d) { return y(d.RecordsStolen); }).strength(1))
.force("collide", d3.forceCollide(4))
.stop();
for (var i = 0; i < 120; ++i) simulation.tick();
var xAxis = d3.axisTop(x),
yAxis = d3.axisLeft(y).ticks(20, ".0s");
focus.append("g")
.attr("class", "axis axis--x")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
var cell = g.append("g")
.attr("class", "cells")
.selectAll("g").data(d3.voronoi()
.extent([[-margin.left, -margin.top], [width + margin.right, height + margin.top]])
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.polygons(data)).enter().append("g");
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// set the colour scale
var color = d3.scaleOrdinal(d3.schemeCategory20);
cell.append("circle")
.attr('class', 'dot')
.attr("r", 3)
.attr("cx", function(d) { return d.data.x; })
.attr("cy", function(d) { return d.data.y; })
.style("fill", function(d) { return color(d.data.MethodOfLeak); });
cell.on("mouseover", function(d) {
div.transition()
.duration(400)
.style("background-color", color(d.data.MethodOfLeak) )
.style("opacity", .8);
div.html(
'<a href= "'+d.data.Source+'" target="_blank">' + //with a link
d.data.Entity +
"</a>" +
"<br/>Records: <b>" + numberWithCommas(d.data.RecordsStolen)+
"</b><br/>Method Of Leak: <b>" + d.data.MethodOfLeak +"</b>")
.style("left", (d3.event.pageX+10) + "px")
.style("top", (d3.event.pageY - 28) + "px");
});
// draw legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(120,"+height/2+")");
var legendItem = legend.selectAll(".legendItem")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(220," + i * 20 + ")"; });
// draw legend colored rectangles
legendItem.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
// draw legendItem text
legendItem.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d;})
});
focus.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Records Stolen");
svg.append("text")
.attr("transform",
"translate(" + ((width + margin.left)/2) + " ," + 30 + ")")
.style("text-anchor", "middle")
.text("Year");
function type(d) {
d.Year = parseTime(d.Year);
if (!d.RecordsStolen) return;
d.RecordsStolen = +d.RecordsStolen;
return d;
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
</script>
https://d3js.org/d3.v4.min.js