Published unlisted
Edited
Mar 29, 2022
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Plot.plot({
insetLeft: 50,
marks: occlude({ names, padding, remove: true }, [
Plot.dot(data, {
x: "sepalLength",
y: "sepalWidth",
fill: "species",
symbol: "species"
}),
Plot.text(data, {
x: "sepalLength",
y: "sepalWidth",
text: (d) => `${d.species}\n${d.sepalLength}/${d.sepalWidth}`,
textAnchor: "end",
dx: -8
})
]),
symbol: { legend: true }
})
Insert cell
Insert cell
Plot.plot({
insetLeft: 50,
marks: [
Plot.dot(data, {
x: "sepalLength",
y: "sepalWidth",
fill: "species",
symbol: "species"
}),
occludeText(
Plot.text(data, {
x: "sepalLength",
y: "sepalWidth",
text: (d) => `${d.species}\n${d.sepalLength}/${d.sepalWidth}`,
textAnchor: "end",
dx: -8
})
)
],
symbol: { legend: true }
})
Insert cell
function occludeText(marks) {
if (!Array.isArray(marks)) marks = [marks];
return occlude({names: ["text"], remove}, marks);
}
Insert cell
html`<style>.occluded { opacity: .1}`
Insert cell
// options: {name} the nodeNames that should be removed if occluded
// input: an array of marks
// output: an array of marks with occlusion of the last one
function occlude({ names = [], padding = 0, remove = true }, marks) {
const collect = [];
let callback = function () {
for (let i = 0; i < collect.length; ++i) {
const node = collect[i];
const bbox = node.getBoundingClientRect();
collect[i] = {
node,
x: bbox.x,
y: bbox.y,
width: bbox.width,
height: bbox.height
};
}
const filled = [];
for (const d of collect) {
const isOccluded =
names.includes(d.node.nodeName) && filled.some((e) => intersect(d, e, padding));
if (isOccluded) {
if (remove) {
d.node.parentElement.removeChild(d.node);
} else {
d3.select(d.node).classed("occluded", true);
}
} else {
filled.push(d);
}
}
};

marks.forEach((mark, j) => {
const { render } = mark;
mark.render = function () {
const g = render.apply(mark, arguments);
for (const e of g.childNodes) {
collect.push(e);
}

if (callback) {
const cb = callback;
setTimeout(cb, 1);
callback = null;
}
return g;
};
});

return marks;

function intersect(a, b, padding) {
return !(
a.x + a.width < b.x - padding ||
b.x + b.width < a.x - padding ||
a.y + a.height < b.y - padding ||
b.y + b.height < a.y - padding
);
}
}
Insert cell
Insert cell
data = require("@observablehq/iris")
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