Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@kneerunjun
Created January 10, 2016 03:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kneerunjun/430c947b69bd9e172d85 to your computer and use it in GitHub Desktop.
Save kneerunjun/430c947b69bd9e172d85 to your computer and use it in GitHub Desktop.
(function () {
var skilllevelsPlot = angular.module("skillmaze").directive("skilllevelsPlot", function () {
return {
restrict: "E",
scope: {
data: "="
},
link: function (scope, elem, attrs) {
var maxWidth = 800;
var maxHeight = 300;
var svg = d3.select(elem[0]).append("svg").attr({
width: maxWidth,
height: maxHeight,
"class": "canvas"
}).style({
"border": "#1abc9c solid 0px"
})
var plot = function () {
d3.select(".canvas").append("g").attr({ "class": "plot" });
}
var unPlot = function () {
d3.select(".canvas .plot").remove();
}
var refresh = function () {
//this is where can draw the graph in the entirety
unPlot();
plot();
var xExtent = [20, maxWidth - 10];
var yExtent = [20, maxHeight - 30];
var skillNested = d3.nest().key(function (d) {
return d.skill.title;
}).entries(scope.data);
var outerPadding = function (padding) {
var maxCategories = 5;
var extent = xExtent[1] - xExtent[0]; //overall length
// (2*op)+[(n-1)*p]*rangeBandWidth + (n*rangeBandWidth) =extent
// for the snug fit condition we are assuming that there is no outer padding
var rangeBandWidth = extent / ((1 + padding) * maxCategories - padding); //this is the snugfit
/*above denotes the rangebandwidth when you have no outerpadding and with internal padding as 'padding' you are able still fit all the bars in the given width*/
/*the padding that is sent in as a parameter is basically the internal padding */
/*1>padding value>0*/
//getting the rangeBand width that snug fits all the categories with no outerpadding
var n = d3.nest().key(function (d) {
return d.skill.title;
}).entries(scope.data).map(function (d) {
return d.key;
}).length;
console.log("the rangebandwidth is: " + rangeBandWidth);
var op = ((extent - (n * rangeBandWidth) - ((n - 1) * padding * rangeBandWidth)) / 2);
var percentOp = op / rangeBandWidth;//calculating the percentage of the outer padding in terms of rangeBandwithd
console.log("outer padding is: " + percentOp);
return percentOp;
}
var xScale = d3.scale.ordinal().domain(skillNested.map(function (d) {
return d.key;
})).rangeRoundBands(xExtent, 0.2, outerPadding(0.2));
var rolledup = d3.nest().key(function (d) {
return d.skill.title;
}).key(function (d) {
return d.level;
}).rollup(function (emps) {
return { count: emps.length }
}).entries(scope.data);
//{key: ".NET", values :[{key: "Sensei", count:1}, {key: "Master", count:2},... ]}
//this is the format of the data that we get from rolling up the data
//to make it more legible, here is what we can do now. .
//mapping the array to a better named array
var mapped = rolledup.map(function (d) {
return {
skill: d.key, rollup: d.values.map(function (e) {
return { level: e.key, count: e.values.count };
})
};
});
//{skill:".NET", rollup:[{level:"Sensei", count:1},{level:"Master", count:2}, ...]}
//this is much better to read
var yScale = d3.scale.linear().domain([
d3.max(mapped.map(function (d) {
return d3.max(d.rollup, function (e) {
return e.count;
});
})),0]).range(yExtent);
//plotting the xaxis
var xAxis = d3.select(".canvas .plot").append("g").attr({ "class": "axes" }).append("g").attr({ "class": "xaxis" })
.call(d3.svg.axis().scale(xScale).orient("bottom").ticks(skillNested.map(function (d) {
return d.key
}).length).tickSize(10));
d3.select(".canvas .plot .axes .xaxis path").style({ "fill": "none", "stroke": "#ecf0f1", "stroke-width": "1px" });
d3.selectAll(".canvas .plot .axes .xaxis .tick line").style({ "fill": "none", "stroke": "#ecf0f1", "stroke-width": "1px" });
d3.selectAll(".canvas .plot .axes .xaxis .tick text").style({ "fill": "#ecf0f1" });
d3.select("g.xaxis").attr("transform", "translate(0," + yExtent[1] + ")");
//plotting the yaxis
var yAxis = d3.select(".canvas .plot g.axes").append("g").attr({ "class": "yaxis" })
.call(d3.svg.axis().scale(yScale).orient("left").ticks(10).tickSize(xExtent[1]-20).tickFormat(d3.format("d")));
d3.select(".canvas .plot .axes .yaxis path").style({ "fill": "none", "stroke": "#ecf0f1", "stroke-width": "1px" });
d3.selectAll(".canvas .plot .axes .yaxis .tick line").style({ "fill": "none", "stroke": "#ecf0f1", "stroke-width": "1px" });
d3.selectAll(".canvas .plot .axes .yaxis .tick text").style({ "fill": "#ecf0f1" });
d3.select("g.yaxis").attr("transform", "translate(" + xExtent[1] + ",0)");
//plotting the rectangles ..
var columns = d3.select(".canvas .plot").append("g").attr("class", "columns");
//lets have groups for each of the skills .. translated by the skill distance on the x axis
var barWidth = 25;
var clrScale = d3.scale.ordinal().domain(d3.nest().key(function (d) {
return d.level;
}).entries(scope.data).map(function (d) {
return d.key;
})).range(["#e74c3c", "#e67e22", "#f1c40f", "#16a085", "#27ae60"]);
var skillGroups = d3.select(".canvas .plot g.columns").selectAll("g").data(mapped).enter().append("g").attr({
"transform": function (d) { return "translate(" + xScale(d.skill) + ",0)"; },
"class": function (d) {
return d.skill;
}
}).selectAll("rect").data(function (d) {
return d.rollup;
}).enter().append("rect").attr({
width: barWidth,
height: 0,
y: yExtent[1],
x: function (d,i) {
return i * barWidth;
},
}).style({
"fill": function (d) {
return clrScale(d.level);
}
})
.transition().duration(1700)
.attr({
height: function (d) { return yExtent[1] - yScale(d.count); },
y: function (d) {
return yScale(d.count);
},
});
}
scope.$watch("data", function (newValue, oldValue) {
refresh();
})
}
}
})
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment