An example of a Scatter Plot component using a React-inspired pattern for stateless components.
forked from curran's block: Scatter Plot with Color Legend
forked from curran's block: Responsive Scatter Plot I
license: mit | |
border: yes |
An example of a Scatter Plot component using a React-inspired pattern for stateless components.
forked from curran's block: Scatter Plot with Color Legend
forked from curran's block: Responsive Scatter Plot I
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Solution</title> | |
<script src="https://d3js.org/d3.v4.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-legend/2.24.0/d3-legend.min.js"></script> | |
<style> | |
body { | |
margin: 0px; | |
} | |
.domain { | |
display: none; | |
} | |
.tick line { | |
stroke: #C0C0BB; | |
} | |
.tick text, .legendCells text { | |
fill: #8E8883; | |
font-size: 28pt; | |
font-family: sans-serif; | |
} | |
.axis-label, .legend-label { | |
fill: #635F5D; | |
font-size: 50pt; | |
font-family: sans-serif; | |
} | |
/* Make the chart container fill the page using CSS. */ | |
#visualization { | |
position: fixed; | |
left: 0px; | |
right: 0px; | |
top: 0px; | |
bottom: 0px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="visualization"></div> | |
<script> | |
// A Scatter Plot component, using the revealing module pattern. | |
const ScatterPlot = (() => { | |
const xScale = d3.scaleLinear(); | |
const yScale = d3.scaleLinear(); | |
const colorScale = d3.scaleOrdinal() | |
.range(d3.schemeCategory10); | |
const xAxis = d3.axisBottom() | |
.scale(xScale) | |
.tickPadding(15); | |
const yAxis = d3.axisLeft() | |
.scale(yScale) | |
.ticks(5) | |
.tickPadding(15); | |
const colorLegend = d3.legendColor() | |
.scale(colorScale) | |
.shape('circle'); | |
let data; | |
let width; | |
let height; | |
let xValue; | |
let xLabel; | |
let yValue; | |
let yLabel; | |
let colorValue; | |
let colorLabel; | |
let margin = { left: 120, right: 300, top: 20, bottom: 120 }; | |
let colorLegendX = 60; | |
let colorLegendY = 150; | |
let colorLegendLabelX = -30; | |
let colorLegendLabelY = -40; | |
let xAxisLabelOffset = 100; | |
let yAxisLabelOffset = -60; | |
let circleOpacity = 1; | |
let circleRadius = 10; | |
const my = selection => { | |
const innerWidth = width - margin.left - margin.right; | |
const innerHeight = height - margin.top - margin.bottom; | |
xAxis.tickSize(-innerHeight); | |
yAxis.tickSize(-innerWidth); | |
let g = selection.selectAll('.container').data([null]); | |
const gEnter = g.enter().append('g').attr('class', 'container'); | |
g = gEnter | |
.merge(g) | |
.attr('transform', `translate(${margin.left},${margin.top})`); | |
const xAxisGEnter = gEnter.append('g').attr('class', 'x-axis'); | |
const xAxisG = xAxisGEnter | |
.merge(g.select('.x-axis')) | |
.attr('transform', `translate(0, ${innerHeight})`); | |
xAxisGEnter | |
.append('text') | |
.attr('class', 'axis-label') | |
.attr('y', xAxisLabelOffset) | |
.merge(xAxisG.select('.axis-label')) | |
.attr('x', innerWidth / 2) | |
.text(xLabel); | |
const yAxisGEnter = gEnter.append('g').attr('class', 'y-axis'); | |
const yAxisG = yAxisGEnter.merge(g.select('.y-axis')); | |
yAxisGEnter | |
.append('text') | |
.attr('class', 'axis-label') | |
.attr('y', yAxisLabelOffset) | |
.style('text-anchor', 'middle') | |
.merge(yAxisG.select('.axis-label')) | |
.attr('x', -innerHeight / 2) | |
.attr('transform', `rotate(-90)`) | |
.text(yLabel); | |
const colorLegendGEnter = gEnter.append('g').attr('class', 'legend'); | |
const colorLegendG = colorLegendGEnter | |
.merge(g.select('.legend')) | |
.attr('transform', `translate(${innerWidth + colorLegendX},${colorLegendY})`); | |
colorLegendGEnter | |
.append('text') | |
.attr('class', 'legend-label') | |
.attr('x', colorLegendLabelX) | |
.attr('y', colorLegendLabelY) | |
.merge(colorLegendG.select('legend-label')) | |
.text(colorLabel); | |
xScale | |
.domain(d3.extent(data, xValue)) | |
.range([0, innerWidth]) | |
.nice(); | |
yScale | |
.domain(d3.extent(data, yValue)) | |
.range([innerHeight, 0]) | |
.nice(); | |
const circles = g.selectAll('.mark').data(data); | |
circles | |
.enter().append('circle') | |
.attr('class', 'mark') | |
.attr('fill-opacity', circleOpacity) | |
.attr('r', circleRadius) | |
.merge(circles) | |
.attr('cx', d => xScale(xValue(d))) | |
.attr('cy', d => yScale(yValue(d))) | |
.attr('fill', d => colorScale(colorValue(d))); | |
xAxisG.call(xAxis); | |
yAxisG.call(yAxis); | |
colorLegendG.call(colorLegend) | |
.selectAll('.cell text') | |
.attr('dy', '0.1em'); | |
}; | |
my.margin = function(_) { | |
return arguments.length ? (margin = _, my) : margin; | |
}; | |
my.data = function(_) { | |
return arguments.length ? (data = _, my) : data; | |
}; | |
my.width = function(_) { | |
return arguments.length ? (width = _, my) : width; | |
}; | |
my.height = function(_) { | |
return arguments.length ? (height = _, my) : height; | |
}; | |
my.xValue = function(_) { | |
return arguments.length ? (xValue = _, my) : xValue; | |
}; | |
my.xLabel = function(_) { | |
return arguments.length ? (xLabel = _, my) : xLabel; | |
}; | |
my.yValue = function(_) { | |
return arguments.length ? (yValue = _, my) : yValue; | |
}; | |
my.yLabel = function(_) { | |
return arguments.length ? (yLabel = _, my) : yLabel; | |
}; | |
my.colorValue = function(_) { | |
return arguments.length ? (colorValue = _, my) : colorValue; | |
}; | |
my.colorLabel = function(_) { | |
return arguments.length ? (colorLabel = _, my) : colorLabel; | |
}; | |
my.colorLegendX = function(_) { | |
return arguments.length ? (colorLegendX = _, my) : colorLegendX; | |
}; | |
my.colorLegendY = function(_) { | |
return arguments.length ? (colorLegendY = _, my) : colorLegendY; | |
}; | |
my.colorLegendLabelX = function(_) { | |
return arguments.length ? (colorLegendLabelX = _, my) : colorLegendLabelX; | |
}; | |
my.colorLegendLabelY = function(_) { | |
return arguments.length ? (colorLegendY = _, my) : colorLegendY; | |
}; | |
my.xAxisLabelOffset = function(_) { | |
return arguments.length ? (xAxisLabelOffset = _, my) : xAxisLabelOffset; | |
}; | |
my.yAxisLabelOffset = function(_) { | |
return arguments.length ? (yAxisLabelOffset = _, my) : yAxisLabelOffset; | |
}; | |
my.circleOpacity = function(_) { | |
return arguments.length ? (circleOpacity = _, my) : circleOpacity; | |
}; | |
my.circleRadius = function(_) { | |
return arguments.length ? (circleRadius = _, my) : circleRadius; | |
}; | |
return my; | |
}); | |
// The main entry point, which uses the Scatter Plot component. | |
function main(){ | |
const visualization = d3.select('#visualization'); | |
const visualizationDiv = visualization.node(); | |
const svg = visualization.append('svg'); | |
const scatterPlot = ScatterPlot() | |
.xValue(d => d.sepalLength) | |
.xLabel('Sepal Length') | |
.yValue(d => d.petalLength) | |
.yLabel('Petal Length') | |
.colorValue(d => d.species) | |
.colorLabel('Species') | |
.circleOpacity(0.6) | |
.circleRadius(8); | |
const row = d => { | |
d.petalLength = +d.petalLength; | |
d.petalWidth = +d.petalWidth; | |
d.sepalLength = +d.sepalLength; | |
d.sepalWidth = +d.sepalWidth; | |
return d; | |
}; | |
d3.csv('iris.csv', row, data => { | |
scatterPlot.data(data); | |
// Render the scatter plot with updated width and height. | |
const render = () => { | |
const width = visualizationDiv.clientWidth; | |
const height = visualizationDiv.clientHeight; | |
scatterPlot | |
.width(width) | |
.height(height); | |
svg | |
.attr('width', width) | |
.attr('height', height) | |
.call(scatterPlot); | |
} | |
// Draw for the first time to initialize. | |
render(); | |
// Redraw based on the new size whenever the browser window is resized. | |
window.addEventListener('resize', render); | |
}); | |
} | |
main(); | |
</script> | |
</body> | |
</html> |
sepalLength | sepalWidth | petalLength | petalWidth | species | |
---|---|---|---|---|---|
5.1 | 3.5 | 1.4 | 0.2 | setosa | |
4.9 | 3.0 | 1.4 | 0.2 | setosa | |
4.7 | 3.2 | 1.3 | 0.2 | setosa | |
4.6 | 3.1 | 1.5 | 0.2 | setosa | |
5.0 | 3.6 | 1.4 | 0.2 | setosa | |
5.4 | 3.9 | 1.7 | 0.4 | setosa | |
4.6 | 3.4 | 1.4 | 0.3 | setosa | |
5.0 | 3.4 | 1.5 | 0.2 | setosa | |
4.4 | 2.9 | 1.4 | 0.2 | setosa | |
4.9 | 3.1 | 1.5 | 0.1 | setosa | |
5.4 | 3.7 | 1.5 | 0.2 | setosa | |
4.8 | 3.4 | 1.6 | 0.2 | setosa | |
4.8 | 3.0 | 1.4 | 0.1 | setosa | |
4.3 | 3.0 | 1.1 | 0.1 | setosa | |
5.8 | 4.0 | 1.2 | 0.2 | setosa | |
5.7 | 4.4 | 1.5 | 0.4 | setosa | |
5.4 | 3.9 | 1.3 | 0.4 | setosa | |
5.1 | 3.5 | 1.4 | 0.3 | setosa | |
5.7 | 3.8 | 1.7 | 0.3 | setosa | |
5.1 | 3.8 | 1.5 | 0.3 | setosa | |
5.4 | 3.4 | 1.7 | 0.2 | setosa | |
5.1 | 3.7 | 1.5 | 0.4 | setosa | |
4.6 | 3.6 | 1.0 | 0.2 | setosa | |
5.1 | 3.3 | 1.7 | 0.5 | setosa | |
4.8 | 3.4 | 1.9 | 0.2 | setosa | |
5.0 | 3.0 | 1.6 | 0.2 | setosa | |
5.0 | 3.4 | 1.6 | 0.4 | setosa | |
5.2 | 3.5 | 1.5 | 0.2 | setosa | |
5.2 | 3.4 | 1.4 | 0.2 | setosa | |
4.7 | 3.2 | 1.6 | 0.2 | setosa | |
4.8 | 3.1 | 1.6 | 0.2 | setosa | |
5.4 | 3.4 | 1.5 | 0.4 | setosa | |
5.2 | 4.1 | 1.5 | 0.1 | setosa | |
5.5 | 4.2 | 1.4 | 0.2 | setosa | |
4.9 | 3.1 | 1.5 | 0.1 | setosa | |
5.0 | 3.2 | 1.2 | 0.2 | setosa | |
5.5 | 3.5 | 1.3 | 0.2 | setosa | |
4.9 | 3.1 | 1.5 | 0.1 | setosa | |
4.4 | 3.0 | 1.3 | 0.2 | setosa | |
5.1 | 3.4 | 1.5 | 0.2 | setosa | |
5.0 | 3.5 | 1.3 | 0.3 | setosa | |
4.5 | 2.3 | 1.3 | 0.3 | setosa | |
4.4 | 3.2 | 1.3 | 0.2 | setosa | |
5.0 | 3.5 | 1.6 | 0.6 | setosa | |
5.1 | 3.8 | 1.9 | 0.4 | setosa | |
4.8 | 3.0 | 1.4 | 0.3 | setosa | |
5.1 | 3.8 | 1.6 | 0.2 | setosa | |
4.6 | 3.2 | 1.4 | 0.2 | setosa | |
5.3 | 3.7 | 1.5 | 0.2 | setosa | |
5.0 | 3.3 | 1.4 | 0.2 | setosa | |
7.0 | 3.2 | 4.7 | 1.4 | versicolor | |
6.4 | 3.2 | 4.5 | 1.5 | versicolor | |
6.9 | 3.1 | 4.9 | 1.5 | versicolor | |
5.5 | 2.3 | 4.0 | 1.3 | versicolor | |
6.5 | 2.8 | 4.6 | 1.5 | versicolor | |
5.7 | 2.8 | 4.5 | 1.3 | versicolor | |
6.3 | 3.3 | 4.7 | 1.6 | versicolor | |
4.9 | 2.4 | 3.3 | 1.0 | versicolor | |
6.6 | 2.9 | 4.6 | 1.3 | versicolor | |
5.2 | 2.7 | 3.9 | 1.4 | versicolor | |
5.0 | 2.0 | 3.5 | 1.0 | versicolor | |
5.9 | 3.0 | 4.2 | 1.5 | versicolor | |
6.0 | 2.2 | 4.0 | 1.0 | versicolor | |
6.1 | 2.9 | 4.7 | 1.4 | versicolor | |
5.6 | 2.9 | 3.6 | 1.3 | versicolor | |
6.7 | 3.1 | 4.4 | 1.4 | versicolor | |
5.6 | 3.0 | 4.5 | 1.5 | versicolor | |
5.8 | 2.7 | 4.1 | 1.0 | versicolor | |
6.2 | 2.2 | 4.5 | 1.5 | versicolor | |
5.6 | 2.5 | 3.9 | 1.1 | versicolor | |
5.9 | 3.2 | 4.8 | 1.8 | versicolor | |
6.1 | 2.8 | 4.0 | 1.3 | versicolor | |
6.3 | 2.5 | 4.9 | 1.5 | versicolor | |
6.1 | 2.8 | 4.7 | 1.2 | versicolor | |
6.4 | 2.9 | 4.3 | 1.3 | versicolor | |
6.6 | 3.0 | 4.4 | 1.4 | versicolor | |
6.8 | 2.8 | 4.8 | 1.4 | versicolor | |
6.7 | 3.0 | 5.0 | 1.7 | versicolor | |
6.0 | 2.9 | 4.5 | 1.5 | versicolor | |
5.7 | 2.6 | 3.5 | 1.0 | versicolor | |
5.5 | 2.4 | 3.8 | 1.1 | versicolor | |
5.5 | 2.4 | 3.7 | 1.0 | versicolor | |
5.8 | 2.7 | 3.9 | 1.2 | versicolor | |
6.0 | 2.7 | 5.1 | 1.6 | versicolor | |
5.4 | 3.0 | 4.5 | 1.5 | versicolor | |
6.0 | 3.4 | 4.5 | 1.6 | versicolor | |
6.7 | 3.1 | 4.7 | 1.5 | versicolor | |
6.3 | 2.3 | 4.4 | 1.3 | versicolor | |
5.6 | 3.0 | 4.1 | 1.3 | versicolor | |
5.5 | 2.5 | 4.0 | 1.3 | versicolor | |
5.5 | 2.6 | 4.4 | 1.2 | versicolor | |
6.1 | 3.0 | 4.6 | 1.4 | versicolor | |
5.8 | 2.6 | 4.0 | 1.2 | versicolor | |
5.0 | 2.3 | 3.3 | 1.0 | versicolor | |
5.6 | 2.7 | 4.2 | 1.3 | versicolor | |
5.7 | 3.0 | 4.2 | 1.2 | versicolor | |
5.7 | 2.9 | 4.2 | 1.3 | versicolor | |
6.2 | 2.9 | 4.3 | 1.3 | versicolor | |
5.1 | 2.5 | 3.0 | 1.1 | versicolor | |
5.7 | 2.8 | 4.1 | 1.3 | versicolor | |
6.3 | 3.3 | 6.0 | 2.5 | virginica | |
5.8 | 2.7 | 5.1 | 1.9 | virginica | |
7.1 | 3.0 | 5.9 | 2.1 | virginica | |
6.3 | 2.9 | 5.6 | 1.8 | virginica | |
6.5 | 3.0 | 5.8 | 2.2 | virginica | |
7.6 | 3.0 | 6.6 | 2.1 | virginica | |
4.9 | 2.5 | 4.5 | 1.7 | virginica | |
7.3 | 2.9 | 6.3 | 1.8 | virginica | |
6.7 | 2.5 | 5.8 | 1.8 | virginica | |
7.2 | 3.6 | 6.1 | 2.5 | virginica | |
6.5 | 3.2 | 5.1 | 2.0 | virginica | |
6.4 | 2.7 | 5.3 | 1.9 | virginica | |
6.8 | 3.0 | 5.5 | 2.1 | virginica | |
5.7 | 2.5 | 5.0 | 2.0 | virginica | |
5.8 | 2.8 | 5.1 | 2.4 | virginica | |
6.4 | 3.2 | 5.3 | 2.3 | virginica | |
6.5 | 3.0 | 5.5 | 1.8 | virginica | |
7.7 | 3.8 | 6.7 | 2.2 | virginica | |
7.7 | 2.6 | 6.9 | 2.3 | virginica | |
6.0 | 2.2 | 5.0 | 1.5 | virginica | |
6.9 | 3.2 | 5.7 | 2.3 | virginica | |
5.6 | 2.8 | 4.9 | 2.0 | virginica | |
7.7 | 2.8 | 6.7 | 2.0 | virginica | |
6.3 | 2.7 | 4.9 | 1.8 | virginica | |
6.7 | 3.3 | 5.7 | 2.1 | virginica | |
7.2 | 3.2 | 6.0 | 1.8 | virginica | |
6.2 | 2.8 | 4.8 | 1.8 | virginica | |
6.1 | 3.0 | 4.9 | 1.8 | virginica | |
6.4 | 2.8 | 5.6 | 2.1 | virginica | |
7.2 | 3.0 | 5.8 | 1.6 | virginica | |
7.4 | 2.8 | 6.1 | 1.9 | virginica | |
7.9 | 3.8 | 6.4 | 2.0 | virginica | |
6.4 | 2.8 | 5.6 | 2.2 | virginica | |
6.3 | 2.8 | 5.1 | 1.5 | virginica | |
6.1 | 2.6 | 5.6 | 1.4 | virginica | |
7.7 | 3.0 | 6.1 | 2.3 | virginica | |
6.3 | 3.4 | 5.6 | 2.4 | virginica | |
6.4 | 3.1 | 5.5 | 1.8 | virginica | |
6.0 | 3.0 | 4.8 | 1.8 | virginica | |
6.9 | 3.1 | 5.4 | 2.1 | virginica | |
6.7 | 3.1 | 5.6 | 2.4 | virginica | |
6.9 | 3.1 | 5.1 | 2.3 | virginica | |
5.8 | 2.7 | 5.1 | 1.9 | virginica | |
6.8 | 3.2 | 5.9 | 2.3 | virginica | |
6.7 | 3.3 | 5.7 | 2.5 | virginica | |
6.7 | 3.0 | 5.2 | 2.3 | virginica | |
6.3 | 2.5 | 5.0 | 1.9 | virginica | |
6.5 | 3.0 | 5.2 | 2.0 | virginica | |
6.2 | 3.4 | 5.4 | 2.3 | virginica | |
5.9 | 3.0 | 5.1 | 1.8 | virginica |
�PNG | |