Commit f0706d956f35903caf32f9edf149cefef8fbbb4b
1 parent
3c21ae9f
Exists in
master
and in
4 other branches
Add color legend
Showing
2 changed files
with
90 additions
and
33 deletions
Show diff stats
app/main/static/css/charges.css
... | ... | @@ -18,6 +18,15 @@ |
18 | 18 | display: inline-block; |
19 | 19 | } |
20 | 20 | |
21 | +text.legend { | |
22 | + font-size: 12px; | |
23 | +} | |
24 | + | |
25 | +rect.legend { | |
26 | + stroke: white; | |
27 | + stroke-width: 0.5pt; | |
28 | +} | |
29 | + | |
21 | 30 | rect.bar { |
22 | 31 | stroke: white; |
23 | 32 | stroke-width: 0.5pt; |
... | ... | @@ -27,7 +36,6 @@ rect.bar:hover { |
27 | 36 | -webkit-filter: brightness(1.5); |
28 | 37 | -moz-filter: brightness(1.5); |
29 | 38 | filter: brightness(1.5); |
30 | - stroke-width: 0; | |
31 | 39 | } |
32 | 40 | |
33 | 41 | .tooltip { | ... | ... |
app/main/static/js/charges.js
1 | -var margin = {top: 60, right: 30, bottom: 130, left: 90}, | |
2 | - width = 1000 - margin.left - margin.right, | |
1 | +const margin = {top: 60, right: 270, bottom: 100, left: 90}, | |
2 | + width = 1200 - margin.left - margin.right, | |
3 | 3 | height = 500 - margin.top - margin.bottom; |
4 | 4 | |
5 | 5 | const tooltip_offset = {dx: 0, dy: 100} |
6 | 6 | |
7 | +const color_scale = d3.scaleOrdinal(d3.schemeCategory10); | |
8 | + | |
9 | +const legend_cell_size = 15; | |
7 | 10 | |
8 | 11 | // TODO: set main scales attr here then domain later |
9 | 12 | // const x = d3.scaleBand() |
... | ... | @@ -14,19 +17,6 @@ const tooltip_offset = {dx: 0, dy: 100} |
14 | 17 | // .range([height, 0]); |
15 | 18 | // |
16 | 19 | |
17 | -var color = d3.scaleOrdinal() | |
18 | - .range(["#4e79a7", | |
19 | - "#f28e2c", | |
20 | - "#e15759", | |
21 | - "#76b7b2", | |
22 | - "#59a14f", | |
23 | - "#edc949", | |
24 | - "#af7aa1", | |
25 | - "#ff9da7", | |
26 | - "#9c755f", | |
27 | - "#bab0ab" | |
28 | - ]); | |
29 | - | |
30 | 20 | function build_chart(div_selector, data_url, project_name, category) { |
31 | 21 | |
32 | 22 | if (category == 'capacity') { |
... | ... | @@ -75,38 +65,99 @@ function build_chart(div_selector, data_url, project_name, category) { |
75 | 65 | .style("top", (e.pageY - tooltip_offset.dy) + "px") |
76 | 66 | } |
77 | 67 | |
68 | + var addlegend = function (color_scale) { | |
69 | + | |
70 | + let reverse_keys = color_scale.domain().reverse(); | |
71 | + | |
72 | + let legend = svg.append('g') | |
73 | + .attr('transform', 'translate(850, -30)'); | |
74 | + | |
75 | + legend.selectAll() | |
76 | + .data(reverse_keys) | |
77 | + .enter().append('rect') | |
78 | + .attr('height', legend_cell_size + 'px') | |
79 | + .attr('width', legend_cell_size + 'px') | |
80 | + .attr('class', 'legend') | |
81 | + .attr('x', 5) | |
82 | + .attr('y', (d, i) => i * legend_cell_size) | |
83 | + .style("fill", d => color_scale(d)); | |
84 | + | |
85 | + legend.selectAll() | |
86 | + .data(reverse_keys) | |
87 | + .enter().append('text') | |
88 | + .attr("transform", (d, i) => "translate(30, " + (i * legend_cell_size + legend_cell_size / 1.6) + ")") | |
89 | + .attr('class', 'legend') | |
90 | + // .style("font-size", "10px") | |
91 | + // .style("font-weight", "bold") | |
92 | + .style("fill", "black") | |
93 | + .text(d => d); | |
94 | + } | |
95 | + | |
78 | 96 | d3.csv(data_url).then(data => { |
79 | - var services = data.columns.slice(1) | |
97 | + // var categories = data.columns.slice(1) | |
80 | 98 | var periods = d3.map(data, d => d.period) |
99 | + var categories_total_charge = {} | |
100 | + | |
101 | + // Get the charge sum for each categories over periods | |
102 | + // That will leave '0' to categories with no charge at all | |
103 | + data.forEach(function (d) { | |
104 | + Object.keys(d).forEach(function (k) { | |
105 | + if (categories_total_charge.hasOwnProperty(k)) { | |
106 | + categories_total_charge[k] += +d[k] | |
107 | + } else { | |
108 | + categories_total_charge[k] = 0 | |
109 | + } | |
110 | + } | |
111 | + ) | |
112 | + }) | |
113 | + | |
114 | + // Now, filter only categories that have some charge | |
115 | + var categories = [] | |
116 | + for (var key in categories_total_charge) { | |
117 | + if (categories_total_charge[key] > 0) { | |
118 | + categories.push(key) | |
119 | + } | |
120 | + } | |
121 | + | |
122 | + // This forme list is the color_scale domain. | |
123 | + // And that allows us to build the legend | |
124 | + color_scale.domain(categories) | |
125 | + addlegend(color_scale) | |
81 | 126 | |
127 | + // Build the stacked data for stacked bars | |
82 | 128 | var stack = d3.stack() |
83 | - .keys(services) | |
129 | + .keys(categories) | |
84 | 130 | // .order(d3.stackOrderNone) |
85 | 131 | // .offset(d3.stackOffsetNone); |
86 | 132 | var stacked_data = stack(data) |
87 | 133 | |
134 | + // Xaxis | |
135 | + // | |
136 | + // x scale from the periods list | |
88 | 137 | const x = d3.scaleBand() |
89 | 138 | .domain(periods) |
90 | 139 | .range([0, width]) |
91 | 140 | .padding(0.2); |
92 | 141 | |
142 | + const xAxis = d3.axisBottom(x) | |
143 | + | |
144 | + // Yaxis | |
145 | + // | |
93 | 146 | // Get the max y to plot |
94 | 147 | y_max = d3.max(stacked_data[stacked_data.length - 1], d => d[1]); |
95 | 148 | if (y_max == 0) { |
96 | 149 | y_max = 100 |
97 | 150 | } |
98 | - // Enhance it by 20% to leave room on the top of the graph | |
151 | + // Enhance it by 10% to leave room on the top of the graph | |
99 | 152 | y_max = y_max * 1.1 |
100 | 153 | |
101 | 154 | const y = d3.scaleLinear() |
102 | 155 | .range([height, 0]) |
103 | 156 | .domain([0, y_max]); |
104 | 157 | |
105 | - // Sur l'axe horizontal, on filtre les dates afficher | |
106 | - const xAxis = d3.axisBottom(x) | |
107 | - // .tickValues(x.domain().filter(d => (d.includes("06/0") || d.includes("21/0")))); | |
108 | - | |
109 | 158 | const yAxis = d3.axisLeft(y) |
159 | + | |
160 | + // Draw Xaxis | |
110 | 161 | svg.append("g") |
111 | 162 | .attr("class", "x axis") |
112 | 163 | .attr("transform", "translate(0," + height + ")") |
... | ... | @@ -117,17 +168,12 @@ function build_chart(div_selector, data_url, project_name, category) { |
117 | 168 | .attr("dy", ".15em") |
118 | 169 | .attr("transform", "rotate(-65)"); |
119 | 170 | |
171 | + // Draw Yaxis | |
120 | 172 | svg.append("g") |
121 | 173 | .attr("class", "y axis") |
122 | 174 | .call(yAxis) |
123 | - // .append("text") | |
124 | - // .attr("fill", "#000") | |
125 | - // .attr("transform", "rotate(-90)") | |
126 | - // .attr("y", 6) | |
127 | - // .attr("dy", "0.91em") | |
128 | - // .style("text-anchor", "end") | |
129 | - // .text("rate"); | |
130 | 175 | |
176 | + // Write Y axis title | |
131 | 177 | svg.append("text") |
132 | 178 | .attr("text-anchor", "end") |
133 | 179 | .attr("transform", "rotate(-90)") |
... | ... | @@ -135,14 +181,15 @@ function build_chart(div_selector, data_url, project_name, category) { |
135 | 181 | .attr("x", -margin.top - 70) |
136 | 182 | .text("Charge (% ETP)") |
137 | 183 | |
184 | + // Write chart Title | |
185 | + // part 1 | |
138 | 186 | svg.append("text") |
139 | 187 | .attr("x", (width / 2)) |
140 | 188 | .attr("y", 0 - (margin.top / 2) - 10) |
141 | 189 | .attr("text-anchor", "middle") |
142 | 190 | .style("font-size", "16px") |
143 | 191 | .text(project_name); |
144 | - | |
145 | - | |
192 | + // part 2 | |
146 | 193 | svg.append("text") |
147 | 194 | .attr("x", (width / 2)) |
148 | 195 | .attr("y", 0 - (margin.top / 2) + 10) |
... | ... | @@ -150,11 +197,13 @@ function build_chart(div_selector, data_url, project_name, category) { |
150 | 197 | .style("font-size", "12px") |
151 | 198 | .text(chart_title); |
152 | 199 | |
200 | + // Draw the stacked bars | |
201 | + // | |
153 | 202 | let groups = svg.selectAll("g.category") |
154 | 203 | .data(stacked_data) |
155 | 204 | .enter() |
156 | 205 | .append("g") |
157 | - .style("fill", (d) => color(d.key)); | |
206 | + .style("fill", (d) => color_scale(d.key)); | |
158 | 207 | |
159 | 208 | let rect = groups.selectAll("rect") |
160 | 209 | .data(d => d) | ... | ... |