Commit 1d31d3ff0f2b49e150412e3fdee54b2d0347f1e6
1 parent
928351df
Exists in
master
feat: move the center in the equidistant graph
Showing
2 changed files
with
314 additions
and
171 deletions
Show diff stats
flaskr/static/js/plots/emissions-equidistant-map.js
1 | -function draw_emissions_equidistant_map(containerSelector, csvUrl) { | |
1 | +// jQuery-free | |
2 | +function draw_emissions_equidistant_map(containerSelector, worldDataUrl, countriesDataUrl, emissionsDataUrl) { | |
2 | 3 | let margin = {top: 48, right: 88, bottom: 68, left: 98}, |
3 | 4 | width = 960 - margin.left - margin.right, |
4 | 5 | height = 540 - margin.top - margin.bottom; |
5 | - const baseAttendeeCircleRadius = 3*0.62; | |
6 | + const baseAttendeeCircleRadius = 2; | |
6 | 7 | const legendAmount = 5; |
7 | - const baseAttendeeCircleRadiusRatio = 150.0*0.62; | |
8 | + const baseAttendeeCircleRadiusRatio = 10.0; | |
8 | 9 | const baseAttendeeCircleColorRatio = 1500.0; |
9 | 10 | |
11 | + let emissionsData = null; | |
12 | + let worldData = null; | |
10 | 13 | |
11 | 14 | let svg = null; |
12 | - let countriesPath = null; | |
13 | - let geoPath = null; | |
14 | - let mapProjection = null; | |
15 | + let cartaContainer = null; | |
15 | 16 | |
16 | - let coordinatesFromFile = null; | |
17 | - let geoJsonFromFile = null; | |
17 | + let geoPath = d3.geoPath(); | |
18 | + let mapProjection = null; | |
19 | + let center_latitude = 0.0; | |
20 | + let center_longitude = 0.0; | |
18 | 21 | |
19 | - let attendeeAmountPerCountry = []; | |
20 | - let countryCoordinates = []; | |
22 | + // Per city | |
21 | 23 | let maxAttendeeAmount = 0; |
24 | + let maxFootprint = 0; | |
22 | 25 | |
23 | - let rotateForm = document.createElement("select"); | |
26 | + // let coordinatesFromFile = null; | |
27 | + // let geoJsonFromFile = null; | |
24 | 28 | |
25 | - let processGeoJson = function (geojson, firstRead = true) { | |
26 | - geoJsonFromFile = geojson; | |
27 | - countriesPath.selectAll("path") | |
28 | - .data(geojson.features) | |
29 | - .enter() | |
30 | - .append("path") | |
31 | - .attr("d", geoPath) | |
32 | - .style("fill", "#444444AA"); | |
33 | - if(firstRead) | |
34 | - { | |
35 | - // Prepare country data | |
36 | - geojson.features.forEach((element) => { | |
37 | - let countryFound = false; | |
38 | - coordinatesFromFile.forEach((countryCoord) => { | |
39 | - if (countryFound) { | |
40 | - return; | |
41 | - } | |
42 | - if (countryCoord.country === element.properties.iso_a2 || countryCoord.country === element.properties.wb_a2) { | |
43 | - countryCoordinates.push({ | |
44 | - name: element.properties.name_long, | |
45 | - latitude: countryCoord.latitude, | |
46 | - longitude: countryCoord.longitude | |
47 | - }); | |
48 | - countryFound = true; | |
49 | - } | |
50 | - }); | |
51 | - if (!countryFound) { | |
52 | - console.log("missing country:" + element.properties.name_long + " by the alpha2:" + element.properties.iso_a2 + " or " + element.properties.wb_a2); | |
53 | - } | |
54 | - }); | |
55 | - | |
56 | - // Sort country data | |
57 | - function compare( a, b ) { | |
58 | - if ( a.name < b.name ){ | |
59 | - return -1; | |
60 | - } | |
61 | - if ( a.name > b.name ){ | |
62 | - return 1; | |
63 | - } | |
64 | - return 0; | |
65 | - } | |
66 | - countryCoordinates.sort( compare ); | |
67 | - // Create Option Elements | |
68 | - countryCoordinates.forEach((element) =>{ | |
69 | - let option = document.createElement("option"); | |
70 | - option.text = element.name; | |
71 | - option.value = "[ " + -element.longitude + ", " + -element.latitude + "] "; | |
72 | - rotateForm.append(option); | |
73 | - }); | |
74 | - // Read actual data Sample | |
75 | - d3.csv(csvUrl, on_csv_datum) | |
76 | - .then(on_csv_ready); | |
77 | - } | |
78 | - }; | |
29 | + // let attendeeAmountPerCountry = []; | |
30 | + // let countryCoordinates = []; | |
79 | 31 | |
80 | 32 | |
81 | - let processCountryCoords = function (countryCoords) { | |
82 | - coordinatesFromFile = countryCoords; | |
83 | - if (geoJsonFromFile) { | |
84 | - processGeoJson(geoJsonFromFile, false); | |
85 | - on_csv_ready(); | |
86 | - } else { | |
87 | - d3.json('worldmap.geo.json').then(processGeoJson); | |
33 | + // let rotateForm = document.createElement("select"); | |
88 | 34 | |
89 | - } | |
90 | - let selector = containerSelector.slice(1, containerSelector.length); | |
91 | - document.getElementById(selector).insertAdjacentElement("beforeend", rotateForm); | |
92 | - rotateForm.onchange = function (event) { | |
93 | - mapProjection = d3.geoAzimuthalEquidistant().scale(100).rotate(JSON.parse(rotateForm.value)).translate([width/2, height/2]); | |
94 | - geoPath.projection(mapProjection); | |
95 | - // console.log(rotateForm.value); | |
96 | - countriesPath.remove(); | |
97 | - countriesPath = svg.append("g"); | |
98 | - processCountryCoords(coordinatesFromFile, false); | |
99 | - }; | |
100 | - // d3.select("svg").on("mousedown", function(event) { | |
101 | - // console.log(mapProjection.invert(d3.pointer(event))); | |
102 | - // }); | |
103 | - }; | |
35 | + // let processGeoJson = function (geojson, firstRead = true) { | |
36 | + // geoJsonFromFile = geojson; | |
37 | + // cartaContainer.selectAll("path") | |
38 | + // .data(geojson.features) | |
39 | + // .enter() | |
40 | + // .append("path") | |
41 | + // .attr("d", geoPath) | |
42 | + // .style("fill", "#444444AA"); | |
43 | + // if (firstRead) { | |
44 | + // // Prepare country data | |
45 | + // geojson.features.forEach((element) => { | |
46 | + // let countryFound = false; | |
47 | + // coordinatesFromFile.forEach((countryCoord) => { | |
48 | + // if (countryFound) { | |
49 | + // return; | |
50 | + // } | |
51 | + // if (countryCoord.country === element.properties.iso_a2 || countryCoord.country === element.properties.wb_a2) { | |
52 | + // countryCoordinates.push({ | |
53 | + // name: element.properties.name_long, | |
54 | + // latitude: countryCoord.latitude, | |
55 | + // longitude: countryCoord.longitude | |
56 | + // }); | |
57 | + // countryFound = true; | |
58 | + // } | |
59 | + // }); | |
60 | + // if (!countryFound) { | |
61 | + // console.log("missing country:" + element.properties.name_long + " by the alpha2:" + element.properties.iso_a2 + " or " + element.properties.wb_a2); | |
62 | + // } | |
63 | + // }); | |
64 | + // | |
65 | + // // Sort country data | |
66 | + // function compare(a, b) { | |
67 | + // if (a.name < b.name) { | |
68 | + // return -1; | |
69 | + // } | |
70 | + // if (a.name > b.name) { | |
71 | + // return 1; | |
72 | + // } | |
73 | + // return 0; | |
74 | + // } | |
75 | + // | |
76 | + // countryCoordinates.sort(compare); | |
77 | + // // Create Option Elements | |
78 | + // countryCoordinates.forEach((element) => { | |
79 | + // let option = document.createElement("option"); | |
80 | + // option.text = element.name; | |
81 | + // option.value = JSON.stringify([ | |
82 | + // element.latitude, | |
83 | + // element.longitude, | |
84 | + // ]); | |
85 | + // rotateForm.append(option); | |
86 | + // }); | |
87 | + // // Read actual data Sample | |
88 | + // d3.csv(emissionsDataUrl, onEmissionsDatum) | |
89 | + // .then(onEmissionsReady); | |
90 | + // } | |
91 | + // }; | |
104 | 92 | |
105 | 93 | |
106 | - const on_csv_datum = function (datum) { | |
107 | - let trainAttendee = parseInt(datum["train trips_amount"]); | |
108 | - let planeAttendee = parseInt(datum["plane trips_amount"]); | |
109 | - if (trainAttendee === 0 && planeAttendee === 0) { | |
110 | - return; | |
111 | - } | |
112 | - let attendeeAmount = trainAttendee + planeAttendee; | |
113 | - let distance_km = datum.distance_km / attendeeAmount; | |
114 | - let co2_kg = parseFloat(datum.co2_kg); | |
115 | - if (co2_kg === "NaN" || distance_km === "NaN") { | |
116 | - return; | |
117 | - } | |
118 | - let countryFound = false; | |
119 | - let countryName = datum["country"].slice(1, datum["country"].length); | |
120 | - attendeeAmountPerCountry.forEach((element) =>{ | |
121 | - if (element.country === countryName) | |
122 | - { | |
123 | - element.attendeeAmount += attendeeAmount; | |
124 | - maxAttendeeAmount = Math.max(maxAttendeeAmount, element.attendeeAmount); | |
125 | - countryFound = true; | |
126 | - } | |
94 | + // let processCountryCoords = function (countryCoords) { | |
95 | + // coordinatesFromFile = countryCoords; | |
96 | + // if (geoJsonFromFile) { | |
97 | + // processGeoJson(geoJsonFromFile, false); | |
98 | + // onEmissionsReady(); | |
99 | + // } else { | |
100 | + // d3.json(worldDataUrl).then(processGeoJson); | |
101 | + // } | |
102 | + // let selector = containerSelector.slice(1, containerSelector.length); | |
103 | + // document.getElementById(selector).insertAdjacentElement("beforeend", rotateForm); | |
104 | + // rotateForm.onchange = function (event) { | |
105 | + // const [latitude, longitude] = JSON.parse(rotateForm.value); | |
106 | + // recenterOnLatLon(latitude, longitude); | |
107 | + // // console.log(rotateForm.value); | |
108 | + // cartaContainer.remove(); | |
109 | + // cartaContainer = svg.append("g"); | |
110 | + // processCountryCoords(coordinatesFromFile, false); | |
111 | + // }; | |
112 | + // // d3.select("svg").on("mousedown", function(event) { | |
113 | + // // console.log(mapProjection.invert(d3.pointer(event))); | |
114 | + // // }); | |
115 | + // }; | |
127 | 116 | |
128 | - }); | |
129 | - if ( ! countryFound ) { | |
130 | - attendeeAmountPerCountry.push({ | |
131 | - country: countryName, | |
132 | - attendeeAmount : attendeeAmount | |
133 | - }); | |
134 | - maxAttendeeAmount = Math.max(maxAttendeeAmount, attendeeAmount); | |
135 | - } | |
136 | - }; | |
117 | + | |
118 | + // const onEmissionsDatum = function (datum) { | |
119 | + // let trainAttendee = parseInt(datum["train trips_amount"]); | |
120 | + // let planeAttendee = parseInt(datum["plane trips_amount"]); | |
121 | + // if (trainAttendee === 0 && planeAttendee === 0) { | |
122 | + // return; | |
123 | + // } | |
124 | + // let attendeeAmount = trainAttendee + planeAttendee; | |
125 | + // let distance_km = datum.distance_km / attendeeAmount; | |
126 | + // let co2_kg = parseFloat(datum.co2_kg); | |
127 | + // if (co2_kg === "NaN" || distance_km === "NaN") { | |
128 | + // return; | |
129 | + // } | |
130 | + // let countryFound = false; | |
131 | + // let countryName = datum["country"].slice(1, datum["country"].length); | |
132 | + // attendeeAmountPerCountry.forEach((element) => { | |
133 | + // if (element.country === countryName) { | |
134 | + // element.attendeeAmount += attendeeAmount; | |
135 | + // maxAttendeeAmount = Math.max(maxAttendeeAmount, element.attendeeAmount); | |
136 | + // countryFound = true; | |
137 | + // } | |
138 | + // | |
139 | + // }); | |
140 | + // if (!countryFound) { | |
141 | + // attendeeAmountPerCountry.push({ | |
142 | + // country: countryName, | |
143 | + // attendeeAmount: attendeeAmount | |
144 | + // }); | |
145 | + // maxAttendeeAmount = Math.max(maxAttendeeAmount, attendeeAmount); | |
146 | + // } | |
147 | + // }; | |
137 | 148 | |
138 | 149 | |
139 | - const drawCircle = function (x, y, radius, color, className = "legend") | |
140 | - { | |
150 | + const drawCircle = function (x, y, radius, color, className = "circle") { | |
141 | 151 | svg.append("circle") |
142 | 152 | .attr("class", className) |
143 | 153 | .attr("cx", x) |
144 | - .attr("cy", y ) | |
154 | + .attr("cy", y) | |
145 | 155 | .attr("r", radius) |
146 | 156 | .style("fill", color) |
147 | 157 | .style("stroke", "rgba(0,0,0,0.7)") |
... | ... | @@ -149,11 +159,11 @@ function draw_emissions_equidistant_map(containerSelector, csvUrl) { |
149 | 159 | }; |
150 | 160 | |
151 | 161 | |
152 | - const setupLegend = function() { | |
162 | + const setupLegend = function () { | |
153 | 163 | svg.append("rect") |
154 | 164 | .attr("class", "legend") |
155 | 165 | .attr("transform", "translate(" + 2 + "," + 2 + ")") |
156 | - .attr("width", 150) | |
166 | + .attr("width", 155) | |
157 | 167 | .attr("height", legendAmount * 34 + 15) |
158 | 168 | .style("fill", "#EEEEEEFF") |
159 | 169 | .style("stroke", "#000000") |
... | ... | @@ -161,33 +171,32 @@ function draw_emissions_equidistant_map(containerSelector, csvUrl) { |
161 | 171 | svg.append("text") |
162 | 172 | .attr("class", "legend") |
163 | 173 | .attr("transform", |
164 | - "translate(" + 60 + " ," + | |
174 | + "translate(" + 50 + " ," + | |
165 | 175 | (28) + ")") |
166 | 176 | .style("text-anchor", "left") |
167 | 177 | .text((1).toFixed(0) + " attendees"); |
168 | 178 | let x = 10 + 20; |
169 | 179 | let y = 25; |
170 | - let radius = baseAttendeeCircleRadius + (baseAttendeeCircleRadiusRatio/maxAttendeeAmount); | |
171 | - let color = "rgba(" + (-(baseAttendeeCircleColorRatio/maxAttendeeAmount) + 255.0) + | |
172 | - ", " + (-(baseAttendeeCircleColorRatio/maxAttendeeAmount) + 255.0) + | |
180 | + let radius = baseAttendeeCircleRadius + (baseAttendeeCircleRadiusRatio / maxAttendeeAmount); | |
181 | + let color = "rgba(" + (-(baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) + | |
182 | + ", " + (-(baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) + | |
173 | 183 | ", 240, 0.7)"; |
174 | 184 | drawCircle(x, y, radius, color); |
175 | - for (let i = 1; i < legendAmount; i++) | |
176 | - { | |
185 | + for (let i = 1; i < legendAmount; i++) { | |
177 | 186 | svg.append("text") |
178 | 187 | .attr("class", "legend") |
179 | 188 | .attr("transform", |
180 | - "translate(" + 60 + " ," + | |
189 | + "translate(" + 50 + " ," + | |
181 | 190 | (28 + 34 * i) + ")") |
182 | 191 | .style("text-anchor", "left") |
183 | 192 | .text((Math.floor(maxAttendeeAmount * (i / legendAmount))).toFixed(0) + " attendees"); |
184 | 193 | let x = 10 + 20; |
185 | 194 | let y = 25 + 34 * i; |
186 | - let radius = baseAttendeeCircleRadius + Math.sqrt(maxAttendeeAmount*(i/legendAmount))*(baseAttendeeCircleRadiusRatio/maxAttendeeAmount); | |
187 | - let color = "rgba(" + (-Math.sqrt(maxAttendeeAmount*(i/legendAmount))*(baseAttendeeCircleColorRatio/maxAttendeeAmount) + 255.0) + | |
188 | - ", " + (-Math.sqrt(maxAttendeeAmount*(i/legendAmount))*(baseAttendeeCircleColorRatio/maxAttendeeAmount) + 255.0) + | |
195 | + let radius = baseAttendeeCircleRadius + Math.sqrt(maxAttendeeAmount * (i / legendAmount)) * (baseAttendeeCircleRadiusRatio / maxAttendeeAmount); | |
196 | + let color = "rgba(" + (-Math.sqrt(maxAttendeeAmount * (i / legendAmount)) * (baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) + | |
197 | + ", " + (-Math.sqrt(maxAttendeeAmount * (i / legendAmount)) * (baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) + | |
189 | 198 | ", 240, 0.7)"; |
190 | - drawCircle(x, y, radius, color); | |
199 | + drawCircle(x, y, radius, color, "legend"); | |
191 | 200 | } |
192 | 201 | |
193 | 202 | // todo: describe those in the legend |
... | ... | @@ -205,54 +214,176 @@ function draw_emissions_equidistant_map(containerSelector, csvUrl) { |
205 | 214 | }; |
206 | 215 | |
207 | 216 | |
208 | - const on_csv_ready = function () { | |
209 | - svg.selectAll("circle.attendee-dot").remove(); | |
210 | - svg.selectAll("rect.legend").remove(); | |
211 | - svg.selectAll("circle.legend").remove(); | |
212 | - svg.selectAll("text.legend").remove(); | |
213 | - | |
214 | - setupLegend(); | |
215 | - attendeeAmountPerCountry.forEach((element) => | |
216 | - { | |
217 | - countryCoordinates.forEach((coordinate) => { | |
218 | - if (element.country === coordinate.name) | |
219 | - { | |
220 | - let x = mapProjection([coordinate.longitude, coordinate.latitude])[0]; | |
221 | - let y = mapProjection([coordinate.longitude, coordinate.latitude])[1]; | |
222 | - let radius = baseAttendeeCircleRadius + Math.sqrt(element.attendeeAmount)*(baseAttendeeCircleRadiusRatio/maxAttendeeAmount); | |
223 | - let color = "rgba(" + (-Math.sqrt(element.attendeeAmount) * (baseAttendeeCircleColorRatio/maxAttendeeAmount) + 255.0) + | |
224 | - ", " + (-Math.sqrt(element.attendeeAmount) * (baseAttendeeCircleColorRatio/maxAttendeeAmount) + 255.0) + | |
225 | - ", 240, 0.7)"; | |
226 | - | |
227 | - drawCircle(x, y, radius, color, "attendee-dot"); | |
228 | - } | |
229 | - }) | |
217 | + // const onEmissionsReady = function () { | |
218 | + // svg.selectAll("circle.attendee-dot").remove(); | |
219 | + // svg.selectAll("rect.legend").remove(); | |
220 | + // svg.selectAll("circle.legend").remove(); | |
221 | + // svg.selectAll("text.legend").remove(); | |
222 | + // | |
223 | + // setupLegend(); | |
224 | + // attendeeAmountPerCountry.forEach((element) => { | |
225 | + // countryCoordinates.forEach((coordinate) => { | |
226 | + // if (element.country === coordinate.name) { | |
227 | + // let x = mapProjection([coordinate.longitude, coordinate.latitude])[0]; | |
228 | + // let y = mapProjection([coordinate.longitude, coordinate.latitude])[1]; | |
229 | + // let radius = baseAttendeeCircleRadius + Math.sqrt(element.attendeeAmount) * (baseAttendeeCircleRadiusRatio / maxAttendeeAmount); | |
230 | + // let color = "rgba(" + (-Math.sqrt(element.attendeeAmount) * (baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) + | |
231 | + // ", " + (-Math.sqrt(element.attendeeAmount) * (baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) + | |
232 | + // ", 240, 0.7)"; | |
233 | + // | |
234 | + // drawCircle(x, y, radius, color, "attendee-dot"); | |
235 | + // } | |
236 | + // }) | |
237 | + // }); | |
238 | + // svg.append("circle") | |
239 | + // .attr("class", "attendee-dot") | |
240 | + // .attr("cx", width / 2) | |
241 | + // .attr("cy", height / 2) | |
242 | + // .attr("r", 3) | |
243 | + // .style("fill", "rgba(255, 0, 0, 1.0)"); | |
244 | + // }; | |
245 | + | |
246 | + | |
247 | + const crunchEmissionsData = () => { | |
248 | + emissionsData.forEach((datum, idx) => { | |
249 | + let trainAttendeesAmount = parseInt(datum["train trips_amount"]); | |
250 | + let planeAttendeesAmount = parseInt(datum["plane trips_amount"]); | |
251 | + if (trainAttendeesAmount === 0 && planeAttendeesAmount === 0) { | |
252 | + return; | |
253 | + } | |
254 | + let attendeesAmount = trainAttendeesAmount + planeAttendeesAmount; | |
255 | + maxAttendeeAmount = Math.max(maxAttendeeAmount, attendeesAmount); | |
256 | + emissionsData[idx].attendeeAmount = attendeesAmount; | |
257 | + | |
258 | + maxFootprint = Math.max(maxFootprint, datum.co2_kg); | |
230 | 259 | }); |
260 | + }; | |
261 | + | |
262 | + | |
263 | + const redrawCentralCircle = () => { | |
264 | + svg.selectAll("circle.central-dot").remove(); | |
265 | + | |
231 | 266 | svg.append("circle") |
232 | - .attr("class", "attendee-dot") | |
233 | - .attr("cx", width /2) | |
234 | - .attr("cy", height/2) | |
235 | - .attr("r", 3) | |
236 | - .style("fill", "rgba(255, 0, 0, 1.0)"); | |
267 | + .attr("cx", width / 2) | |
268 | + .attr("cy", height / 2) | |
269 | + .attr("r", 2) | |
270 | + .classed("central-dot", true) | |
271 | + .style("fill", "rgba(255, 0, 0, 0.777)"); | |
272 | + }; | |
273 | + | |
274 | + | |
275 | + const redrawDistanceCircles = () => { | |
276 | + // TODO: draw a few circles and a label with the distance for each | |
277 | + // โฆ | |
278 | + // or not. | |
279 | + // We might instead draw the circle under the mouse | |
280 | + }; | |
281 | + | |
282 | + | |
283 | + const redrawAttendees = () => { | |
284 | + svg.selectAll("circle.attendee-dot").remove(); | |
285 | + | |
286 | + emissionsData.forEach((datum) => { | |
287 | + // console.log("Emission datum", datum); | |
288 | + let x = mapProjection([datum.longitude, datum.latitude])[0]; | |
289 | + let y = mapProjection([datum.longitude, datum.latitude])[1]; | |
290 | + let radius = ( | |
291 | + baseAttendeeCircleRadius | |
292 | + + | |
293 | + ( | |
294 | + baseAttendeeCircleRadiusRatio | |
295 | + * | |
296 | + Math.sqrt( | |
297 | + datum.attendeeAmount | |
298 | + / | |
299 | + maxAttendeeAmount | |
300 | + ) | |
301 | + ) | |
302 | + ); | |
303 | + let color = ( | |
304 | + 255.0 | |
305 | + - | |
306 | + ( | |
307 | + baseAttendeeCircleColorRatio | |
308 | + * | |
309 | + Math.sqrt( | |
310 | + datum.co2_kg | |
311 | + / | |
312 | + maxFootprint | |
313 | + ) | |
314 | + ) | |
315 | + ); | |
316 | + drawCircle( | |
317 | + x, y, radius, | |
318 | + `rgba(${color}, ${color}, 240.0, 0.618)`, | |
319 | + "attendee-dot" | |
320 | + ); | |
321 | + }); | |
322 | + }; | |
323 | + | |
324 | + | |
325 | + const redrawWorldMap = () => { | |
326 | + cartaContainer.selectAll("path").remove(); | |
327 | + cartaContainer.selectAll("path") | |
328 | + .data(worldData.features) | |
329 | + .enter() | |
330 | + .append("path") | |
331 | + .attr("d", geoPath) | |
332 | + .style("fill", "#d5d5d5"); | |
333 | + }; | |
334 | + | |
335 | + | |
336 | + const rebuildProjection = () => { | |
337 | + mapProjection = d3.geoAzimuthalEquidistant() | |
338 | + .scale(79.4188) | |
339 | + .rotate([ | |
340 | + // Don't ask me why | |
341 | + -1 * center_longitude, | |
342 | + -1 * center_latitude, | |
343 | + ]) | |
344 | + .translate([width / 2, height / 2]); | |
345 | + geoPath.projection(mapProjection); | |
346 | + }; | |
347 | + | |
348 | + | |
349 | + const recenterOnLatLon = (latitude, longitude) => { | |
350 | + center_latitude = latitude; | |
351 | + center_longitude = longitude; | |
352 | + | |
353 | + rebuildProjection(); | |
354 | + // Draw in order from back to front | |
355 | + redrawWorldMap(); | |
356 | + redrawDistanceCircles(); | |
357 | + redrawAttendees(); | |
358 | + redrawCentralCircle(); | |
359 | + | |
360 | + //setupLegend(); | |
237 | 361 | }; |
238 | 362 | |
239 | 363 | |
240 | 364 | document.addEventListener("DOMContentLoaded", () => { |
241 | - width = Math.max(880, $(containerSelector).parent().width()); | |
365 | + width = document.querySelector(containerSelector).parentElement.offsetWidth; | |
242 | 366 | width = width - margin.left - margin.right; |
243 | 367 | svg = d3.select(containerSelector) |
244 | 368 | .append("svg") |
245 | 369 | .attr("width", width) |
246 | 370 | .attr("height", height); |
247 | - svg.append("circle") | |
248 | - .attr("cx", width /2) | |
249 | - .attr("cy", height/2) | |
250 | - .attr("r", 25) | |
251 | - .style("fill", "rgba(255, 0, 0, 0.7)"); | |
252 | - geoPath = d3.geoPath(); | |
253 | - countriesPath = svg.append("g"); | |
254 | - mapProjection = d3.geoAzimuthalEquidistant().scale(100).rotate([-122, -13]).translate([width/2, height/2]); | |
255 | - geoPath.projection(mapProjection); | |
256 | - d3.csv('countries-coordinates.csv').then(processCountryCoords); | |
371 | + cartaContainer = svg.append("g"); | |
372 | + Promise.all([ | |
373 | + d3.csv(emissionsDataUrl), | |
374 | + d3.json(worldDataUrl), | |
375 | + ]).then((allTheData) => { | |
376 | + [emissionsData, worldData] = allTheData; | |
377 | + crunchEmissionsData(); | |
378 | + recenterOnLatLon( | |
379 | + parseFloat(emissionsData[0].latitude), | |
380 | + parseFloat(emissionsData[0].longitude) | |
381 | + ); | |
382 | + }); | |
383 | + | |
384 | + d3.select(containerSelector+" svg").on("mousedown", function(event) { | |
385 | + const pointerLonLat = mapProjection.invert(d3.pointer(event)); | |
386 | + recenterOnLatLon(pointerLonLat[1], pointerLonLat[0]); | |
387 | + }); | |
257 | 388 | }); |
258 | 389 | } |
259 | 390 | \ No newline at end of file |
... | ... |
flaskr/templates/estimation.html
... | ... | @@ -212,6 +212,8 @@ |
212 | 212 | |
213 | 213 | <hr> |
214 | 214 | |
215 | + <div id="d3viz_emissions_equidistant_map" class="plot-container-noborder"></div> | |
216 | + | |
215 | 217 | <div id="d3viz_travels" class="plot-container-noborder"></div> |
216 | 218 | |
217 | 219 | </div> |
... | ... | @@ -254,6 +256,7 @@ |
254 | 256 | <script src="/static/js/vendor/d3-geo-projection.v2.min.js"></script> |
255 | 257 | <script src="/static/js/plots/utils.js"></script> |
256 | 258 | <script src="/static/js/plots/emissions-per-distance.js"></script> |
259 | +<script src="/static/js/plots/emissions-equidistant-map.js"></script> | |
257 | 260 | <script src="/static/js/plots/sorted-emissions-inequality.js"></script> |
258 | 261 | <script src="/static/js/plots/travel-legs-worldmap.js"></script> |
259 | 262 | |
... | ... | @@ -275,6 +278,15 @@ draw_sorted_emissions_inequality( |
275 | 278 | {% endif %} |
276 | 279 | |
277 | 280 | |
281 | +draw_emissions_equidistant_map( | |
282 | + "#d3viz_emissions_equidistant_map", | |
283 | + {#"/static/public/data/worldmap.geo.json",#} | |
284 | + "/static/public/data/world-earth.geojson", | |
285 | + "/static/public/data/countries-coordinates.csv", | |
286 | + "/estimation/{{ estimation.public_id }}.csv" | |
287 | + {#"/estimation/{{ estimation.public_id }}/trips_to_destination_0.csv"#} | |
288 | +); | |
289 | + | |
278 | 290 | draw_travel_legs_worldmap( |
279 | 291 | "#d3viz_travels", |
280 | 292 | "/static/public/data/world-earth.geojson", |
... | ... |