Part of the video course: D3.js in Motion.
An example scatter plot with color legend using D3 and d3-legend.
This scatter plot shows the dataset of nuclear bomb test history. Originally published at Johnston's Archive - Nuclear Weapons, this small dataset records every nuclear bomb tests from 1945 to 2009. The original dataset have 6 countries' each year nuclear test. The raw data is not well organized in CSV format. So I use Python with Pandas to parse it into a proper format. Each row of the table represents an country's nuclear bomb test counts for that year.
In this scatter plot, Y axis is the count of the nuclear bomb test and X axis is representing year. Each circle is represent that year's total test for one country. Each country has different color and hover on the circle will display the actual count number for the nuclear test.
forked from curran's block: Stylized Scatter Plot with Color Legend
forked from YouthBread's block: Scatter Plot
forked from YouthBread's block: Scatter Plot
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<script src="https://d3js.org/d3.v4.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Open+Sans:400,400i,700" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js"></script>
<title>Scatter Plot</title>
<style>
#chart {
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
body {
margin: 0px;
}
.tick text, .legendCells text {
fill: #111111;
font-size: 10pt;
font-family: 'Open Sans', sans-serif;
}
.axis-label, .legend-label {
fill: #AAAAAA;
font-size: 10pt;
font-family: 'Open Sans', sans-serif;
}
.subtitle {
font-size: 34pt;
font-family: 'Open Sans', sans-serif;
alignment-baseline: middle;
fill: #001f3f;
}
circle:hover {
fill: #F012BE;
}
div.tooltip {
position: absolute;
text-align: center;
width: 150px;
height: 30px;
vertical-align: middle;
line-height: 30px;
font-family:'Open Sans', sans-serif;
background: #FFDC00;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
</head>
<body>
<div id="chart"></div>
<script>
const parseTime = d3.timeParse("%Y");
const xValue = d => parseTime(d.Year);
const xLabel = 'Year';
const yValue = d => d.Counts;
const yLabel = 'Bomb Counts';
const colorValue = d => d.Country;
const colorLabel = 'Countries';
const margin = { left: 120, right: 120, top: 20, bottom: 120 };
const xScale = d3.scaleTime();
const yScale = d3.scaleLinear();
const rScale = d3.scaleLinear();
const colorScale = d3.scaleOrdinal()
.range(d3.schemeCategory10);
var chartDiv = document.getElementById("chart");
var svg = d3.select(chartDiv).append("svg");
var data_g = svg.append('g');
var g = svg.append('g');
var xAxisG = g.append('g')
.attr('class', 'x-axis');
var yAxisG = g.append('g')
.attr('class', 'y-axis');
var colorLegendG = g.append('g')
.attr('class', 'color-legend');
var title = g.append('text')
.attr('class', 'subtitle');
var x_label = g.append('text')
.attr('class', 'axis-label');
var y_label = g.append('text')
.attr('class', 'axis-label');
var color_legend = colorLegendG.append('text')
.attr('class', 'legend-label')
function draw_scatter(){
var width = chartDiv.clientWidth;
var height = chartDiv.clientHeight;
svg
.attr("width", width)
.attr("height", height);
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
g.attr('transform', `translate(${margin.left},${margin.top+50})`);
data_g.attr('transform', `translate(${margin.left},${margin.top+50})`);
xAxisG
.attr('transform', `translate(0, ${innerHeight})`);
colorLegendG
.attr('transform', `translate(${innerWidth - 100}, 50)`);
var temp = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
title
.attr('x', 0)
.attr('y', margin.top-50)
.style('font-weight', 'bold')
.text('Nuclear Bombs Test History - Resize');
x_label
.attr('x', innerWidth / 2)
.attr('y', innerHeight+60)
.text(xLabel);
y_label
.attr('x', -innerHeight / 2)
.attr('y', -60)
.attr('transform', `rotate(-90)`)
.style('text-anchor', 'middle')
.text(yLabel);
color_legend
.attr('x', -30)
.attr('y', -20)
.text(colorLabel);
const xAxis = d3.axisBottom()
.scale(xScale)
.ticks(20)
.tickPadding(10)
.tickSize(5);
const yAxis = d3.axisLeft()
.scale(yScale)
.ticks(10)
.tickPadding(15)
.tickSize(5);
const colorLegend = d3.legendColor()
.scale(colorScale)
.shape('circle');
d3.csv('nuclear.csv', data => {
xScale
.domain(d3.extent(data, xValue))
.range([0, innerWidth])
.nice();
yScale
.domain(d3.extent(data, yValue))
.range([innerHeight, 0])
.nice();
rScale
.domain(d3.extent(data, yValue))
.range([3, 15])
.nice();
var circles = data_g.selectAll('circle').data(data)
circles.exit().remove();
circles
.enter().append('circle')
.attr('fill', d => colorScale(colorValue(d)))
.merge(circles)
.attr('cx', d => xScale(xValue(d)))
.attr('cy', d => yScale(yValue(d)))
.attr('Country', d=>d.Country)
.attr('fill-opacity', 0.6)
.attr('r', d => rScale(yValue(d)))
.on("mouseover", function(d) {
temp.transition()
.duration(200)
.style("opacity", 1);
temp.html("Bomb count:"+d.Counts)
.style("left", (d3.event.pageX - 60) + "px")
.style("top", (d3.event.pageY + 20) + "px");
})
.on("mouseout", function(d) {
temp.transition()
.duration(500)
.style("opacity", 0);
});
xAxisG.call(xAxis);
yAxisG.call(yAxis);
colorLegendG.call(colorLegend)
.selectAll('.cell text');
});
}
draw_scatter();
window.addEventListener("resize", draw_scatter);
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js
https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js