Created
October 4, 2013 03:32
-
-
Save estk/6820581 to your computer and use it in GitHub Desktop.
This file contains hidden or 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> | |
<title>Radar chart</title> | |
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/> | |
<style> | |
body { | |
font: 10px sans-serif; | |
} | |
.axis + .axis g text { | |
display: none; | |
} | |
.chart { | |
margin: auto; | |
text-align:center; | |
} | |
</style> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
<script src="RadarChart.js" charset="utf-8"></script> | |
</head> | |
<body> | |
<div class="chart"></div> | |
<script type="text/javascript"> | |
d3.select(self.frameElement).style("height", "900px"); | |
var colorscale = d3.scale.category10(); | |
// Data | |
d = [ | |
[{ axis: "Novelty", value: .5}, | |
{ axis:"Originiality", value: .5}, | |
{ axis:"Multidimentionality", value: .5}, | |
{ axis:"Density", value: .5}, | |
{ axis:"Functionality", value: .5}, | |
{ axis:"Abstraction", value: .5}, | |
{ axis:"Redundancy", value: .5}, | |
{ axis:"Familiarity", value: .5}, | |
{ axis:"Unidimensionality", value: .5}, | |
{ axis:"Lightness", value: .5}, | |
{ axis:"Decoration", value: .5}, | |
{ axis:"Figuration", value: .5}] | |
]; | |
//Options for the Radar chart, other than default | |
var config = { | |
w: 600, | |
h: 600, | |
rotate: Math.PI, | |
maxValue: 1, | |
levels: 0, | |
factor: .925, | |
opacityArea: .2, | |
opacityFill: .5, | |
ExtraWidthX: 200 | |
} | |
RadarChart.draw(".chart", d, config); | |
</script> | |
</body> | |
</html> | |
This file contains hidden or 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
//Practically all this code comes from https://github.com/alangrafu/radar-chart-d3 | |
//I only made some additions and aesthetic adjustments to make the chart look better | |
//(of course, that is only my point of view) | |
//Such as a better placement of the titles at each line end, | |
//adding numbers that reflect what each circular level stands for | |
//Not placing the last level and slight differences in color | |
// | |
//For a bit of extra information check the blog about it: | |
//http://nbremer.blogspot.nl/2013/09/making-d3-radar-chart-look-bit-better.html | |
// | |
var RadarChart = { | |
draw: function(id, d, options){ | |
var cfg = { | |
radius: 5, | |
w: 600, | |
h: 600, | |
rotate: 0, | |
factor: 1, | |
factorLegend: 0.85, | |
levels: 3, | |
maxValue: 0, | |
radians: 2 * Math.PI, | |
opacityArea: 0.5, | |
opacityFill: 0.7, | |
ToRight: 5, | |
TranslateX: 80, | |
TranslateY: 30, | |
ExtraWidthX: 100, | |
ExtraWidthY: 100, | |
color: d3.scale.category10() | |
}; | |
if('undefined' !== typeof options){ | |
for(var i in options){ | |
if('undefined' !== typeof options[i]){ | |
cfg[i] = options[i]; | |
} | |
} | |
} | |
cfg.maxValue = Math.max(cfg.maxValue, d3.max(d, function(i){return d3.max(i.map(function(o){return o.value;}))})); | |
var allAxis = (d[0].map(function(i, j){return i.axis;})); | |
var total = allAxis.length; | |
var radius = cfg.factor*Math.min(cfg.w/2, cfg.h/2); | |
var Format = d3.format('%'); | |
d3.select(id).select("svg").remove(); | |
function getXCoord(i) { | |
return cfg.w/2*(1-cfg.factor*Math.sin((i*cfg.radians+cfg.rotate)/total)); | |
} | |
function getYCoord(i) { | |
return cfg.h/2*(1-cfg.factor*Math.cos((i*cfg.radians+cfg.rotate)/total)); | |
} | |
var g = d3.select(id) | |
.append("svg") | |
.attr("width", cfg.w+cfg.ExtraWidthX) | |
.attr("height", cfg.h+cfg.ExtraWidthY) | |
.append("g") | |
.attr("transform", "translate(" + cfg.TranslateX + "," + cfg.TranslateY + ")"); | |
var tooltip; | |
//Circular segments | |
for(var j=0; j<cfg.levels-1; j++){ | |
var levelFactor = cfg.factor*radius*((j+1)/cfg.levels); | |
g.selectAll(".levels") | |
.data(allAxis) | |
.enter() | |
.append("svg:line") | |
.attr("x1", function(d, i){return levelFactor*(1-cfg.factor*Math.sin(i*cfg.radians/total));}) | |
.attr("y1", function(d, i){return levelFactor*(1-cfg.factor*Math.cos(i*cfg.radians/total));}) | |
.attr("x2", function(d, i){return levelFactor*(1-cfg.factor*Math.sin((i+1)*cfg.radians/total));}) | |
.attr("y2", function(d, i){return levelFactor*(1-cfg.factor*Math.cos((i+1)*cfg.radians/total));}) | |
.attr("class", "line") | |
.style("stroke", "grey") | |
.style("stroke-opacity", "0.75") | |
.style("stroke-width", "0.3px") | |
.attr("transform", "translate(" + (cfg.w/2-levelFactor) + ", " + (cfg.h/2-levelFactor) + ")"); | |
} | |
//Text indicating at what % each level is | |
for(var j=0; j<cfg.levels; j++){ | |
var levelFactor = cfg.factor*radius*((j+1)/cfg.levels); | |
g.selectAll(".levels") | |
.data([1]) //dummy data | |
.enter() | |
.append("svg:text") | |
.attr("x", function(d){return levelFactor*(1-cfg.factor*Math.sin(0));}) | |
.attr("y", function(d){return levelFactor*(1-cfg.factor*Math.cos(0));}) | |
.attr("class", "legend") | |
.style("font-family", "sans-serif") | |
.style("font-size", "10px") | |
.attr("transform", "translate(" + (cfg.w/2-levelFactor + cfg.ToRight) + ", " + (cfg.h/2-levelFactor) + ")") | |
.attr("fill", "#737373") | |
.text(Format((j+1)*cfg.maxValue/cfg.levels)); | |
} | |
series = 0; | |
var axis = g.selectAll(".axis") | |
.data(allAxis) | |
.enter() | |
.append("g") | |
.attr("class", "axis"); | |
axis.append("line") | |
.attr("x1", cfg.w/2) | |
.attr("y1", cfg.h/2) | |
.attr("x2", function(d, i){return getXCoord(i);}) | |
.attr("y2", function(d, i){return getYCoord(i);}) | |
.attr("class", "line") | |
.style("stroke", "grey") | |
.style("stroke-width", "1px"); | |
axis.append("text") | |
.attr("class", "legend") | |
.text(function(d){return d}) | |
.style("font-family", "sans-serif") | |
.style("font-size", "11px") | |
.attr("text-anchor", "middle") | |
.attr("dy", "1.5em") | |
.attr("transform", function(d, i){return "translate(0, -10)";}) | |
.attr("x", function(d, i){return getXCoord(i)-60*Math.sin(i*cfg.radians/total);}) | |
.attr("y", function(d, i){return getYCoord(i)-20*Math.cos(i*cfg.radians/total);}); | |
d.forEach(function(y, x){ | |
dataValues = []; | |
g.selectAll(".nodes") | |
.data(y, function(j, i){ | |
dataValues.push([ | |
cfg.w/2*(1-(parseFloat(Math.max(j.value, 0))/cfg.maxValue)*cfg.factor*Math.sin(i*cfg.radians/total)), | |
cfg.h/2*(1-(parseFloat(Math.max(j.value, 0))/cfg.maxValue)*cfg.factor*Math.cos(i*cfg.radians/total)) | |
]); | |
}); | |
dataValues.push(dataValues[0]); | |
g.selectAll(".area") | |
.data([dataValues]) | |
.enter() | |
.append("polygon") | |
.attr("class", "radar-chart-serie"+series) | |
.style("stroke-width", "2px") | |
.style("stroke", cfg.color(series)) | |
.attr("points",function(d) { | |
var str=""; | |
for(var pti=0;pti<d.length;pti++){ | |
str=str+d[pti][0]+","+d[pti][1]+" "; | |
} | |
return str; | |
}) | |
.style("fill", function(j, i){return cfg.color(series)}) | |
.style("fill-opacity", cfg.opacityArea) | |
.on('mouseover', function (d){ | |
z = "polygon."+d3.select(this).attr("class"); | |
g.selectAll("polygon") | |
.transition(200) | |
.style("fill-opacity", 0.1); | |
g.selectAll(z) | |
.transition(200) | |
.style("fill-opacity", cfg.opacityFill); | |
}) | |
.on('mouseout', function(){ | |
g.selectAll("polygon") | |
.transition(200) | |
.style("fill-opacity", cfg.opacityArea); | |
}); | |
series++; | |
}); | |
series=0; | |
d.forEach(function(y, x){ | |
g.selectAll(".nodes") | |
.data(y).enter() | |
.append("svg:circle") | |
.attr("class", "radar-chart-serie"+series) | |
.attr('r', cfg.radius) | |
.attr("alt", function(j){return Math.max(j.value, 0)}) | |
.attr("cx", function(j, i){ | |
dataValues.push([ | |
cfg.w/2*(1-(parseFloat(Math.max(j.value, 0))/cfg.maxValue)*cfg.factor*Math.sin(i*cfg.radians/total)), | |
cfg.h/2*(1-(parseFloat(Math.max(j.value, 0))/cfg.maxValue)*cfg.factor*Math.cos(i*cfg.radians/total)) | |
]); | |
return cfg.w/2*(1-(Math.max(j.value, 0)/cfg.maxValue)*cfg.factor*Math.sin(i*cfg.radians/total)); | |
}) | |
.attr("cy", function(j, i){ | |
return cfg.h/2*(1-(Math.max(j.value, 0)/cfg.maxValue)*cfg.factor*Math.cos(i*cfg.radians/total)); | |
}) | |
.attr("data-id", function(j){return j.axis}) | |
.style("fill", cfg.color(series)).style("fill-opacity", .9) | |
.on('mouseover', function (d){ | |
newX = parseFloat(d3.select(this).attr('cx')) - 10; | |
newY = parseFloat(d3.select(this).attr('cy')) - 5; | |
tooltip | |
.attr('x', newX) | |
.attr('y', newY) | |
.text(Format(d.value)) | |
.transition(200) | |
.style('opacity', 1); | |
z = "polygon."+d3.select(this).attr("class"); | |
g.selectAll("polygon") | |
.transition(200) | |
.style("fill-opacity", 0.1); | |
g.selectAll(z) | |
.transition(200) | |
.style("fill-opacity", .7); | |
}) | |
.on('mouseout', function(){ | |
tooltip | |
.transition(200) | |
.style('opacity', 0); | |
g.selectAll("polygon") | |
.transition(200) | |
.style("fill-opacity", cfg.opacityArea); | |
}) | |
.append("svg:title") | |
.text(function(j){return Math.max(j.value, 0)}); | |
series++; | |
}); | |
//Tooltip | |
tooltip = g.append('text') | |
.style('opacity', 0) | |
.style('font-family', 'sans-serif') | |
.style('font-size', '13px'); | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment