Commit 289db173f905e370cf9396476fdcd0d473048c33

Authored by Antoine Goutenoir
1 parent e0905f97
Exists in master

feat: add the distance circle under the pointer

Co-authored by: @Adrenesis
flaskr/static/js/plots/emissions-equidistant-map.js
1 1 // jQuery-free
2 2 function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissionsDataUrl) {
  3 + const EARTH_RADIUS = 6371000; // meters
  4 +
3 5 let margin = {top: 48, right: 88, bottom: 68, left: 98},
4 6 width = 960 - margin.left - margin.right,
5 7 height = 540 - margin.top - margin.bottom;
... ... @@ -13,6 +15,7 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
13 15  
14 16 let svg = null;
15 17 let cartaContainer = null;
  18 + let attendeesLayer = null;
16 19  
17 20 let geoPath = d3.geoPath();
18 21 let mapProjection = null;
... ... @@ -147,8 +150,8 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
147 150 // };
148 151  
149 152  
150   - const drawCircle = function (x, y, radius, color, className = "circle") {
151   - svg.append("circle")
  153 + const drawCircle = function (into, x, y, radius, color, className = "circle") {
  154 + into.append("circle")
152 155 .attr("class", className)
153 156 .attr("cx", x)
154 157 .attr("cy", y)
... ... @@ -181,7 +184,7 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
181 184 let color = "rgba(" + (-(baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) +
182 185 ", " + (-(baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) +
183 186 ", 240, 0.7)";
184   - drawCircle(x, y, radius, color);
  187 + drawCircle(svg, x, y, radius, color);
185 188 for (let i = 1; i < legendAmount; i++) {
186 189 svg.append("text")
187 190 .attr("class", "legend")
... ... @@ -196,7 +199,7 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
196 199 let color = "rgba(" + (-Math.sqrt(maxAttendeeAmount * (i / legendAmount)) * (baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) +
197 200 ", " + (-Math.sqrt(maxAttendeeAmount * (i / legendAmount)) * (baseAttendeeCircleColorRatio / maxAttendeeAmount) + 255.0) +
198 201 ", 240, 0.7)";
199   - drawCircle(x, y, radius, color, "legend");
  202 + drawCircle(svg, x, y, radius, color, "legend");
200 203 }
201 204  
202 205 // todo: describe those in the legend
... ... @@ -281,7 +284,7 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
281 284  
282 285  
283 286 const redrawAttendees = () => {
284   - svg.selectAll("circle.attendee-dot").remove();
  287 + attendeesLayer.selectAll("circle.attendee-dot").remove();
285 288  
286 289 emissionsData.forEach((datum) => {
287 290 // console.log("Emission datum", datum);
... ... @@ -314,6 +317,7 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
314 317 )
315 318 );
316 319 drawCircle(
  320 + attendeesLayer,
317 321 x, y, radius,
318 322 `rgba(${color}, ${color}, 240.0, 0.618)`,
319 323 "attendee-dot"
... ... @@ -323,12 +327,13 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
323 327  
324 328  
325 329 const redrawWorldMap = () => {
326   - cartaContainer.selectAll("path").remove();
  330 + cartaContainer.selectAll("path.world-map").remove();
327 331 cartaContainer.selectAll("path")
328 332 .data(worldData.features)
329 333 .enter()
330 334 .append("path")
331 335 .attr("d", geoPath)
  336 + .classed("world-map", true)
332 337 .style("fill", "#d5d5d5");
333 338 };
334 339  
... ... @@ -360,6 +365,65 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
360 365 //setupLegend();
361 366 };
362 367  
  368 + const distanceCircles = {};
  369 +
  370 + const redrawDistanceCircle = (circle_name, distance_meters) => {
  371 + let distance_tooltip;
  372 + let distance_tooltip_shadow;
  373 + if ( ! distanceCircles.hasOwnProperty(circle_name)) {
  374 + distance_tooltip_shadow = svg
  375 + .append("text")
  376 + .classed("pointer-tooltip-"+circle_name, true)
  377 + .style("pointer-events", "none")
  378 + .style("stroke", "#FFFFFF99")
  379 + .style("stroke-width", "0.2em");
  380 + distance_tooltip = svg
  381 + .append("text")
  382 + .classed("pointer-tooltip-"+circle_name, true)
  383 + .style("pointer-events", "none");
  384 + distanceCircles[circle_name] = {
  385 + 'distance_tooltip': distance_tooltip,
  386 + 'distance_tooltip_shadow': distance_tooltip_shadow,
  387 + };
  388 + } else {
  389 + distance_tooltip = distanceCircles[circle_name]['distance_tooltip'];
  390 + distance_tooltip_shadow = distanceCircles[circle_name]['distance_tooltip_shadow'];
  391 + }
  392 +
  393 + const gCircleRadius = (distance_meters / EARTH_RADIUS) * 360 / Math.TAU;
  394 + const gCircle = d3.geoCircle();
  395 + gCircle
  396 + .center([center_longitude, center_latitude])
  397 + .radius(gCircleRadius);
  398 +
  399 + svg.selectAll("path.pointer-circle-"+circle_name).remove();
  400 + svg
  401 + .append("path")
  402 + .attr("d", geoPath(gCircle()))
  403 + .classed("pointer-circle-"+circle_name, true)
  404 + // .style("fill", "#21d51d");
  405 + .style("fill", "#00000000")
  406 + .style("stroke", "#0e5b0c")
  407 + .style("stroke-dasharray", 3);
  408 +
  409 + const tooltip_pos = mapProjection([center_longitude+gCircleRadius, center_latitude]);
  410 + distance_tooltip
  411 + .attr("transform", `translate(${tooltip_pos[0]}, ${tooltip_pos[1]})`)
  412 + .text(
  413 + ((distance_meters*0.001)).toFixed(0)
  414 + +
  415 + "km"
  416 + );
  417 + distance_tooltip_shadow
  418 + .attr("transform", `translate(${tooltip_pos[0]}, ${tooltip_pos[1]})`)
  419 + .text(
  420 + ((distance_meters*0.001)).toFixed(0)
  421 + +
  422 + "km"
  423 + );
  424 +
  425 + };
  426 +
363 427  
364 428 document.addEventListener("DOMContentLoaded", () => {
365 429 console.info("[Emissions Equidistant Map] Startingโ€ฆ");
... ... @@ -369,7 +433,8 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
369 433 .append("svg")
370 434 .attr("width", width)
371 435 .attr("height", height);
372   - cartaContainer = svg.append("g");
  436 + cartaContainer = svg.append("g").classed("carta-layer", true);
  437 + attendeesLayer = svg.append("g").classed("attendees-layer", true);
373 438 Promise.all([
374 439 d3.csv(emissionsDataUrl),
375 440 d3.json(worldDataUrl),
... ... @@ -388,5 +453,19 @@ function draw_emissions_equidistant_map(containerSelector, worldDataUrl, emissio
388 453 const pointerLonLat = mapProjection.invert(d3.pointer(event));
389 454 recenterOnLatLon(pointerLonLat[1], pointerLonLat[0]);
390 455 });
  456 +
  457 + d3.select(containerSelector+" svg").on("mousemove", function(event) {
  458 + if ( ! mapProjection) {
  459 + console.warn("Too fast! Wait a little.");
  460 + return;
  461 + }
  462 + const pointerLonLat = mapProjection.invert(d3.pointer(event));
  463 + const centerLonLat = [center_longitude, center_latitude];
  464 + // Great Circle Distance
  465 + const gcd_radians = d3.geoDistance(pointerLonLat, centerLonLat);
  466 + const gcd_meters = gcd_radians * EARTH_RADIUS;
  467 +
  468 + redrawDistanceCircle("pointer", gcd_meters)
  469 + });
391 470 });
392 471 }
393 472 \ No newline at end of file
... ...
flaskr/static/js/plots/utils.js
1 1 /** POLYFILLS **/
2 2 Math.log10 = Math.log10 || function(x) { return Math.log(x) * Math.LOG10E; };
3 3  
  4 +Math.TAU = Math.TAU || Math.PI * 2;
  5 +
  6 +
4 7 /**
5 8 * Useful for axes' domains on plots.
6 9 * @param value
... ...
flaskr/templates/estimation.html
... ... @@ -267,19 +267,19 @@ var plots_config = {
267 267 };
268 268  
269 269 {% if not estimation.is_many_to_many() %}
270   -/**
  270 +
271 271 draw_emissions_per_distance(
272 272 "#emissions_per_distance_histogram",
273 273 "/estimation/{{ estimation.public_id }}.csv"
274 274 );
275   -**/
  275 +
276 276 draw_sorted_emissions_inequality(
277 277 "#sorted_emissions_inequality",
278 278 "/estimation/{{ estimation.public_id }}.csv"
279 279 );
280 280 {% endif %}
281 281  
282   -/**
  282 +
283 283 draw_emissions_equidistant_map(
284 284 "#d3viz_emissions_equidistant_map",
285 285 {#"/static/public/data/worldmap.geo.json",#}
... ... @@ -288,7 +288,7 @@ draw_emissions_equidistant_map(
288 288 "/estimation/{{ estimation.public_id }}.csv"
289 289 {#"/estimation/{{ estimation.public_id }}/trips_to_destination_0.csv"#}
290 290 );
291   -**/
  291 +
292 292  
293 293 draw_travel_legs_worldmap(
294 294 "#d3viz_travels",
... ...