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

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more