{
const svg = d3.create('svg')
.attr('width', visWidth + margin.left + margin.right)
.attr('height', visHeight + margin.top + margin.bottom);
const g = svg.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
const xAxisGroup = g.append("g")
.attr('transform', `translate(0, ${visHeight})`)
.call(xAxis)
.call(g => g.selectAll('.domain').remove());
xAxisGroup.append('text')
.attr('x', visWidth / 2)
.attr('y', 40)
.attr('fill', 'black')
.attr('text-anchor', 'middle')
.text(xvalue);
const yAxisGroup = g.append('g')
.call(yAxis)
.call(g => g.selectAll('.domain').remove());
yAxisGroup.append('text')
.attr('x', -40)
.attr('y', visHeight / 2)
.attr('fill', 'black')
.attr('dominant-baseline', 'middle')
.text(yvalue);
// ----------------------------------------------
// DRAW THE GRID ********************************
const grid = g.append('g');
grid.append('rect')
.attr('width', visWidth)
.attr('height', visHeight)
.attr('fill', 'white');
let yLines = grid.append('g')
.selectAll('line');
let xLines = grid.append('g')
.selectAll('line');
function drawGridLines(x, y) {
yLines = yLines.data(y.ticks())
.join('line')
.attr('stroke', '#d3d3d3')
.attr('x1', 0)
.attr('x2', visWidth)
.attr('y1', d => 0.5 + y(d))
.attr('y2', d => 0.5 + y(d));
xLines = xLines.data(x.ticks())
.join('line')
.attr('stroke', '#d3d3d3')
.attr('x1', d => 0.5 + x(d))
.attr('x2', d => 0.5 + x(d))
.attr('y1', d => 0)
.attr('y2', d => visHeight);
}
drawGridLines(xscale, yscale);
// ----------------------------------------------
// DRAW THE POINTS ******************************
svg.append('clipPath')
.attr('id', 'border')
.append('rect')
.attr('width', visWidth)
.attr('height', visHeight)
.attr('fill', 'white');
const dotsGroup = g.append('g')
.attr('clip-path', 'url(#border)');
const dots = dotsGroup.selectAll('circle')
.data(covid_totals) // Use the totals data
.join('circle')
.attr('cx', d => xscale(d[xvalue]))
.attr('cy', d => yscale(d[yvalue]))
.attr('fill', 'steelblue') // Set the fill of the dots
.attr('opacity', .7) // Make a little transparent so we can see overlap
.attr('r', 5)
.on('mouseenter', mouseEnter)
.on('mouseleave', mouseLeave);
const zoom = d3.zoom()
.extent([[0, 0], [visWidth, visHeight]])
// Determine how much you can zoom out and in
// 1 is the factor by which you can zoom out, smaller number means zoom
// out more, 1 means you can't zoom out more than the default zoom.
// 10 is the factor by which you can zoom in, larger number means you can
// zoom in more.
.scaleExtent([-2, 10])
.on('zoom', onZoom);
g.call(zoom);
function onZoom(event) {
// get updated scales
const xNew = event.transform.rescaleX(xscale);
const yNew = event.transform.rescaleY(yscale);
// update the position of the dots
dots.attr('cx', d => xNew(d[xvalue]))
.attr('cy', d => yNew(d[yvalue]));
// update the axes
xAxisGroup.call(xAxis.scale(xNew))
.call(g => g.selectAll('.domain').remove());
yAxisGroup.call(yAxis.scale(yNew))
.call(g => g.selectAll('.domain').remove());
// update the grid
drawGridLines(xNew, yNew);
}
// ----------------------------------------------
// TOOLTIP SECTION ******************************
// create tooltip
const tooltip = svg.append('g')
.attr('visibility', 'hidden');
const tooltipHeight = 15;
const tooltipRect = tooltip.append('rect')
.attr('fill', 'black')
.attr('rx', 5)
.attr('height', tooltipHeight);
const tooltipText = tooltip.append('text')
.attr('fill', 'white')
.attr('font-family', 'sans-serif')
.attr('font-size', 10)
.attr('dy', 2)
.attr('dx', 2)
.attr('dominant-baseline', 'hanging')
// handle hovering over a circle
function mouseEnter(event, d) {
// make the circle larger
d3.select(this)
.attr('r', 5 * 1.5);
// update the text and get its width
tooltipText.text(d.locality);
const labelWidth = tooltipText.node().getComputedTextLength();
// set the width of the tooltip's background rectangle
// to match the width of the label
tooltipRect.attr('width', labelWidth + 4);
// move the tooltip and make it visible
const xPos = xscale(d[xvalue]) + 5 * 3;
const yPos = yscale(d[yvalue]) - tooltipHeight / 2;
tooltip.attr('transform', `translate(${xPos},${yPos})`)
.attr('visibility', 'visible');
}
// handle leaving a circle
function mouseLeave(event, d) {
// reset its size
d3.select(this)
.attr('r', 5)
// make the tooltip invisible
tooltip
.attr('visibility', 'hidden');
}
// END TOOLTIP SECTION **************************
// ----------------------------------------------
return svg.node();
}