Public
Edited
Feb 1, 2023
4 forks
Importers
13 stars
Also listed in…
utils-geo
Insert cell
Insert cell
Insert cell
Insert cell
circleLegend({scale: d3.scaleSqrt().domain([0, 500]).range([0, 40])})
Insert cell
Insert cell
circleLegend({
scale: d3.scaleSqrt().domain([0, 500]).range([0, 40]),
tickValues: [15, 150, 500],
tickFormat: (d, i, g) => i === g.length - 1 ? `${d} bushels of hay` : d,
marginRight: 120
})
Insert cell
Insert cell
circleLegend({
scale: d3.scaleSqrt().domain([0, 500]).range([0, 40]),
tickValues: [15, 150, 500],
tickFormat: (d, i, g) => i === g.length - 1 ? `${d} bushels of your best barley` : d,
tickWrap: true,
marginRight: 80,
marginBottom: 20
})
Insert cell
Insert cell
circleLegend({
scale: d3.scaleSqrt().domain([0, 500]).range([0, 150]),
fill: d3.scaleSequentialSqrt([0, 500], d3.interpolateViridis),
tickValues: [15, 150, 500],
marginLeft: 20
})
Insert cell
Insert cell
function circleLegend({
scale,
fill = "none",
stroke = "black",
strokeWidth = 1,
marginTop = 5,
marginRight = 50,
marginBottom = 10,
marginLeft = 1,
ticks = 3,
tickSize = 5,
tickFormat = (d) => d,
tickValues,
tickStroke = "currentColor",
tickStrokeDash = "4, 2",
tickStrokeWidth = 1,
tickFont = "12px sans-serif",
tickWrap = false,
tickWrapSpacing = 12
} = {}) {
ticks = tickValues || scale.ticks(ticks);
const r = scale(d3.max(ticks));
const width = r * 2 + marginRight;
const height = r * 2 + marginBottom;
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-marginLeft, -marginTop, width, height])
.attr("overflow", "visible");

svg
.selectAll("circle")
.data(ticks)
.join("circle")
.sort((a, b) => b - a)
.attr("fill", fill)
.attr("stroke", stroke)
.attr("stroke-width", strokeWidth)
.attr("cx", r)
.attr("cy", scale)
.attr("r", scale);

svg
.selectAll("line")
.data(ticks)
.join("line")
.attr("stroke", tickStroke)
.attr("stroke-width", tickStrokeWidth)
.attr("stroke-dasharray", tickStrokeDash)
.attr("x1", r)
.attr("x2", tickSize + r * 2)
.attr("y1", (d) => scale(d) * 2)
.attr("y2", (d) => scale(d) * 2);

const ticksFormatted = ticks
.map((d, i, g) => ({ d, text: tickFormat(d, i, g) }))
.map((d) => ({
...d,
lines: getLines(d.text, marginRight - (tickSize + 3), tickFont)
}));

const tickLabels = svg
.selectAll("text")
.data(ticksFormatted)
.join("text")
.attr(
"transform",
({ d }) => `translate(${r * 2 + tickSize + 3}, ${scale(d) * 2})`
)
.style("font", tickFont);

if (tickWrap) {
tickLabels
.selectAll("tspan")
.data((d) => d.lines)
.join("tspan")
.attr("x", 0)
.attr("y", (d, i, g) => (i - g.length / 2 + 0.8) * tickWrapSpacing)
.text((d) => d.text);
} else {
tickLabels.attr("dominant-baseline", "middle").text((d) => d.text);
}

return Object.assign(svg.node(), { dimensions: { height, width } });
}
Insert cell
getPackScale = (pack) => {
const root = pack.leaves()
return d3.scaleSqrt()
.domain(d3.extent(root, d => d.value))
.range(d3.extent(root, d => d.r))
}
Insert cell
// adapted from https://observablehq.com/@mbostock/fit-text-to-circle
getLines = (text, targetWidth, textStyle = "12px sans-serif") => {
const words = text.toString().split(/\s+/g); // To hyphenate: /\s+|(?<=-)/
if (!words[words.length - 1]) words.pop();
if (!words[0]) words.shift();
let line;
let lineWidth0 = Infinity;
const lines = [];
for (let i = 0, n = words.length; i < n; ++i) {
let lineText1 = (line ? line.text + " " : "") + words[i];
let lineWidth1 = getLabelLength(lineText1, textStyle);
if ((lineWidth0 + lineWidth1) / 2 < targetWidth) {
line.width = lineWidth0 = lineWidth1;
line.text = lineText1;
} else {
lineWidth0 = getLabelLength(words[i], textStyle);
line = {width: lineWidth0, text: words[i]};
lines.push(line);
}
}
return lines;
}
Insert cell
import {getLabelLength} from "@bayre/svg-swatches"
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