Public
Edited
Jan 17, 2023
3 forks
6 stars
Insert cell
Insert cell
vis = html`<div class="vis-1"></div>`
Insert cell
voronoiTreemap(data, {
bind: d3.select(vis),
treemapRadius: 250
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
vis2 = html`<div class="vis-2"></div>`
Insert cell
voronoiTreemap(data2, {
bind: d3.select(vis2),
treemapRadius: 250
})
Insert cell
Insert cell
function voronoiTreemap (data, {
bind = null, // pass in a d3 selection i.e d3.select('class/div')
width = 500,
height = 500,
margin = { top: 10, right: 10, bottom: 10, left: 10 },
treemapRadius = 205,
treemapCenter = [width / 2, height / 2],
} = {}) {

// set the dimensions and margins of the graph
const w = width + margin.left + margin.right;
const h = height + margin.top + margin.bottom;

bind.selectAll('svg').remove();
const svg = bind.append('svg')
.attr('width', w)
.attr('height', h)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);

// begin: constants
const _2PI = 2 * Math.PI;
// end: layout conf.
const formatThousands = d3.format(',d');

// begin: treemap conf.
const _voronoiTreemap = d3.voronoiTreemap();
let hierarchy;
let circlingPolygon;
// end: treemap conf.

initData()
initLayout(data)

hierarchy = d3.hierarchy(data).sum(d => d.weight);
_voronoiTreemap
.clip(circlingPolygon)
(hierarchy);

drawTreemap(hierarchy);

function initData (rootData) {
circlingPolygon = computeCirclingPolygon(treemapRadius);
}

function computeCirclingPolygon (radius) {
let points = 60;
let increment = _2PI / points;
let circlingPolygon = [];

for (let a = 0, i = 0; i < points; i++, a += increment) {
circlingPolygon.push(
[radius + radius * Math.cos(a), radius + radius * Math.sin(a)]
)
}

return circlingPolygon;
};

function initLayout (rootData) {
const drawingArea = svg.append('g')
.classed('drawingArea', true)
.attr('transform', 'translate(' + [margin.left, margin.top] + ')');

const treemapContainer = drawingArea.append('g')
.classed('treemap-container', true)
.attr('transform', 'translate(' + treemapCenter + ')');

treemapContainer.append('path')
.classed('world', false)
.attr('transform', 'translate(' + [-treemapRadius, -treemapRadius] + ')')
.attr('d', 'M' + circlingPolygon.join(',') + 'Z');
}

function drawTreemap (hierarchy) {
const leaves = hierarchy.leaves();
const _treemapContainer = svg.select('g.treemap-container');
console.log('_treemapContainer', _treemapContainer);

const cells = _treemapContainer.append('g')
.classed('cells', true)
.attr('transform', 'translate(' + [-treemapRadius, -treemapRadius] + ')')
.selectAll('path.cell')
.data(leaves)
.join('path')
.classed('cell', true)
.attr('d', d => 'M' + d.polygon.join(',') + 'z')
.attr('fill', d => d.parent.data.color);

const labels = _treemapContainer.append('g')
.classed('labels', true)
.attr('transform', 'translate(' + [-treemapRadius, -treemapRadius] + ')')
.selectAll('.label')
.data(leaves)
.join('g')
.classed('label', true)
.attr('transform', d => 'translate(' + [d.polygon.site.x, d.polygon.site.y] + ')');

labels.append('text')
.classed('name', true)
.html(d => (d.data.weight < 1) ? d.data.code : d.data.name);
labels.append('text')
.classed('value', true)
.text(d => formatThousands(d.data.weight));
}

return svg.node();

}
Insert cell
Insert cell
data = {
return {
'name': 'groups',
'children': [
{
'name': 'GROUP-1',
'color': '#fcb315',
'children': [
{ 'name': 'GROUP-1', 'weight': group_1, 'code': '--' }
]
},
{
'name': 'GROUP-2',
'color': '#00939b',
'children': [
{ 'name': 'GROUP-2', 'weight': group_2, 'code': '--' }
]
},
{
'name': 'GROUP-3',
'color': '#40456a',
'children': [
{ 'name': 'GROUP-3.1', 'weight': group_3, 'code': '--' }
]
},
{
'name': 'GROUP-4',
'color': '#40456a',
'children': [
{ 'name': 'GROUP-3.2', 'weight': group_4, 'code': '--' }
]
},
{
'name': 'GROUP-5',
'color': '#40456a',
'children': [
{ 'name': 'GROUP-3.3', 'weight': group_5, 'code': '--' }
]
}
]
}
}
Insert cell
data2 = {
return {
'name': 'groups',
'children': [
{
'name': 'GROUP-1',
'color': '#fcb315',
'children': [
{ 'name': 'GROUP-1', 'weight': group_1, 'code': '--' }
]
},
{
'name': 'GROUP-2',
'color': '#00939b',
'children': [
{ 'name': 'GROUP-2', 'weight': group_2, 'code': '--' }
]
},
{
'name': 'GROUP-3',
'color': '#40456a',
'children': [
{ 'name': 'GROUP-3.1', 'weight': group_3, 'code': '--' }
]
}
]
}
}
Insert cell
Insert cell
d3 = require("d3@6", "d3-weighted-voronoi", "d3-voronoi-map", "d3-voronoi-treemap")
Insert cell
Insert cell
<hr>
<link href="https://fonts.googleapis.com/css?family=Space+Mono" rel="stylesheet">
<style>
text {
font-family:'Space Mono',monospace;
}
.label {
text-anchor: middle;
fill: white;
font-size: 14px;
}
.label>.name {
dominant-baseline: text-after-edge;
}
.label>.value {
dominant-baseline: text-before-edge;
}
.cells {
stroke: #fff;
stroke-width: 4px;
}
</style>
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