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 @@ | @@ -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 | \ No newline at end of file | 348 | \ No newline at end of file |