Public
Edited
Jan 30, 2023
3 stars
Insert cell
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
plot_chart()
Insert cell
import {move} from "@wattenberger/chart-utility-functions"
Insert cell
Insert cell
data = FileAttachment("beeswarm.json").json()
Insert cell
Insert cell
// accessor function
xAccessor = d => d.impact
Insert cell
// accessor function
rAccessor = d => d.size
Insert cell
// constant y position
y = dms.boundedHeight / 2
Insert cell
Insert cell
dms = {
let dms = {
width: width,
height: 300,
marginTop: 1,
marginRight: 50,
marginBottom: 75,
marginLeft: 70,
}
dms.boundedWidth = dms.width - dms.marginLeft - dms.marginRight
dms.boundedHeight = dms.height - dms.marginTop - dms.marginBottom
return dms
}


Insert cell
dms
Insert cell
Insert cell
// set up beeswarm function
beeswarm_func = beeswarmForce()
Insert cell
// convert raw data to beeswarm layout
data_bs = beeswarm_func(data)
Insert cell
data_bs
Insert cell
Insert cell
x_scale = d3.scaleLinear()
.domain(d3.extent(data, xAccessor))
.range([0, dms.boundedWidth])
Insert cell
r_scale = d3.scaleSqrt()
.domain([0, 100])
.range([1, Math.sqrt(dms.width * dms.boundedHeight) / 30])
Insert cell
colorScale = d3.scaleOrdinal()
.domain(["Sensory", "Demands", "Rest", "Activity"])
.range(["#E48367", "#CC66A3", "#6E99C3", "#3F736E"])
Insert cell
colorScaleHighlight = d3.scaleOrdinal()
.domain(["Sensory", "Demands", "Rest", "Activity"])
.range(["#FF3900", "#FF0099", "#0082FF", "#00FFE6"])
Insert cell
Insert cell
xAxisGen = d3.axisBottom(x_scale)
.tickSizeOuter(0)
Insert cell
Insert cell
// svg = wrapper.append("svg")
// .attr("viewBox", [0,0, width, dms.height])
// .style("overflow", "visible")

plot_chart = function(){

// draw chart structure
const container = d3.select(
html`<div style="position:relative;">${tooltipTemplate}</div>`
);

const tooltipDiv = container.select(".tooltip");
const svg = container.append("svg")
.attr("width", dms.width)
.attr("height", dms.height);
const bounds = svg.append("g")
.attr("transform", move(dms.marginLeft, dms.marginTop));

// draw axis
const drawXAxes = bounds.append("g")
.attr("transform", move(0, dms.boundedHeight))
.attr("class", "xAxisGroup")
.call(xAxisGen);

const addData = bounds.selectAll("circle")
.data(data_bs)
// .join("circle")
.enter()
.append("circle")
.attr("stroke", "black")
.attr("stroke-width", 0)
.attr("fill-opacity", 0.8)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", d => r_scale(d.data.size))
.attr("fill", (d) => colorScale(d.data.class))

// add tool tips
.call(tooltip, tooltipDiv);

// Add X axis label:
const addAxisLab = bounds.append("g")
.attr("class", "xAxisLab")
.append("text")
.attr("text-anchor", "middle")
.attr("x", (dms.boundedWidth / 2))
.attr("y", dms.height - 20)
//.attr("fill", "red")
.text("Impact on my energy resources");

return container.node()
}

Insert cell
Insert cell
<style type="text/css">

@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');

/* set global colours */
:root {
--greyAxis: #808080;
--greyTicks: #CECECE;
}

/* style the x axis */
.xAxisGroup line{
stroke: var(--greyTicks);
}
.xAxisGroup path{
stroke: var(--greyAxis);
}
.xAxisGroup text{
fill: var(--greyAxis);
font-family: 'Lato', sans-serif;
font-size: 16px;
}

.xAxisLab text{
fill: var(--greyAxis);
font-family: 'Lato', sans-serif;
font-size: 20px;
}
</style>
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Insert cell
// {
// const svg = d3.create("svg")
// .attr("width", chartwidth + margin.left + margin.right)
// .attr("height", chartheight + margin.top + margin.bottom);
// const g = svg.append("g")
// .attr("transform", `translate(${[margin.left, margin.top]})`);
// g.append("g")
// .call(d3.axisBottom(x_scale).tickSizeOuter(0))
// .attr("transform", `translate(0, ${chartheight / 1.25})`);
// g.selectAll("circle")
// .data(beeswarm_cell(data))
// .join("circle")
// .attr("stroke", "black")
// .attr("fill-opacity", 0.8)
// .attr("cx", d => d.x)
// .attr("cy", d => d.y)
// .attr("r", d => r_scale(d.data.size));
// return svg.node();
// }
Insert cell
Insert cell
beeswarmForce = function(){

// number of steps in the force simulation
let ticks = 300;

// create a function to map from data to data in beeswarm layout
function beeswarm(dat){

// scale the raw data to pixels
const entries = dat.map(d => {
return {
x0: x_scale(xAccessor(d)),
y0: y,
r: 1 + r_scale(rAccessor(d)),
data: d
}
});

// run force simulation
const simulation = d3.forceSimulation(entries)
.force("x", d3.forceX(d => d.x0))
.force("y", d3.forceY(d => d.y0))
.force("collide", d3.forceCollide(d => d.r));
for (let i = 0; i < ticks; i++) simulation.tick();
return entries;
}
return beeswarm;
}
Insert cell
Insert cell
//d3 = require("d3-selection@2")
Insert cell
tooltipId = DOM.uid().id
Insert cell
tooltipTemplate = html`<div class="tooltip tooltip-${tooltipId}">
<style>

@import url('https://fonts.googleapis.com/css2?family=Lato&display=swap');

/* modify these styles to however you see fit */
div.tooltip-${tooltipId} {
box-sizing: border-box;
position: absolute;
display: none;
top: 0;
left: -100000000px;
padding: 8px 12px;
font-family: 'Lato', sans-serif;
font-size: 16px;
color: #808080;
background-color: #fff;
border: 1px solid #808080;
border-radius: 4px;
pointer-events: none;
z-index: 1;
max-width: 10;
}
div.tooltip-${tooltipId} p {
margin: 0;
}
</style>
<div class="tooltip-contents"></div>
</div>`
Insert cell
tooltip = (selectionGroup, tooltipDiv) => {
// padding between the tooltip and mouse cursor
const MOUSE_POS_OFFSET = 8;

selectionGroup.each(function () {
d3.select(this)
.on("mouseover.tooltip", handleMouseover)
.on("mousemove.tooltip", handleMousemove)
.on("mouseleave.tooltip", handleMouseleave);
});

function handleMouseover() {
// show/reveal the tooltip, set its contents,
// style the element being hovered on
showTooltip();
setContents(d3.select(this).datum());
setStyle(d3.select(this));
}

function handleMousemove(event) {
// update the tooltip's position
const [mouseX, mouseY] = d3.pointer(event, this);
// add the left & top margin values to account for the SVG g element transform
setPosition(mouseX + dms.marginLeft, mouseY + dms.marginTop);
}

function handleMouseleave() {
// do things like hide the tooltip
// reset the style of the element being hovered on
hideTooltip();
resetStyle(d3.select(this));
}

function showTooltip() {
tooltipDiv.style("display", "block");
}

function hideTooltip() {
tooltipDiv.style("display", "none");
}

function setPosition(mouseX, mouseY) {
tooltipDiv
.style(
"top",
mouseY < dms.height / 2 ? `${mouseY + MOUSE_POS_OFFSET}px` : "initial"
)
.style(
"right",
mouseX > width / 2
? `${width - mouseX + MOUSE_POS_OFFSET}px`
: "initial"
)
.style(
"bottom",
mouseY > dms.height / 2
? `${dms.height - mouseY + MOUSE_POS_OFFSET}px`
: "initial"
)
.style(
"left",
mouseX < width / 2 ? `${mouseX + MOUSE_POS_OFFSET}px` : "initial"
);
}

function setContents(datum) {
// customize this function to set the tooltip's contents however you see fit
tooltipDiv
.selectAll("p")
.data(Object.entries(simplifyForTT(datum)))
.join("p")
.filter(([key, value]) => value !== null && value !== undefined)
//.filter(([key, value]) => typeof value === "object")
.html(([key, value]) =>
`<strong>${key}</strong>: ${
typeof value === "object" ? value.toLocaleString("en-US") : value
}`);
}
}
Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
Type JavaScript, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
function resetStyle(selection) {
selection.attr("fill", (d) => colorScale(d.data.class));
}
Insert cell
function setStyle(selection) {
selection.attr("fill", (d) => colorScaleHighlight(d.data.class));
}
Insert cell
simplifyForTT = function(obj)
{let obj2 = {impact: obj.data.impact,
notes: obj.data.notes};
return obj2;}
Insert cell
simplifyForTT(data_bs[1])
Insert cell
data_bs[1]
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