Public
Edited
Feb 28, 2023
Insert cell
Insert cell
Insert cell
mutable min = 0
Insert cell
mutable max = 10.5
Insert cell
mutable lower_color = "white"
Insert cell
mutable higher_color = "blue"
Insert cell
mutable legend_title = "normalized counts"
Insert cell
VertContLegend = legend({
color: d3.scaleSequential([mutable min, mutable max], [mutable lower_color, mutable higher_color]),
title: mutable legend_title
})
Insert cell
legend({
color: d3.scaleSequentialSqrt([0, 1], d3.interpolateTurbo),
title: "Speed (kts)"
})
Insert cell
legend({
color: d3.scaleDiverging([-0.1, 0, 0.1], d3.interpolatePiYG),
title: "Daily change",
tickFormat: "+%"
})
Insert cell
legend({
color: d3.scaleDivergingSqrt([-0.1, 0, 0.1], d3.interpolateRdBu),
title: "Daily change",
tickFormat: "+%"
})
Insert cell
legend({
color: d3.scaleSequentialLog([1, 100], d3.interpolateBlues),
title: "Energy (joules)",
ticks: 10,
tickFormat: ".0s"
})
Insert cell
legend({
color: d3.scaleSequentialQuantile(Array.from({length: 100}, () => Math.random() ** 2), d3.interpolateBlues),
title: "Quantile",
tickFormat: ".2f"
})
Insert cell
legend({
color: d3.scaleSqrt([-100, 0, 100], ["blue", "white", "red"]),
title: "Temperature (°C)"
})
Insert cell
legend({
color: d3.scaleQuantize([1, 10], d3.schemePurples[9]),
title: "Unemployment rate (%)"
})
Insert cell
legend({
color: d3.scaleQuantile(d3.range(1000).map(d3.randomNormal(100, 20)), d3.schemeSpectral[9]),
title: "Height (cm)",
tickFormat: ".0f"
})
Insert cell
legend({
color: d3.scaleThreshold([2.5, 3.1, 3.5, 3.9, 6, 7, 8, 9.5], d3.schemeRdBu[9]),
title: "Unemployment rate (%)",
tickSize: 0
})
Insert cell
legend({
color: d3.scaleOrdinal(["<10", "10-19", "20-29", "30-39", "40-49", "50-59", "60-69", "70-79", "≥80"], d3.schemeSpectral[10]),
title: "Age (years)",
tickSize: 0
})
Insert cell
Insert cell
swatches({
color: d3.scaleOrdinal(["blueberries", "oranges", "apples"], d3.schemeCategory10)
})
Insert cell
swatches({
color: d3.scaleOrdinal(["Wholesale and Retail Trade", "Manufacturing", "Leisure and hospitality", "Business services", "Construction", "Education and Health", "Government", "Finance", "Self-employed", "Other"], d3.schemeTableau10),
columns: "280px",
swatchSize: 20
})
Insert cell
Insert cell
function legend({
color,
title,
tickSize = 6,
width = 36 + tickSize,
height = 320,
marginTop = 20,
marginRight = 10 + tickSize,
marginBottom = 20,
marginLeft = 5,
ticks = height / 64,
tickFormat,
tickValues
} = {}) {

const svg = d3.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.style("overflow", "visible")
.style("display", "block");

let tickAdjust = g => g.selectAll(".tick line").attr("x1", marginLeft - width + marginRight);
let x;

// Continuous
if (color.interpolate) {
const n = Math.min(color.domain().length, color.range().length);

x = color.copy().rangeRound(d3.quantize(d3.interpolate(height - marginBottom, marginTop), n));

svg.append("image")
.attr("x", marginLeft)
.attr("y", marginTop)
.attr("width", width - marginLeft - marginRight)
.attr("height", height - marginTop - marginBottom)
.attr("preserveAspectRatio", "none")
.attr("xlink:href", ramp(color.copy().domain(d3.quantize(d3.interpolate(0, 1), n))).toDataURL());
}

// Sequential
else if (color.interpolator) {
x = Object.assign(color.copy()
.interpolator(d3.interpolateRound(height - marginBottom, marginTop)),
{range() { return [height - marginBottom, marginTop]; }});

svg.append("image")
.attr("x", marginLeft)
.attr("y", marginTop)
.attr("width", width - marginLeft - marginRight)
.attr("height", height - marginTop - marginBottom)
.attr("preserveAspectRatio", "none")
.attr("xlink:href", ramp(color.interpolator()).toDataURL());

// scaleSequentialQuantile doesn’t implement ticks or tickFormat.
if (!x.ticks) {
if (tickValues === undefined) {
const n = Math.round(ticks + 1);
tickValues = d3.range(n).map(i => d3.quantile(color.domain(), i / (n - 1)));
}
if (typeof tickFormat !== "function") {
tickFormat = d3.format(tickFormat === undefined ? ",f" : tickFormat);
}
}
}

// Threshold
else if (color.invertExtent) {
const thresholds
= color.thresholds ? color.thresholds() // scaleQuantize
: color.quantiles ? color.quantiles() // scaleQuantile
: color.domain(); // scaleThreshold

const thresholdFormat
= tickFormat === undefined ? d => d
: typeof tickFormat === "string" ? d3.format(tickFormat)
: tickFormat;

x = d3.scaleLinear()
.domain([-1, color.range().length - 1])
.rangeRound([height - marginBottom, marginTop]);

svg.append("g")
.selectAll("rect")
.data(color.range())
.join("rect")
.attr("y", (d, i) => x(i))
.attr("x", marginLeft)
.attr("height", (d, i) => x(i - 1) - x(i))
.attr("width", width - marginRight - marginLeft)
.attr("fill", d => d);

tickValues = d3.range(thresholds.length);
tickFormat = i => thresholdFormat(thresholds[i], i);
}

// Ordinal
else {
x = d3.scaleBand()
.domain(color.domain())
.rangeRound([height - marginBottom, marginTop]);

svg.append("g")
.selectAll("rect")
.data(color.domain())
.join("rect")
.attr("y", x)
.attr("x", marginLeft)
.attr("height", Math.max(0, x.bandwidth() - 1))
.attr("width", width - marginLeft - marginRight)
.attr("fill", color);

tickAdjust = () => {};
}

svg.append("g")
.attr("transform", `translate(${width - marginRight},0)`)
.call(d3.axisRight(x)
.ticks(ticks, typeof tickFormat === "string" ? tickFormat : undefined)
.tickFormat(typeof tickFormat === "function" ? tickFormat : undefined)
.tickSize(tickSize)
.tickValues(tickValues))
.call(tickAdjust)
.call(g => g.select(".domain").remove())
.call(g => g.append("text")
.attr("x", marginLeft - width + marginRight)
.attr("y", 0)
.attr("fill", "currentColor")
.attr("text-anchor", "start")
.attr("font-weight", "bold")
.attr("class", "title")
.text(title));

return svg.node();
}
Insert cell
Insert cell
function entity(character) {
return `&#${character.charCodeAt(0).toString()};`;
}
Insert cell
function ramp(color, n = 256) {
const canvas = DOM.canvas(1, n);
const context = canvas.getContext("2d");
for (let i = 0; i < n; ++i) {
context.fillStyle = color(i / (n - 1));
context.fillRect(0, n-i, 1, 1);
}
return canvas;
}
Insert cell
d3 = require("d3@6")
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