Published
Edited
May 28, 2021
5 forks
1 star
Insert cell
Insert cell
Insert cell
Insert cell
chart = {
const colorScale = d3.scaleLinear()
.domain([0, 200000])
.range(["#eee", "#009900"]);
const x = d3.scaleLinear().rangeRound([0, width]);
const y = d3.scaleLinear().rangeRound([0, height]);

const svg = d3.create("svg")
.attr('id', 'chart')
.attr("viewBox", [0.5, -30.5, width, height + 30])
.style("font", "11px Roboto Condensed")

let group = svg.append("g")
.call(render, treemap(data));

function render(group, root) {
const node = group
.selectAll("g")
.data(root.children.concat(root))
.join("g");

node.filter(d => d === root ? d.parent : d.children)
.attr("cursor", "pointer")
.on("click", (event, d) => d === root ? zoomout(root) : zoomin(d));

node.append("title")
.text(d => `${name(d)}\n${format(d.value)}`);

node.append("rect")
.attr("id", d => (d.leafUid = DOM.uid("leaf")).id)
.attr("fill", d => {
return colorScale(d.value)//d === root ? "#fff" : d.children ? "#ccc" : "#ddd"
})
.attr("stroke", "#000");

node.append("clipPath")
.attr("id", d => (d.clipUid = DOM.uid("clip")).id)
.append("use")
.attr("xlink:href", d => d.leafUid.href);

node.append("text")
.attr("clip-path", d => d.clipUid)
.attr("font-weight", d => d === root ? "bold" : null)
.selectAll("tspan")
.data(d => (d === root ? [d.data.id.replace("@","/")+"/"+format(d.value)] : d.data.id.split("@").concat(format(d.value))))
// .data(d => {
// var labels = [d.data.id.replace("@","/"),d.value]
// console.log(labels)
// console.log(d.data.id.split("@").concat(format(d.value)))
// return d.data.id.split("@").concat(format(d.value))
// })
//.data(d => (d === root ? name(d) : d.data.name).split(/(?=[A-Z][^A-Z])/g).concat(format(d.value)))
.join("tspan")
.attr("x", 3)
.attr("y", (d, i, nodes) => i === nodes.length - 1 ? 20 + i*10 : 11 + i*10)
.attr("fill-opacity", (d, i, nodes) => i === nodes.length - 1 ? 0.7 : null)
.attr("font-weight", (d, i, nodes) => i === nodes.length - 1 ? "normal" : null)
.attr("font-size", (d, i, nodes) => i === nodes.length - 1 ? 20 : null)
.text(d => d);

group.call(position, root);
}

function position(group, root) {
group.selectAll("g")
.attr("transform", d => d === root ? `translate(0,-30)` : `translate(${x(d.x0)},${y(d.y0)})`)
.select("rect")
.attr("width", d => d === root ? width : x(d.x1) - x(d.x0))
.attr("height", d => d === root ? 30 : y(d.y1) - y(d.y0));
}

// When zooming in, draw the new nodes on top, and fade them in.
function zoomin(d) {
const group0 = group.attr("pointer-events", "none");
const group1 = group = svg.append("g").call(render, d);

x.domain([d.x0, d.x1]);
y.domain([d.y0, d.y1]);

svg.transition()
.duration(750)
.call(t => group0.transition(t).remove()
.call(position, d.parent))
.call(t => group1.transition(t)
.attrTween("opacity", () => d3.interpolate(0, 1))
.call(position, d));
}

// When zooming out, draw the old nodes on top, and fade them out.
function zoomout(d) {
const group0 = group.attr("pointer-events", "none");
const group1 = group = svg.insert("g", "*").call(render, d.parent);

x.domain([d.parent.x0, d.parent.x1]);
y.domain([d.parent.y0, d.parent.y1]);

svg.transition()
.duration(750)
.call(t => group0.transition(t).remove()
.attrTween("opacity", () => d3.interpolate(1, 0))
.call(position, d))
.call(t => group1.transition(t)
.call(position, d.parent));
}

return svg.node();
}
Insert cell
data = d3.csvParse(await FileAttachment("AN_sales_data_small.csv").text(), d3.autoType)
Insert cell
treemap = data => d3.treemap()
.tile(tile)
//(d3.hierarchy(data)
(stratify(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value))
Insert cell
stratify = d3.stratify()
.id(d => d.id)
.parentId(d => d.id.substring(0, d.id.lastIndexOf("@")))
Insert cell
Insert cell
function tile(node, x0, y0, x1, y1) {
d3.treemapBinary(node, 0, 0, width, height);
for (const child of node.children) {
child.x0 = x0 + child.x0 / width * (x1 - x0);
child.x1 = x0 + child.x1 / width * (x1 - x0);
child.y0 = y0 + child.y0 / height * (y1 - y0);
child.y1 = y0 + child.y1 / height * (y1 - y0);
}
}
Insert cell
name = d => d.ancestors().reverse().map(d => d.data.name).join("/")
Insert cell
//width = 1200//954
Insert cell
height = 924
Insert cell
html`
<style>
#chart {
background: #FF0000;
}

.title {
font-family: 'Montserrat', sans-serif;
font-weight: 700;
font-size: 28px;
fill: #28CC5F;
}

.subtitle {
font-family: 'Montserrat', sans-serif;
font-weight: 400;
font-size: 18px;
fill: white;
}

.description {
font-family: 'Montserrat', sans-serif;
font-weight: 400;
font-size: 13px;
fill: white;
}

.tick * {
font-family: 'Montserrat', sans-serif;
font-weight: 400;
font-size: 10px;
fill: white;
}

.tick line {
stroke: white;
}

.domain {
stroke: white;
}

.line-bar {
fill: none;
}

.label-artist {
font-family: 'Montserrat', sans-serif;
font-weight: 400;
font-size: 12px;
fill: white;
}

.label-song {
font-family: 'Montserrat', sans-serif;
font-weight: 300;
font-size: 10px;
fill: lightgray;
}

.label-week {
font-family: 'Montserrat', sans-serif;
font-weight: 300;
font-size: 10px;
fill: lightgray;
}

.label-date {
font-family: 'Montserrat', sans-serif;
font-weight: 300;
font-size: 9px;
fill: lightgray;
}
`
Insert cell
format = d3.format(",d")
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