Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active December 26, 2015 18:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nitaku/7dc03ae7fd1c5c337c5a to your computer and use it in GitHub Desktop.
Save nitaku/7dc03ae7fd1c5c337c5a to your computer and use it in GitHub Desktop.
Treemaps: boundary coloring

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.

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;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Treemaps: boundary coloring</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg></svg>
<script src="index.js"></script>
</body>
</html>
// 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);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment