Unlisted
Edited
Nov 1, 2023
Insert cell
Insert cell
Plot.plot({
width,
height: 500,
// this also works with faceting
facet: { data: flare, x: (d) => d.size > 10000 },
x: { axis: null },
y: { axis: null, _type: "sqrt" },

marks: [
Plot.rect(
flare,
treemap({
value: "size",
fill: (d) => d.name.split(".")[1],
stroke: "white",
title: "name"
})
),
Plot.text(
flare,
treemap({
value: "size",
z: (d) => d.name.split(".")[1],
text: (d) => (d.size > 8000 ? d.name.split(".").pop() : "")
})
),
Plot.frame()
]
})
Insert cell
import {flare} from "@d3/treemap"
Insert cell
treemap = ({ layout, value, ...options } = {}) => ({
x1: 0, // force a linear scale (for Plot.rect)
x2: 1,
y1: 0,
y2: value, // continuous scale too, hopefully not rounded; type: sqrt is OK
x: options.z || options.fill || options.stroke, // hack to retrieve maybeZ for Plot.text
y: value, // x, y are for Plot.text
// todo: frameAnchor?
...options,

layout(index, scales, values, dimensions) {
// this could call Plot.layout for composition, but it's fine like this
// if (layout != null)
// values = layout.call(this, index, scales, values, dimensions);

const {
width,
marginLeft,
marginRight,
height,
marginTop,
marginBottom
} = dimensions;

const Z = values.x || values.z || values.fill || values.stroke || (() => 0); // maybeZ

const X1 = new Array();
const X2 = new Array();
const Y1 = new Array();
const Y2 = new Array();

const sizes = new Map();

for (const I of index) {
const root = d3.hierarchy({
children: d3
.rollup(
I,
(v) => ({ children: v }),
(i) => Z[i]
)
.values()
});
root.sum(
values.y2
? (i) => Math.abs(values.y2[i] - values.y1[i])
: (i) => Math.abs(values.y[i] - scales.y.scale(0))
);

sizes.set(I, root.value);

const tree = d3
.treemap()
.size([
width - marginLeft - marginRight,
height - marginTop - marginBottom
])(root);

for (const { x0, x1, y0, y1, data: i } of root.leaves()) {
X1[i] = x0;
X2[i] = x1;
Y1[i] = y0;
Y2[i] = y1;
}
}

// resize proportionally to the largest facet; then, add margins
const maxSize = d3.max(sizes.values());
for (const I of index) {
const r = Math.sqrt(sizes.get(I) / maxSize);
for (const i of I) {
X1[i] = X1[i] * r + marginLeft;
X2[i] = X2[i] * r + marginLeft;
Y1[i] = Y1[i] * r + marginTop;
Y2[i] = Y2[i] * r + marginTop;
}
}

values = {
...values,
x1: X1,
x2: X2,
y1: Y1,
y2: Y2,
x: mid(X1, X2),
y: mid(Y1, Y2)
};
return { index, values };
}
})
Insert cell
function mid(X1, X2) {
return Array.from(X1, (d, i) => (d + X2[i]) / 2);
}
Insert cell
import {Plot} from "@observablehq/plot-hexbin-775"
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