Commit 11ab55967280569490066cb5e4d33a59606ac934
1 parent
4ecd3df0
Exists in
master
Add emission-per-distance.js
Showing
1 changed file
with
290 additions
and
0 deletions
Show diff stats
... | ... | @@ -0,0 +1,290 @@ |
1 | +// set the dimensions and margins of the graph | |
2 | +let margin = {top: 30, right: 62, bottom: 62, left: 72}, | |
3 | + width = 960 - margin.left - margin.right, | |
4 | + height = 540 - margin.top - margin.bottom; | |
5 | + | |
6 | +let divId = "chart-container"; | |
7 | + | |
8 | +function getTicks(maxValue, interval, startValue=0) | |
9 | +{ | |
10 | + let range = []; | |
11 | + for (let i = startValue ; i <= maxValue ; i+=interval) | |
12 | + { | |
13 | + range.push(i); | |
14 | + } | |
15 | + return range; | |
16 | +} | |
17 | + | |
18 | +function getBottomTicks(maxDistance) { | |
19 | + let range = getTicks(maxDistance, 2500, 2500); | |
20 | + range.push(500); | |
21 | + return range; | |
22 | +} | |
23 | + | |
24 | +function getLeftTicks(maxEmission) { | |
25 | + return getTicks(maxEmission, Math.floor((maxEmission / 8) / 1000)* 1000) ; | |
26 | +} | |
27 | + | |
28 | +function getRightTicks(maxEmissionPercent) { | |
29 | + return getTicks(maxEmissionPercent, 2); | |
30 | +} | |
31 | + | |
32 | +function getAttendeeOnRight(sliceId, attendeeNumberPerGroup) | |
33 | +{ | |
34 | + let attendeeSum = 0; | |
35 | + let sliceInt = Math.floor(sliceId); | |
36 | + let sliceFloat = sliceId - sliceInt; | |
37 | + for(let i = sliceInt; i < attendeeNumberPerGroup.length; i++) | |
38 | + { | |
39 | + attendeeSum += attendeeNumberPerGroup[i] * (1 - sliceFloat); | |
40 | + sliceFloat = 0; | |
41 | + } | |
42 | + return attendeeSum; | |
43 | + | |
44 | +} | |
45 | + | |
46 | +function setupCursorBoxes(event, attendeeSum, attendeeNumberPerGroup, xScale, box, vertical, rightArea) | |
47 | +{ | |
48 | + // mousex = d3.mouse(this); | |
49 | + var x = d3.pointer(event)[0]; | |
50 | + var y = d3.pointer(event)[1]; | |
51 | + // var y = d3.event.pageY - document.getElementById(<id-of-your-svg>).getBoundingClientRect().y + 10 | |
52 | + // x += 5; | |
53 | + vertical.style("left", x + "px" ); | |
54 | + rightArea.style("left", x + "px" ); | |
55 | + box.style("left", (x + 10) + "px"); | |
56 | + box.style("top", (y - 20) + "px"); | |
57 | + let sliceId = xScale.invert(x - d3.selectAll("g.yl.axis")._groups[0][0].getBoundingClientRect().right)/500; | |
58 | + // let sliceId = xScale.invert(x * 1.025 - d3.selectAll("g.yl.axis")._groups[0][0].getBoundingClientRect().x- margin.left)/500 +1; | |
59 | + let attendeePercent = (getAttendeeOnRight(sliceId, attendeeNumberPerGroup) / attendeeSum * 100.0).toFixed(1); | |
60 | + if(x > d3.selectAll("g.yr.axis")._groups[0][0].getBoundingClientRect().x || | |
61 | + x < d3.selectAll("g.yl.axis")._groups[0][0].getBoundingClientRect().right) | |
62 | + { | |
63 | + rightArea.style("width", 0); | |
64 | + vertical.style("width", 0); | |
65 | + box.style("display", "none"); | |
66 | + } | |
67 | + else | |
68 | + { | |
69 | + rightArea.style("width", d3.selectAll("g.yr.axis")._groups[0][0].getBoundingClientRect().x - x ); | |
70 | + vertical.style("width", "2px"); | |
71 | + box.style("display", "inherit"); | |
72 | + } | |
73 | + box.text(attendeePercent + " % of attendees"); | |
74 | + box.text(attendeePercent + " % of attendees"); | |
75 | + // console.log(d3.selectAll("g.x.axis")._groups[0][0]); | |
76 | + // console.log(d3.selectAll("g.x.axis")._groups[0][0].getBoundingClientRect().x); | |
77 | +} | |
78 | + | |
79 | +function addVerticalLineAndListenCursor(xScale, attendeeNumberPerGroup, attendeeSum) { | |
80 | + let vertical = d3.select("#" + divId) | |
81 | + .append("div") | |
82 | + .attr("class", "remove") | |
83 | + .style("position", "absolute") | |
84 | + .style("z-index", "19") | |
85 | + .style("width", "2px") | |
86 | + .style("height", (height) + "px") | |
87 | + .style("top", (10 + margin.top) + "px") | |
88 | + .style("bottom", "30px") | |
89 | + .style("left", "0px") | |
90 | + .style("background", "#000"); | |
91 | + | |
92 | + let rightArea = d3.select("#" + divId) | |
93 | + .append("div") | |
94 | + .attr("class", "remove") | |
95 | + .style("position", "absolute") | |
96 | + .style("z-index", "-50") | |
97 | + .style("width", "2000px") | |
98 | + .style("height", (height) + "px") | |
99 | + .style("top", (10 + margin.top) + "px") | |
100 | + .style("bottom", "30px") | |
101 | + .style("left", "0px") | |
102 | + .style("background", "rgba(60, 200, 60, 0.3)"); | |
103 | + | |
104 | + | |
105 | + let box = d3.select("#" + divId) | |
106 | + .append("div") | |
107 | + .attr("class", "remove") | |
108 | + .style("position", "absolute") | |
109 | + .style("z-index", "20") | |
110 | + .style("width", "150px") | |
111 | + .style("height", "22px") | |
112 | + .style("top", "10px") | |
113 | + .style("bottom", "30px") | |
114 | + .style("left", "0px") | |
115 | + .style("border", "1px solid grey") | |
116 | + .style("background", "rgba(255, 255, 255, 0.7)"); | |
117 | + d3.select("#" + divId) | |
118 | + .on("mousemove", function(event){ | |
119 | + setupCursorBoxes(event, attendeeSum, attendeeNumberPerGroup, xScale, box, vertical, rightArea); | |
120 | + }) | |
121 | + .on("mouseover", function(event){ | |
122 | + setupCursorBoxes(event, attendeeSum, attendeeNumberPerGroup, xScale, box, vertical, rightArea); | |
123 | + | |
124 | + }); | |
125 | +} | |
126 | + | |
127 | + | |
128 | +document.onreadystatechange = () => { | |
129 | + if (document.readyState === 'complete') { | |
130 | + let maxEmission = 0; | |
131 | + let maxEmissionPercent = 0; | |
132 | + let maxDistance = 0; | |
133 | + let svg = d3.select("#" + divId) | |
134 | + .append("svg") | |
135 | + .attr("id", "graph-svg") | |
136 | + .attr("width", width + margin.left + margin.right) | |
137 | + .attr("height", height + margin.top + margin.bottom) | |
138 | + .append("g") | |
139 | + .attr("transform", | |
140 | + "translate(" + margin.left + "," + margin.top + ")"); | |
141 | + | |
142 | + let emissionPerGroup = []; | |
143 | + let attendeeNumberPerGroup = []; | |
144 | + let emissionSum = 0; | |
145 | + let attendeeSum = 0; | |
146 | + let rows = []; | |
147 | + | |
148 | + | |
149 | + d3.csv("2020-09-30_04_21_19_87a1.csv", function (data) { | |
150 | + let trainAttendee = parseInt(data["train trips_amount"]); | |
151 | + let planeAttendee = parseInt(data["plane trips_amount"]); | |
152 | + if(trainAttendee === 0 && planeAttendee === 0) | |
153 | + { | |
154 | + return; | |
155 | + } | |
156 | + let attendeeNumber = trainAttendee + planeAttendee; | |
157 | + let distance_km = data.distance_km / attendeeNumber; | |
158 | + let co2_kg = parseFloat(data.co2_kg); | |
159 | + if (co2_kg === "NaN" || distance_km/500 > 37 || distance_km === "NaN") | |
160 | + { | |
161 | + return; | |
162 | + } | |
163 | + rows.push(data); | |
164 | + maxDistance = Math.max(maxDistance, distance_km); | |
165 | + emissionSum += co2_kg; | |
166 | + }).then((() => { | |
167 | + for (let i = 0; i <= maxDistance/500; i++) | |
168 | + { | |
169 | + emissionPerGroup[i] = 0; | |
170 | + attendeeNumberPerGroup[i] = 0; | |
171 | + } | |
172 | + rows.forEach((element, index) => { | |
173 | + let trainAttendee = parseInt(element["train trips_amount"]); | |
174 | + let planeAttendee = parseInt(element["plane trips_amount"]); | |
175 | + let attendeeNumber = trainAttendee + planeAttendee; | |
176 | + let distance_km = element.distance_km / attendeeNumber; | |
177 | + let co2_kg = parseFloat(element.co2_kg); | |
178 | + emissionPerGroup[Math.floor(distance_km/500)] += parseFloat(co2_kg); | |
179 | + attendeeNumberPerGroup[Math.floor(distance_km/500)] += attendeeNumber; | |
180 | + attendeeSum += attendeeNumber; | |
181 | + }); | |
182 | + emissionPerGroup.forEach((element, index) => { | |
183 | + maxEmission = Math.max(maxEmission, element); | |
184 | + maxEmissionPercent = Math.max(maxEmissionPercent, element / emissionSum * 100.0) | |
185 | + }); | |
186 | + maxDistance += 2000; | |
187 | + // console.log(maxDistance); | |
188 | + | |
189 | + //Title | |
190 | + svg.append("text") | |
191 | + .attr("transform", | |
192 | + "translate(" + (70 + margin.left) + ", -12)") | |
193 | + .style("text-anchor", "middle") | |
194 | + .style("font-weight", "bold") | |
195 | + .style("font-size", "130%") | |
196 | + .text("Emissions per distance"); | |
197 | + | |
198 | + // X axis: scale and draw: | |
199 | + let x = d3.scaleLinear() | |
200 | + .domain([0, maxDistance]) | |
201 | + .range([0, width]); | |
202 | + let xAxis = d3.axisBottom(x) | |
203 | + .tickValues(getBottomTicks(maxDistance)); | |
204 | + svg.append("g") | |
205 | + .attr("transform", "translate(0," + height + ")") | |
206 | + .attr("class", "x axis") | |
207 | + .call(xAxis); | |
208 | + d3.selectAll("g.x.axis") | |
209 | + .selectAll(".tick") | |
210 | + .filter(function (d) { | |
211 | + return d === 0; | |
212 | + }) | |
213 | + .remove(); | |
214 | + | |
215 | + // Y axis Left | |
216 | + let yl = d3.scaleLinear() | |
217 | + .domain([0, maxEmission]) | |
218 | + .range([height, 0]); | |
219 | + let ylAxis = d3.axisLeft(yl) | |
220 | + .tickValues(getLeftTicks(maxEmission)); | |
221 | + svg.append("g") | |
222 | + .attr("class", "yl axis") | |
223 | + .call(ylAxis); | |
224 | + | |
225 | + // Y axis Right | |
226 | + let yr = d3.scaleLinear() | |
227 | + .domain([0, maxEmissionPercent]) | |
228 | + .range([height, 0]); | |
229 | + let yrAxis = d3.axisRight(yr) | |
230 | + .tickValues(getRightTicks(maxEmissionPercent)); | |
231 | + svg.append("g") | |
232 | + .attr("transform", "translate(" + width + ", 0)") | |
233 | + .attr("class", "yr axis") | |
234 | + .call(yrAxis); | |
235 | + | |
236 | + svg.append("text") | |
237 | + .attr("transform", | |
238 | + "translate(" + (width/2) + " ," + | |
239 | + (height + margin.top + 12) + ")") | |
240 | + .style("text-anchor", "middle") | |
241 | + .text("Distance travelled (km)"); | |
242 | + | |
243 | + svg.append("text") | |
244 | + .attr("transform", | |
245 | + "translate(" + (width) + ", 0), rotate(-90)") | |
246 | + .attr("x",0 - (height / 2)) | |
247 | + .attr("y", 42) | |
248 | + .style("text-anchor", "middle") | |
249 | + .text("Share of emission [%]"); | |
250 | + | |
251 | + svg.append("text") | |
252 | + .attr("transform", "rotate(-90)") | |
253 | + .attr("y", 0 - margin.left) | |
254 | + .attr("x",0 - (height / 2)) | |
255 | + .attr("dy", "1em") | |
256 | + .style("text-anchor", "middle") | |
257 | + .text("Emission (tCO2e)"); | |
258 | + // set the parameters for the histogram | |
259 | + var histogram = d3.histogram() | |
260 | + .domain(x.domain()) // then the domain of the graphic | |
261 | + .thresholds(x.ticks(Math.floor(maxDistance/500))); // then the numbers of bins | |
262 | + | |
263 | + let histolol = histogram(0); | |
264 | + // console.log(histolol); | |
265 | + let barSettings = []; | |
266 | + emissionPerGroup.forEach((element, index) => { | |
267 | + barSettings[index]= | |
268 | + { | |
269 | + height : element, | |
270 | + leftBorder : histolol[index].x0, | |
271 | + rightBorder : histolol[index].x1, | |
272 | + }; | |
273 | + // console.log(index); | |
274 | + // console.log(barSettings[index]); | |
275 | + }); | |
276 | + svg.selectAll("rect") | |
277 | + .data(barSettings) | |
278 | + .enter() | |
279 | + .append("rect") | |
280 | + .attr("x", 1) | |
281 | + .attr("transform", function(d) { return "translate(" + x(d.leftBorder) + "," + yl(d.height) + ")"; }) | |
282 | + .attr("width", function(d) { return x(d.rightBorder) - x(d.leftBorder) -1 ; }) | |
283 | + .attr("height", function(d) { return (height-yl(d.height)); }) | |
284 | + .style("z-index", "500") | |
285 | + .style("fill", "#4444E5"); | |
286 | + addVerticalLineAndListenCursor(x, attendeeNumberPerGroup, attendeeSum); | |
287 | + })); | |
288 | + | |
289 | + } | |
290 | +}; | |
0 | 291 | \ No newline at end of file | ... | ... |