|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<div class="fan"></div> |
|
<style> |
|
|
|
body { |
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
width: 960px; |
|
height: 500px; |
|
position: relative; |
|
} |
|
|
|
.label { |
|
font: 9px sans-serif; |
|
fill: black; |
|
} |
|
text { |
|
font: 10px sans-serif; |
|
} |
|
.spoke { |
|
fill: none; |
|
stroke: #999; |
|
shape-rendering: crispEdges; |
|
stroke-width: 1; |
|
stroke-dasharray: 4, 2; |
|
} |
|
.axis path, .arc, .line { |
|
fill: none; |
|
stroke: #999; |
|
shape-rendering: crispEdges; |
|
stroke-width: 1; |
|
} |
|
|
|
circle { |
|
fill: #1ABC9C; |
|
} |
|
</style> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script> |
|
//Add data |
|
var dataset = [ |
|
[100, 235, "Microtech"], |
|
[345, 120, "Datalab"], |
|
[65, 420, "Nanomesh"], |
|
[110, 112, "Squilch"], |
|
[701, 77, "Red Mango"], |
|
[203, 303, "Blue Gerbil"] |
|
]; |
|
|
|
|
|
//Set constants |
|
var w = 500, |
|
padding = 50; |
|
|
|
//Create adaptable scales |
|
var maxDist = d3.max(dataset, function (d) { |
|
return Math.sqrt(Math.pow(d[0], 2) + Math.pow(d[1], 2)); |
|
}) |
|
|
|
var xScale = d3.scale.linear() |
|
.domain([0, maxDist + 100]) |
|
.range([padding, w - padding]); |
|
|
|
var yScale = d3.scale.linear() |
|
.domain([0, maxDist + 50]) |
|
.range([w - padding, padding]); |
|
|
|
//Set up the svg |
|
var svg = d3.select(".fan") |
|
.append("svg") |
|
.attr("width", w) |
|
.attr("height", w); |
|
|
|
//Set up dragging |
|
var drag = d3.behavior.drag().on("drag", dragmove); |
|
|
|
function dragmove(d) { |
|
d3.select(this) |
|
.attr("x", d.x = d3.event.x) |
|
.attr("y", d.y = d3.event.y) |
|
.attr("dx", 0).attr("dy", 0); |
|
} |
|
|
|
|
|
//Draw an arc |
|
var radius = w - 2 * padding, |
|
radians = Math.PI / 2 |
|
m = w - padding; |
|
|
|
var points = 50; |
|
|
|
var angle = d3.scale.linear() |
|
.domain([0, points - 1]) |
|
.range([0, radians]); |
|
|
|
var arc = d3.svg.line.radial() |
|
.interpolate("basis") |
|
.tension(0) |
|
.radius(radius) |
|
.angle(function (d, i) { |
|
return angle(i); |
|
}); |
|
|
|
svg.append("path").datum(d3.range(points)).attr("class", "arc").attr("d", arc).attr("transform", "translate(" + padding + "," + m + ")"); |
|
|
|
//Calculate the angles for each ROI |
|
roi = [0.1, 0.25, 0.5, 0.75, 1, 1.5, 2, 3, 4, 5, 10, 50]; |
|
|
|
thetas = roi.map(function (x) { |
|
return 180 / Math.PI * Math.atan(x) |
|
}); |
|
|
|
//Add in the spokes |
|
var ga = svg.selectAll(".spokegroup") |
|
.data(thetas) |
|
.enter().append("g") |
|
.attr('class', 'spokegroup') |
|
.attr("transform", function (d) { |
|
return "translate(" + padding + "," + m + ") rotate(" + -d + ")"; |
|
}); |
|
ga.append("line") |
|
.attr("class", "spoke") |
|
.attr("x2", radius); |
|
|
|
//Add in the labels |
|
ga.append("text") |
|
.attr("x", radius + 6) |
|
.attr("dy", ".35em") |
|
.style("text-anchor", function (d) { |
|
return "rotate(180 " + (radius + 6) + ",0)"; |
|
}) |
|
.text(function (d) { |
|
return Math.round(Math.tan(Math.PI / 180 * d) * 100) + "%"; |
|
}); |
|
|
|
//Add in circles for each data point |
|
svg.selectAll(".node") |
|
.data(dataset) |
|
.enter() |
|
.append("g") |
|
.attr("class", "node") |
|
.append("circle") |
|
.attr("cx", function (d) { |
|
return xScale(d[0]); |
|
}) |
|
.attr("cy", function (d) { |
|
return yScale(d[1]); |
|
}) |
|
.attr("r", 5); |
|
|
|
//Add in the labels |
|
svg.selectAll(".node").append("text").attr("class", "label") |
|
.attr("x", function (d) { |
|
return xScale(d[0]); |
|
}) |
|
.attr("y", function (d) { |
|
return yScale(d[1]); |
|
}) |
|
.attr("dx", 10).attr("dy", 10) |
|
.text(function (d) { |
|
return d[2] |
|
}); |
|
|
|
|
|
d3.selectAll(".label").call(drag); |
|
|
|
//Set up the axes |
|
var xAxis = d3.svg.axis() |
|
.scale(xScale) |
|
.orient("bottom"); |
|
|
|
var yAxis = d3.svg.axis() |
|
.scale(yScale) |
|
.orient("left"); |
|
|
|
svg.append("g") |
|
.attr("class", "axis") |
|
.attr("transform", "translate(0," + (w - padding) + ")") |
|
.call(xAxis); |
|
|
|
svg.append("g") |
|
.attr("class", "axis") |
|
.attr("transform", "translate(" + padding + ",0)") |
|
.call(yAxis); |
|
|
|
//Finally some axis labels |
|
svg.append("text") |
|
.attr("class", "xlabel") |
|
.attr("text-anchor", "end") |
|
.attr("x", w / 2) |
|
.attr("y", w - 10) |
|
.text("Cost"); |
|
|
|
svg.append("text") |
|
.attr("class", "ylabel") |
|
.attr("text-anchor", "middle") |
|
.attr("x", -w / 2) |
|
.attr("y", 0) |
|
.attr("dy", ".75em") |
|
.attr("transform", "rotate(-90)") |
|
.text("Revenue"); |
|
|
|
svg.append("text") |
|
.attr("class", "zlabel") |
|
.attr("text-anchor", "middle") |
|
.attr("x", radius + 100) |
|
.attr("y", 0) |
|
.attr("dy", ".75em") |
|
.attr("transform", "translate(30, 470) rotate(-45 0 0) ") |
|
.text("ROI"); |
|
</script> |