Built with blockbuilder.org.
Click on Open in a new window to see responsiveness.
forked from coderextreme's block: Responsive Double Hierarchy DAG cluster
license: mit |
Built with blockbuilder.org.
Click on Open in a new window to see responsiveness.
forked from coderextreme's block: Responsive Double Hierarchy DAG cluster
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Double Hierarchy DAG</title> | |
<style> | |
body { | |
width:100%; | |
height:100%; | |
} | |
</style> | |
<script src="http://d3js.org/d3.v3.min.js"></script> | |
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> | |
</head> | |
<body> | |
<div style="width:100%;height:100%"></div> | |
<script type="text/javascript"> | |
function setup(json) { | |
var domain = []; | |
var range = []; | |
var i = 0; | |
for (ch in json.children) { | |
child = json.children[ch]; | |
domain.push(child.name); | |
range.push(i); | |
for (gc in child.children) { | |
grandchild = child.children[gc]; | |
domain.push(grandchild.name); | |
range.push(i); | |
} | |
i++; | |
} | |
var map = d3.scale.ordinal() | |
.domain(domain) | |
.range(range); | |
d3.select(window) | |
.on("resize", function() { | |
/* | |
width = window.innerWidth; | |
height = window.innerHeight; | |
d3.select("svg") | |
.attr("width", width) | |
.attr("height", height); | |
*/ | |
d3.select("svg").remove(); | |
redraw(json, map); | |
}) | |
redraw(json, map); | |
} | |
function getLinks(json, count) { | |
var map = {}; | |
var other = []; | |
json.forEach(function(d) { | |
map[d.name] = d; | |
count[d.name] = 0; | |
}); | |
json.forEach(function(d) { | |
if (d.other) d.other.forEach(function(o) { | |
count[o.name]++; | |
other.push({source: map[d.name], target: map[o.name]}); | |
}); | |
// once the big circles have been plotted, move y inward | |
if (d.depth == 2) { | |
d.y /= 1.15; | |
} | |
}); | |
json.forEach(function(d) { | |
if (d.depth == 2 && count[d.name] > 1) { | |
d.x -= count[d.name] + 0.5; | |
} | |
}); | |
return other; | |
} | |
function redraw(json, map) { | |
var width = $("div").width() || Math.max(document.documentElement.clientWidth, window.innerWidth || 0); | |
var height = $("div").height() || Math.max(document.documentElement.clientHeight, window.innerHeight || 0) | |
var min = width < height ? width : height; | |
var rx = width/2; | |
var ry = height/2; | |
var rm = min/2; | |
var color = d3.scale.category20(); | |
var pi = Math.PI; | |
var bundle = d3.layout.bundle(); | |
var line = d3.svg.line.radial() | |
.interpolate("bundle") | |
.tension(0.50) | |
.radius(function(d) { return d.y; }) | |
.angle(function(d) { return d.x / 180 * pi; }); | |
var arc = d3.svg.arc() | |
.innerRadius(0) | |
.outerRadius(function(d,i) {return min/7; }) | |
.startAngle(function(d,i) { return 0;}) | |
.endAngle(function(d) { return 2*pi;}); | |
var svg = d3.select("div") | |
.append("svg:svg") | |
.attr("viewBox", "0 0 " + width + " " + height); | |
svg.append('defs') | |
.append('path') | |
.attr("d", arc) | |
.attr("id", "curvedPath"); | |
var center = svg.append("svg:g") | |
.attr("transform", "translate(" + rx + "," + ry + ")"); | |
var cluster = d3.layout.cluster() | |
.separation(function(d) { return 1;}) | |
.size([360, min/4]) | |
.sort(null); | |
var nodes = cluster.nodes(json); | |
var node = center.selectAll("g.node") | |
.data(nodes) | |
.enter().append("svg:g") | |
node.append("svg:text") | |
.attr("text-anchor", function(d) { return "middle"; }) | |
.append('textPath') | |
.attr("startOffset", function(d) { | |
var percent = 100*d.x/360; | |
return (percent >= 50 ? percent - 50 : percent + 50) +"%"; | |
}) | |
.attr("xlink:href", function(d) { return (d.depth == 1 ? "#curvedPath" : null); }) | |
.attr("fill", function(d, i) { return color(map(d.name)); } ) | |
.style("font-size", min/45 + "px") | |
.text(function(d) { return (d.depth == 1 ? d.name : ""); }); | |
var g = node.append("svg:g") | |
.attr("class", "node") | |
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ") translate(" + d.y + ")"; }); | |
g.append("svg:circle") | |
.attr("r", function(d) { return d.size * min / 900; }) | |
.attr("fill", function (d,i) { return color(map(d.name));}); | |
g.append("svg:text") | |
.attr("dx", function(d) { return d.x < 180 ? 18 : -18; }) | |
.attr("dy", ".31em") | |
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) | |
.attr("transform", function(d) { return d.x < 180 ? "" : "rotate(180)"; }) | |
.style("font-size", min/45 + "px") | |
.text(function(d) { return (d.depth == 2 ? d.name : ""); }) | |
// minor links | |
var count = {}; | |
var links = getLinks(nodes, count); | |
var splines = bundle(links); | |
center.selectAll(".path") | |
.data(links) | |
.enter().append("svg:path") | |
.attr("stroke", function(d) {return color(map(d.source.name));}) | |
.attr("fill", "none") | |
.attr("class", "path") | |
.attr("d", function(d, i) { | |
var o = splines[i][3]; | |
if (o.depth == 2 && count[o.name] > 1) { | |
o.x += 2.5; | |
} | |
return line(splines[i]); }); | |
// set o.x back | |
for (i in splines) { | |
var o = splines[i][3]; | |
if (o.depth == 2 && count[o.name] > 1) { | |
o.x -= 2.5; | |
} | |
} | |
// minor circles | |
center.selectAll(".circle") | |
.data(links) | |
.enter().append("svg:g") | |
.attr("class", "circle") | |
.attr("transform", function(d,i) { | |
var o = splines[i][3]; | |
if (o.depth == 2 && count[o.name] > 1) { | |
o.x += 2.5; | |
} | |
return "rotate(" + (o.x - 90) + ") translate(" + d.target.y + ")"; | |
}) | |
.append("svg:circle") | |
.attr("r", function(d) { return d.source.size * min / 900; }) | |
.attr("fill", function (d,i) { return color(map(d.source.name));}); | |
} | |
$(document).ready(function() { | |
d3.json("sw.json", function(error, json) { | |
if (error) return console.warn(error); | |
setup(json); | |
}); | |
}); | |
</script> | |
<script type="text/javascript"> | |
// Hack to make this example display correctly in an iframe on bl.ocks.org | |
d3.select(self.frameElement).style("height", "700px"); | |
</script> | |
</body> | |
</html> |
{ | |
"name": "Center", | |
"size": 0, | |
"children": [ | |
{ | |
"name": "Research", | |
"size": 5, | |
"children": [ | |
{ "name": "Blanc Brain", "size": 15}, | |
{ "name": "Consultation", "size": 15} | |
] | |
}, | |
{ | |
"name": "Plan", | |
"size": 5, | |
"children": [ | |
{ "name": "Information Architecture", "size": 15}, | |
{ "name": "Accessibility", "size": 15} | |
], | |
"other": [ | |
{ "name": "Consultation", "size": 5}, | |
{ "name": "High availability", "size": 5}, | |
{ "name": "Open Source", "size": 5} | |
] | |
}, | |
{ | |
"name": "Create", | |
"size": 5, | |
"children": [ | |
{ "name": "Creative/Design", "size": 15}, | |
{ "name": "User Interface", "size": 15}, | |
{ "name": "Content Creation", "size": 15}, | |
{ "name": "Photography & Video Production", "size": 15}, | |
{ "name": "Infographics", "size": 15} | |
], | |
"other": [ | |
{ "name": "Responsive Design", "size": 5}, | |
{ "name": "Information Architecture", "size": 5}, | |
{ "name": "Accessibility", "size": 5} | |
] | |
}, | |
{ | |
"name": "Build", | |
"size": 5, | |
"children": [ | |
{ "name": "Website Development", "size": 15}, | |
{ "name": "Web App Development", "size": 15}, | |
{ "name": "Bespoke CMS", "size": 15}, | |
{ "name": "Django", "size": 15}, | |
{ "name": "Python", "size": 15}, | |
{ "name": "Open Source", "size": 15}, | |
{ "name": "Responsive Design", "size": 15} | |
], | |
"other": [ | |
{ "name": "Information Architecture", "size": 5}, | |
{ "name": "Accessibility", "size": 5} | |
] | |
}, | |
{ | |
"name": "Launch", | |
"size": 5, | |
"children": [ | |
{ "name": "High availability", "size": 15}, | |
{ "name": "Hosting", "size": 15}, | |
{ "name": "Support & Maintenance", "size": 15} | |
], | |
"other": [ | |
{ "name": "Blanc Brain", "size": 5} | |
] | |
} | |
] | |
} |