Skip to content

Instantly share code, notes, and snippets.

@nitaku
Last active August 29, 2015 14:06
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/3424eed86f55550622ef to your computer and use it in GitHub Desktop.
Save nitaku/3424eed86f55550622ef to your computer and use it in GitHub Desktop.
Labeled treemap (flare)
# layout, behaviors and scales
SCALE = 400
PADDING = 8
TIP = 10
LABEL_SCALE = 0.7
treemap = d3.layout.treemap()
.size([SCALE, SCALE])
.value((node) -> node.size)
svg = d3.select('svg')
width = svg.node().getBoundingClientRect().width
height = svg.node().getBoundingClientRect().height
# 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(#{-SCALE/2},#{-SCALE/2})"
d3.json 'http://wafi.iit.cnr.it/webvis/tmp/flare.json', (tree) ->
nodes_data = treemap.nodes(tree)
nodes = vis.selectAll('.node')
.data(nodes_data)
enter_nodes = nodes.enter().insert('rect','rect')
.attr
class: 'node'
x: (node) -> node.x
y: (node) -> node.y
width: (node) -> node.dx
height: (node) -> node.dy
stroke: (node) ->
switch node.depth
when 0 then '#333'
when 1 then '#999'
else '#DDD'
'stroke-width': (node) ->
switch node.depth
when 0 then 3
when 1 then 2
else 1
vis.selectAll('.label')
.data(nodes_data.filter((node) -> node.depth is 1))
.enter().append('text')
.text((node) -> node.name)
.attr
class: 'label'
dy: '0.35em'
transform: (node) ->
bbox = this.getBBox()
bbox_aspect = bbox.width / bbox.height
node_bbox = {width: node.dx, height: node.dy}
node_bbox_aspect = node_bbox.width / node_bbox.height
rotate = bbox_aspect >= 1 and node_bbox_aspect < 1 or bbox_aspect < 1 and node_bbox_aspect >= 1
if rotate
bbox_width = node_bbox.height
bbox_height = node_bbox.width
else
bbox_width = node_bbox.width
bbox_height = node_bbox.height
w_ratio = bbox_width / bbox.width
h_ratio = bbox_height / bbox.height
ratio = Math.min(w_ratio, h_ratio)*LABEL_SCALE
return "translate(#{node.x+node.dx/2},#{node.y+node.dy/2}),scale(#{ratio}),rotate(#{if rotate then -90 else 0})"
svg {
background: white;
}
.node {
shape-rendering: crispEdges;
vector-effect: non-scaling-stroke;
fill: none;
}
.label {
pointer-events: none;
fill: #444;
text-anchor: middle;
font-family: sans-serif;
text-shadow: -1px 0 white, 0 1px white, 1px 0 white, 0 -1px white;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="description" content="Labeled treemap (flare)" />
<title>Labeled treemap (flare)</title>
<link type="text/css" href="index.css" rel="stylesheet"/>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg height="500" width="960"></svg>
<script src="index.js"></script>
</body>
</html>
(function() {
var LABEL_SCALE, PADDING, SCALE, TIP, height, svg, treemap, vis, width, zoom, zoomable_layer;
SCALE = 400;
PADDING = 8;
TIP = 10;
LABEL_SCALE = 0.7;
treemap = d3.layout.treemap().size([SCALE, SCALE]).value(function(node) {
return node.size;
});
svg = d3.select('svg');
width = svg.node().getBoundingClientRect().width;
height = svg.node().getBoundingClientRect().height;
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(" + (-SCALE / 2) + "," + (-SCALE / 2) + ")"
});
d3.json('http://wafi.iit.cnr.it/webvis/tmp/flare.json', function(tree) {
var enter_nodes, nodes, nodes_data;
nodes_data = treemap.nodes(tree);
nodes = vis.selectAll('.node').data(nodes_data);
enter_nodes = nodes.enter().insert('rect', 'rect').attr({
"class": 'node',
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) {
switch (node.depth) {
case 0:
return '#333';
case 1:
return '#999';
default:
return '#DDD';
}
},
'stroke-width': function(node) {
switch (node.depth) {
case 0:
return 3;
case 1:
return 2;
default:
return 1;
}
}
});
return vis.selectAll('.label').data(nodes_data.filter(function(node) {
return node.depth === 1;
})).enter().append('text').text(function(node) {
return node.name;
}).attr({
"class": 'label',
dy: '0.35em',
transform: function(node) {
var bbox, bbox_aspect, bbox_height, bbox_width, h_ratio, node_bbox, node_bbox_aspect, ratio, rotate, w_ratio;
bbox = this.getBBox();
bbox_aspect = bbox.width / bbox.height;
node_bbox = {
width: node.dx,
height: node.dy
};
node_bbox_aspect = node_bbox.width / node_bbox.height;
rotate = bbox_aspect >= 1 && node_bbox_aspect < 1 || bbox_aspect < 1 && node_bbox_aspect >= 1;
if (rotate) {
bbox_width = node_bbox.height;
bbox_height = node_bbox.width;
} else {
bbox_width = node_bbox.width;
bbox_height = node_bbox.height;
}
w_ratio = bbox_width / bbox.width;
h_ratio = bbox_height / bbox.height;
ratio = Math.min(w_ratio, h_ratio) * LABEL_SCALE;
return "translate(" + (node.x + node.dx / 2) + "," + (node.y + node.dy / 2) + "),scale(" + ratio + "),rotate(" + (rotate ? -90 : 0) + ")";
}
});
});
}).call(this);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment