Skip to content

Instantly share code, notes, and snippets.

@aaronkyle
Forked from wizicer/README.md
Created March 30, 2018 18:01
Show Gist options
  • Save aaronkyle/e9ead9129e1d544cab75c907bc65b3ce to your computer and use it in GitHub Desktop.
Save aaronkyle/e9ead9129e1d544cab75c907bc65b3ce to your computer and use it in GitHub Desktop.
Sunburst for your skill map

Extract from my work, you can a demo in live Combined sunburst and line chart, currently you must generate the data by yourself, I also uploaded the skills.xlsx file to help generate this file(skillsdata.js).

Features:

  • [TODO]works with data that is in a CSV format (you don't need to pre-generate a hierarchical JSON file, unless your data file is very large)
  • interactive breadcrumb trail helps to emphasize the sequence, so that it is easy for a first-time user to understand what they are seeing

If you want to simply reuse this with your own data, here are some tips for generating the skillsdata.js file:

  • Fill in your data to skills.xlsx
  • Select whole W column and copy
  • Convert to js by using http://js2coffee.org/
  • Save and overwrite to skillsdata.js file

Indeed, I'm greatly inspired by someone else, but now I can't find who he is and where his site located, maybe I'll add his site link here later, if you do, please let me know, thanks!

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Skills sunburst</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<link rel="stylesheet" type="text/css" href="skill.css"/>
</head>
<body>
<div id="skills">
<div id="skillmap">
<div class="skills-wrapper">
<div class="skills-sunburst"></div>
<div class="skills-chart">
<div id="skills-chart-breadcrumb"></div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="skillsdata.js"></script>
<script type="text/javascript" src="skill.js"></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>
body {
font-family: 'Open Sans', sans-serif;
font-size: 12px;
font-weight: 400;
background-color: #fff;
width: 1000px;
height: 700px;
margin-top: 10px;
}
#skillmap .skills-wrapper {
margin-top: 10px;
overflow: auto;
}
#skillmap .skills-sunburst {
float: left;
width: 500px;
margin-left: -80px;
}
#skillmap .skills-sunburst svg {
font: 11px 'Source Sans Pro', sans-serif;
font-weight: 900;
cursor: pointer;
}
#skillmap .skills-chart {
float: right;
font-family: 'Source Sans Pro', sans-serif;
font-size: 15px;
margin: 80px 20px 0 0;
}
#skillmap .skills-chart .breadcumb-text {
font-size: 13px;
font-weight: 700;
}
#skillmap .skills-chart #skills-chart-line {
fill: none;
stroke-width: 3.5px;
}
#skillmap .skills-chart .axis path,
#skillmap .skills-chart .axis line {
fill: none;
}
#skillmap .skills-chart .x-axis path,
#skillmap .skills-chart .y-axis path {
stroke: #444;
stroke-width: 1px;
shape-rendering: geometricPrecision;
}
function initchart() {
var data = {
_proficiency: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
children: null,
value: 0,
key: "",
depth: 1
};
chart.refreshChart(data)
}
function mouseover(data) {
chart.refreshChart(data);
var c = getcrumbpath(data);
i(c);
d3
.selectAll(".skills-sunburst path")
.style("opacity", .3), sunburst
.selectAll("path")
.filter(function (a) { return c.indexOf(a) >= 0 })
.style("opacity", 1)
}
function mouseleave() {
d3
.selectAll("path")
.on("mouseover", null);
d3
.selectAll("path")
.transition()
.duration(1e3)
.style("opacity", 1)
.each("end", function () { d3.select(this).on("mouseover", mouseover) })
}
function getcrumbpath(a) {
for (var temp = [], c = a; c.parent;) temp.unshift(c), c = c.parent;
return temp
}
function initbreadcrumb() {
d3
.select("#skills-chart-breadcrumb")
.append("svg:svg")
.attr("width", 500)
.attr("height", 50)
.attr("class", "trail")
}
function h(a, d3) {
var c = [];
c.push("0,0");
c.push(r.w + ",0");
c.push(r.w + r.t + "," + r.h / 2);
c.push(r.w + "," + r.h);
c.push("0," + r.h);
d3 > 0 && c.push(r.t + "," + r.h / 2);
return c.join(" ");
}
function i(a) {
a[a.length - 1]._color, a.length;
var c = d3
.select("#skills-chart-breadcrumb .trail")
.selectAll("g")
.remove();
c = d3
.select("#skills-chart-breadcrumb .trail")
.selectAll("g")
.data(a, function (a) { return a.key + a.depth });
var d = c.enter().append("svg:g");
d
.append("svg:polygon")
.attr("points", h)
.style("fill", function (a) { return a._color }),
d
.append("svg:text")
.attr("x", r.w / 2 + 2)
.attr("y", r.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.attr("class", "breadcumb-text")
.style("fill", function (a) { return getcolor(d3.rgb(a._color)) < 150 ? "#fff" : "#000" })
.text(function (a) { return a.key }),
c
.attr("transform", function (a, b) { return "translate(" + b * (r.w + r.s) + ", 0)" }),
c.exit().remove(),
d3.select(".trail").style("visibility", "")
}
function getcolor(color) {
return .299 * color.r + .587 * color.g + .114 * color.b
}
function k(a) {
var c = ["#4CC3D9", "#FFC65D", "#7BC8A4", "#93648D", "#404040"],
d = [-.1, -.05, 0];
if (1 == a.depth) {
var e = c[coloralternative % 5];
return coloralternative++, e
}
if (a.depth > 1) {
var f = d[a.value % 3];
return d3.rgb(a.parent._color).brighter(.2 * a.depth + f * a.depth)
}
}
var l;
var chart = function (d3) {
function processdata(data) {
var b = [],
c = 0;
return data._proficiency.forEach(function (a) {
c <= i.length && (b.push({
p: a,
date: i[c]
}), c++)
}), b
}
function c(b, c) {
j.domain(d3.extent(b, function (a) { return a.date }));
k
.domain([0, 100]), cpath
.append("g")
.attr("class", "x-axis axis")
.attr("transform", "translate(0," + h + ")")
.call(bottomtick)
.append("text")
.attr("x", 450)
.attr("y", -8)
.style("text-anchor", "end")
.text("Time"), cpath
.append("g")
.attr("class", "y-axis axis")
.call(lefttick)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".91em")
.style("text-anchor", "end")
.text("Proficiency"), cpath
.append("path")
.datum(b)
.attr("class", "line")
.attr("id", "skills-chart-line")
.attr("d", n)
.attr("stroke", function () { return c._color })
}
function refreshChart(data) {
var e = processdata(data),
f = d3.select("#skills-chart-line");
null === f[0][0]
? c(e, data)
: f
.datum(e)
.attr("d", n)
.attr("stroke", function () { return data._color })
}
var chart = {},
rect = {
top: 20,
right: 20,
bottom: 30,
left: 50
},
g = 500 - rect.left - rect.right,
h = 400 - rect.top - rect.bottom,
i = [1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013],
j = d3.scale.linear().range([0, g]),
k = d3.scale.linear().range([h, 0]),
bottomtick = d3
.svg
.axis()
.scale(j)
.tickValues([1999, 2004, 2009, 2013])
.tickFormat(d3.format(".0f"))
.tickPadding(10)
.tickSize(0)
.orient("bottom"),
lefttick = d3
.svg
.axis()
.scale(k)
.tickSize(0)
.tickPadding(10)
.tickValues([20, 40, 60, 80, 100])
.orient("left"),
n = d3.svg.line().interpolate("basis").x(function (a) {
return j(a.date)
}).y(function (a) {
return k(a.p)
}),
cpath = d3
.select(".skills-chart")
.append("svg")
.attr("width", g + rect.left + rect.right)
.attr("height", h + rect.top + rect.bottom)
.append("g")
.attr("transform", "translate(" + rect.left + "," + rect.top + ")");
chart.refreshChart = refreshChart;
return chart;
}(d3),
width = 580,
height = 580,
rad = Math.min(width, height) / Math.PI - 25,
q = k,
r = {
w: 116,
h: 30,
s: 3,
t: 7
},
sunburst = d3
.select(".skills-sunburst")
.append("svg:svg")
.attr("width", width)
.attr("height", height)
.append("svg:g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
sunburst.append("svg:circle").attr("r", rad).style("opacity", 0);
var t = function (a, b) {
var c = [],
d = a.length;
if (a.length !== b.length) c = a.length > b.length ? a : b;
else for (var e = 0; d > e; e++) {
var f = Math.max(a[e], b[e]) - Math.abs(a[e] - b[e]) / 8;
c.push(f)
}
return c
},
u = function (a) {
if (a instanceof Array) return a;
var b = [];
return $.each(a, function (a, c) {
b = t(u(c), b)
}), b
},
proficiencydata = d3
.layout
.partition()
.sort(null)
.size([2 * Math.PI, rad])
.children(function (a) {
return a.value instanceof Array
? (a._proficiency = a.value, d3.entries([a.value[a.value.length - 1]]))
: (a._proficiency = u(a.value), isNaN(a.value) ? d3.entries(a.value) : null)
})
.value(function (a) { return a.value }),
arc = d3.svg
.arc()
.startAngle(function (a) { return a.x })
.endAngle(function (a) { return a.x + a.dx - .01 / (a.depth + .5) })
.innerRadius(function (a) { return rad / Math.PI * a.depth })
.outerRadius(function (a) { return rad / Math.PI * (a.depth + 1) - 1 });
var coloralternative = 0
initbreadcrumb();
var path = sunburst
.data(d3.entries(skillsdata))
.selectAll("g")
.data(proficiencydata)
.enter()
.append("svg:g")
.attr("display", function (a) { return a.depth ? null : "none" });
path
.append("svg:path")
.attr("d", arc)
.attr("stroke", "#fff")
.attr("fill", function (a) { return a._color = q(a), a._color })
.attr("fill-rule", "evenodd").attr("display", function (a) { return a.children ? null : "none" })
.on("mouseover", mouseover);
path.
append("svg:text")
.attr("transform", function (a) {
var r = 180 * ((a.x + a.dx / 2 - Math.PI / 2) / Math.PI);
return "rotate(" + r + ")"
})
.attr("x", function (a) { return rad / Math.PI * a.depth})
.attr("dx", "6").attr("dy", ".1em").text(function (a) { return a.key })
.attr("display", function (a) { return a.children ? null : "none" })
.on("mouseover", mouseover);
d3
.select(".skills-sunburst")
.on("mouseleave", mouseleave);
l = path.node().__data__.value;
sunburst
.append("circle")
.attr("r", rad / Math.PI)
.attr("opacity", 0);
initchart();
var skillsdata;
skillsdata = {
"Skills": {
"Server & WinForm": {
"Protocol": {
"Propose": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 50, 50, 50],
"USSD": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 60, 50, 40, 30],
"UAP": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 70, 50, 30],
"Socket Raw": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 50, 50, 50, 70, 80]
},
"Optimization": {
"Performance": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 40, 40, 50, 50, 50],
"Distribute": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 40, 50, 50],
"Stability": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 50, 60, 70, 80, 90]
},
"UI": {
"WinForm": [0, 0, 20, 40, 50, 50, 50, 50, 60, 80, 90, 95, 95, 95, 95],
"WPF": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 50],
"GDI": [0, 0, 0, 0, 0, 40, 50, 40, 40, 30, 30, 20, 20, 10, 5],
"DX": [0, 0, 0, 0, 0, 10, 50, 50, 40, 40, 30, 20, 10, 10, 5],
"Flash": [0, 0, 10, 30, 40, 50, 50, 40, 30, 20, 10, 5, 5, 5, 5]
},
"Algorithm": {
"Image Processing": [0, 0, 0, 0, 0, 0, 0, 10, 30, 50, 50, 50, 45, 45, 40],
"Face recognition": [0, 0, 0, 0, 0, 0, 0, 10, 40, 70, 60, 50, 40, 40, 40]
}
},
"Web Development": {
"Server Side": {
"Active Page": {
"ASP": [0, 0, 10, 30, 60, 60, 40, 20, 20, 20, 20, 20, 20, 20, 20],
"ASP.Net": [0, 0, 0, 0, 0, 0, 10, 30, 70, 80, 90, 90, 90, 90, 90]
},
"Node.js": {
"express": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30],
"npm": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 40]
},
"DB": {
"SqlServer": [0, 0, 0, 0, 0, 10, 20, 40, 40, 40, 60, 90, 90, 90, 80],
"Sqlite": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 20],
"Mongo": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30]
},
"Server": {
"IIS": [0, 10, 10, 30, 40, 40, 40, 60, 70, 70, 80, 80, 80, 80, 80],
"Apache": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 40]
},
"API": {
"OAuth2.0": [0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 40, 40, 40, 30, 30],
"WeChat": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30],
"Propose": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 40, 30]
}
},
"Front Side": {
"HTML": [0, 0, 10, 30, 50, 50, 60, 80, 90, 60, 50, 30, 30, 30, 40],
"CSS": {
"CSS": [0, 0, 10, 30, 50, 50, 60, 80, 85, 60, 50, 30, 20, 20, 30],
"LESS": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30],
"Responsive": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20]
},
"JSFramework": {
"jQuery": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 30, 30, 25, 30],
"ExtJs": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30],
"BackboneJs": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10],
"D3.js": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20],
"Rapheal": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20],
"kinetic": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20]
},
"Template": {
"Jade": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20],
"Razor": [0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 50, 60, 80, 80, 80]
}
}
},
"Language": {
"Dotnet": {
"C#": [0, 0, 0, 0, 0, 0, 20, 50, 70, 80, 90, 95, 95, 95, 95],
"Xaml": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 50],
"F#": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 30, 30]
},
"Script": {
"Javascript": [0, 0, 10, 30, 50, 50, 60, 60, 60, 80, 80, 60, 50, 50, 60],
"VBScript": [0, 0, 0, 0, 20, 20, 30, 30, 40, 30, 20, 20, 20, 20, 20],
"CoffeeScript": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 50],
"Python": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20]
},
"Java": {
"Android": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30]
},
"Embedded": {
"X86 Asm": [0, 0, 0, 0, 0, 0, 0, 0, 10, 40, 40, 30, 25, 20, 20],
"C": [0, 0, 0, 0, 0, 0, 0, 20, 25, 30, 50, 50, 45, 40, 40]
},
"Other": {
"T-SQL": [0, 0, 0, 0, 0, 10, 20, 40, 40, 40, 60, 90, 90, 90, 80],
"(E)BNF": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30],
"XSD/XSLT": [0, 0, 0, 0, 0, 0, 20, 40, 40, 40, 40, 40, 50, 50, 60],
"QB": [40, 50, 40, 30, 20, 10, 5, 5, 5, 5, 5, 5, 5, 5, 5],
"VB": [0, 10, 20, 50, 60, 80, 90, 80, 70, 60, 60, 60, 60, 50, 50],
"Pascal": [0, 0, 0, 30, 50, 60, 40, 20, 10, 5, 5, 5, 5, 5, 5],
"IEC61131": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 40]
}
},
"Other": {
"HW&FW": {
"Protues": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 25, 20, 20, 25],
"Keil": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 25, 20, 20, 25],
"Code Warrior": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 15, 15, 10, 10, 20],
"Protel": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 15, 10, 10, 5],
"Multisim": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 30, 25, 20, 15],
"Lego": [0, 0, 0, 0, 10, 40, 50, 45, 40, 35, 30, 25, 20, 15, 10]
},
"Productivity": {
"Vim": [0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 40, 45, 50, 55],
"AHK": [0, 0, 0, 0, 0, 0, 0, 0, 10, 20, 30, 30, 30, 35, 40],
"Office": [0, 10, 20, 50, 60, 60, 70, 70, 80, 90, 90, 90, 90, 90, 90],
"Photoshop": [0, 10, 20, 30, 35, 40, 45, 50, 60, 65, 60, 60, 55, 60, 60],
"Batch": [40, 45, 50, 50, 50, 60, 65, 70, 75, 75, 80, 80, 80, 80, 80],
"Shell": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 30, 35, 40],
"Linux": [0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10, 40, 50, 55],
"Marked Text": [10, 20, 20, 30, 40, 40, 40, 40, 40, 50, 60, 70, 80, 80, 80]
},
"Project": {
"Agile": [0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 40, 50, 60, 70, 80],
"CI": [0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 50, 70, 70, 80, 80],
"Repos": {
"Git": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 50],
"SVN": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 50, 50, 50],
"Clearcase": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30]
},
"Track": {
"Redmine": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30],
"Trello": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 20]
},
"Wiki": [0, 0, 0, 0, 0, 0, 0, 0, 10, 40, 50, 40, 40, 40, 40],
"Quality Assurance": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 30, 50, 60, 60]
},
"Cloud": {
"IaaS": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 15],
"PaaS": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15, 30, 30],
"SaaS": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 10, 10]
},
"Test": {
"TDD": [0, 0, 0, 0, 0, 0, 0, 0, 5, 10, 5, 5, 15, 5, 5],
"cucumber": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 10, 30],
"Load test": [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 30, 25, 20, 15]
}
}
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment