Skip to content

Instantly share code, notes, and snippets.

@KoGor
Last active March 27, 2018 00:57
Show Gist options
  • Save KoGor/4d0c59bbc150cee5a00415692b84c95a to your computer and use it in GitHub Desktop.
Save KoGor/4d0c59bbc150cee5a00415692b84c95a to your computer and use it in GitHub Desktop.
Double Radial Opposing Stacked Bar Chart
license: gpl-3.0
height: 800

Two radial opposing stacked bar charts as concentric rings.

<!DOCTYPE html>
<meta charset="utf-8">
<svg width="960" height="800" font-family="sans-serif" font-size="10"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var xScaleOffset = Math.PI * 75/180;
var x = d3.scaleBand()
.range([xScaleOffset, 2 * Math.PI + xScaleOffset])
.align(0);
var z = d3.scaleOrdinal()
.range(["#a1d76a", "#91bfdb"]);
var zClasses = ['внешняя сторона', 'внутренняя сторона'];
d3.csv("periods_stat.csv", function(d, i, columns) {
d.left_lane = (-d.left_lane);
d.right_lane = (+d.right_lane);
return d;
}, function(error, data) {
if (error) throw error;
var nestedByDay = d3.nest().key(function(d) { return d.holiday; }).entries(data);
nestedByDay.forEach(function(d) { d.values.columns = data.columns });
var innerRadius = 140,
outerRadius = Math.min(width, height) / 2,
dmz = 100;
var borderRadius = innerRadius + (outerRadius - innerRadius) / 2;
var y1 = d3.scaleLinear()
.range([innerRadius, borderRadius - dmz/2]);
ringChart(nestedByDay[0].values, x, y1, z, g, d3.ticks(0, 40, 4), "");
var y2 = d3.scaleLinear()
.range([borderRadius, outerRadius - dmz/2]);
ringChart(nestedByDay[1].values, x, y2, z, g, d3.ticks(0, 40, 4), "МКАД, аварийность");
function ringChart(data, x, y, z, g, ticks, tittle) {
x.domain(data.map(function(d) { return d.km; }));
y.domain([0, d3.max(data, function(d) { return Math.abs(d.left_lane); }) + d3.max(data, function(d) { return Math.abs(d.right_lane); })]);
z.domain(data.columns.slice(2));
// Accidents
g.append('g')
.selectAll("g")
.data(d3.stack().keys(data.columns.slice(2)).offset(stackOffsetOpposing)(data))
.enter().append("g")
.attr("fill", function(d) { return z(d.key); })
.selectAll("path")
.data(function(d) { return d; })
.enter().append("path")
.attr("d", d3.arc()
.innerRadius(function(d) { return y(d[0]); })
.outerRadius(function(d) { return y(d[1]); })
.startAngle(function(d) { return x(d.data.km); })
.endAngle(function(d) { return x(d.data.km) + x.bandwidth(); })
.padAngle(0.01)
.padRadius(innerRadius));
// Labels for xAxis
var label = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("text-anchor", "middle")
.attr("transform", function(d) { return "rotate(" + ((x(d.km) + x.bandwidth() / 2) * 180 / Math.PI - 90) + ")translate(" + y(0) + ",0)"; });
label.append("line")
.attr("x2", function(d) { return (((d.km % 5) == 0) | (d.km == '1')) ? -7 : -4 })
.attr("stroke", "#000");
label.append("text")
.attr("transform", function(d) { return (x(d.km) + x.bandwidth() / 2 + Math.PI / 2) % (2 * Math.PI) < Math.PI ? "rotate(90)translate(0,16)" : "rotate(-90)translate(0,-9)"; })
.text(function(d) {
var xlabel = (((d.km % 5) == 0) | (d.km == '1')) ? d.km : '';
return xlabel;
});
//yAxis and Mean
var yAxis = g.append("g")
.attr("text-anchor", "middle");
// Max tick must match max y value!
var yTicksValues = ticks;
var borderTick = yTicksValues.slice(-1)[0];
var yTick = yAxis
.selectAll("g")
.data(yTicksValues)
.enter().append("g");
yTick.append("circle")
.attr("fill", "none")
.attr("stroke", "#ccdcea")
.attr("r", y);
// Hallo
yTick.append("text")
.attr("y", function(d) { return -y(d); })
.attr("dy", "0.35em")
.attr("fill", "none")
.attr("stroke", "#fff")
.attr("stroke-width", 5)
.text(function(d, i) {
var format = y.tickFormat(5, "s")((d > borderTick/2) == true ? (borderTick - 10 * i) : 10*i);
return ((d == 0) | (d == borderTick)) ? '' : format;
});
// Text
yTick.append("text")
.attr("y", function(d) { return -y(d); })
.attr("dy", "0.35em")
.text(function(d, i) {
var format = y.tickFormat(5, "s")((d > borderTick/2) == true ? (borderTick - 10 * i) : 10*i);
return ((d == 0) | (d == borderTick)) ? '' : format;
});
yAxis.append("text")
.attr("y", function(d) { return -y(yTicksValues.pop()); })
.attr("dy", "-2em")
.text(tittle);
// Legend
var legend = g.append("g")
.selectAll("g")
.data(zClasses)
.enter().append("g")
.attr("transform", function(d, i) { return "translate(-50," + (i - (zClasses.length - 1) / 2) * 25+ ")"; });
legend.append("circle")
.attr("r", 8)
.attr("fill", z);
legend.append("text")
.attr("x", 15)
.attr("y", 0)
.attr("dy", "0.35em")
.text(function(d) { return d; });
};
});
function stackOffsetOpposing(series, order) {
// Check if no staks (amount of series < 1)
if (!((n = series.length) > 1)) return;
// find max sum
var stackSums = [];
var stackMaxes = [];
for (var i = 0, n = series.length; i < n; i++) {
var stackMax = d3.max(series[i], function(d) { return Math.abs(d[1] - d[0])});
stackMaxes.push(stackMax);
}
var max = d3.sum(stackMaxes);
// Redifining baselines
for (var i, j = 0, d, dy, yp, yn, n, m = series[order[0]].length; j < m; ++j) {
for (yp = 0, yn = max, i = 0; i < n; ++i) {
if ((dy = (d = series[order[i]][j])[1] - d[0]) >= 0) {
//d[0] -bottom; d[1] -top
d[0] = yp, d[1] = yp += dy;
} else if (dy < 0) {
d[1] = yn;
yn += dy;
d[0] = yn;
} else {
d[0] = yp;
}
}
}
}
</script>
km holiday left_lane right_lane
1 0 7 13
2 0 7 9
3 0 16 15
4 0 1 8
5 0 11 11
6 0 2 17
7 0 18 3
8 0 12 20
9 0 20 1
10 0 8 2
11 0 11 7
12 0 13 5
13 0 2 7
14 0 11 14
15 0 3 12
16 0 3 5
17 0 8 3
18 0 2 18
19 0 13 10
20 0 13 17
21 0 13 14
22 0 16 16
23 0 20 4
24 0 20 9
25 0 10 3
26 0 5 17
27 0 5 14
28 0 1 7
29 0 15 15
30 0 9 5
31 0 12 2
32 0 1 7
33 0 16 14
34 0 8 18
35 0 2 16
36 0 6 4
37 0 10 14
38 0 0 19
39 0 18 4
40 0 10 16
41 0 8 1
42 0 9 11
43 0 2 0
44 0 10 8
45 0 0 5
46 0 6 12
47 0 14 14
48 0 9 8
49 0 9 9
50 0 6 4
51 0 2 5
52 0 2 3
53 0 2 20
54 0 1 10
55 0 2 13
56 0 13 3
57 0 7 6
58 0 7 7
59 0 15 10
60 0 1 0
61 0 16 12
62 0 6 13
63 0 2 8
64 0 12 5
65 0 14 10
66 0 4 3
67 0 18 10
68 0 4 4
69 0 4 2
70 0 16 14
71 0 14 20
72 0 13 3
73 0 5 5
74 0 17 0
75 0 2 12
76 0 5 10
77 0 3 13
78 0 16 6
79 0 14 19
80 0 12 2
81 0 2 10
82 0 16 3
83 0 14 6
84 0 5 10
85 0 18 13
86 0 16 19
87 0 18 13
88 0 4 19
89 0 3 6
90 0 12 2
91 0 6 20
92 0 20 18
93 0 18 16
94 0 8 8
95 0 11 3
96 0 12 5
97 0 13 11
98 0 8 13
99 0 17 9
100 0 18 14
101 0 4 7
102 0 11 9
103 0 10 17
104 0 9 12
105 0 7 10
106 0 13 9
107 0 14 1
108 0 11 2
109 0 9 15
1 1 8 0
2 1 2 4
3 1 12 5
4 1 4 3
5 1 17 11
6 1 17 15
7 1 2 17
8 1 13 9
9 1 14 7
10 1 6 16
11 1 16 15
12 1 5 10
13 1 13 15
14 1 15 13
15 1 3 5
16 1 1 3
17 1 5 16
18 1 16 10
19 1 14 14
20 1 14 9
21 1 13 20
22 1 12 8
23 1 18 17
24 1 3 9
25 1 2 5
26 1 6 9
27 1 11 19
28 1 15 14
29 1 0 11
30 1 1 0
31 1 4 9
32 1 8 11
33 1 3 5
34 1 16 14
35 1 13 14
36 1 0 16
37 1 13 8
38 1 15 11
39 1 13 5
40 1 6 15
41 1 1 6
42 1 8 0
43 1 12 4
44 1 14 1
45 1 2 13
46 1 16 9
47 1 15 4
48 1 8 9
49 1 12 20
50 1 9 19
51 1 1 19
52 1 4 20
53 1 1 6
54 1 19 15
55 1 7 8
56 1 3 11
57 1 5 4
58 1 19 7
59 1 19 5
60 1 5 15
61 1 20 8
62 1 3 14
63 1 14 5
64 1 3 14
65 1 19 7
66 1 6 20
67 1 3 10
68 1 12 15
69 1 1 10
70 1 16 15
71 1 18 10
72 1 2 16
73 1 3 15
74 1 10 15
75 1 16 18
76 1 6 8
77 1 6 4
78 1 11 1
79 1 15 17
80 1 18 3
81 1 0 0
82 1 16 16
83 1 1 3
84 1 1 6
85 1 15 11
86 1 9 12
87 1 5 18
88 1 20 15
89 1 7 19
90 1 3 2
91 1 4 9
92 1 7 7
93 1 6 16
94 1 19 14
95 1 10 0
96 1 5 3
97 1 4 14
98 1 13 10
99 1 17 19
100 1 5 3
101 1 8 4
102 1 15 8
103 1 8 20
104 1 12 20
105 1 5 3
106 1 4 18
107 1 19 7
108 1 11 7
109 1 15 19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment