function scatterPlot({
chart,
width,
height,
data,
xfield,
yfield,
xpadding,
ypadding,
colorField,
size,
title,
xlabel,
ylabel,
xgrid,
ygrid
}) {
let topPadding = titleHeight + figPadding;
let leftPadding = figPadding + yAxisWidth;
let bottomPadding = figPadding + xAxisHeight;
let svg = chart.node();
let w = width - leftPadding - figPadding - 1;
let h = height - topPadding - bottomPadding;
let x = linearScale(data, xfield, [0, w], xpadding);
let y = linearScale(data, yfield, [h, 0], ypadding);
let xAxis = d3
.axisBottom(x)
.tickPadding(4)
.tickSizeOuter(0);
chart
.append("g")
.attr("class", "x axis")
.attr(
"transform",
"translate(" + leftPadding + "," + (h + topPadding) + ")"
)
.call(xAxis);
// Add x axis label
chart
.append("text")
.attr(
"transform",
"translate(" + (leftPadding + w / 2) + " ," + (height - figPadding) + ")"
)
.style("text-anchor", "middle")
.text(xlabel);
// x axis
// Ref: https://bl.ocks.org/d3noob/c506ac45617cf9ed39337f99f8511218
if (xgrid) {
chart
.append("g")
.attr("class", "x grid")
.attr("transform", "translate(" + leftPadding + "," + topPadding + ")")
.call(
xAxis
.tickSize(h)
.tickFormat("")
.ticks(5)
);
}
// Add y axis
let yAxis = d3
.axisLeft(y)
.tickSizeOuter(0)
.ticks(4);
chart
.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + leftPadding + "," + topPadding + ")")
.call(yAxis);
// Add y axis label
chart
.append("text")
.attr(
"transform",
"rotate(-90)," +
"translate(" +
(0 - topPadding - h / 2) +
" ," +
(figPadding + textHeight / 2) +
")"
)
.style("text-anchor", "middle")
.text(ylabel);
// y grid
if (ygrid) {
chart
.append("g")
.attr("class", "y grid")
.attr("transform", "translate(" + leftPadding + "," + topPadding + ")")
.call(yAxis.tickSize(-w).tickFormat(""));
}
// create a new group, use `selectAll` to build an empty
// list context
let dots = chart
.append("g")
.attr("class", "dots")
.selectAll()
.data(data)
.enter()
.append("circle")
.attr("cx", d => leftPadding + x(d[xfield]))
.attr("cy", d => topPadding + y(d[yfield]))
.attr("r", size);
// add Legend
if (colorField) {
let color = d3.scaleOrdinal(d3.schemeSet2);
color.domain(pointScale(data, colorField).domain());
// Or single color?
// let color = d3.scaleOrdinal(d3.schemeReds[9].slice(3))
// color.domain(pointScale(data, colorField).domain())
dots = dots.attr("fill", d => color(d[colorField]));
// draw legend
let legend = chart
.append("g")
.attr("class", "legend")
.attr(
"transform",
"translate(" + (leftPadding + w - 97) + "," + (topPadding + 20) + ")"
);
// legend background
let legendBox = legend
.append("rect")
.attr("class", "legend-box")
.attr("x", "-1.5em")
.attr("y", "-2em")
.attr("width", 105)
.attr("height", 60);
// draw legend title
legend
.append("text")
.attr("class", "legend-label")
.attr("transform", "translate(-11," + -9 + ")")
.text(colorField);
// add wrapper for legend items
legend = legend
.selectAll()
.data(color.domain())
.enter()
.append("g")
.attr("class", "legend-item")
.attr(
"transform",
(d, i) =>
"translate(" + (i % 3) * 32 + "," + Math.floor(i / 3) * 15 + ")"
);
// draw legend colored rectangles
legend
.append("rect")
.attr("width", 10)
.attr("height", 10)
.style("fill", color);
// draw legend text
legend
.append("text")
.attr("x", -3)
.attr("y", 5)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(d => d);
}
// Add title
chart
.append("text")
.attr("class", "title")
.attr("text-anchor", "middle")
.attr("x", w / 2 + leftPadding)
.attr("y", figPadding + titleHeight / 2)
.text(title);
return chart;
}