Published
Edited
Oct 7, 2020
Comments locked
Insert cell
Insert cell
Insert cell
d3 = require("d3@5")
Insert cell
import { cereals } from '@zachpino/week-5-animation-and-interactivity'
Insert cell
cereals
Insert cell
Insert cell
// cerealViz = {
// //svg variables
// let width = 800;
// let height = 800;
// let margin = 150;

// //create SVG artboard
// let svg = d3
// .create("svg")
// .attr("viewBox", [0, 0, width, height])
// .attr("width", width)
// .attr("height", height);

// //create svg background color
// 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);

// 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);

// let proteinScale = d3
// .scaleLinear()
// .domain(d3.extent(cereals, d => parseFloat(d.protein)))
// .range([margin, width - margin]);

// let calorieScale = d3
// .scaleLinear()
// .domain(d3.extent(cereals, d => parseFloat(d.calories)))
// .range([margin, width - margin]);

// let sugarsScale = d3
// .scaleLinear()
// .domain(d3.extent(cereals, d => parseFloat(d.sugars)))
// .range([margin, width - margin]);

// let potassiumScale = d3
// .scaleLinear()
// .domain(d3.extent(cereals, d => parseFloat(d.potass)))
// .range([margin, width - margin]);

// let fatScale = d3
// .scaleLinear()
// .domain(d3.extent(cereals, d => parseFloat(d.fat)))
// .range([margin, width - margin]);

// let vitaminScale = d3
// .scaleLinear()
// .domain(d3.extent(cereals, d => parseFloat(d.vitamins)))
// .range([margin, width - margin]);

// let ratingScale = d3
// .scaleLinear()
// .domain(d3.extent(cereals, d => parseFloat(d.rating)))
// .range([0, 1]);

// //standard data binding with our dataset and SVG circles
// svg
// .selectAll('.fakeDots')
// .data(cereals)
// .enter()
// .append("circle")
// .attr("cx", d => calorieScale(parseFloat(d.calories)))
// .attr("cy", d => potassiumScale(parseFloat(d.potass)))
// .attr('fill', d => d3.interpolateViridis(ratingScale(parseFloat(d.rating))))
// .attr('r', 10)
// .attr('opacity', 1)
// .attr('stroke', 'white')
// .attr('stroke-width', '0')
// .attr('class', 'cerealCircles')
// .on('mouseover', function(d) {
// d3.select(this)
// .raise()
// .transition()
// .attr('stroke-width', '2');

// d3.select('#cerealInfo').text(d.name + " | Rating : " + d.rating);
// })
// .on('mouseout', function(d) {
// d3.select(this)
// .transition()
// .attr('stroke-width', '0');

// d3.select('#cerealInfo').text('Hover on a circle!');
// });

// svg
// .append('text')
// .attr('y', height - margin / 2)
// .attr('x', width / 2)
// .attr('text-anchor', 'middle')
// .attr('fill', 'white')
// .attr('id', 'cerealInfo')
// .style('font-family', 'courier')
// .text('Hover on a circle!');

// svg
// .append('text')
// .attr('y', margin / 2)
// .attr('x', margin)
// .attr('fill', 'white')
// .style('font-family', 'courier')
// .text('X-Axis:');

// svg
// .append('text')
// .attr('y', margin / 2)
// .attr('x', margin * 1.6)
// .attr('fill', 'white')
// .attr('stroke', 'white')
// .attr('stroke-width', 0)
// .attr('class', 'xChoice')
// .style('font-family', 'courier')
// .text('Calories')
// .on('click', function(d) {
// d3.selectAll('.xChoice').attr('stroke-width', 0);
// d3.select(this).attr('stroke-width', 2);

// d3.selectAll('.cerealCircles')
// .transition()
// .ease(d3.easeElasticOut)
// .duration(1000)
// .attr("cx", d => calorieScale(parseFloat(d.calories)));
// });

