TBC This experiment tries to solve some problems of word clouds by using a treemap layout to create one. See the chapter about Wordle in Steele and Iliinski's Beautiful Visualization (page 37).
Last active
August 29, 2015 14:07
-
-
Save nitaku/79f16392f7e1bed77f07 to your computer and use it in GitHub Desktop.
Word cloud treemap (flare)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# layout, behaviors and scales | |
SCALE = 400 | |
treemap = d3.layout.treemap() | |
.size([SCALE, SCALE]) | |
.value((node) -> node.size) | |
correct_x = d3.scale.linear() | |
.domain([0, SCALE]) | |
.range([0, 420]) | |
correct_y = d3.scale.linear() | |
.domain([0, SCALE]) | |
.range([0, 300]) | |
color = (txt, light) -> | |
Math.seedrandom(txt+'abcdef') | |
noise = (W) -> Math.random()*W - W/2 | |
d3.hcl(0+noise(360), 20, if light then 65 else 25) | |
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.filter((node) -> node.depth is 1)) | |
# | |
#enter_nodes = nodes.enter().append('rect') | |
# .attr | |
# class: 'node' | |
# x: (node) -> node.x | |
# y: (node) -> node.y | |
# width: (node) -> node.dx | |
# height: (node) -> node.dy | |
# fill: (node) -> color(node.name, true) | |
labels = vis.selectAll('.label') | |
.data(nodes_data.filter((node) -> node.depth is 1)) | |
enter_labels = labels.enter().append('svg') | |
.attr | |
class: 'label' | |
enter_labels.append('text') | |
.text((node) -> node.name.toUpperCase()) | |
.attr | |
dy: '0.35em' | |
fill: (node) -> color(node.name, false) | |
.each (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 | |
node.label_bbox = { | |
x: bbox.x+(bbox.width-correct_x(bbox.width))/2, | |
y: bbox.y+(bbox.height-correct_y(bbox.height))/2, | |
width: correct_x(bbox.width), | |
height: correct_y(bbox.height) | |
} | |
if rotate | |
node.label_bbox = { | |
x: node.label_bbox.y, | |
y: node.label_bbox.x, | |
width: node.label_bbox.height, | |
height: node.label_bbox.width | |
} | |
d3.select(this).attr('transform', 'rotate(-90)') | |
enter_labels | |
.attr | |
x: (node) -> node.x | |
y: (node) -> node.y | |
width: (node) -> node.dx | |
height: (node) -> node.dy | |
viewBox: (node) -> "#{node.label_bbox.x} #{node.label_bbox.y} #{node.label_bbox.width} #{node.label_bbox.height}" | |
preserveAspectRatio: 'none' | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
svg { | |
background: white; | |
} | |
.node { | |
shape-rendering: crispEdges; | |
vector-effect: non-scaling-stroke; | |
stroke: white; | |
stroke-width: 2; | |
} | |
.label { | |
pointer-events: none; | |
text-anchor: middle; | |
font-family: Impact; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<meta name="description" content="Word cloud treemap (flare)" /> | |
<title>Word cloud treemap (flare)</title> | |
<link type="text/css" href="index.css" rel="stylesheet"/> | |
<script src="http://davidbau.com/encode/seedrandom-min.js"></script> | |
<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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var SCALE, color, correct_x, correct_y, height, svg, treemap, vis, width, zoom, zoomable_layer; | |
SCALE = 400; | |
treemap = d3.layout.treemap().size([SCALE, SCALE]).value(function(node) { | |
return node.size; | |
}); | |
correct_x = d3.scale.linear().domain([0, SCALE]).range([0, 420]); | |
correct_y = d3.scale.linear().domain([0, SCALE]).range([0, 300]); | |
color = function(txt, light) { | |
var noise; | |
Math.seedrandom(txt + 'abcdef'); | |
noise = function(W) { | |
return Math.random() * W - W / 2; | |
}; | |
return d3.hcl(0 + noise(360), 20, light ? 65 : 25); | |
}; | |
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_labels, labels, nodes_data; | |
nodes_data = treemap.nodes(tree); | |
labels = vis.selectAll('.label').data(nodes_data.filter(function(node) { | |
return node.depth === 1; | |
})); | |
enter_labels = labels.enter().append('svg').attr({ | |
"class": 'label' | |
}); | |
enter_labels.append('text').text(function(node) { | |
return node.name.toUpperCase(); | |
}).attr({ | |
dy: '0.35em', | |
fill: function(node) { | |
return color(node.name, false); | |
} | |
}).each(function(node) { | |
var bbox, bbox_aspect, node_bbox, node_bbox_aspect, rotate; | |
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; | |
node.label_bbox = { | |
x: bbox.x + (bbox.width - correct_x(bbox.width)) / 2, | |
y: bbox.y + (bbox.height - correct_y(bbox.height)) / 2, | |
width: correct_x(bbox.width), | |
height: correct_y(bbox.height) | |
}; | |
if (rotate) { | |
node.label_bbox = { | |
x: node.label_bbox.y, | |
y: node.label_bbox.x, | |
width: node.label_bbox.height, | |
height: node.label_bbox.width | |
}; | |
return d3.select(this).attr('transform', 'rotate(-90)'); | |
} | |
}); | |
return enter_labels.attr({ | |
x: function(node) { | |
return node.x; | |
}, | |
y: function(node) { | |
return node.y; | |
}, | |
width: function(node) { | |
return node.dx; | |
}, | |
height: function(node) { | |
return node.dy; | |
}, | |
viewBox: function(node) { | |
return "" + node.label_bbox.x + " " + node.label_bbox.y + " " + node.label_bbox.width + " " + node.label_bbox.height; | |
}, | |
preserveAspectRatio: 'none' | |
}); | |
}); | |
}).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment