Skip to content

Instantly share code, notes, and snippets.

@Saigesp Saigesp/.block
Last active Jun 21, 2019

Embed
What would you like to do?
d3js radarchart
07a63b6ff6e854c8792cbd1ad7afd6a6

Radar chart

D3 implementation of radar chart

See the demo and more charts from d3graphs repository

Features: Requires:

  • D3 v4+

Default options:

{
    margin: {top: 40, right: 30, bottom: 50, left: 40},
    key: 'key',
}
class RadarChart {
constructor(selection, data, config = {}) {
let self = this;
this.selection = selection;
this.data = data;
// Graph configuration
this.cfg = {
margin: { top: 40, right: 20, bottom: 40, left: 40 },
keys: [],
gridSteps: 5,
currentkey: false,
title: false,
source: false,
circlesize: 5,
colors: ['#1f77b4','#ff7f0e','#2ca02c','#d62728','#9467bd','#8c564b','#e377c2','#7f7f7f','#bcbd22','#17becf'],
};
Object.keys(config).forEach(function(key) {
if (config[key] instanceof Object && config[key] instanceof Array === false) {
Object.keys(config[key]).forEach(function(sk) {
self.cfg[key][sk] = config[key][sk];
});
} else self.cfg[key] = config[key];
});
this.cfg.width = parseInt(this.selection.node().offsetWidth) - this.cfg.margin.left - this.cfg.margin.right,
this.cfg.height = parseInt(this.selection.node().offsetHeight) - this.cfg.margin.top - this.cfg.margin.bottom;
this.angle = Math.PI * 2 / this.data.length
this.initGraph();
}
calculeMax() {
var self = this;
var max = d3.max(this.data, function(d){
return d3.max(self.cfg.keys, function(k){
return d[k];
})
})
// Calcule scales
this.vScale = d3.scaleLinear()
.range([0, d3.min([this.cfg.height,this.cfg.width], function(d){ return d})/2])
.domain([0, max]);
// Calcule grid lines
this.innerCircleData = []
for(var i = 1; i<=this.cfg.gridSteps; i++){
this.innerCircleData.push(max*i/this.cfg.gridSteps);
}
}
computeData() {
var self = this;
this.tdata = []
this.cfg.keys.forEach(function(k){
var q = {name: k, values:[]};
self.data.forEach(function(d,i){
q.values.push({
name:k,
column: i,
axis: d.name,
value:d[k]
});
})
q.values.push(q.values[0])
self.tdata.push(q)
})
this.line = d3.line()
.x(function(d) { return self.originCenter()[0] + self.vScale(Math.sin(d.column*self.angle)*d.value) })
.y(function(d) { return self.originCenter()[1] + self.vScale(Math.cos(d.column*self.angle)*d.value) })
}
initGraph() {
var self = this;
this.calculeMax();
this.computeData();
// SVG
this.svg = this.selection.append('svg')
.attr("class", "chart radarchart")
.attr("viewBox", "0 0 " + (this.cfg.width + this.cfg.margin.left + this.cfg.margin.right) + " " + (this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom))
.attr("width", this.cfg.width + this.cfg.margin.left + this.cfg.margin.right)
.attr("height", this.cfg.height + this.cfg.margin.top + this.cfg.margin.bottom);
this.g = this.svg.append("g")
.attr("transform", "translate(" + (self.cfg.margin.left) + "," + (self.cfg.margin.top) + ")");
// GRIDLINES
this.grid = this.g.append("g")
.attr("class", "grid")
this.grid.selectAll('circle')
.data(this.innerCircleData)
.enter().append('circle')
.attr('class', 'circle circle--grid')
.attr('cx', this.cfg.width/2)
.attr('cy', this.cfg.height/2)
.attr('r', function(d){ return self.vScale(d)})
// POINTS GROUPS
this.points = this.g.append('g')
.attr('class', 'points')
this.tdata.forEach(function(t,u){
// Subgroup
t.series = self.points.append('g')
.attr('class', 'points--'+t.name)
// Area
t.series.append("path")
.datum(t.values)
.attr("class", "area area--"+t.name)
.attr('stroke', self.cfg.colors[u])
.attr("d", self.line);
// Points
t.series.selectAll('circle')
.data(t.values)
.enter().append('circle')
.attr('cx', function(d){
return self.originCenter()[0] + self.vScale(Math.sin(d.column*self.angle)*d.value)
})
.attr('cy', function(d){
return self.originCenter()[1] + self.vScale(Math.cos(d.column*self.angle)*d.value)
})
.attr('r', self.cfg.circlesize)
.attr('fill', self.cfg.colors[u])
.append('title')
.text(function(d){
return d.axis + ': ' + d.value
})
})
}
originCenter(){
return [this.cfg.width/2,this.cfg.height/2]
}
// Data functions
setData(data) {
this.data = data;
}
getDat() {
return data;
}
}
[{
"summaryid": 419,
"questiongroupid": 55,
"questiongrpdesc": "General questions",
"isdefault": true,
"overallscore": 1.63,
"overallmarketscore": 2.40,
"responsefact": [
{
"reponseid": 1684,
"respondantid": 41,
"scoremasterid": 629,
"score": 2,
"questiongroupid_questionid": 11,
"responseweightid": 1,
"responseweightscore": 1,
"questionmaster":
{
"marketanswer": 2.5
}
},
{
"reponseid": 1685,
"respondantid": 41,
"scoremasterid": 631,
"score": 2,
"questiongroupid_questionid": 12,
"responseweightid": 1,
"responseweightscore": 1,
"questionmaster":
{
"marketanswer": 1.7
}
},
{
"reponseid": 1686,
"respondantid": 41,
"scoremasterid": 634,
"score": 1,
"questiongroupid_questionid": 13,
"responseweightid": 2,
"responseweightscore": 0.9,
"questionmaster":
{
"marketanswer": 3
}
}]
},
{
"summaryid": 420,
"questiongroupid": 1,
"questiongrpdesc": "Cost of finance",
"isdefault": false,
"overallscore": 1.82,
"overallmarketscore": 2.00,
"responsefact": [
{
"reponseid": 1687,
"respondantid": 41,
"scoremasterid": 2,
"score": 3,
"questiongroupid_questionid": 2,
"responseweightid": 2,
"responseweightscore": 0.9,
"questionmaster":
{
"marketanswer": 2
}
},
{
"reponseid": 1688,
"respondantid": 41,
"scoremasterid": 6,
"score": 2,
"questiongroupid_questionid": 3,
"responseweightid": 2,
"responseweightscore": 0.9,
"questionmaster":
{
"marketanswer": 2
}
},
{
"reponseid": 1689,
"respondantid": 41,
"scoremasterid": 10,
"score": 1,
"questiongroupid_questionid": 4,
"responseweightid": 1,
"responseweightscore": 1,
"questionmaster":
{
"marketanswer": 2
}
},
{
"reponseid": 1690,
"respondantid": 41,
"scoremasterid": 16,
"score": 2,
"questiongroupid_questionid": 15,
"responseweightid": 2,
"responseweightscore": 0.9,
"questionmaster":
{
"marketanswer": 2
}
}]
},
{
"summaryid": 421,
"questiongroupid": 53,
"questiongrpdesc": "Test question group",
"isdefault": false,
"overallscore": 1.80,
"overallmarketscore": 2.00,
"responsefact": [
{
"reponseid": 1691,
"respondantid": 41,
"scoremasterid": 16,
"score": 2,
"questiongroupid_questionid": 14,
"responseweightid": 2,
"responseweightscore": 0.9,
"questionmaster":
{
"marketanswer": 2
}
}]
},
{
"summaryid": 422,
"questiongroupid": 2,
"questiongrpdesc": "Audit costs",
"isdefault": false,
"overallscore": 1.17,
"overallmarketscore": 2.00,
"responsefact": [
{
"reponseid": 1692,
"respondantid": 41,
"scoremasterid": 20,
"score": 1,
"questiongroupid_questionid": 6,
"responseweightid": 2,
"responseweightscore": 0.9,
"questionmaster":
{
"marketanswer": 2
}
},
{
"reponseid": 1693,
"respondantid": 41,
"scoremasterid": 25,
"score": 1,
"questiongroupid_questionid": 7,
"responseweightid": 3,
"responseweightscore": 0.8,
"questionmaster":
{
"marketanswer": 2
}
},
{
"reponseid": 1694,
"respondantid": 41,
"scoremasterid": 16,
"score": 2,
"questiongroupid_questionid": 5,
"responseweightid": 2,
"responseweightscore": 0.9,
"questionmaster":
{
"marketanswer": 2
}
}]
}]
<html>
<head>
<meta charset="utf-8">
</head>
<style>
.grid {}
.grid .circle{
fill: transparent;
stroke: grey;
stroke-width: 1px;
}
.chart .area {
fill: transparent;
stroke-width: 3px;
}
</style>
<body>
<div id="chart" style="width: 100%; height: 560px;"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="d3.radarchart.js"></script>
<script>
d3.json('data.json', function(data) {
var keys = ['market', 'client']
data.forEach(function(d){
d.name = d.questiongrpdesc
d.market = d3.sum(d.responsefact, function(r){ return r.questionmaster.marketanswer})
d.client = d3.sum(d.responsefact, function(r){ return r.responseweightscore * r.score})
})
var chart = new RadarChart(d3.select('#chart'), data, {
keys: keys,
colors: ['#9b7ecf', '#ff6d71']
})
})
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.