Public
Edited
Nov 4, 2022
Fork of Simple D3
1 fork
Insert cell
Insert cell
goals = {
// this some placeholder text you can replace with real data
const csv_text =
`date,quadrant
1/20/2021,1
2/21/2021,2
3/22/2021,3
1/23/2021,4
1/24/2021,5
1/25/2021,6
1/26/2022,1
1/26/2022,6
1/27/2022,1
1/27/2022,1
`
return d3.csvParse(csv_text).map(d => ({...d, date:new Date(d.date)})); // parse out the dates
}
Insert cell
viewof filteredGoals = rangeSlider(goals, d=>d.date, value=>d3.sum(value.values.map(d=>1)));
Insert cell
chart = {
const svg = d3.create("svg")
.attr("width", width)
.attr("height", config.height);
// draw a background hockey rink shape
const rinkRectTop = (config.width-config.rinkWidth)/2;
const rinkRectLeft = (config.height-config.rinkHeight)/2;
const rinkWrapper = svg.append('svg')
.attr('x', rinkRectTop)
.attr('y', rinkRectLeft)
.attr('width', config.rinkWidth)
.attr('height', config.rinkHeight);
rinkWrapper.append('svg:image')
.attr('x', 0)
.attr('y', 0)
.attr('opacity', 0.4)
.attr('width', config.rinkWidth)
.attr('height', config.rinkHeight)
.attr('xlink:href', 'https://media.istockphoto.com/id/459298881/vector/vector-ice-hockey-rink-with-skate-marks.jpg?s=1024x1024&w=is&k=20&c=LqPeqxljyf0MyP9kPRV7IscPTSsrVZI1RN6S2Fjotjg=');
rinkWrapper
.selectAll("svg")
.data(filteredGoalsByQuadrant.keys())
.join("svg") // each quadrant gets its own box to draw pucks in - one for each goal scored from that quadrant
// this resets the 0,0 point to the be top-left of the rink, not teh large `svg` defined previously
.attr('x', d => quandrantToXY(d)[0])
// have to offset the y so that the stack of pucks goes up, not down from base position
.attr('y', d => quandrantToXY(d)[1] - (5+config.iconRadius+filteredGoalsByQuadrant.get(d).length*config.iconRadius))
.attr('width', 5+config.iconRadius*2) // one puck wide
.attr('height', d => 5+config.iconRadius+filteredGoalsByQuadrant.get(d).length*config.iconRadius) // taller if more goals
// now inside the quadrant's box draw one puck per goal
.selectAll("circle")
.data(d => filteredGoalsByQuadrant.get(d))
.join("circle")
.attr("cx", 2+config.iconRadius)
.attr("cy", (d, idx) => 2+(config.iconRadius)+(idx*config.iconRadius))
.attr("r", config.iconRadius)
.attr("stroke", "#333")
.attr("fill", "#af0");
return svg.node();
}
Insert cell
filteredGoalsByQuadrant = d3.group(filteredGoals.data, d => d.quadrant);
Insert cell
quandrantToXY = function(quandrant) {
// for each quadrant this needs to return an [x-coordinate, y-coordinate] array
if (quandrant == 1) return [(config.rinkWidth / 6), ( config.rinkHeight/ 4)*1];
if (quandrant == 2) return [(config.rinkWidth / 6), (config.rinkHeight / 4)*2];
if (quandrant == 3) return [(config.rinkWidth / 6), (config.rinkHeight / 4)*3];
if (quandrant == 4) return [(config.rinkWidth / 6)*2, (config.rinkHeight / 4)*1];
if (quandrant == 5) return [(config.rinkWidth / 6)*2, (config.rinkHeight / 4)*2];
if (quandrant == 6) return [(config.rinkWidth / 6)*2, (config.rinkHeight / 4)*3];
return [0,0]; // just here as a failsafe
}
Insert cell
import {rangeSlider} from "@bumbeishvili/utils"
Insert cell
config = ({
rinkWidth: 200*4, // the internet said hockey rinks are 200x85
rinkHeight: 85*4,
height: 400,
width: width, // this uses the Observable magic `width` variable - would need to be replaced with a number
iconRadius: 10
});
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more