|
//cfg for the radar chart formatting |
|
var cfg = { |
|
w: 675, |
|
h: 675, |
|
maxValue: 10, |
|
levels: 10, |
|
opacityArea: 0.20, |
|
radius:5, |
|
radians: 2 * Math.PI, |
|
factor:1, |
|
factorLegend: 0.85, |
|
ToRight: 5, |
|
duration:200, |
|
ExtraWidthX: 700, |
|
ExtraWidthY: 200, |
|
TranslateX: 100, |
|
TranslateY:60, |
|
}; |
|
|
|
function init(){ |
|
|
|
cfg.maxValue = Math.max(cfg.maxValue, d3.max(d, function(i){return d3.max(i.skills.map(function(o){return o.value;}));})); |
|
var allAxis = (d[0].skills.map(function(i, j){return i.axis;})); |
|
var total = allAxis.length; |
|
var radius = cfg.factor*Math.min(cfg.w/2, cfg.h/2); |
|
d3.select("#chart").select("svg").remove(); |
|
var maxAxisValues = []; |
|
|
|
var g = d3.select("#chart") |
|
.append("svg") |
|
.attr("width", cfg.w+cfg.ExtraWidthX) |
|
.attr("height", cfg.h+cfg.ExtraWidthY) |
|
.append("g") |
|
.attr("transform", "translate(" + cfg.TranslateX + "," + cfg.TranslateY + ")") |
|
.attr("id","chartArea"); |
|
|
|
var tooltip; |
|
|
|
//Web and text labels |
|
for(var j=0; j<cfg.levels; j++){ |
|
|
|
var levelFactor = cfg.factor*radius*((j+1)/cfg.levels); |
|
|
|
//Drawing web |
|
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", "web-"+j) |
|
.style("stroke", "grey") |
|
.style("stroke-opacity", "0.75") |
|
.style("stroke-width", "0.3px") |
|
.attr("transform", "translate(" + (cfg.w/2-levelFactor) + ", " + (cfg.h/2-levelFactor) + ")"); |
|
|
|
//Drawing text labels |
|
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", "skill-value") |
|
.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((j+1)*cfg.maxValue/cfg.levels); |
|
} |
|
|
|
function drawAxis(){ |
|
|
|
var axis = g.selectAll(".axis") |
|
.data(allAxis) |
|
.enter() |
|
.append("g") |
|
.attr("class", "axis") |
|
.attr("id",function(d){return d;}); |
|
|
|
axis.append("line") |
|
.attr("x1", cfg.w/2) |
|
.attr("y1", cfg.h/2) |
|
.attr("x2", function(j, i){ |
|
maxAxisValues[i] = {x:cfg.w/2*(1-cfg.factor*Math.sin(i*cfg.radians/total)), y:0}; |
|
return maxAxisValues[i].x; |
|
}) |
|
.attr("y2", function(j, i){ |
|
maxAxisValues[i].y = cfg.h/2*(1-cfg.factor*Math.cos(i*cfg.radians/total)); |
|
return maxAxisValues[i].y; |
|
}) |
|
.attr("class", "skill-axis").style("stroke", "grey").style("stroke-width", "1px"); |
|
|
|
axis.append("text") |
|
.attr("class", "skill-legend") |
|
.text(function(d){return d;}) |
|
.style("font-family", "sans-serif") |
|
.style("font-size", "11px") |
|
.style("cursor","pointer") |
|
.attr("text-anchor", "middle") |
|
.attr("dy", "1.5em") |
|
.attr("transform", function(d, i){return "translate(0, -10)";}) |
|
.attr("x", function(d, i){ |
|
return cfg.w/2*(1-cfg.factorLegend*Math.sin(i*cfg.radians/total))-80*Math.sin(i*cfg.radians/total); |
|
}) |
|
.attr("y", function(d, i){ |
|
return cfg.h/2*(1-Math.cos(i*cfg.radians/total))-20*Math.cos(i*cfg.radians/total); |
|
}) |
|
.on("click",removeAttribute); |
|
} |
|
drawAxis(); |
|
} //end init(); |
|
init(); |
|
|
|
function removeSkillFromGlobalData(skill){ |
|
for(var i=0; i < d.length; i++){ |
|
for(var j=0; j<d[i].skills.length; j++){ |
|
if(d[i].skills[j].axis===skill){ |
|
d[i].skills.splice(j,1); |
|
} |
|
} |
|
} |
|
} |
|
|
|
function adSkillToGlobalData(skill){ |
|
for(var i=0; i < d.length; i++){ |
|
d[i].skills.push({ |
|
axis: skill, |
|
value: 1 |
|
}); |
|
} |
|
} |
|
|
|
function updateRadarChart(){ |
|
|
|
cfg.maxValue = Math.max(cfg.maxValue, d3.max(d, function(i){return d3.max(i.skills.map(function(o){return o.value;}));})); |
|
var allAxis = (d[0].skills.map(function(i, j){return i.axis;})); |
|
var total = allAxis.length; |
|
var radius = cfg.factor*Math.min(cfg.w/2, cfg.h/2); |
|
var maxAxisValues = []; |
|
|
|
var chart = d3.select('#chartArea'); |
|
|
|
var groups = chart.selectAll(".axis") |
|
.data(allAxis); |
|
|
|
// groups.attr('class', 'axis update'); |
|
|
|
var newGroup = groups.enter() |
|
.append("g") |
|
.attr('class','axis enter') |
|
.attr('id', function(d){ return d;}); |
|
|
|
newGroup.append("line") |
|
.attr("x1", cfg.w/2) |
|
.attr("y1", cfg.h/2) |
|
.attr("x2", function(j, i){ |
|
maxAxisValues[i] = {x:cfg.w/2*(1-cfg.factor*Math.sin(i*cfg.radians/total)), y:0}; |
|
return maxAxisValues[i].x; |
|
}) |
|
.attr("y2", function(j, i){ |
|
maxAxisValues[i].y = cfg.h/2*(1-cfg.factor*Math.cos(i*cfg.radians/total)); |
|
return maxAxisValues[i].y; |
|
}) |
|
.attr("class", "skill-axis") |
|
.style("stroke", "grey") |
|
.style("stroke-width", "1px") |
|
.style("opacity",0); |
|
|
|
newGroup.append("text") |
|
.attr("class", "skill-legend") |
|
.text(function(d){return d;}) |
|
.style("font-family", "sans-serif") |
|
.style("font-size", "11px") |
|
.style("cursor","pointer") |
|
.style("opacity",0) |
|
.attr("text-anchor", "middle") |
|
.attr("dy", "1.5em") |
|
.attr("transform", function(d, i){return "translate(0, -10)";}) |
|
.attr("x", function(d, i){ |
|
return cfg.w/2*(1-cfg.factorLegend*Math.sin(i*cfg.radians/total))-80*Math.sin(i*cfg.radians/total); |
|
}) |
|
.attr("y", function(d, i){ |
|
return cfg.h/2*(1-Math.cos(i*cfg.radians/total))-20*Math.cos(i*cfg.radians/total); |
|
}) |
|
.on("click",removeAttribute); |
|
|
|
var axis = d3.selectAll(".skill-axis") |
|
.data(allAxis,function(d){return d;}); |
|
|
|
|
|
axis.transition().duration(1000) |
|
.attr("x1", cfg.w/2) |
|
.attr("y1", cfg.h/2) |
|
.attr("x2", function(j, i){ |
|
maxAxisValues[i] = {x:cfg.w/2*(1-cfg.factor*Math.sin(i*cfg.radians/total)), y:0}; |
|
return maxAxisValues[i].x; |
|
}) |
|
.attr("y2", function(j, i){ |
|
maxAxisValues[i].y = cfg.h/2*(1-cfg.factor*Math.cos(i*cfg.radians/total)); |
|
return maxAxisValues[i].y; |
|
}) |
|
.style("opacity",1); |
|
|
|
|
|
var labels = d3.selectAll(".skill-legend") |
|
.data(allAxis).transition().duration(1000) |
|
.attr("transform", function(d, i){return "translate(0, -10)";}) |
|
.attr("x", function(d, i){ |
|
return cfg.w/2*(1-cfg.factorLegend*Math.sin(i*cfg.radians/total))-80*Math.sin(i*cfg.radians/total); |
|
}) |
|
.attr("y", function(d, i){ |
|
return cfg.h/2*(1-Math.cos(i*cfg.radians/total))-20*Math.cos(i*cfg.radians/total); |
|
}) |
|
.style("opacity",1); |
|
|
|
// axis.text(function(d){return d;}) |
|
// .attr("transform", function(d, i){return "translate(0, -10)";}) |
|
// .attr("x", function(d, i){ |
|
// return cfg.w/2*(1-cfg.factorLegend*Math.sin(i*cfg.radians/total))-80*Math.sin(i*cfg.radians/total); |
|
// }) |
|
// .attr("y", function(d, i){ |
|
// return cfg.h/2*(1-Math.cos(i*cfg.radians/total))-20*Math.cos(i*cfg.radians/total); |
|
// }) |
|
// .on("click",removeAttribute); |
|
|
|
for(var j=0; j<cfg.levels; j++){ |
|
|
|
var levelFactor = cfg.factor*radius*((j+1)/cfg.levels); |
|
|
|
var web = d3.selectAll(".web-" + j) |
|
.data(allAxis); |
|
|
|
var newLines = web.enter() |
|
.append("line") |
|
.attr("class", "web-"+j) |
|
.style("stroke", "grey") |
|
.style("stroke-width", "0.3px") |
|
.attr("transform", "translate(" + (cfg.w/2-levelFactor) + ", " + (cfg.h/2-levelFactor) + ")") |
|
.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));}); |
|
|
|
web.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("transform", "translate(" + (cfg.w/2-levelFactor) + ", " + (cfg.h/2-levelFactor) + ")"); |
|
|
|
web.exit().remove(); |
|
|
|
} |
|
|
|
// debugger; |
|
groups.exit().transition().duration(500).style("opacity",0).remove(); |
|
} |
|
|
|
// //ToDo:: Instead of fading out, redrawing everything, and fading in here, it would be nice to transition in the new axis & web |
|
// function fadeOut(){ |
|
// d3.selectAll(".web").transition().duration(500).style("opacity",0); |
|
// d3.selectAll(".axis").transition().duration(500).style("opacity",0); |
|
// d3.selectAll(".skill-value").transition().duration(500).style("opacity",0); |
|
// } |
|
|
|
// function fadeIn(){ |
|
// setTimeout(function(){ |
|
// d3.selectAll(".axis").remove(); |
|
// d3.selectAll(".web").remove(); |
|
// d3.selectAll(".skill-value").remove(); |
|
|
|
// init(); |
|
|
|
// d3.selectAll(".web").transition().duration(500).style("opacity",1); |
|
// d3.selectAll(".axis").transition().duration(500).style("opacity",1); |
|
// d3.selectAll(".skill-value").transition().duration(500).style("opacity",1); |
|
|
|
// },500); |
|
// } |
|
|
|
|
|
function removeAttribute(){ |
|
|
|
if(confirm("Remove Skill "+d3.select(this)[0][0].__data__+"?")){ |
|
removeSkillFromGlobalData(d3.select(this)[0][0].__data__); |
|
updateRadarChart(); |
|
// fadeOut(); |
|
// fadeIn(); |
|
} |
|
} |
|
|
|
function addSkill(){ |
|
var skill = prompt("Enter A Skill"); |
|
if(skill.length <1){ |
|
alert("Please enter a valid skill!"); |
|
} |
|
else{ |
|
adSkillToGlobalData(skill); |
|
updateRadarChart(); |
|
// fadeOut(); |
|
// fadeIn(); |
|
} |
|
} |
|
|
|
d3.select(".add-skill").on("click",addSkill); |
|
|
|
|
|
|