An experiment on treemap coloring, using color encoding for depth. Instead of regions, boundaries are colored. A categorical color scale is used to help find nodes at the same level in other parts of the map, while some padding between lines is used together with decreasing thickness to convey a sense of nesting.
// Generated by CoffeeScript 1.10.0
(function() {
var boundary_color, boundary_thickness, height, size, svg, treemap, vis, width, zoom, zoomable_layer;
svg = d3.select('svg');
width = svg.node().getBoundingClientRect().width;
height = svg.node().getBoundingClientRect().height;
size = Math.round(Math.min(width, height) * 0.8);
boundary_thickness = d3.scale.sqrt().range([3, 0.5]);
boundary_color = function(i) {
return d3.hcl(50 + 100 * i, 50, 50);
};
treemap = d3.layout.treemap().size([size, size]).value(function(node) {
return node.size;
});
svg.attr({
viewBox: (-width / 2) + " " + (-height / 2) + " " + width + " " + height
});
zoomable_layer = svg.append('g');
zoom = d3.behavior.zoom().scaleExtent([1, 10]).on('zoom', function() {
return zoomable_layer.attr({
transform: "translate(" + (zoom.translate()) + ")scale(" + (zoom.scale()) + ")"
});
});
svg.call(zoom);
vis = zoomable_layer.append('g').attr({
transform: "translate(" + (-size / 2) + "," + (-size / 2) + ")"
});
d3.json('http://wafi.iit.cnr.it/webvis/tmp/flare.json', function(tree) {
var data, enter_lines, enter_strips, levels, levels_data, lines, strips;
data = treemap.nodes(tree);
boundary_thickness.domain([
d3.min(data, function(d) {
return d.depth;
}), d3.max(data, function(d) {
return d.depth;
})
]);
levels_data = d3.nest().key(function(d) {
return d.depth;
}).entries(data);
levels_data.forEach(function(d) {
return d.key = +d.key;
});
levels_data.sort(function(a, b) {
return d3.descending(a.key, b.key);
});
levels = vis.selectAll('.level').data(levels_data);
levels.enter().append('g').attr({
"class": 'level'
});
strips = levels.selectAll('.boundary_strip').data(function(d) {
return d.values;
});
enter_strips = strips.enter().append('rect').attr({
"class": 'boundary_strip',
x: function(node) {
return node.x;
},
y: function(node) {
return node.y;
},
width: function(node) {
return node.dx;
},
height: function(node) {
return node.dy;
},
'stroke-width': function(node) {
return 12;
}
});
lines = levels.selectAll('.boundary_line').data(function(d) {
return d.values;
});
return enter_lines = lines.enter().append('rect').attr({
"class": 'boundary_line',
x: function(node) {
return node.x;
},
y: function(node) {
return node.y;
},
width: function(node) {
return node.dx;
},
height: function(node) {
return node.dy;
},
stroke: function(node) {
return boundary_color(node.depth);
},
'stroke-width': function(node) {
return boundary_thickness(node.depth);
}
});
});
}).call(this);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Treemaps: boundary coloring</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="//d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg></svg>
<script src="index.js"></script>
</body>
</html>
svg = d3.select('svg')
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height
size = Math.round(Math.min(width, height) * 0.8)
boundary_thickness = d3.scale.sqrt()
.range([3,0.5])
boundary_color = (i) -> d3.hcl(50+100*i,50,50)
treemap = d3.layout.treemap()
.size([size, size])
.value((node) -> node.size)
# translate the viewBox to have (0,0) at the center of the vis
svg
.attr
viewBox: "#{-width/2} #{-height/2} #{width} #{height}"
# append a group for zoomable content
zoomable_layer = svg.append('g')
# define a zoom behavior
zoom = d3.behavior.zoom()
.scaleExtent([1,10]) # min-max zoom
.on 'zoom', () ->
# GEOMETRIC ZOOM
zoomable_layer
.attr
transform: "translate(#{zoom.translate()})scale(#{zoom.scale()})"
# bind the zoom behavior to the main SVG
svg.call(zoom)
# group the visualization
vis = zoomable_layer.append('g')
.attr
transform: "translate(#{-size/2},#{-size/2})"
d3.json 'http://wafi.iit.cnr.it/webvis/tmp/flare.json', (tree) ->
data = treemap.nodes(tree)
boundary_thickness
.domain([d3.min(data, (d) -> d.depth), d3.max(data, (d) -> d.depth)])
levels_data = d3.nest()
.key (d) -> d.depth
.entries data
levels_data.forEach (d) -> d.key = +d.key
# drawing order: deep to shallow
levels_data.sort (a,b) -> d3.descending(a.key,b.key)
levels = vis.selectAll '.level'
.data levels_data
levels.enter().append 'g'
.attr
class: 'level'
strips = levels.selectAll '.boundary_strip'
.data (d) -> d.values
enter_strips = strips.enter().append 'rect'
.attr
class: 'boundary_strip'
x: (node) -> node.x
y: (node) -> node.y
width: (node) -> node.dx
height: (node) -> node.dy
'stroke-width': (node) -> 12
lines = levels.selectAll '.boundary_line'
.data (d) -> d.values
enter_lines = lines.enter().append 'rect'
.attr
class: 'boundary_line'
x: (node) -> node.x
y: (node) -> node.y
width: (node) -> node.dx
height: (node) -> node.dy
stroke: (node) -> boundary_color(node.depth)
'stroke-width': (node) -> boundary_thickness(node.depth)
html, body {
padding: 0;
margin: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
svg {
width: 100%;
height: 100%;
background: white;
}
.boundary_line {
vector-effect: non-scaling-stroke;
fill: none;
}
.boundary_strip {
stroke: white;
vector-effect: non-scaling-stroke;
fill: none;
}