cerViz = {
cereals.forEach(function(cereal) {
cereal.mfrNumber = cereal.mfr.charCodeAt(0);
cereal.typeNumber = cereal.type.charCodeAt(0);
});
let width = 800;
let height = 800;
let margin = 100;
let plotWidth = width - margin * 2;
let textSize = "11px";
let textSpacing = 60;
let textStrokeWidth = 1;
let axisLabelOffset = 20;
let svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height);
let bg = svg
.append('rect')
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr('fill', '#002');
let xAxis = svg
.append('line')
.attr('stroke', 'white')
.attr('stroke-width', 3)
.attr('x1', margin)
.attr('x2', width - margin)
.attr('y1', height - margin)
.attr('y2', height - margin);
//draw Y axis
let yAxis = svg
.append('line')
.attr('stroke', 'white')
.attr('stroke-width', 3)
.attr('x1', margin)
.attr('x2', margin)
.attr('y1', margin)
.attr('y2', height - margin);
//draw Y-Axis Min and Max
svg
.append('text')
.attr('x', margin - axisLabelOffset)
.attr('y', margin)
.attr('fill', 'white')
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('text-anchor', 'end')
.attr('alignment-baseline', 'middle')
.attr('id', 'yMax')
.text('100');
svg
.append('text')
.attr('x', margin - axisLabelOffset)
.attr('y', height - margin)
.attr('fill', 'white')
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('text-anchor', 'end')
.attr('alignment-baseline', 'middle')
.attr('id', 'yMin')
.text('0');
//draw X-Axis Min and Max
svg
.append('text')
.attr('x', margin)
.attr('y', height - margin + axisLabelOffset * 1.5)
.attr('fill', 'white')
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('text-anchor', 'middle')
.attr('alignment-baseline', 'top')
.attr('id', 'xMin')
.text('0');
svg
.append('text')
.attr('x', width - margin)
.attr('y', height - margin + axisLabelOffset * 1.5)
.attr('fill', 'white')
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('text-anchor', 'middle')
.attr('alignment-baseline', 'middle')
.attr('id', 'xMax')
.text('100');
//standard data binding with our dataset and SVG circles
svg
.selectAll('.cerealCircles')
.data(cereals)
.enter()
.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr('fill', "purple")
.attr('r', 10)
.attr('opacity', 1)
.attr('stroke', 'white')
.attr('stroke-width', '0')
.attr('class', 'cerealCircles')
//attach mouseover behavior
.on('mouseover', function(d) {
d3.select(this)
.raise()
.transition()
.attr('stroke-width', '2');
d3.select('#cerealInformation').text(d.name + " | Rating : " + d.rating);
})
//attach mouseout behavior
.on('mouseout', function(d) {
d3.select(this)
.transition()
.attr('stroke-width', '0');
d3.select('#cerealInformation').text('Hover on a circle!');
});
//add text area below plot for mouseover info
svg
.append('text')
.attr('y', height - margin / 2)
.attr('x', width / 2)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.attr('id', 'cerealInformation')
.style('font-family', 'courier')
.text('Hover on a circle!');
//X-Axis--------------------------------------------------------------------------
//label options
svg
.append('text')
.attr('y', margin / 2)
.attr('x', margin)
.attr('fill', 'white')
.style('font-family', 'courier')
.style('font-size', textSize)
.text('X-Axis:');
//dataset to hold options and properties in dataset
let xChoiceSet = [
{ name: "Sugar", value: "sugars" },
{ name: "Calories", value: "calories" },
{ name: "Fat", value: "fat" }
];
//create text buttons
svg
.selectAll('.xChoice')
.data(xChoiceSet)
.enter()
.append('text')
.attr('y', (d, i) => ((i + 1) / (xChoiceSet.length + 1)) * margin)
.attr('x', margin + textSpacing)
.attr('fill', 'white')
.attr('stroke', 'white')
.attr('stroke-width', 0)
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('class', 'xChoice')
.attr('id', d => d.value)
.text(d => d.name)
//attach click listener
.on('click', function(xC) {
//turn off highlight stroke for all, and re-add to selection
d3.selectAll('.xChoice').attr('stroke-width', 0);
d3.select(this).attr('stroke-width', textStrokeWidth);
//update axis labels
d3.select('#xMax').text(d3.max(cereals, d => parseFloat(d[xC.value])));
d3.select('#xMin').text(d3.min(cereals, d => parseFloat(d[xC.value])));
//create scale
let cerealScale = d3
.scaleLinear()
.domain(d3.extent(cereals, d => parseFloat(d[xC.value])))
.range([margin, width - margin]);
//update circles
d3.selectAll('.cerealCircles')
.transition()
.ease(d3.easeElasticOut)
.duration(1000)
.attr("cx", d => cerealScale(parseFloat(d[xC.value])));
});
//Y-Axis--------------------------------------------------------------------------
svg
.append('text')
.attr('y', margin / 2)
.attr('x', 1 * (plotWidth / 4) + margin)
.attr('fill', 'white')
.attr('stroke', 'white')
.attr('stroke-width', 0)
.style('font-family', 'courier')
.style('font-size', textSize)
.text('Y-Axis:');
let yChoiceSet = [
{ name: "Vitamin", value: "vitamins" },
{ name: "Potassium", value: "potass" },
{ name: "Protein", value: "protein" }
];
svg
.selectAll('.yChoice')
.data(yChoiceSet)
.enter()
.append('text')
.attr('y', (d, i) => ((i + 1) / (yChoiceSet.length + 1)) * margin)
.attr('x', 1 * (plotWidth / 4) + margin + textSpacing)
.attr('fill', 'white')
.attr('stroke', 'white')
.attr('stroke-width', 0)
.attr('class', 'yChoice')
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('id', d => d.value)
.text(d => d.name)
.on('click', function(yC) {
d3.selectAll('.yChoice').attr('stroke-width', 0);
d3.select(this).attr('stroke-width', textStrokeWidth);
d3.select('#yMax').text(d3.max(cereals, d => parseFloat(d[yC.value])));
d3.select('#yMin').text(d3.min(cereals, d => parseFloat(d[yC.value])));
let cerealScale = d3
.scaleLinear()
.domain(d3.extent(cereals, d => parseFloat(d[yC.value])))
.range([height - margin, margin]);
d3.selectAll('.cerealCircles')
.transition()
.ease(d3.easeElasticOut)
.duration(1000)
.attr("cy", d => cerealScale(parseFloat(d[yC.value])));
});
// Radius -------------------------------------------------------------------
svg
.append('text')
.attr('y', margin / 2)
.attr('x', 2 * (plotWidth / 4) + margin)
.attr('fill', 'white')
.style('font-family', 'courier')
.style('font-size', textSize)
.text('Radius:');
let radChoiceSet = [
{ name: "Weight", value: "weight" },
{ name: "Volume", value: "cups" },
{ name: "Shelf", value: "shelf" }
];
svg
.selectAll('.radChoice')
.data(radChoiceSet)
.enter()
.append('text')
.attr('y', (d, i) => ((i + 1) / (radChoiceSet.length + 1)) * margin)
.attr('x', 2 * (plotWidth / 4) + margin + textSpacing)
.attr('fill', 'white')
.attr('stroke', 'white')
.attr('stroke-width', 0)
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('class', 'radChoice')
.attr('id', d => d.value)
.text(d => d.name)
.on('click', function(rC) {
d3.selectAll('.radChoice').attr('stroke-width', 0);
d3.select(this).attr('stroke-width', textStrokeWidth);
let cerealScale = d3
.scaleLinear()
.domain(d3.extent(cereals, d => parseFloat(d[rC.value])))
.range([5, 15]);
d3.selectAll('.cerealCircles')
.transition()
.ease(d3.easeElasticOut)
.duration(1000)
.attr("r", d => cerealScale(parseFloat(d[rC.value])));
});
// Color -------------------------------------------------------------------
svg
.append('text')
.attr('y', margin / 2)
.attr('x', 3 * (plotWidth / 4) + margin)
.attr('fill', 'white')
.style('font-family', 'courier')
.style('font-size', textSize)
.text('Color:');
let colorChoiceSet = [
{ name: "Rating", value: "rating" },
{ name: "Manufacturer", value: "mfrNumber" },
{ name: "Type", value: "typeNumber" }
];
svg
.selectAll('.colorChoice')
.data(colorChoiceSet)
.enter()
.append('text')
.attr('y', (d, i) => ((i + 1) / (colorChoiceSet.length + 1)) * margin)
.attr('x', 3 * (plotWidth / 4) + margin + textSpacing)
.attr('fill', 'white')
.attr('stroke', 'white')
.attr('stroke-width', 0)
.style('font-family', 'courier')
.style('font-size', textSize)
.attr('class', 'colorChoice')
.attr('id', d => d.value)
.text(d => d.name)
.on('click', function(cC) {
d3.selectAll('.colorChoice').attr('stroke-width', 0);
d3.select(this).attr('stroke-width', textStrokeWidth);
let cerealScale = d3
.scaleLinear()
.domain(d3.extent(cereals, d => parseFloat(d[cC.value])))
.range([0, 1]);
d3.selectAll('.cerealCircles')
.transition()
.ease(d3.easeElasticOut)
.duration(1000)
.attr("fill", d =>
d3.interpolateViridis(cerealScale(parseFloat(d[cC.value])))
);
});
return svg.node();
}