// svg
// .append('text')
// .attr('y', (margin / 4) * 3)
// .attr('x', margin * 1.6)
// .attr('fill', 'white')
// .attr('stroke', 'white')
// .attr('stroke-width', 0)
// .attr('class', 'xChoice')
// .style('font-family', 'courier')
// .text('Fat')
// .on('click', function(d) {
// d3.selectAll('.xChoice').attr('stroke-width', 0);
// d3.select(this).attr('stroke-width', 2);

// d3.selectAll('.cerealCircles')
// .transition()
// .ease(d3.easeElasticOut)
// .duration(1000)
// .attr("cx", d => fatScale(parseFloat(d.fat)));
// });

// svg
// .append('text')
// .attr('y', margin / 4)
// .attr('x', margin * 1.6)
// .attr('fill', 'white')
// .attr('stroke', 'white')
// .attr('stroke-width', 0)
// .attr('class', 'xChoice')
// .style('font-family', 'courier')
// .text('Sugars')
// .on('click', function(d) {
// d3.selectAll('.xChoice').attr('stroke-width', 0);
// d3.select(this).attr('stroke-width', 2);

// d3.selectAll('.cerealCircles')
// .transition()
// .ease(d3.easeElasticOut)
// .duration(1000)
// .attr("cx", d => sugarsScale(parseFloat(d.sugars)));
// });

// svg
// .append('text')
// .attr('y', margin / 2)
// .attr('x', width - margin * 2)
// .attr('fill', 'white')
// .attr('stroke', 'white')
// .attr('stroke-width', 0)
// .style('font-family', 'courier')
// .text('Y-Axis:');

// svg
// .append('text')
// .attr('y', margin / 4)
// .attr('x', width - margin * 1.4)
// .attr('fill', 'white')
// .attr('stroke', 'white')
// .attr('stroke-width', 0)
// .attr('class', 'yChoice')
// .style('font-family', 'courier')
// .text('Vitamins')
// .on('click', function(d) {
// d3.selectAll('.yChoice').attr('stroke-width', 0);
// d3.select(this).attr('stroke-width', 2);

// d3.selectAll('.cerealCircles')
// .transition()
// .ease(d3.easeElasticOut)
// .duration(1000)
// .attr("cy", d => vitaminScale(parseFloat(d.vitamins)));
// });

// svg
// .append('text')
// .attr('y', margin / 2)
// .attr('x', width - margin * 1.4)
// .attr('fill', 'white')
// .attr('stroke', 'white')
// .attr('stroke-width', 0)
// .attr('class', 'yChoice')
// .style('font-family', 'courier')
// .text('Potassium')
// .on('click', function(d) {
// d3.selectAll('.yChoice').attr('stroke-width', 0);
// d3.select(this).attr('stroke-width', 2);

// d3.selectAll('.cerealCircles')
// .transition()
// .ease(d3.easeElasticOut)
// .duration(1000)
// .attr("cy", d => potassiumScale(parseFloat(d.potass)));
// });

// svg
// .append('text')
// .attr('y', (margin / 4) * 3)
// .attr('x', width - margin * 1.4)
// .attr('fill', 'white')
// .attr('stroke', 'white')
// .attr('stroke-width', 0)
// .attr('class', 'yChoice')
// .style('font-family', 'courier')
// .text('Protein')
// .on('click', function(d) {
// d3.selectAll('.yChoice').attr('stroke-width', 0);
// d3.select(this).attr('stroke-width', 2);

// d3.selectAll('.cerealCircles')
// .transition()
// .ease(d3.easeElasticOut)
// .duration(1000)
// .attr("cy", d => proteinScale(parseFloat(d.protein)));
// });

// return svg.node();
// }
Insert cell
Insert cell
cerViz = {
//add new numerical properties for categorical letter properties
cereals.forEach(function(cereal) {
cereal.mfrNumber = cereal.mfr.charCodeAt(0);
cereal.typeNumber = cereal.type.charCodeAt(0);
});

//svg variables
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;

//create SVG artboard
let svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.attr("width", width)
.attr("height", height);

//create svg background color
let bg = svg
.append('rect')
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr('fill', '#002');

//draw X axis
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();
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more