Commit 566355a9362ddae819f7a11a43c4036a67ccfb01
1 parent
7a14bc18
Exists in
master
Add sorted-carbon-emissions.js
Showing
1 changed file
with
347 additions
and
0 deletions
Show diff stats
... | ... | @@ -0,0 +1,347 @@ |
1 | +// set the dimensions and margins of the graph | |
2 | +let margin = {top: 30, right: 10, bottom: 62, left: 72}, | |
3 | + width = 600 - margin.left - margin.right, | |
4 | + height = 700 - margin.top - margin.bottom; | |
5 | + | |
6 | +function getTicks(maxValue, interval, startValue=0) | |
7 | +{ | |
8 | + let range = []; | |
9 | + for (let i = startValue ; i <= maxValue ; i+=interval) | |
10 | + { | |
11 | + range.push(i); | |
12 | + } | |
13 | + return range; | |
14 | +} | |
15 | + | |
16 | +function getBottomTicks(maxAttendeePercent) { | |
17 | + return getTicks(maxAttendeePercent, 20); | |
18 | +} | |
19 | + | |
20 | +function getLeftTicks(maxemissionsPercent) { | |
21 | + return getTicks(maxemissionsPercent, 20); | |
22 | +} | |
23 | + | |
24 | +function getSliceCollision(x, y, barSettings) | |
25 | +{ | |
26 | + let result = false; | |
27 | + barSettings.forEach((element, index) => { | |
28 | + if(x < element.rightBorder && x > element.leftBorder) | |
29 | + { | |
30 | + let binYDiff = element.topBorder - element.bottomBorder; | |
31 | + let binXDiff = element.rightBorder - element.leftBorder; | |
32 | + let binDiagonalAngle = Math.atan2(binYDiff , binXDiff); | |
33 | + // let binYCenter = (element.topBorder + element.bottomBorder)/2; | |
34 | + // let binXCenter = (element.rightBorder + element.leftBorder)/2; | |
35 | + let mouseXDiff = element.rightBorder - x; | |
36 | + let mouseYDiff = element.topBorder - y; | |
37 | + let mouseToCenterAngle = Math.atan2(mouseYDiff , mouseXDiff); | |
38 | + // console.log(binDiagonalAngle); | |
39 | + // console.log(mouseToCenterAngle); | |
40 | + | |
41 | + if (mouseToCenterAngle < binDiagonalAngle ) | |
42 | + { | |
43 | + barSettings.forEach((element, index) => { | |
44 | + if(y < element.topBorder && y > element.bottomBorder) | |
45 | + { | |
46 | + let lb = barSettings[index].leftBorder; | |
47 | + let rb = barSettings[index].rightBorder; | |
48 | + let tb = barSettings[index].topBorder; | |
49 | + let bb = barSettings[index].bottomBorder; | |
50 | + let binCollisionRatio = (y - bb) / (tb - bb); | |
51 | + let otherAxisLevel = (rb - lb) * binCollisionRatio + lb; | |
52 | + result = { | |
53 | + binDiagonalAngle : binDiagonalAngle, | |
54 | + isXDriving: false, | |
55 | + binIndex: index, | |
56 | + otherAxisLevel: otherAxisLevel | |
57 | + }; | |
58 | + | |
59 | + } | |
60 | + }); | |
61 | + | |
62 | + | |
63 | + } | |
64 | + else | |
65 | + { | |
66 | + let lb = barSettings[index].leftBorder; | |
67 | + let rb = barSettings[index].rightBorder; | |
68 | + let tb = barSettings[index].topBorder; | |
69 | + let bb = barSettings[index].bottomBorder; | |
70 | + let binCollisionRatio = (x - lb) / (rb - lb); | |
71 | + let otherAxisLevel = (tb - bb) * binCollisionRatio + bb; | |
72 | + // console.log(otherAxisLevel); | |
73 | + result = { | |
74 | + binDiagonalAngle : binDiagonalAngle, | |
75 | + isXDriving: true, | |
76 | + binIndex: index, | |
77 | + otherAxisLevel: otherAxisLevel | |
78 | + } | |
79 | + } | |
80 | + } | |
81 | + }); | |
82 | + return result; | |
83 | + | |
84 | +} | |
85 | + | |
86 | +function setupCursorBoxes(event, xScale, yScale, barSettings, vertical, horizontal, box) | |
87 | +{ | |
88 | + // mousex = d3.mouse(this); | |
89 | + var x = d3.pointer(event)[0]; | |
90 | + var y = d3.pointer(event)[1]; | |
91 | + // var y = d3.event.pageY - document.getElementById(<id-of-your-svg>).getBoundingClientRect().y + 10 | |
92 | + // x += 5; | |
93 | + box.style("left", (x + 10) + "px"); | |
94 | + box.style("top", (y - 20) + "px"); | |
95 | + let xInGraph = xScale.invert(x - d3.selectAll("g.y.axis")._groups[0][0].getBoundingClientRect().right); | |
96 | + let yInGraph = yScale.invert(y - margin.top ); | |
97 | + | |
98 | + | |
99 | + | |
100 | + // let sliceId = xScale.invert(x * 1.025 - d3.selectAll("g.yl.axis")._groups[0][0].getBoundingClientRect().x- margin.left)/500 +1; | |
101 | + // let attendeePercent = (getAttendeeOnRight(sliceId, attendeeNumberPerGroup) / attendeeSum * 100.0).toFixed(1); | |
102 | + if(x > width + d3.selectAll("g.y.axis")._groups[0][0].getBoundingClientRect().right || | |
103 | + x < d3.selectAll("g.y.axis")._groups[0][0].getBoundingClientRect().right || | |
104 | + y < margin.top +5 || | |
105 | + y > d3.selectAll("g.x.axis")._groups[0][0].getBoundingClientRect().top) | |
106 | + { | |
107 | + vertical.style("width", 0); | |
108 | + horizontal.style("height", 0); | |
109 | + box.style("display", "none"); | |
110 | + } | |
111 | + else | |
112 | + { | |
113 | + vertical.style("width", "2px"); | |
114 | + horizontal.style("height", "2px"); | |
115 | + box.style("display", "inherit"); | |
116 | + let sliceCollision = getSliceCollision(xInGraph, yInGraph, barSettings); | |
117 | + // console.log(sliceCollision); | |
118 | + if(sliceCollision) | |
119 | + { | |
120 | + if(sliceCollision.isXDriving) | |
121 | + { | |
122 | + | |
123 | + vertical.style("left", x + "px" ); | |
124 | + horizontal.style("top", yScale(sliceCollision.otherAxisLevel) + margin.top + "px"); | |
125 | + | |
126 | + box.text(xInGraph + " " + sliceCollision.otherAxisLevel); | |
127 | + } | |
128 | + else | |
129 | + { | |
130 | + horizontal.style("top", y + "px"); | |
131 | + vertical.style("left", xScale(sliceCollision.otherAxisLevel) + d3.selectAll("g.y.axis")._groups[0][0].getBoundingClientRect().right + "px" ); | |
132 | + | |
133 | + box.text(sliceCollision.otherAxisLevel + " " + yInGraph); | |
134 | + } | |
135 | + } | |
136 | + | |
137 | + } | |
138 | + // box.text(attendeePercent + " % of attendees"); | |
139 | + // console.log(d3.selectAll("g.x.axis")._groups[0][0]); | |
140 | + // console.log(d3.selectAll("g.x.axis")._groups[0][0].getBoundingClientRect().x); | |
141 | +} | |
142 | + | |
143 | +function addVerticalLineAndListenCursor(xScale, yScale, barSettings) { | |
144 | + let vertical = d3.select("#chart-container") | |
145 | + .append("div") | |
146 | + .attr("class", "remove") | |
147 | + .style("position", "absolute") | |
148 | + .style("z-index", "19") | |
149 | + .style("width", "2px") | |
150 | + .style("height", (height) + "px") | |
151 | + .style("top", (10 + margin.top) + "px") | |
152 | + .style("bottom", "30px") | |
153 | + .style("left", "0px") | |
154 | + .style("background", "#000"); | |
155 | + | |
156 | + let horizontal = d3.select("#chart-container") | |
157 | + .append("div") | |
158 | + .attr("class", "remove") | |
159 | + .style("position", "absolute") | |
160 | + .style("z-index", "19") | |
161 | + .style("width", (width) + "px") | |
162 | + .style("height", "2px") | |
163 | + .style("top", "0px") | |
164 | + .style("bottom", "30px") | |
165 | + .style("left", (10 + margin.left) + "px") | |
166 | + .style("background", "#000"); | |
167 | + | |
168 | + let box = d3.select("#chart-container") | |
169 | + .append("div") | |
170 | + .attr("class", "remove") | |
171 | + .style("position", "absolute") | |
172 | + .style("z-index", "20") | |
173 | + .style("width", "150px") | |
174 | + .style("height", "44px") | |
175 | + .style("top", "10px") | |
176 | + .style("bottom", "30px") | |
177 | + .style("left", "0px") | |
178 | + .style("border", "1px solid grey") | |
179 | + .style("background", "rgba(255, 255, 255, 0.7)"); | |
180 | + | |
181 | + d3.select("#chart-container") | |
182 | + .on("mousemove", function(event){ | |
183 | + setupCursorBoxes(event, xScale, yScale, barSettings, vertical, horizontal, box); | |
184 | + }) | |
185 | + .on("mouseover", function(event){ | |
186 | + setupCursorBoxes(event, xScale, yScale, barSettings, vertical, horizontal, box); | |
187 | + | |
188 | + }); | |
189 | +} | |
190 | + | |
191 | + | |
192 | + | |
193 | +document.onreadystatechange = () => { | |
194 | + if (document.readyState === 'complete') { | |
195 | + let maxAttendeePercent = 100; | |
196 | + let maxEmissionsPercent = 100; | |
197 | + let svg = d3.select("#chart-container") | |
198 | + .append("svg") | |
199 | + .attr("id", "graph-svg") | |
200 | + .attr("width", width + margin.left + margin.right) | |
201 | + .attr("height", height + margin.top + margin.bottom) | |
202 | + .append("g") | |
203 | + .attr("transform", | |
204 | + "translate(" + margin.left + "," + margin.top + ")"); | |
205 | + | |
206 | + let emissionsPerGroup = []; | |
207 | + let attendeeNumberPerGroup = []; | |
208 | + let emissionsSum = 0; | |
209 | + let attendeeSum = 0; | |
210 | + let rows = []; | |
211 | + d3.csv("2020-09-30_04_21_19_87a1.csv", function (data) { | |
212 | + let trainAttendee = parseInt(data["train trips_amount"]); | |
213 | + let planeAttendee = parseInt(data["plane trips_amount"]); | |
214 | + if(trainAttendee === 0 && planeAttendee === 0) | |
215 | + { | |
216 | + return; | |
217 | + } | |
218 | + let attendeeNumber = trainAttendee + planeAttendee; | |
219 | + let distance_km = data.distance_km / attendeeNumber; | |
220 | + let co2_kg = parseFloat(data.co2_kg); | |
221 | + if (co2_kg === "NaN" || distance_km === "NaN") | |
222 | + { | |
223 | + return; | |
224 | + } | |
225 | + rows.push(data); | |
226 | + }).then((() => { | |
227 | + rows.forEach((element, index) => { | |
228 | + let trainAttendee = parseInt(element["train trips_amount"]); | |
229 | + let planeAttendee = parseInt(element["plane trips_amount"]); | |
230 | + let attendeeNumber = trainAttendee + planeAttendee; | |
231 | + element["emissions_per_capita"] = parseFloat(element.co2_kg) / attendeeNumber; | |
232 | + }); | |
233 | + | |
234 | + rows.sort(function (a, b) { | |
235 | + if (a.emissions_per_capita > b.emissions_per_capita) { | |
236 | + return 1; | |
237 | + } | |
238 | + if (b.emissions_per_capita > a.emissions_per_capita) { | |
239 | + return -1; | |
240 | + } | |
241 | + return 0; | |
242 | + }); | |
243 | + console.log(rows); | |
244 | + let dataIndex = 0; | |
245 | + rows.forEach((element, index) => { | |
246 | + let trainAttendee = parseInt(element["train trips_amount"]); | |
247 | + let planeAttendee = parseInt(element["plane trips_amount"]); | |
248 | + let attendeeNumber = trainAttendee + planeAttendee; | |
249 | + let co2_kg = parseFloat(element.co2_kg); | |
250 | + emissionsSum += co2_kg; | |
251 | + attendeeSum += attendeeNumber; | |
252 | + emissionsPerGroup[dataIndex] = emissionsSum; | |
253 | + attendeeNumberPerGroup[dataIndex] = attendeeSum; | |
254 | + dataIndex += 1; | |
255 | + }); | |
256 | + // emissionsPerGroup.forEach((element, index) => { | |
257 | + // maxemissions = Math.max(maxemissions, element); | |
258 | + // maxemissionsPercent = Math.max(maxemissionsPercent, element / emissionsSum * 100.0) | |
259 | + // }); | |
260 | + // maxDistance += 2000; | |
261 | + // console.log(maxDistance); | |
262 | + | |
263 | + //Title | |
264 | + svg.append("text") | |
265 | + .attr("transform", | |
266 | + "translate(" + (70 + margin.left) + ", -12)") | |
267 | + .style("text-anchor", "middle") | |
268 | + .style("font-weight", "bold") | |
269 | + .style("font-size", "130%") | |
270 | + .text("Sorted carbon emissions"); | |
271 | + | |
272 | + // X axis: scale and draw: | |
273 | + let x = d3.scaleLinear() | |
274 | + .domain([0, maxAttendeePercent]) | |
275 | + .range([0, width]); | |
276 | + let xAxis = d3.axisBottom(x) | |
277 | + .tickValues(getBottomTicks(maxAttendeePercent)); | |
278 | + svg.append("g") | |
279 | + .attr("transform", "translate(0," + height + ")") | |
280 | + .attr("class", "x axis") | |
281 | + .call(xAxis); | |
282 | + | |
283 | + // Y axis Left | |
284 | + let y = d3.scaleLinear() | |
285 | + .domain([0, maxEmissionsPercent]) | |
286 | + .range([height, 0]); | |
287 | + let yAxis = d3.axisLeft(y) | |
288 | + .tickValues(getLeftTicks(maxEmissionsPercent)); | |
289 | + svg.append("g") | |
290 | + .attr("class", "y axis") | |
291 | + .call(yAxis); | |
292 | + | |
293 | + svg.append("text") | |
294 | + .attr("transform", | |
295 | + "translate(" + (width/2) + " ," + | |
296 | + (height + margin.top + 12) + ")") | |
297 | + .style("text-anchor", "middle") | |
298 | + .text("% of participants, sorted by per capita emission"); | |
299 | + | |
300 | + svg.append("text") | |
301 | + .attr("transform", "rotate(-90)") | |
302 | + .attr("y", 0 - margin.left) | |
303 | + .attr("x",0 - (height / 2)) | |
304 | + .attr("dy", "1em") | |
305 | + .style("text-anchor", "middle") | |
306 | + .text("% of total emissions"); | |
307 | + | |
308 | + let barSettings = []; | |
309 | + emissionsPerGroup.forEach((element, index) => { | |
310 | + let bottomBorder; | |
311 | + let leftBorder; | |
312 | + if(index === 0) | |
313 | + { | |
314 | + bottomBorder = 0; | |
315 | + leftBorder = 0; | |
316 | + } | |
317 | + else | |
318 | + { | |
319 | + bottomBorder = emissionsPerGroup[index-1] / emissionsSum * 100.0; | |
320 | + leftBorder = attendeeNumberPerGroup[index-1] /attendeeSum * 100.0; | |
321 | + } | |
322 | + barSettings[index]= | |
323 | + { | |
324 | + topBorder : emissionsPerGroup[index] / emissionsSum * 100.0, | |
325 | + bottomBorder: bottomBorder, | |
326 | + leftBorder : leftBorder, | |
327 | + rightBorder : attendeeNumberPerGroup[index] / attendeeSum * 100.0, | |
328 | + }; | |
329 | + // console.log(index); | |
330 | + // console.log(barSettings[index]); | |
331 | + }); | |
332 | + // console.log(barSettings); | |
333 | + svg.selectAll("rect") | |
334 | + .data(barSettings) | |
335 | + .enter() | |
336 | + .append("rect") | |
337 | + .attr("x", 1) | |
338 | + .attr("transform", function(d) { return "translate(" + x(d.leftBorder) + "," + y(d.topBorder) + ")"; }) | |
339 | + .attr("width", function(d) { return x(d.rightBorder) - x(d.leftBorder) -1 ; }) | |
340 | + .attr("height", function(d) { return (y(d.bottomBorder)- y(d.topBorder)); }) | |
341 | + .style("z-index", "500") | |
342 | + .style("fill", "#4444E5"); | |
343 | + addVerticalLineAndListenCursor(x, y, barSettings); | |
344 | + })); | |
345 | + | |
346 | + } | |
347 | +}; | |
0 | 348 | \ No newline at end of file | ... | ... |